1387 字
7 分钟
- 次浏览
A1 视觉导航那 17 个输出,我最后还是写成了统一定义
读前导览

A1 的训练脚本、ONNX 导出、板端解析和调试页都要认同一套 17 维输出。这里一乱,后面看到的就全像模型问题。

正文
1387 字
阅读
7 分钟
结构
8 节
来源线索
GitLaughs/See-you-more-than-her
a1-vision-robot:navgrid-schema a1-vision-robot:check-navgrid-contract a1-vision-robot:dataprocess-modeltrain

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 / NoTarget
region: left / center / right
nav: clear / obstacle_near / obstacle_mid / obstacle_far

这样组合出来就是:

5 semantic + 3 region x 4 nav = 17 outputs

它还定义了 offset:

semantic -> 0..4
left -> 5..8
center -> 9..12
right -> 13..16

这些数字不该散落在多个文件里。只要散落,就会出现“训练脚本已经改了,板端还是旧解析”的问题。

旧标签最好直接拒绝#

数据集迭代时,标签名会变。

比如早期可能会用 free_near / free_mid / free_far 这类状态,后来改成更明确的 clear / obstacle_near / obstacle_mid / obstacle_far。这时我不想让脚本悄悄把旧标签映射过去。

自动兼容看起来省事,但会让训练数据的语义变模糊。

我更喜欢让准备脚本直接拒绝旧标签:如果标签空间已经升级,旧数据就应该被显式清理或重新导出。这样失败来得早,但定位清楚。

模型训练最怕的是“能跑完但标签含义不确定”。

中心区域决定动作,但不能吞掉左右信息#

当前底盘动作主要看 center 区域:

center clear / obstacle_far -> forward
center 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

这条路里的每一层都要使用同一个输出定义。这样后面看到板端行为不对时,才比较容易判断问题是在模型、数据分布,还是控制策略。

否则你很可能只是在排查一个维度错位。

A1 视觉导航那 17 个输出,我最后还是写成了统一定义
https://blog.sunmmyapi.xyz/posts/a1-navgrid-contract-before-training/
作者
Sun
发布于
2026-05-29
许可协议
CC BY-NC-SA 4.0

继续读

相关内容