最近在用 ROS 2 和 Gazebo 做小车仿真,想让小车按照设定的矩形轨迹运动。本来以为写好程序就差不多了,结果运行的时候遇到了一个莫名其妙的问题,折腾了挺长时间。
最后发现,居然是之前的后台进程没有清理干净……
一、遇到的问题
当时我写了一个 rectangle_task.py,通过订阅 /odom 话题获取小车的位置,再根据位置变化控制小车沿矩形轨迹运动。
刚开始运行的时候,小车并没有按照预期完成任务,而是中途突然停了下来。
我第一反应是代码写错了,比如距离计算有问题,或者程序判断小车已经到达目标位置。但检查了一下输出的数据,发现情况有点奇怪。
/odom 中的小车坐标一直在两个位置之间跳变:
- (0.7685, -0.1568)
- (0, 0)
算了一下,这两个位置相距大约 0.784 米。
小车显然不可能在这么短的时间内来回移动这么远,而且从 Gazebo 里看,小车也没有发生这样的运动。
程序却把这些数据当成了小车的实际位置变化,检测到异常后触发保护机制,直接停车了。
所以问题可能不是小车真的跑错了,而是程序接收到的位置数据出了问题。
二、寻找原因
一开始还是怀疑自己的代码,后来觉得应该先看看 /odom 到底是什么情况。
查了一些资料之后,发现 ROS 2 允许多个节点向同一个话题发布消息。如果不同的节点同时发布里程计数据,订阅这个话题的程序就有可能收到不一致的位置。
于是,我开始怀疑是不是同时运行了多个里程计发布节点。
可以通过下面的命令查看 /odom 话题的信息:
ros2 topic info /odom --verbose
还可以查看当前运行的节点:
ros2 node list
除此之外,我还怀疑 Gazebo 和 ROS 2 之间的桥接出了问题。
因为之前一直在反复运行和调试程序,有可能启动了新的仿真环境,却没有把旧的进程完全关闭。
如果旧进程还在后台运行,就可能影响后续的仿真。
可以使用下面的命令检查相关进程:
pgrep -af 'gz sim|ign gazebo|parameter_bridge'
需要注意的是,发现多个进程不代表它们一定有问题,还需要判断哪些是当前仿真正常使用的,哪些是之前残留下来的。
三、解决问题
折腾了一圈,最后发现问题其实没那么复杂。
之前运行的后台进程没有清理干净。
调试过程中,我反复启动小车仿真,但没有注意检查之前的程序是不是已经完全退出,结果旧进程还在后台运行。
之前一直以为是代码或者里程计配置的问题,没想到最后是这么一个简单的原因。
清理掉不需要的后台进程,重新启动仿真后,问题就解决了。
以后如果再遇到类似的问题,可以先检查一下后台进程,再用下面的命令看看里程计数据:
ros2 topic echo /odom
正常情况下,小车静止时,位置数据应该基本保持稳定;小车运动时,坐标也应该连续变化,而不是像之前那样反复跳变。
确认数据正常后,再运行小车控制程序。
四、最后
本来以为是自己代码写错了,检查了半天,结果只是忘了清理后台。
之前把注意力都放在 Python 程序上,反而忽略了最简单的问题。以后运行新的仿真之前,还是先检查一下有没有没关掉的进程吧。
记录一下这次踩坑,免得下次又折腾半天。