refine power module
This commit is contained in:
1
app.yaml
1
app.yaml
@@ -8,7 +8,6 @@ files:
|
||||
- 4g_download_manager.py
|
||||
- app.yaml
|
||||
- archery_netcore.cpython-311-riscv64-linux-gnu.so
|
||||
- aruco_detector.py
|
||||
- at_client.py
|
||||
- camera_manager.py
|
||||
- cameraParameters.xml
|
||||
|
||||
71
config.py
71
config.py
@@ -65,21 +65,20 @@ LOG_FILE = APP_DIR + "/app.log"
|
||||
BACKUP_BASE = APP_DIR + "/backups"
|
||||
|
||||
# ==================== 硬件配置 ====================
|
||||
# WiFi模块开关(True=有WiFi模块,False=无WiFi模块)
|
||||
HAS_WIFI_MODULE = True # 根据实际硬件情况设置
|
||||
|
||||
# UART配置
|
||||
UART4G_DEVICE = "/dev/ttyS2"
|
||||
UART4G_BAUDRATE = 115200
|
||||
DISTANCE_SERIAL_DEVICE = "/dev/ttyS1"
|
||||
DISTANCE_SERIAL_BAUDRATE = 9600
|
||||
|
||||
# I2C配置(根据WiFi模块开关自动选择)
|
||||
# 无WiFi模块:I2C_BUS_NUM = 1,引脚:P18(I2C1_SCL), P21(I2C1_SDA)
|
||||
# 有WiFi模块:I2C_BUS_NUM = 5,引脚:A15(I2C5_SCL), A27(I2C5_SDA)
|
||||
I2C_BUS_NUM = 5 if HAS_WIFI_MODULE else 1
|
||||
# I2C:板载 WiFi 方案固定 I2C5,引脚 A15(SCL) / A27(SDA),供 INA226 等
|
||||
I2C_BUS_NUM = 5
|
||||
|
||||
INA226_ADDR = 0x40
|
||||
# False=完全不访问 INA226(无电源计量板或未供电时避免 ~2.5s writeto 重试与底层 write failed 日志);量产有芯片时设为 True
|
||||
INA226_ENABLE = True
|
||||
# True=整总线 I2C scan 探测 INA226(在部分平台上极慢,可达 ~90s+);False=仅对 INA226_ADDR 快速探测(writeto 空写)
|
||||
INA226_PROBE_FULL_BUS_SCAN = False
|
||||
REG_CONFIGURATION = 0x00
|
||||
REG_BUS_VOLTAGE = 0x02
|
||||
REG_CURRENT = 0x04 # 电流寄存器
|
||||
@@ -165,7 +164,7 @@ TRIANGLE_EARLY_EXIT_CANDIDATES = 4 # 找到多少个候选就提前停
|
||||
TRIANGLE_ADAPTIVE_BLOCK_SIZES = (11, 21) # 自适应阈值 blockSize 尝试列表;置空 () 可完全关闭 adaptiveThreshold
|
||||
TRIANGLE_MAX_FILTERED_FOR_COMBO = 10 # 参与四点组合评分的最大候选数(超过则截断到最可能的一部分)
|
||||
|
||||
FLASH_LASER_WHILE_SHOOTING = True # 是否在拍摄时闪一下激光(True=闪,False=不闪)
|
||||
FLASH_LASER_WHILE_SHOOTING = False # 是否在拍摄时闪一下激光(True=闪,False=不闪)
|
||||
FLASH_LASER_DURATION_MS = 1000 # 闪一下激光的持续时间(毫秒)
|
||||
|
||||
# ==================== 显示配置 ====================
|
||||
@@ -185,67 +184,17 @@ MAX_BACKUPS = 5
|
||||
LOG_MAX_BYTES = 10 * 1024 * 1024 # 10MB
|
||||
LOG_BACKUP_COUNT = 5
|
||||
|
||||
# ==================== 引脚映射配置 ====================
|
||||
# 无WiFi模块的引脚映射(I2C1)
|
||||
PIN_MAPPINGS_NO_WIFI = {
|
||||
"A18": "UART1_RX",
|
||||
"A19": "UART1_TX",
|
||||
"A29": "UART2_RX",
|
||||
"A28": "UART2_TX",
|
||||
"P18": "I2C1_SCL",
|
||||
"P21": "I2C1_SDA",
|
||||
}
|
||||
|
||||
# 有WiFi模块的引脚映射(I2C5)
|
||||
PIN_MAPPINGS_WITH_WIFI = {
|
||||
# ==================== 引脚映射配置(板载 WiFi,I2C5)====================
|
||||
PIN_MAPPINGS = {
|
||||
"A18": "UART1_RX",
|
||||
"A19": "UART1_TX",
|
||||
"A29": "UART2_RX",
|
||||
"A28": "UART2_TX",
|
||||
"A15": "I2C5_SCL",
|
||||
"A27": "I2C5_SDA",
|
||||
"A24": "GPIOA24", # 电源板的引脚
|
||||
"A24": "GPIOA24", # 电源板关机控制
|
||||
}
|
||||
|
||||
# 根据WiFi模块开关选择引脚映射
|
||||
PIN_MAPPINGS = PIN_MAPPINGS_WITH_WIFI if HAS_WIFI_MODULE else PIN_MAPPINGS_NO_WIFI
|
||||
|
||||
# ==================== ArUco标定配置 ====================
|
||||
USE_ARUCO = False # 是否使用ArUco标定(True=使用ArUco,False=使用传统黄色靶心检测)
|
||||
|
||||
# ArUco标记配置
|
||||
if USE_ARUCO:
|
||||
import cv2
|
||||
ARUCO_DICT_TYPE = cv2.aruco.DICT_4X4_50 # ArUco字典类型
|
||||
ARUCO_MARKER_SIZE_MM = 40 # ArUco标记边长(毫米)
|
||||
ARUCO_MARKER_IDS = [0, 1, 2, 3] # 四个角的ArUco标记ID
|
||||
|
||||
# 靶纸物理尺寸(毫米)
|
||||
TARGET_PAPER_SIZE_MM = 400 # 靶纸边长 400mm x 400mm
|
||||
|
||||
# ArUco标记在靶纸上的中心坐标(毫米,以靶纸中心为原点)
|
||||
# 靶纸坐标系:中心(0,0),X向右,Y向下(图像坐标系)
|
||||
# 四个角位置:(20,20), (20,380), (380,380), (380,20)
|
||||
# 转换为以中心为原点的坐标:
|
||||
# 左上角(0): (-180, -180) -> 实际(20,20)相对于中心(200,200) = (-180,-180)
|
||||
# 右上角(1): (180, -180) -> 实际(380,20)相对于中心 = (180,-180)
|
||||
# 右下角(2): (180, 180) -> 实际(380,380)相对于中心 = (180,180)
|
||||
# 左下角(3): (-180, 180) -> 实际(20,380)相对于中心 = (-180,180)
|
||||
ARUCO_MARKER_POSITIONS_MM = {
|
||||
0: (-180, -180), # 左上角
|
||||
1: (180, -180), # 右上角
|
||||
2: (180, 180), # 右下角
|
||||
3: (-180, 180), # 左下角
|
||||
}
|
||||
|
||||
# 靶心(黄心)在靶纸上的位置(毫米,相对于靶纸中心)
|
||||
# 标准靶纸靶心就在正中心
|
||||
TARGET_CENTER_OFFSET_MM = (0, 0)
|
||||
|
||||
# ArUco检测参数
|
||||
ARUCO_MIN_MARKER_PERIMETER_RATE = 0.03 # 最小标记周长比例(相对于图像)
|
||||
ARUCO_CORNER_REFINEMENT_METHOD = cv2.aruco.CORNER_REFINE_SUBPIX # 角点精修方法
|
||||
|
||||
# ==================== 电源配置 ====================
|
||||
AUTO_POWER_OFF_IN_SECONDS = 10 * 60 # 自动关机时间(秒),0表示不自动关机
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
program exit failed. exit code: 1.
|
||||
|
||||
解决方案:
|
||||
从日志看,就是开始发送登录信息之后就崩溃了。出发了底层的read failed。经过排查,是一定要插上电源板的数据连线,以及电源板要插上电池。这个应该是
|
||||
登录时需要读电源电压数据,
|
||||
从日志看,就是开始发送登录信息之后就崩溃了。出发了底层的read failed。经过排查,是一定要插上电源板的数据连线,以及电源板要插上电池。这个应该是登录时需要读电源电压数据。后面我们已经优化了日志,而且增加了对ina226的试探,但发现ina226不存在的时候,就直接返回电压和电流为0.0。而且,一定要注意,在新配套的电源板和核心板上面,才能正常读到电流和电压。
|
||||
|
||||
3. a)问题描述:202609 批次的拓展版,有一块maixcam的蓝灯常亮,询问maixcam的人,他们觉得应该是卡没有插好。但是拓展版上的激光口挡住了数据卡的出口,
|
||||
没法拔出检查,
|
||||
|
||||
16
main.py
16
main.py
@@ -14,6 +14,7 @@ from maix.peripheral import adc
|
||||
import _thread
|
||||
import os
|
||||
import json
|
||||
import time as wall_time
|
||||
|
||||
# 导入新模块
|
||||
import config
|
||||
@@ -96,12 +97,25 @@ def cmd_str():
|
||||
# 3. 初始化激光模块(串口 + 开机关闭激光防误触发)
|
||||
laser_manager.init()
|
||||
|
||||
# 3. 初始化 INA226 电量监测芯片
|
||||
# 3. 初始化 INA226 电量监测芯片(与后续相机启动之间的耗时,便于定位启动卡顿)
|
||||
_w_boot = wall_time.time()
|
||||
print(f"[BOOT] init_ina226 开始 wall_s={_w_boot:.3f}")
|
||||
init_ina226()
|
||||
print(f"[BOOT] init_ina226 结束 wall +{int(round((wall_time.time() - _w_boot) * 1000))} ms")
|
||||
|
||||
# 4. 初始化显示和相机
|
||||
_w_boot = wall_time.time()
|
||||
print(
|
||||
f"[BOOT] init_camera({getattr(config, 'CAMERA_WIDTH', 640)}x{getattr(config, 'CAMERA_HEIGHT', 480)}) "
|
||||
f"开始 wall_s={_w_boot:.3f}"
|
||||
)
|
||||
camera_manager.init_camera(getattr(config, "CAMERA_WIDTH", 640), getattr(config, "CAMERA_HEIGHT", 480))
|
||||
print(f"[BOOT] init_camera 结束 wall +{int(round((wall_time.time() - _w_boot) * 1000))} ms")
|
||||
|
||||
_w_boot = wall_time.time()
|
||||
print(f"[BOOT] init_display 开始 wall_s={_w_boot:.3f}")
|
||||
camera_manager.init_display()
|
||||
print(f"[BOOT] init_display 结束 wall +{int(round((wall_time.time() - _w_boot) * 1000))} ms")
|
||||
|
||||
# ==================== 第二阶段:软件初始化 ====================
|
||||
|
||||
|
||||
@@ -1984,7 +1984,10 @@ class NetworkManager:
|
||||
elif inner_cmd == 4: # 上报电量
|
||||
voltage = get_bus_voltage()
|
||||
battery_percent = voltage_to_percent(voltage)
|
||||
battery_data = {"battery": battery_percent, "voltage": round(voltage, 3)}
|
||||
battery_data = {
|
||||
"battery": battery_percent,
|
||||
"voltage": round(float(voltage), 3),
|
||||
}
|
||||
self.safe_enqueue(battery_data, 2)
|
||||
self.logger.info(f"电量上报: {battery_percent}%")
|
||||
elif inner_cmd == 5: # OTA 升级
|
||||
|
||||
64
power.py
64
power.py
@@ -6,13 +6,36 @@
|
||||
"""
|
||||
import config
|
||||
from logger_manager import logger_manager
|
||||
from maix import time as maix_time
|
||||
|
||||
|
||||
_INA226_PRESENT = None
|
||||
|
||||
|
||||
def _ina226_ready() -> bool:
|
||||
"""
|
||||
是否允许访问 INA226。
|
||||
|
||||
重要:
|
||||
- 这里刻意不做任何 I2C 探测/读写。
|
||||
- 经验上,在 INA226 未供电/未响应时,I2C 的 readfrom_mem 可能直接触发底层崩溃(SIGSEGV),try/except 无法拦截。
|
||||
- 因此只在开机 init_ina226() 成功后才允许后续读电压/电流。
|
||||
"""
|
||||
return bool(getattr(config, "INA226_ENABLE", True)) and (_INA226_PRESENT is True)
|
||||
|
||||
|
||||
def write_register(reg, value):
|
||||
"""写入INA226寄存器"""
|
||||
from hardware import hardware_manager
|
||||
logger = logger_manager.logger
|
||||
data = [(value >> 8) & 0xFF, value & 0xFF]
|
||||
hardware_manager.bus.writeto_mem(config.INA226_ADDR, reg, bytes(data))
|
||||
# 某些底层驱动在失败时只打印 “write failed” 并返回 -1,而不是抛异常;
|
||||
# 为避免误判“初始化成功”导致后续 readfrom_mem SIGSEGV,这里把失败显式转成异常。
|
||||
ret = hardware_manager.bus.writeto_mem(config.INA226_ADDR, reg, bytes(data))
|
||||
if isinstance(ret, int) and ret < 0:
|
||||
if logger:
|
||||
logger.error(f"[INA226] writeto_mem 失败: addr=0x{config.INA226_ADDR:02X} reg=0x{reg:02X} ret={ret}")
|
||||
raise OSError(ret)
|
||||
|
||||
|
||||
def read_register(reg):
|
||||
@@ -24,14 +47,39 @@ def read_register(reg):
|
||||
|
||||
def init_ina226():
|
||||
"""初始化 INA226 芯片:配置模式 + 校准值"""
|
||||
write_register(config.REG_CONFIGURATION, 0x4527)
|
||||
write_register(config.REG_CALIBRATION, config.CALIBRATION_VALUE)
|
||||
global _INA226_PRESENT
|
||||
logger = logger_manager.logger
|
||||
if not getattr(config, "INA226_ENABLE", True):
|
||||
if logger:
|
||||
logger.info("[INA226] INA226_ENABLE=False,跳过初始化与 I2C 探测")
|
||||
# 显式标记不可用,避免后续误读
|
||||
_INA226_PRESENT = False
|
||||
return False
|
||||
try:
|
||||
# 仅通过“写寄存器成功”来判定可用,避免额外的读操作触发底层崩溃
|
||||
write_register(config.REG_CONFIGURATION, 0x4527)
|
||||
write_register(config.REG_CALIBRATION, config.CALIBRATION_VALUE)
|
||||
_INA226_PRESENT = True
|
||||
return True
|
||||
except Exception as e:
|
||||
_INA226_PRESENT = False
|
||||
if logger:
|
||||
logger.error(f"[INA226] 初始化失败:{e}")
|
||||
return False
|
||||
|
||||
|
||||
def get_bus_voltage():
|
||||
"""读取总线电压(单位:V)"""
|
||||
raw = read_register(config.REG_BUS_VOLTAGE)
|
||||
return raw * 1.25 / 1000
|
||||
"""读取总线电压(单位:V)。未探测到 INA226 或读失败时返回 0.0(上报用,避免 null)。"""
|
||||
logger = logger_manager.logger
|
||||
if not _ina226_ready():
|
||||
return 0.0
|
||||
try:
|
||||
raw = read_register(config.REG_BUS_VOLTAGE)
|
||||
return raw * 1.25 / 1000
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.error(f"[INA226] 读取电压失败:{e}")
|
||||
return 0.0
|
||||
|
||||
|
||||
def get_current():
|
||||
@@ -44,6 +92,8 @@ def get_current():
|
||||
Current_LSB = 0.001 × CALIBRATION_VALUE / 4096
|
||||
"""
|
||||
try:
|
||||
if not _ina226_ready():
|
||||
return 0.0
|
||||
raw = read_register(config.REG_CURRENT)
|
||||
# INA226 电流寄存器是16位有符号整数
|
||||
# 最高位是符号位:0=正(充电),1=负(放电)
|
||||
@@ -92,6 +142,8 @@ def is_charging(threshold_ma=10.0):
|
||||
|
||||
def voltage_to_percent(voltage):
|
||||
"""根据电压估算电池百分比(查表插值)"""
|
||||
if voltage is None:
|
||||
return 0
|
||||
points = [
|
||||
(4.20, 100), (4.10, 95), (4.05, 85), (4.00, 75), (3.95, 65),
|
||||
(3.90, 55), (3.85, 45), (3.80, 35), (3.75, 25), (3.70, 15),
|
||||
|
||||
23
vision.py
23
vision.py
@@ -14,10 +14,6 @@ from maix import image
|
||||
import config
|
||||
from logger_manager import logger_manager
|
||||
|
||||
# 导入ArUco检测器(如果启用)
|
||||
if config.USE_ARUCO:
|
||||
from aruco_detector import detect_target_with_aruco, aruco_detector
|
||||
|
||||
# 存图队列 + worker
|
||||
_save_queue = queue.Queue(maxsize=16)
|
||||
_save_worker_started = False
|
||||
@@ -927,18 +923,9 @@ def detect_target(frame, laser_point=None):
|
||||
与detect_circle_v3保持相同的返回格式
|
||||
"""
|
||||
logger = logger_manager.logger
|
||||
|
||||
if config.USE_ARUCO:
|
||||
# 使用ArUco检测
|
||||
if logger:
|
||||
logger.debug("[VISION] 使用ArUco标记检测靶心")
|
||||
|
||||
# 延迟导入以避免循环依赖
|
||||
from aruco_detector import detect_target_with_aruco
|
||||
return detect_target_with_aruco(frame, laser_point)
|
||||
else:
|
||||
# 使用传统黄色靶心检测
|
||||
if logger:
|
||||
logger.debug("[VISION] 使用传统黄色靶心检测")
|
||||
return detect_circle_v3(frame, laser_point)
|
||||
|
||||
# 项目当前统一使用黄色靶心检测(圆/椭圆),不再保留 ArUco 路径
|
||||
if logger:
|
||||
logger.debug("[VISION] 使用传统黄色靶心检测")
|
||||
return detect_circle_v3(frame, laser_point)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user