当年学ROS的时候,我犯过一个特别蠢的错:把网上所有“常用命令”抄在笔记本上,背了两天,结果一上真机还是懵。后来才明白,命令不是靠背的,而是靠“场景驱动”去理解的。你只有在排查话题不更新、节点反复崩溃、TF树对不齐、bag回放时间轴错乱的时候,把这些命令真正用一遍,它们才会变成你的肌肉记忆。
这篇东西我很早就想写了,起因是群里太多人问“ROS常用命令有哪些”,然后有人甩一张几十条命令的截图。说真的,列表没错,但它缺少一个东西:每条命令到底该在什么故障现场用。所以我按自己的调试经验,把ROS1环境里最高频的命令重新梳理了一遍。没装好ROS的可以先去看一键安装的教程,我这里默认你已经能在终端里跑通roscore,下面直接进入干货。
1. 装完环境后先别急着跑Demo:几条“地基”级命令帮你确认本体状态
1.1 roscore、ROS_MASTER_URI 和节点注册:ROS分布式架构的第一道门槛
很多人跑完教程,看到新终端窗口就习惯性敲 rosrun,结果等半天找不到节点,报错还没看明白。这里最容易被忽略的是:ROS 不是单进程软件,它默认是个“主从式”通信架构。所有节点要先到 ROS Master 那里注册自己,然后才能互相发现、收发话题。
确认 Master 是否活着,命令非常简单:
bash复制roscore
它会启动一个 rosout、一个 master,还有一个参数服务器。正常启动后在另一个终端里可以用:
bash复制rosnode list
如果能看到 /rosout,至少说明Master在跑。要是看到 ERROR: unable to communicate with master,先看 ROS_MASTER_URI 环境变量对不对:
bash复制echo $ROS_MASTER_URI
常见输出是 http://localhost:11311。如果换成了别的IP,或者本地调试时多个终端彼此设置不一致,节点就根本注册不到同一个Master上。这点在多机分布式跑机器人时尤其容易出事,主从机都要指向同一台机器的同一个端口。
1.2 rospack与rosversion:先搞清楚你现在用的是哪个ROS发行版
每个ROS发行版对应的系统环境不一样。Kinetic对应Ubuntu16.04,Melodic对应Ubuntu18.04,Noetic对应Ubuntu20.04。你拿Melodic的教程跑到Noetic环境里,部分的包源、Python版本都会不兼容。
常用排查命令是:
bash复制rosversion -d
它会直接输出当前shell环境中ROS的版本名。如果输出为空,说明你当前终端的ROS环境没有source过。这时候需要手动来源一下:
bash复制source /opt/ros/noetic/setup.bash
发行版名写不熟没关系,用 ls /opt/ros/ 看看到底装了哪个目录:
bash复制ls /opt/ros/
能看到 noetic 或 melodic 之类的目录。这个做法在排查环境问题时比翻设置文件快得多。
1.3 source环境变量这件事:为什么你每次开终端都要执行同一句话
ROS装好后,终端里默认不会自动加载ROS路径,必须执行:
bash复制source /opt/ros/noetic/setup.bash
很多教程会让你把它写进 ~/.bashrc 文件。但有个细节值得单独拎出来:当你创建了自己的工作空间后,还需要再 source 一份 devel/setup.bash,而且这个source必须放在ROS系统setup之后。
我自己吃过这个亏。只source了系统的ROS环境,然后运行 rosrun 我自己写的包 节点,发现老是提示找不到包。因为系统ROS环境只知道 /opt/ros/noetic/share 下的包,不知道你 ~/catkin_ws/devel 里的包。执行 rospack find 包名 找不到,rosrun 自然也就没了下文。
正确的叠加逻辑是:
bash复制source /opt/ros/noetic/setup.bash
source ~/catkin_ws/devel/setup.bash
这里的 ~/catkin_ws 是我自己的工作空间,你换成自己的路径就行。顺手可以用 echo $ROS_PACKAGE_PATH 查看当前包搜索路径,如果能看到你的工作空间目录,那说明source成功。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 工作空间与功能包:catkin命令用不好,后面写代码全是泪
2.1 catkin_create_pkg的正确姿势:依赖不是越多越好
创建一个新功能包的命令非常固定:
bash复制cd ~/catkin_ws/src
catkin_create_pkg my_robot_pkg roscpp rospy std_msgs
这条命令会生成 my_robot_pkg 目录,里面带着 CMakeLists.txt、package.xml 和两个代码目录。很多新手会漏掉最后的依赖参数,创建出来的包几乎是空的,后面写节点前又要手动往 package.xml 里补 <depend> 标签,特别麻烦。
所以创建包之前最好想清楚这包大概要用到什么接口。常用的依赖项我列了一个快速参考表:
| 依赖包名 | 典型用途 |
|---|---|
| roscpp | 用C++写ROS节点 |
| rospy | 用Python写ROS节点 |
| std_msgs | 标准消息,比如String、Int32 |
| sensor_msgs | 传感器数据,比如Image、LaserScan、Imu |
| geometry_msgs | 几何消息,比如Pose、Twist、Point |
| nav_msgs | 导航消息,比如Odometry、Path、OccupancyGrid |
| tf2 / tf2_ros | 坐标变换相关 |
| cv_bridge | ROS图像与OpenCV图像转换 |
如果不确定以后会用到什么,可以先只填语言依赖和std_msgs,后面编译报缺包时再补依赖。
2.2 catkin_make和它的编译元信息:不要在src目录里瞎找build文件
在工作空间根目录下执行:
bash复制cd ~/catkin_ws
catkin_make
这个过程会生成 build/ 和 devel/ 两个目录。有时候你改了 CMakeLists.txt,重新 catkin_make,终端里可能提示没有变化,但实际编译产物没更新。这时候我习惯加一点参数强制清理:
bash复制catkin_make --pkg my_robot_pkg
只编指定的包,比全量编译省时间。如果遇到改了CMakeLists但cmake缓存抽风的情况,最直接的暴力办法是把 build/ 和 devel/ 删掉,重新 catkin_make。放心,源码一般在 src/ 里,不受影响。真机内存不够时,我还会加上:
bash复制catkin_make -j2
把并行任务从默认的CPU核数降到2,避免内存爆炸。这话听起来像废话,但家里那台老电脑编译 ORB-SLAM3 时真把我卡死过好几次。
2.3 roscd不是万能的:它依赖包路径索引
roscd 命令可以直接跳到某个功能包目录,比如:
bash复制roscd turtlesim
你会直接切到 /opt/ros/noetic/share/turtlesim。这命令在自己功能包上也生效,前提是你source过工作空间。如果没有source,终端会告诉你:
code复制No such package 'my_robot_pkg'
这不是你的包没建成功,就是当前shell没有载入工作空间环境。除roscd外,还可以用:
bash复制rospack find my_robot_pkg
它会打印出包的绝对路径。这命令在写launch文件、配置路径时非常实用,尤其是你需要知道某个包的share目录在哪,好去找模型文件、配置文件。
3. rosrun和roslaunch:一个用于单点启动,一个用于整机编排
3.1 rosrun启动单个节点的三板斧
语法是:
bash复制rosrun 包名 可执行文件名
例如:
bash复制rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
第一行启动了乌龟仿真窗口,第二行启动键盘控制节点。你会发现两个节点各自占用一个终端。对于单个节点调试,rosrun 非常方便,尤其是想单独看某个节点能不能跑起来。
有些节点可执行文件名不是当初写的源码名,而是 CMakeLists.txt 里 add_executable 指定的目标名。所以源码叫 talker.cpp,编译后可执行文件可能叫 talker。如果 rosrun 提示找不到可执行文件,先看有没有编译成功,再到 devel/lib/my_robot_pkg/ 目录下看看里面到底生成了哪些文件:
bash复制ls ~/catkin_ws/devel/lib/my_robot_pkg/
一个初学者经常忽略的点:Python脚本要让 rosrun顺利找到,需要保证脚本有执行权限:
bash复制chmod +x scripts/my_python_node.py
否则你会在终端看到 Permission denied,而不是Python语法错误。
3.2 roslaunch为什么更接近真实系统:一次性拉起一窝节点
roslaunch 的语法是:
bash复制roslaunch 包名 launch文件名.launch
它读的是一个XML文件,里面定义了要启动哪些节点、哪些参数、哪些命名空间。相比 rosrun 一个一个开终端,launch 方式能按顺序启动几十个节点,还自动帮你处理节点重启策略。
在很多导航、机械臂项目中,launch 文件会同时拉起底盘驱动、激光雷达驱动、move_base、map_server、rviz、AMCL等一堆节点。手动用 rosrun 一个个开的话,不但费劲,而且一旦某个节点崩了,系统整体状态很难恢复。launch 文件里甚至还能设置:
xml复制<node name="laser_driver" pkg="hokuyo_node" type="hokuyo_node" output="screen" respawn="true"/>
respawn="true" 表示节点非正常退出后会自动重启。这个参数在机器人长时间运行时至关重要,能救很多场突发崩溃。
3.3 launch文件里最常见的隐藏参数:output、required、ns和args
新手写launch最容易踩的坑是看不到节点输出。有的节点运行后终端安安静静,报错全被吞了。这是因为默认的日志输出被重定向到日志文件,而不是当前终端。想在启动时就把print信息打到终端,给节点加 output="screen":
xml复制<node name="talker" pkg="demo" type="talker" output="screen"/>
想让某个节点一挂掉就让整个launch系统退出,用:
xml复制<node name="camera_driver" pkg="camera" type="camera_node" required="true"/>
required="true" 适合最关键节点,比如底盘驱动、雷达驱动。如果它死了继续跑着剩下的节点,机器人就像个没有传感器的盲人,很危险。
全局参数在launch中可以用param标签写,例如:
xml复制<param name="robot_description" command="$(find urdf_tutorial)/urdf/robot.urdf" />
很多涉及机械臂的包都会用到 robot_description参数,把URDF模型内容塞进参数服务器,供robot_state_publisher和RViz读取。
4. 现场排查四件套:rosnode、rostopic、rosservice、rosparam
4.1 rosnode list和info:先判断节点是活着还是假死
系统跑起来后,最需要回答的问题永远是“哪个节点还在,哪个死了”。看当前所有运行节点:
bash复制rosnode list
如果你觉得某个节点好像有问题,单独看它的详细信息:
bash复制rosnode info /turtlesim
它会列出这个节点的订阅、发布、服务列表。这个输出非常有价值:假如底盘驱动节点发布 /odom,但 rosnode info /base_driver 看不到 /odom 出现在Publications列表里,说明驱动内部根本没发这个话题,跟你上层导航调度半毛钱关系都没有,问题在驱动层。
还有一个操作很妙:rosnode ping,它和普通网络ping逻辑相似:
bash复制rosnode ping /turtlesim
能ping通只代表节点master注册正常,不代表节点内部算法正常,但至少能区分出“节点死了”和“节点活着但卡死”两种状态。活着的节点ping很顺畅,卡死的节点会超时。
4.2 rostopic echo/hz/info:从话题数据判断问题,比看日志快十倍
节点状态看着正常,但机器人行为不对的时候,就要从话题层面找糖。
先列出所有话题:
bash复制rostopic list
看某话题有没有人发消息,最直接是:
bash复制rostopic echo /odom
如果什么都没打印,可能话题没数据或频率极低。这时用:
bash复制rostopic hz /odom
这个命令会统计话题发布频率。一般底盘odom话题在10Hz到50Hz之间。如果hz为0,说明话题没有发布者;如果hz忽高忽低,比如期望100Hz实际只有12Hz,说明发布端压力过大或循环里可能存在阻塞。
rostopic info 也挺好用:
bash复制rostopic info /odom
它会告诉你这个topic的发布者和订阅者都有谁,谁在发,谁在听,一目了然。我在排查导航问题时的习惯链条是:先 rosnode list 看节点,再 rostopic list 看话题,再 rostopic info 定位发布订阅关系,最后 rostopic echo 看具体数值。这套链路能覆盖80%的通信类问题。
4.3 rostopic pub手动注入数据:没有传感器时也能骗过一次导航
没有真机、没有传感器时,想测试后续节点是否正常工作,可以手动往话题里发数据:
bash复制rostopic pub /cmd_vel geometry_msgs/Twist "linear:
x: 0.2
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.2" -r 10
-r 10 表示以10Hz的频率反复发送。如果只想发一次,用 -n 1 或干脆 -1:
bash复制rostopic pub -1 /cmd_vel geometry_msgs/Twist '{linear: {x: 0.1}, angular: {z: 0.0}}'
注意消息字段格式是YAML风格,不同消息类型需要的字段不一样。拼不对时建议先用 rosmsg show geometry_msgs/Twist 看消息结构,再照着填。
4.4 rosservice与rosparam:节点运行之后,别忘了还有一套受控接口
有些操作不是话题这种持续流,而是客户端请求一次、服务端响应一次的模型。查看所有可用服务:
bash复制rosservice list
查看具体服务的类型和输入输出:
bash复制rosservice info /spawn
rosservice type /spawn
调用一个服务:
bash复制rosservice call /spawn "{x: 2, y: 2, theta: 0.2, name: 'turtle2'}"
这在调gazebo时很常用,比如重置仿真:
bash复制rosservice call /gazebo/reset_simulation "{}"
参数服务器方面的常用命令是:
bash复制rosparam list # 列出所有参数
rosparam get /use_sim_time # 获取参数
rosparam set /use_sim_time true # 设置参数
rosparam dump params.yaml # 把当前参数保存到文件
rosparam load params.yaml # 从文件加载参数
rosparam的一个典型使用场景:用bag回放数据时,需要设置 /use_sim_time 为true,否则RViz里会看到时间轴乱飞、TF显示过期、地图一直对不上。
4.5 rosmsg和rossrv:与其去翻源码,不如直接让命令告诉你消息长什么样
写订阅器、发消息时,最烦的是记不住消息的字段。其实完全不用记,终端里一条命令直接看结构:
bash复制rosmsg show sensor_msgs/LaserScan
会输出:
code复制std_msgs/Header header
uint32 seq
time stamp
string frame_id
float32 angle_min
float32 angle_max
float32 angle_increment
...
同理服务消息用:
bash复制rossrv show nav_msgs/GetPlan
我要强调一个心得:在写代码前先查一下消息结构,比对着老代码瞎猜高效得多。尤其在做雷达、相机、IMU数据融合的时候,sensor_msgs/Imu 里面的 covariance 矩阵很容易搞错方向,用 rosmsg show sensor_msgs/Imu 看清楚字段顺序,再写映射代码,反而更快。
5. TF坐标树:命令能让你立刻看清机器人的“骨骼”是否错位
5.1 tf_echo:直接查两个坐标系之间的变换关系
机器人系统里,每个刚体都有一个坐标系。激光雷达在雷达坐标系下看到一个点,要变成机器人坐标系的点,必须经过坐标变换。TF树如果错乱,后果直接反应为点云错位、导航路径穿墙、机械臂抓取偏到一边。
当你怀疑两个坐标系关系不对头时,用:
bash复制rosrun tf tf_echo base_link laser
它会不断打印从 base_link 到 laser 的平移和旋转矩阵:
code复制At time 123.456
- Translation: [0.100, 0.000, 0.200]
- Rotation: in Quaternion [0.000, 0.000, 0.000, 1.000]
...
这里Translation表示lidar在base_link坐标系下的安装位置。比如X=0.1米,Z=0.2米,说明雷达安装在车体中心前方10厘米、上方20厘米处。数值明显不合理时,马上就能定位到URDF或static_transform_publisher配错了。
如果 tf_echo 长时间不输出,或者提示 Could not transform from ... to ...,说明两坐标系之间断链了,最常见的原因是某个tf发布节点没启动或没数据。
5.2 view_frames:让整棵TF树变成一张PDF,全局检查比局部echo更管用
tf_echo 只查两个坐标系,但大型机器人动辄几十个坐标系,局部查容易漏问题。更推荐整体生成TF树图:
bash复制rosrun tf view_frames
命令运行几秒后,会在当前目录生成一个 frames.pdf。打开它,能看到从 base_link 出发连接的所有坐标系:laser、camera_link、odom、map,以及每个坐标系间的发布频率和延迟。如果某段线断了,PDF里会直接缺失,问题定位非常直白。
我做真实机器人时,每次换硬件结构或改URDF,都会先跑一次view_frames看树结构。很多看起来像算法问题、参数问题的情况,最后都是TF树少了一根枝。
5.3 static_transform_publisher:临时补坐标关系,千万不要每次都重写URDF
有时候只是调试某个传感器位置,不想反复改URDF再重启一堆节点,可以直接用命令行发布静态坐标变换:
bash复制rosrun tf2_ros static_transform_publisher 0.1 0.0 0.3 0 0 0 base_link laser
这里参数顺序是 x y z yaw pitch roll,之后是父坐标系和子坐标系。发布后,其他节点能立刻查到 base_link 到 laser 的变换。
注意它是个常驻进程,会一直占用终端。而且如果和URDF里已有的静态变换冲突,后面启动的发布者可能覆盖前者,也可能被拒绝发布,要看tf2的版本策略。我建议静态变换只在调试阶段用,最终还是要写进URDF或专门的tf发布节点,让整条系统启动流程统一管理。
6. rosbag:把真机现场完整搬回实验室,它是复现问题和调试的“时间机器”
6.1 record的正确姿势:不是录得越多越好
真实机器人调试有个痛点:现象是偶发的,你没法让机器人每一次都按剧本走。这时就需要把数据完整录下来,回放分析。录包命令很简单:
bash复制rosbag record -O mybag /scan /odom /tf /cmd_vel
-O 指定输出文件名。话题名我建议按需选取,不要无脑用 rosbag record -a。全录会把所有话题包括 /rosout、可视化数据都收进去,bag文件体积暴涨,回放时还得手动过滤。
如果只想录一小段时间,可以加 --duration 参数,比如:
bash复制rosbag record -O test.bag --duration=30 /scan /odom /tf
这条会录30秒后自动停止。
6.2 info和play:回放前先看包信息,别急着把bag丢给算法
录完第一件事不是直接回放,而是先检查bag内容:
bash复制rosbag info mybag.bag
输出里会包含起始时间、时长、话题列表、消息总数、每条消息的平均频率。这里重点看topic频率是否合理。如果录包时雷达频率显示10Hz,但info显示只有2Hz,那说明录包本身可能就有丢帧,回放时问题依旧。
回放:
bash复制rosbag play mybag.bag
默认按录制时的真实时间速度回放。如果想让系统跑快点或慢点,用 -r 参数:
bash复制rosbag play -r 0.5 mybag.bag
0.5倍速回放对定位算法调试很有用,因为节点处理数据需要时间,CPU跟不上时会丢帧。
6.3 回放时最容易踩的时间同步坑:/use_sim_time和/clock
很多人回放bag给AMCL或者cartographer跑,发现地图建不出来,定位一直在飘。排查到最后,大概率是时间戳问题。
录制bag时,话题消息里的 header.stamp 是按当时系统时间写的。回放时如果想让节点认为“现在就是录制那一刻”,必须开启仿真时间:
bash复制rosparam set /use_sim_time true
然后在当前终端回放:
bash复制rosbag play mybag.bag
设置之后,节点不再读取电脑本地时钟,而是从 /clock 话题读取时间。此时如果你打开RViz,会看到时间轴跟着bag走,TF和地图数据才能对得上。
这个参数还有个隐藏坑:设置完以后,如果你想在同一个终端跑普通节点,它的所有计时都依赖 /clock。如果bag没在播,节点的 spin 延时和定时器全都不动,看起来像死锁了一样。所以调试完记得:
bash复制rosparam set /use_sim_time false
或者直接新开一个终端。
7. 日志与可视化:把看不见的内部状态拉到台面上来
7.1 别只在终端里看报错:ROS日志目录里藏着完整现场
节点跑崩了,但终端只打印了最后几行,很常见。这种时候别慌,去看日志目录:
bash复制ls ~/.ros/log/
目录下会有以时间戳或run_id命名的子目录,比如 2025-03-15-10-23-12-12345-12345。最近一次运行的日志一般也能通过 latest 软链接找到:
bash复制tail -f ~/.ros/log/latest/rosout.log
如果启动launch时节点输出被吞,一个常用办法是让所有节点输出到屏幕:
bash复制roslaunch my_pkg my.launch --screen
--screen 会把日志输出直接打到当前终端,调试阶段非常建议加上。正式跑机器人时再回到默认文件模式,免得终端滚屏太快。
7.2 rqt_graph:比脑补通信关系图靠谱得多
想验证节点间通信关系,不少人的第一反应是翻代码,其实没必要。运行中的动态关系可以直接可视化:
bash复制rqt_graph
会画出一个节点和话题的关系图。节点是方框,话题是箭头,频率高的话题箭头会更粗。如果两个节点你明明觉得该通信,但图上没有连线,要么是话题名对不上,要么是消息类型对不上。
还有 rqt_plot,适合看数值型话题随时间变化的趋势:
bash复制rqt_plot /odom/twist/twist/linear/x
它比 echo 一堆数字直观得多。比如底盘在发cmd_vel但实际速度一直为0,曲线一眼就能看出来是速度没跟上,比盯一屏幕的翻滚日志舒服。话题数据如果是向量、数组,rqt_plot也支持 field/x 这样的子字段选择。
7.3 常用报错速查:每条错误信息背后都是一个具体问题
把常见的命令报错集中说一下,你以后照方抓药就行。
| 报错信息片段 | 常见原因 | 快速排查命令 |
|---|---|---|
Unable to register with master |
Master地址不通或没启动 | rosnode list、echo $ROS_MASTER_URI |
package not found |
包路径没source或包名错误 | rospack find 包名、检查devel/setup.bash |
rosrun: command not found |
ROS环境没source | source /opt/ros/noetic/setup.bash |
Permission denied |
Python脚本没执行权限 | chmod +x scripts/xxx.py |
[ERROR] [WallTime]: ... |
节点内部报错,原因多样 | 查看 ~/.ros/log/latest/rosout.log |
Could not transform from ... to ... |
TF树断链 | rosrun tf view_frames、检查tf发布节点 |
Message filter dropping message |
时间戳或TF不同步 | 回放时设置 /use_sim_time |
我建议把这张表存在本地,比反复查搜索引擎高效得多。
7.4 把“查询命令族”整理成alias,让肌肉记忆更可信
最后分享一个我自己的习惯:常用命令不是一个一个敲的,而是会在 ~/.bashrc 里做好别名缩写。比如:
bash复制alias rn='rosnode'
alias rt='rostopic'
alias rs='rosservice'
alias rp='rosparam'
alias rbag='rosbag'
alias rg='rqt_graph'
然后排查问题时就有了一串肌肉记忆式动作:
bash复制rn list
rt list
rt hz /scan
rs list
这个思路不是为了少敲几个字符,而是把“节点-话题-服务-参数”这条调试信息链固化在操作习惯里。问题来了,先跑一圈,看哪个环节断了,再深入翻日志。比起翻收藏夹里的“ROS常用命令大全”,这套流程对我实际的帮助大得多。
