blog • 2026年9月22日 • 69 次阅读

用 MATLAB 遥控老主机上的 Gazebo:一次把仿真链路打通的技术流水账

ros2 matlab gazebo 仿真 分布式 计算机
--- title: 用 MATLAB 遥控老主机上的 Gazebo:一次把仿真链路打通的技术流水账 date: 2026-09-22 tags: [ROS2, MATLAB, Gazebo, 仿真, 分布式] --- # 用 MATLAB 遥控老主机上的 Gazebo ## 起因 想做的实验很朴素:**笔记本上跑 MATLAB 做控制,老主机(Linux)跑 Gazebo 场景**,两边用 ROS 2 连起来。先把链路打通,再谈算法。 一路上踩的坑比想象多,记一下流水账。 ![仿真链路图-20260922.png](/uploads/2026/9/1790088108985-122379246.png) ## 一、链路:MATLAB ↔ ROS 2 ↔ Gazebo - **老主机**:ROS 2 Jazzy + gz sim 8.10(**headless**,只跑服务端)+ `ros_gz_bridge`(用一个 yaml 逐条写话题映射)+ `foxglove_bridge`(8765) - **MATLAB 侧**:ROS Toolbox,直接当 ROS 2 节点用 - **Domain ID**:老主机上 `ROS_DOMAIN_ID` **根本没设**,走默认 **0**(进程环境变量、bashrc、启动脚本我都翻过了),MATLAB 侧也设 0 就行 顺手记两个小坑: 1. **关窗口 ≠ 退出 MATLAB**。同一个会话里节点名必须唯一,第二次建同名节点直接报「节点名称必须唯一」。现在我的脚本里节点名带随机数,随便重跑。 2. MATLAB 自带的 **ROS 2 Network Analyzer**(`ros2NetworkAnalyzer`,R2024b 才有)连接时要填 Domain ID;不想开 App 就用命令行:`ros2 node list` / `ros2 topic list`。 ## 二、这其实是一套分布式网络(没有中心节点) 跑了半天才意识到:这套东西本质上是个**去中心的分布式网络**,物理拓扑长这样: ``` Wi-Fi 主网段 192.168.1.0/24(路由器 192.168.1.1) ┌──────────────┬──────────────────┬─────────────────┐ MATLAB 笔记本 老主机 .88 PN40 .180 其它设备… │ │ 有线直连专线 192.168.50.0/24 └────── 老主机 .2 ←→ PN40 .1 ────────┘ ``` 老主机和 PN40 各有两张网卡,那条 `192.168.50.x` 是两机之间的**直连专线**,不经路由器。 **机制上,ROS 2 没有 master、没有中心服务器**,全靠 DDS 的自动发现: - 每个节点启动时往**多播地址**广播"我在这、叫什么、有哪些话题"(域 0,UDP 7400–7500 一段); - 同一网段 + 同一 Domain ID 的节点互相听见 → 自动建立点对点连接(**多播只负责介绍,数据走单播**); - 所以 MATLAB 一建节点,老主机那边的桥和 gz 节点就自动连上了——**不用配任何 IP**。 这也解释了我为什么只需要确认"Domain ID 是多少":域对了,剩下的它自己会找。 **但"自动"就意味着"不保证"。** 多播有几个天然的脆弱点,今晚的坑基本都在这里: 1. **多网卡选错路**:老主机同时有 `.88` 和 `.2`,多播可能从"错的那张网卡"出去。笔记本只在 Wi-Fi 网段,如果它拿到的是 `192.168.50.x` 的地址,就会**发现成功、数据传不过去**(最难受的一种:看得见连不上)。 2. **Wi-Fi 的 AP 隔离 / 多播过滤**:很多路由器默认掐掉客户端之间的多播,节点互相就"看不见"。 3. **Windows 防火墙**:MATLAB 第一次跑会弹窗,没允许"专用网络"就静默丢包。 兜底招式(按顺序试): ```matlab setenv('ROS_DOMAIN_ID','0') % 域对上 setenv('ROS_STATIC_PEERS','192.168.1.88') % 绕过多播,直接点名连 % 还不行就两边显式指定 RMW:rmw_fastrtps_cpp ``` **三条通道各有各的脾气**: | 通道 | 地址 | 特点 | |---|---|---| | **DDS(ROS 2)** | 多播发现,动态 | 零配置、自动组网;但依赖多播,跨网段/多网卡容易翻车 | | **Foxglove** | TCP `老主机:8765` | 点对点、**确定能通**,不受多播影响 | | **SSH** | TCP `老主机:22` | 控制面(我干活全靠它) | 排障套路也就出来了:**Foxglove 能连 → 网络本身是通的,问题在多播/域;Foxglove 也连不上 → 先查端口和路由。** 想把这套"做稳",可以:固定静态 IP(省得 DHCP 换地址找不着人)、把那条直连专线专门用来跑 DDS(把 Wi-Fi 留给上网),或者干脆两边写死 `ROS_STATIC_PEERS`,彻底不依赖多播。 ## 三、桥接:把 gz 的东西变成 ROS 能看的 `ros_gz_bridge` 的配置文件里一条条写映射:`/clock`、`/odom`、位姿信息、`/cmd_vel`…… 另外自己写了两个小节点补桥的空白: - **gz_param_node**:订阅 `/gz_cmd`(字符串 JSON),能 spawn / delete 模型、改重力与物理步长,甚至跑任意 gz 命令,结果回在 `/gz_cmd_result` - **gz_tf_fix**:直读 gz 的 `pose/info` 文本流,发 `/tf` 和 `/gz_markers` 为什么不用桥自带的 `Pose_V → TFMessage`?因为**实体名会被丢掉**(`frame_id` / `child_frame_id` 全是空)。 ## 四、Foxglove 里"只有轴系没有模型" Foxglove 的 3D 面板不认 Gazebo 的场景几何——要么自己把模型转成 marker,要么就只能看 TF 的轴系。 做法:gz_tf_fix 每 100 ms 取一次位姿,给每个实体画一个 Marker(车身方块 + **绿色车头箭头** + 四个轮子圆柱),Foxglove 里勾上 `/gz_markers`、Fixed frame 设 `world`。 两个坑: 1. **连杆(轮子)在 gz 里给的是"相对车身"的局部位姿**,我一开始当成世界坐标发 → 车跑走了,轮子留在原地。改成「车身世界位姿 ∘ 连杆局部位姿」合成才正常。 2. 未知实体也画方块 → 场景里一堆 0.45 m 的幽灵方块;加了白名单,只画认识的东西。 ## 五、造车:从差速走到阿克曼转向 - **v1 差速小车**(DiffDrive):`/cmd_vel` 发速度,车动了,`/odom` 稳定 49 Hz ✓ - **v2 加前轮转向**(AckermannSteering):前轮确实会摆(指令 0.6 rad → 左轮 30.8°、右轮 20.7°,阿克曼差角是对的),**但车身物理上根本不转**:真实航向 0°,一路直线。 这里有个关键教训:**插件报的 odom 是"运动学乐观值"**。它自己算出来"转了 130°",而 Gazebo 场景里的真实位姿是 0°。判断仿真里到底动没动,**要看场景真值(pose/info),不能信插件里程计**——我一开始就被它骗了。 继续排查:给轮子加摩擦(mu 2.0)、换回纯差速驱动,**都不转**。结论是 DART 的轮式接触在侧滑——轮子照转、前轮照摆,但抓不住地,车身拿不到偏航力矩。 **v3 的解法**:车身运动交给 gz 的 **VelocityControl**(直接给模型线速度/角速度,不受打滑影响),前轮摆角继续由阿克曼插件负责。又发现 VelocityControl 的订阅话题是**挂在 link 上**的(`/model/diffbot/link/base_link/cmd_vel`,SDF 里写的 `<topic>` 被忽略),于是在桥的配置里补了一条映射。 结果: ``` v = 0.5 m/s,前轮偏转 0.3 rad(ω = v·tanδ/L = 0.455 rad/s),跑 5 秒 → 真实航向 +112.7°,位移 2.09 m ✅ → 前轮摆角 +12.6° / +10.5° ``` ## 六、MATLAB 脚本长什么样 就两步:`/cmd_vel` 给**前轮偏转角**(阿克曼插件),`/cmd_vel_kin` 给**车身速度**(VelocityControl),角速度按自行车模型 `ω = v·tanδ/L` 自动换算。 改脚本顶上的 `v_cruise` 和 `delta` 就能玩,节点名带随机数,随便重跑。 ## 心得 1. **仿真里"插件说的"不一定是真的**——odom 说转了、场景真值说没转,这种情况真实存在。 2. **先把最小可验证单元做出来**:先让车能直线动起来,再去谈转向。 3. **话题名要对齐作用域**:link 级话题和全局话题不是一回事,桥里必须写全。 4. **分布式靠多播"自动",但自动不等于可靠**:多网卡、Wi-Fi 隔离、防火墙,任何一个都能让你"看得见连不上"。 5. **每次改动都落成脚本**——今晚的定位全靠脚本反复复现,靠手点是查不出来的。 ![1000186016.jpg](/uploads/2026/9/1790118830292-394240135.jpg) <video controls style="width:100%; max-width:800px;" src="/uploads/2026/9/1790088177309-944695644.mp4"></video>

相关推荐