Python Launch 重构:部署图,而不是第二套业务代码
按子系统拆分、参数外置、顶层组装;用冒烟守住入口,把业务逻辑赶回节点。

1. 谁都不敢动的 bringup
最危险的文件常常是 bringup.launch.py:几百行条件、硬编码路径、复制粘贴的节点块,以及「临时」的 TimerAction(period=5.0)。Launch 本应描述部署图——谁启动、参数从哪来、命名空间、生命周期顺序——却吞下了业务分支与环境侦探逻辑。典型事故:用未声明环境变量切换雷达 remap,开发机 .bashrc 里碰巧有值,CI 与另一台车没有——同提交、不同图。业务判断放错层,调试按指数涨。
2. 三层配置,越界就脏
- xacro/URDF:模型与接口名
- YAML:节点与控制器参数
- Launch:装配(起谁、命名空间、注入路径)
Launch 里算轨迹、拼控制律、解析传感器语义,都是泄漏。Python 表达力很强,要主动克制。按子系统拆文件,顶层只 IncludeLaunchDescription;参数进 yaml + 少量 LaunchConfiguration;OpaqueFunction 只用于找 share 路径;条件用 DeclareLaunchArgument,不要读环境变量碰运气。
def generate_launch_description():
use_sim = LaunchConfiguration("use_sim")
pkg = FindPackageShare("my_robot_bringup")
return LaunchDescription([
DeclareLaunchArgument("use_sim", default_value="false"),
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([pkg, "launch", "control.launch.py"])
),
launch_arguments={"use_sim": use_sim}.items(),
),
])3. 删掉 sleep,换成依赖边
睡 5 秒「等硬件」在 CI 与慢盘上必碎。换成 lifecycle 事件、话题就绪或超时失败。每删一处 sleep,补一条依赖边;失败打进诊断。控制栈顺序应是硬件 active → 控制器 → 导航,而不是平铺乱序靠运气。use_sim_time 由顶层唯一注入;子系统只接收。混用时钟的锅常背在算法上,其实是 launch 参数不一致。
旧 launch 用 os.environ.get("LEGACY_LIDAR") 决定 remap,一人本机有 export,另一台没有,同提交不同图。改为 DeclareLaunchArgument("lidar_model"),由部署系统按车注入。环境变量只留给真正的环境(如 ROS_DOMAIN_ID),不留给产品分支。重构后「偶发无雷达」单接近消失——雷达没变,条件树变诚实了。
4. 多入口与冒烟
至少三个入口:minimal(驱动联调)、sim、robot。禁止一个全能 launch 靠二十个参数切换世界。参数尽量共享,差异用 overlay yaml。冒烟断言:进程在、关键 topic 在、lifecycle/控制器 active。不替代路测,但挡住图断裂。断言少而硬;flaky 要 quarantine。入口改名,测试必须改。干净机安装后能起,才能发现漏掉的 package.xml 依赖。
现场需要「只重启导航/雷达」——子系统 launch 可独立启动是运维要求。一切只能整车 relaunch,故障隔离成本极高。旧入口保留一个版本周期并转发,避免出差现场踩空。
5. 覆盖顺序、路径、命名空间
固定:命令行 > launch > yaml > 代码默认;启动后抽样 ros2 param get。路径一律 FindPackageShare,禁止 /home/xxx。节点内相对名,launch 推命名空间;双实例启动测串话。兼容旧硬件的条件要有参数名、负责人与到期日——无限兼容是债务利息。XML/YAML 适合稳定声明式图;Python 适合复杂装配。团队默认一种,例外写进贡献指南。迁移时只换装配层,别顺便改话题名。
6. 案例:环境变量条件
旧 launch 用环境变量决定 remap。一人本机有 export,另一台没有,同提交不同图。改为 DeclareLaunchArgument("lidar_model"),由部署系统按车注入。这类「偶发」故障在重构前很难复现,因为依赖开发者本机环境。Launch 是部署的源代码——条件树不诚实,第二台车就是第一台车的镜像幻觉。
7. 验收
- 顶层短、子系统可单独起。
- 无业务 sleep;时钟模式顶层注入。
- 冒烟覆盖 minimal/sim/robot 入口。
- 双实例不串话;干净机可启动。
- 兼容项有到期日;环境变量不承载产品分支。
Launch 是部署的源代码。业务回节点,装配留 launch,冒烟守门——第二台车应是改 yaml/xacro,而不是分叉 800 行 bringup。
相关
也可以看看
- ·4 分钟阅读
Launch:XML/YAML 与 Python 的分工,不是信仰站队
稳定子图用声明式,条件装配用 Python;团队默认一种,迁移只换装配层。
- ·15 分钟阅读
ContentFilteredTopic:把过滤下推到中间件还是应用层
对比订阅端丢弃、应用层条件判断与 DDS 内容过滤的 CPU/带宽边界,说明表达式能力与发现时序限制;用高频率噪声话题与延迟尖峰验收过滤位置选择。
- ·16 分钟阅读
ROS 2 Action 取消语义:收到 cancel 不等于执行器已经停下
梳理 goal handle 状态机、取消回调与硬件停止之间的异步边界;用可中断执行循环、截止时间和终态发布构建契约并以注入验收。
johan's blog