A1 的训练脚本、ONNX 导出、板端解析和调试页都要认同一套 17 维输出。这里一乱,后面看到的就全像模型问题。
A1 视觉机器人项目里,动手训练前,我先把模型到底输出什么讲清楚。
这件事比调学习率更早。
当前主线是一个 1x480x640 灰度多任务导航模型。它不只输出“前进、停止、避障”三类,还把语义分类和三区域导航状态放在同一个输出里:
combined_logits[17] = semantic[5] + navgrid[12]这 17 维如果没定义清楚,训练脚本、ONNX 导出、板端解析、Windows UI 和底盘动作映射很容易对不上。模型本身可能没问题,系统表现却会出错。
公开仓库在这里:
https://github.com/GitLaughs/See-you-more-than-her输出维度不是训练脚本内部细节
很多模型项目会把 label schema 当成训练目录里的内部实现。这样做在离线实验里问题不大,但放到嵌入式机器人整个处理流程里就很危险。
因为模型输出会被很多地方消费:
- 数据准备脚本要知道标签是否合法。
- 训练脚本要知道每个 head 的损失怎么计算。
- 导出脚本要保证 ONNX 输出名和维度稳定。
- 板端 C++ 要按固定 offset 解析结果。
- Aurora 页面要把 12 个 navgrid 分数显示成人能看懂的区域状态。
- 底盘控制要知道 center 区域的状态如何映射到动作。
只要其中一处把维度理解错,后面的现象就会非常像“模型不准”。
Schema 要先成为共享模块
这个项目里,navgrid_schema.py 把标签集中写成共享定义:
semantic: person / stop / forward / obstacle / NoTargetregion: left / center / rightnav: clear / obstacle_near / obstacle_mid / obstacle_far这样组合出来就是:
5 semantic + 3 region x 4 nav = 17 outputs它还定义了 offset:
semantic -> 0..4left -> 5..8center -> 9..12right -> 13..16这些数字不该散落在多个文件里。只要散落,就会出现“训练脚本已经改了,板端还是旧解析”的问题。
旧标签最好直接拒绝
数据集迭代时,标签名会变。
比如早期可能会用 free_near / free_mid / free_far 这类状态,后来改成更明确的 clear / obstacle_near / obstacle_mid / obstacle_far。这时我不想让脚本悄悄把旧标签映射过去。
自动兼容看起来省事,但会让训练数据的语义变模糊。
我更喜欢让准备脚本直接拒绝旧标签:如果标签空间已经升级,旧数据就应该被显式清理或重新导出。这样失败来得早,但定位清楚。
模型训练最怕的是“能跑完但标签含义不确定”。
中心区域决定动作,但不能吞掉左右信息
当前底盘动作主要看 center 区域:
center clear / obstacle_far -> forwardcenter obstacle_near / obstacle_mid -> stop or avoid这并不意味着 left 和 right 没用。左右区域是避让策略的备选信息,也能帮助 UI 和调试工具解释为什么某次动作选择了停止或转向。
如果模型只输出一个整体类别,调试时很难知道“为什么停”。三区域输出牺牲了一点模型结构简单性,但换来了更好的工程可解释性。
检查脚本要把几处定义一起验
我更想保留的是这种检查:它能一次发现几处定义有没有对偏。
check_navgrid_contract.py 做的事情就是把这些地方一起拉进来:
- Python 训练 schema。
- Aurora 侧接口定义。
- 板端 C++ 头文件。
- UI 里的区域和状态名称。
- 文档里的当前主线说明。
它验证的是“整个仓库是不是还在按同一套输出定义工作”,而不是单测某个函数。
这类检查对嵌入式项目尤其有用。因为系统跨 Python、C++、HTML、脚本和文档,这些东西靠人记着同步,迟早会漏一处。
UI 也要跟着一起对齐
很多时候我们只检查训练和板端,不检查调试 UI。
但 Aurora 这种联调页面会直接影响人的判断。如果 UI 把 12 个 navgrid 分数标错了,人看到的解释就是错的。工程师会根据错误解释去改模型、改数据或改底盘策略。
所以 UI 里的区域名、状态名和 score 展示顺序也应该被检查脚本覆盖。
调试界面会直接影响我对系统状态的判断,标错一项就可能把排查方向带偏。
ONNX 导出要保留稳定名字
嵌入式部署里,导出模型不是最后随手点一下。
下游转换工具、校准数据包、板端加载逻辑都会依赖输入输出形状和名字。当前主线把输出名固定为:
combined_logits这类名字稳定性看起来不起眼,但能少掉很多隐式猜测。
如果每次导出都换名字,后面的错误会出现在更远的地方,排查成本更高。
最后别只盯着模型
训练视觉导航模型时,模型结构只是一部分。
真正容易出问题的,是模型外面的这一路:
labels -> dataset -> training -> export -> board parser -> UI -> action mapping这条路里的每一层都要使用同一个输出定义。这样后面看到板端行为不对时,才比较容易判断问题是在模型、数据分布,还是控制策略。
否则你很可能只是在排查一个维度错位。
继续读