• 欢迎访问少将全栈,学会感恩,乐于付出,珍惜缘份,成就彼此、推荐使用最新版火狐浏览器和Chrome浏览器访问本网站。
  • 吐槽,投稿,删稿,交个朋友
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏少将全栈吧

Microduck 的三个传感器为什么不放在一个进程里:IMU、ToF 和摄像头的隔离哲学

Build in Public admin 50分钟前 2次浏览 已收录 扫描二维码

做过嵌入式机器人的人都知道一个诱惑:把所有传感器塞进一个进程,一个循环里全读完,多省事。Microduck 有三个关键传感器——IMU、ToF 深度传感器、摄像头——它们分别归 robotd、tofd、mediad 三个独立守护进程管。第一次看架构图的人会觉得这是过度设计。

但当你读完 Pollen Robotics 在架构文档里写的理由,会发现每一条拆分都是被具体的故障模式逼出来的。不是”万一崩了怎么办”的假设,是”已经崩过了所以拆”的教训。

IMU:跟舵机挤在同一条总线上

Microduck 的 IMU 不是一个独立 I²C 设备。它是一块叫 imu_to_dxl 的 v2 板,挂在 Dynamixel 总线上,ID 200,和 15 个 XL330 舵机共享同一根 UART 线。

这意味着什么?robotd 的 50Hz 控制循环每一步做的是:发一个 sync_read 指令,把总线上所有 16 个设备(15 舵机 + 1 IMU)的状态一次性读回来。IMU 在 ID 向量里排第一个,所以它最先应答。它返回的是一个片上 SFLP 四元数,直接从 Dynamixel 协议的寄存器块里读出来——和读舵机位置编码器是同一套代码路径。

这个设计选择有几个实际的好处。首先是延迟。如果 IMU 走 I²C 或 SPI 单独读,那 50Hz 循环里就得做两次总线操作:一次读 Dynamixel 总线拿舵机状态,一次读 I²C 拿 IMU 数据。两次操作之间可能有几百微秒的间隔,对高速运动中的姿态估计来说,这个时间差就是误差。挤在一条总线上,一次 sync_read 全搞定,时间戳严格对齐。

其次是代码简单。不需要 IMU 抽象层,不需要第二条总线的驱动代码,不需要处理两种不同的通信协议。duck_control::bus::DynamixelIo 这个 struct 统一管所有设备,读 IMU 和读舵机是同一个方法调用的不同 ID。

代价是 IMU 数据只有 robotd 能直接拿到。其他进程要用?走 IPC,订阅 robot.state,拿到的已经是降采样后的姿态数据,不是原始 IMU 读数。但这正是设计意图——原始 IMU 数据只在控制循环里有用,没必要广播。

ToF 深度传感器:为什么不能塞进 robotd

Microduck 的脑袋上装了一个 VL53L5/8CX ToF 传感器,输出 8×8 的深度矩阵。这个传感器跑在 tofd 守护进程里,独立于 robotd,发布到 /run/tofd/tof.sock。

为什么不把它放进 robotd?架构文档里给了三个具体的理由。

第一,初始化太慢。 VL53L5/8CX 上电时要通过 I²C 上传大约 90KB 的固件,耗时数秒。如果这东西在 robotd 里,那机器人启动时控制循环就得等这个固件传完才能开始读传感器——而控制循环不读 ToF 数据,它只读 IMU 和舵机。让一个 50Hz 的实时循环等一个跟自己无关的传感器初始化,这不合理。

第二,总线冲突。 ToF 传感器用的 I²C 总线和音频编解码器共享。如果 robotd 在读 ToF,它就得同时管 I²C 总线的仲裁——而 robotd 的核心职责是管 Dynamixel 总线和跑控制循环。让它再管一条共享总线,职责就混了。

第三,不是每只鸭子都有。 大部分 Microduck 不装 ToF 传感器。如果 tofd 的功能在 robotd 里,那没装传感器的鸭子启动时就得处理”传感器不存在”的分支——增加了代码复杂度和故障路径。独立成 tofd 后,没有传感器的板上 tofd 照跑,只是报告”未检测到传感器”,robotd 完全不用知道这件事。

tofd 的设计很克制:它只发布数据,不接收任何指令。tof.stream 是一个单向订阅,消费者(mediad 或 robotd 的感知模块)来了就发一帧,走了就停。没有请求-响应,没有状态查询,没有配置接口。一个发布者,零个回声。

这种设计意味着 tofd 崩了对控制循环零影响。鸭子该走还走,该站还站,只是少了深度感知。而如果你把 ToF 塞进 robotd,一个 I²C 总线异常就可能拖垮整个控制循环——50Hz 的预算只有 20ms,一次 I²C 超时就是 5-10ms 没了。

摄像头:最重的进程,离控制循环最远

mediad 是 Microduck 七个守护进程里最重的一个。它管摄像头、麦克风、视频编码、WebRTC 管道,还是远程 API 的前门。一帧原始摄像头画面大约 1.8 MiB——这个尺寸大到不能走 WebRTC 控制通道传输。

把摄像头从 robotd 里拆出来的理由最直接:媒体/感知进程崩溃不能拖垮电机控制。 摄像头编码器、WebRTC 协商栈、音频管道,这些任何一个出 bug 崩了,控制循环不该受影响。一个机器人摄像头坏了还是机器人,控制循环坏了就是一块塑料。

mediad 和 robotd 之间的通信走 Unix socket 上的 JSON-RPC。mediad 转发客户端的 intent 给 robotd,同时从 robotd 订阅 robot.state 做遥测。它本质上是一个 relay——翻译都不用做,因为手机、浏览器、SSH 终端用的都是同一套 IPC 协议。

这个设计有个隐藏的好处。mediad 重,所以它依赖一大堆库——视频编码器、WebRTC 栈、可能还有感知模型。这些库有内存泄漏、有段错误、有版本兼容问题。把它们隔离在 mediad 里,robotd 的依赖就保持干净:Rust 核心库、Dynamixel 协议、ONNX runtime。robotd 的二进制小,启动快,崩溃概率低。

三个传感器,三种总线,三个进程

把三个传感器的物理层和进程归属放在一起看:

  • IMU → Dynamixel 总线(UART, 1 Mbps, 协议 v2)→ robotd 控制循环直接读
  • ToF → I²C 总线(与音频编解码器共享)→ tofd 独立进程,单向发布
  • 摄像头 → MIPI/USB → mediad 独立进程,走 WebRTC

三种不同的物理总线,三种不同的通信协议,三个不同级别的可靠性要求。IMU 数据喂进 50Hz 控制循环,延迟必须 <1ms。ToF 数据用于感知,100ms 延迟无所谓。摄像头数据用于远程监控和人机交互,300ms 延迟也能接受。

延迟要求不同,可靠性要求不同,初始化时间不同,总线不同——这些差异任何一个都可能成为”放一起会出事”的理由。Pollen 的选择是把差异变成架构边界:每个传感器归一个进程,进程之间走 IPC,谁的延迟要求高谁离控制循环近。

没有传感器抽象层

Microduck 的传感器架构里有一个值得注意的”不做”决定:没有统一的”传感器接口”抽象。robotd 直接调 Dynamixel 协议读 IMU,tofd 直接调 I²C 读 ToF,mediad 直接调 V4L2 或类似接口读摄像头。三个进程各自跟自己的硬件对话,没有一层 Sensor trait 统一它们。

这在软件工程里会被很多人批评——”不够抽象”。但对一个 25cm 的机器人来说,加抽象层意味着加代码、加间接调用、加故障路径。三种传感器的接口本来就完全不同,强行统一只会得到一个谁都用着别扭的最低公约数接口。

Pollen 的做法更务实:每个进程把自己的传感器管好,IPC 协议统一数据格式就够了。你想用 ToF 数据?订阅 tof.stream。你想用 IMU 数据?订阅 robot.state。消费端不需要知道数据是从哪条总线、哪个进程来的,只需要知道 socket 地址和消息格式。

对复刻者的启示

如果你在复刻 Microduck 或者做自己的小型机器人,传感器架构的教训可以浓缩成一条:按故障影响范围划进程,不按功能相似性划进程。

别把”所有传感器”放一个进程里。按这个标准分:哪些传感器数据直接进控制循环?那些跟 robotd 走。哪些传感器初始化慢或可能崩?独立进程。哪些传感器有重的依赖栈?离控制循环越远越好。

Microduck 用七个守护进程管一只鸭子,看起来重,实际上每个进程都很轻。tofd 大概几百行 Rust,robotd 的核心循环也不到一千行。进程多不等于复杂——进程多但每个都简单,比进程少但每个都臃肿要可靠得多。

七个守护进程,三条总线,一个鸭子。每个传感器归一个家,每个家有一扇门,门关了不影响隔壁。这就是 Microduck 的传感器隔离哲学。


参考来源:

  • Pollen Robotics. microduck 仓库 docs/design/architecture.md §1 七守护进程分工、tofd 与 mediad 的职责边界、传感器总线归属。GitHub.
  • Pollen Robotics. microduck 仓库 docs/design/robotd-design.md §1.1 Dynamixel 总线拓扑、IMU ID 200 读取方式、TIOCEXCL 独占锁。GitHub.
  • Pollen Robotics. microduck 官方产品页 pollen-robotics.com/microduck:硬件规格(15 舵机、25cm、800g、Camera + LiDAR + 2 IMU)。
喜欢 (0)赏
[🍬谢谢你请我吃糖果🍬🍬~]
分享 (0)
关于作者:
少将,关注Web全栈开发、项目管理,持续不断的学习、努力成为一个更棒的开发,做最好的自己,让世界因你不同。