refine power module

This commit is contained in:
gcw_4spBpAfv
2026-04-28 16:58:51 +08:00
parent fe3e26e21d
commit 4a3b111ce4
7 changed files with 93 additions and 90 deletions

View File

@@ -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

View File

@@ -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 = {
# ==================== 引脚映射配置(板载 WiFiI2C5====================
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=使用ArUcoFalse=使用传统黄色靶心检测)
# 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表示不自动关机

View File

@@ -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
View File

@@ -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")
# ==================== 第二阶段:软件初始化 ====================

View File

@@ -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 升级

View File

@@ -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 可能直接触发底层崩溃SIGSEGVtry/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),

View File

@@ -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)