本节摘要:"在我机器上是好的"是机器人项目交付期最高频的台词。容器化用镜像把系统连同依赖整体封箱,交付物从"一堆安装步骤"变成"一个可校验的镜像"。本节讲镜像分层策略、机器人场景特有的设备与网络配置、Jetson 类边缘板的部署差异,以及容器与第 4 章 Launch 体系的配合。交付一致性的最终形态就是这一节。
没有容器的机器人交付是这样的:登机器、装 Ubuntu、配软件源、装 ROS2、装驱动依赖、拷代码、编译、改环境变量——每一步都可能因网络、版本、手误而失败,交付十台机器是十次独立的冒险。容器化把这一切压缩成:拷镜像、起容器。更重要的是可校验性——镜像的哈希值唯一确定其内容,"装的什么版本"从口头回答变成一行命令。
Dockerfile 的每一层都是一个缓存单元,分层的艺术在于把变化慢的放下面、变化快的放上面:
# 第一层:官方 ROS2 基础镜像(变化最慢,几月一更) FROM ros:humble-ros-base # 第二层:系统与第三方依赖(周级变化) RUN apt-get update && apt-get install -y \ ros-humble-slam-toolbox ros-humble-nav2-bringup \ ros-humble-rmw-cyclonedds-cpp && rm -rf /var/lib/apt/lists/* # 第三层:你的工作空间(日级变化) WORKDIR /ros2_ws COPY src/ src/ RUN . /opt/ros/humble/setup.sh && colcon build --symlink-install # 第四层:启动配置(最常变) COPY bringup/ bringup/ CMD ["ros2", "launch", "my_bringup", "navigation.launch.py"]
这套分层的收益是迭代速度:改一行代码只重建第三四层,秒级到分钟级;依赖不变时基础镜像永不重拉。反模式是把所有 RUN 揉成一条——任何改动都触发全量重建,团队会因此放弃容器更新,镜像逐渐腐化。另一个纪律是别把源码放镜像外挂载着跑:开发期可以挂载调试,交付镜像必须内含编译产物,否则又回到了"这台机器上恰好有源码"的旧世界。
机器人不是普通服务,容器要摸到硬件、要有网络、常还要图形。三处特调配置:
docker run -it --rm \ --device /dev/ttyUSB0 \ # 串口设备透传:底盘雷达的物理口 --net=host \ # host 网络:DDS 组播发现需要 --ipc=host \ # 共享内存互通:DDS 共享内存传输 --privileged \ # 仅调试用:全设备访问 -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix \ # 图形外传 my_bot:humble
--net=host 值得单独强调:DDS 的自动发现依赖组播,默认的 bridge 网络模式会隔离组播,症状是"容器内外互相看不见"——1.3 节的排查清单在容器场景的头号嫌疑人。共享内存传输(8.1 节的调优旋钮)依赖 --ipc=host,忘配它,调优悄悄失效。这两项是 ROS2 容器化区别于普通服务的本质差异:普通服务怕网络太通,机器人怕网络不通。

Jetson 类边缘板有两处特殊考虑。架构:x86 开发机上的镜像在 ARM 板上跑不起来,必须按目标架构构建——要么在板上直接构建(慢但简单),要么交叉构建(快但工具链要配)。加速栈:CUDA、TensorRT 这些推理层与板级驱动版本强耦合,基础镜像要选厂商的发行版,通用镜像装完驱动多半起不来。
交付纪律收束成四条。镜像标签用语义化版本加 git 哈希(my_bot:1.4.0-a3f2c1d),禁用 latest 上产线;配置(参数 YAML、安全策略、Launch)与镜像分离管理,配置变更不重打镜像;每台产线机器记录镜像哈希与配置版本,问题报告必须附带;回归用 7.3 节的仿真航线,全绿才放行到真机。这套纪律与 7.4 的证据包配合,构成"交付可追溯"的闭环。
⚠️ 常见坑:容器里时间同步问题——容器默认与宿主机共享时钟,但跨机部署时各机时钟不同步会让 TF 插值与日志对账全乱。产线机器要跑时间同步服务,这在普通服务里是常识,在机器人集群里常被遗漏。
分层与特调都讲了,用一份迁移实录看存量工程怎么走到容器化——这不是"新项目从零设计",而是大多数团队的真实起点。
第一天:冻结与盘点。 把现有机器上的系统完整盘点成清单:系统包、ROS2 包、自研工作空间、环境变量、设备清单、启动顺序。盘点的产出是一张依赖表,它决定 Dockerfile 的分层与 run 参数。这一步最容易省略也最不该省——照着感觉写 Dockerfile,漏掉的环境变量会在三个月后以"容器里行为诡异"的形式还债。
第二天:镜像化与功能等价验证。 按分层原则写 Dockerfile,构建出首个镜像。验证标准是功能等价:同一套 bringup 分别在裸机与容器里拉起,巡检五步的输出逐项对比——节点清单一致、话题频率一致、TF 拓扑一致。首跑通常在组播发现处翻车(net 模式),加上 host 网络与 IPC 后对齐。
第三天:交付形态与回滚预案。 镜像打语义化标签入仓库,产线机器执行"拉镜像、起容器、跑仿真回归"三步完成升级;同时保留上一版镜像,回滚 = 改一个标签重启。这次迁移后,团队交付一台新机器的时间从两天降到半小时。
实录的三条经验:盘点先行(依赖表是迁移的地基)、等价验证(容器不是重写,是不改变行为的封箱)、回滚预设(没有回滚预案的升级是赌博)。容器化的成功标准不是"用上了容器",而是交付一致性真正可验证——镜像哈希对得上,行为就对得上。
一致性关过了。最后一道扩展关:从一台到一群,怎么组网、怎么隔离、怎么协同。