第2章 ROS环境搭建与通信机制

ROS,说白了就是机器人的「操作系统」—— 但它其实更像一套通信框架。我做了这么多年SLAM,可以负责任地告诉你:ROS玩不溜,后面所有传感器驱动、数据融合、建图定位全都会卡壳。这一章咱们就把ROS的底子打扎实,从安装到通信,再到Launch文件,一步不落。

ROS 环境与通信 ROS 安装与配置 工作空间创建 Topic / Service Launch 文件编写 全流程实战

2.1 ROS 安装 —— 别踩我踩过的坑

ROS 版本很多,我个人习惯用 ROS Noetic(Ubuntu 20.04)。为什么?因为目前主流SLAM框架像LOAM、LIO-SAM、FAST-LIO都在这上面跑得最稳。你如果非要用Melodic或者Humble,后面编译依赖够你折腾一阵子。

⚠️ 我曾经… 在Ubuntu 18.04上装Melodic,结果某个激光雷达驱动死活找不到serial包,最后发现是Python版本冲突。所以听我一句劝:新手直接上Ubuntu 20.04 + Noetic,省心。

安装命令其实就几行,但顺序别搞反:

# 1. 设置 sources.list
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'

# 2. 添加密钥
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

# 3. 更新并安装完整桌面版
sudo apt update
sudo apt install ros-noetic-desktop-full

# 4. 初始化 rosdep
sudo rosdep init
rosdep update

# 5. 设置环境变量(每次开终端都要source,或者写进.bashrc)
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
💡 小技巧: 装完记得跑一下 roscore,如果看到类似 started core service [/rosout] 的日志,说明安装成功。我见过有人装完没启动 roscore 就以为没装好,白忙活半天。

2.2 工作空间创建 —— 你的代码「工地」

工作空间(workspace)就是你的项目根目录。我习惯叫它 catkin_ws,但你可以随意。创建流程很简单:

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws
catkin_make
source devel/setup.bash

跑完 catkin_make 后,你会看到 build 和 devel 两个文件夹自动生成。build 是编译中间文件,devel 里放着编译好的可执行文件和库。嗯,这里要注意:每次新增了包,都要重新 catkin_make 并 source,不然系统找不到你的节点。

📌 我的习惯: 在 ~/.bashrc 里加一行 source ~/catkin_ws/devel/setup.bash,这样每次开终端自动加载。但如果你同时有多个工作空间,小心覆盖——我吃过这个亏,两个工作空间里都有同名包,结果调了半天才发现 source 顺序错了。

2.3 Topic 通信 —— 机器人的「群」

Topic 是 ROS 里最常用的通信方式。发布者(Publisher)往一个话题里发消息,订阅者(Subscriber)接收。就像群:你发一条消息,群里所有人都能看到。

写一个简单的发布节点和订阅节点,我带你过一遍:

// talker.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"

int main(int argc, char **argv) {
    ros::init(argc, argv, "talker");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<std_msgs::String>("chatter", 10);
    ros::Rate rate(10);
    while (ros::ok()) {
        std_msgs::String msg;
        msg.data = "Hello SLAM!";
        pub.publish(msg);
        rate.sleep();
    }
    return 0;
}
// listener.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"

void chatterCallback(const std_msgs::String::ConstPtr& msg) {
    ROS_INFO("I heard: [%s]", msg->data.c_str());
}

int main(int argc, char **argv) {
    ros::init(argc, argv, "listener");
    ros::NodeHandle nh;
    ros::Subscriber sub = nh.subscribe("chatter", 10, chatterCallback);
    ros::spin();
    return 0;
}

你想想看,Topic 通信是异步的、一对多的。适合传感器数据流,比如激光雷达点云、IMU数据。我在做多传感器融合时,经常同时订阅 /scan、/imu、/odom 三个话题,时间同步是个大坑,后面章节会细讲。

2.4 Service 通信 —— 一问一答

Service 跟 Topic 不一样,它是同步的、有请求有响应。就像你打电话问客服:「现在电量多少?」客服查一下告诉你。适合偶尔触发的操作,比如保存地图、重置位置。

定义一个 Service 文件(srv):

# AddTwoInts.srv
int64 a
int64 b
---
int64 sum

服务端代码示例:

bool add(add_two_ints::AddTwoInts::Request  &req,
         add_two_ints::AddTwoInts::Response &res) {
    res.sum = req.a + req.b;
    ROS_INFO("request: %ld + %ld", (long int)req.a, (long int)req.b);
    return true;
}
💡 什么时候用 Service? 我个人只在需要「确认完成」的场景用 Service,比如触发建图保存、切换定位模式。高频数据流千万别用 Service,否则节点会卡死——我早期一个项目里用 Service 传点云,结果帧率直接掉到 2Hz,后来改成 Topic 才解决。

2.5 Launch 文件编写 —— 一键启动全家桶

Launch 文件就是 ROS 的「一键启动脚本」。你想想,每次开多个终端、敲一堆 rosrun,多烦。Launch 文件帮你一次性启动所有节点。

一个典型的 Launch 文件长这样:

<launch>
    <node name="talker" pkg="beginner_tutorials" type="talker" output="screen"/>
    <node name="listener" pkg="beginner_tutorials" type="listener" output="screen"/>
</launch>

保存为 demo.launch,然后运行:

roslaunch beginner_tutorials demo.launch

Launch 文件里还能设置参数、包含其他 launch、分组命名空间。我常用的几个技巧:

  • output="screen":把节点日志打印到终端,调试必备。
  • respawn="true":节点挂了自动重启,野外采集时特别有用。
  • args:传命令行参数,比如波特率、话题名。
⚠️ 注意: 别在 Launch 文件里写死路径!用 $(find pkg_name) 来引用包路径。我曾经把路径写死成 /home/lihua/...,结果代码发给同事,他用户名不一样,跑不起来……尴尬。

2.6 本章小结

这一章我们走通了 ROS 的安装、工作空间创建、Topic/Service 通信,还有 Launch 文件。说白了,这些就是 ROS 的「四梁八柱」。后面每一章都会用到这些知识——传感器驱动靠 Topic 发数据,建图节点靠 Service 触发保存,多传感器融合靠 Launch 一键启动。

我个人建议你:把上面的代码亲手敲一遍,别复制粘贴。敲的过程中你会遇到各种小问题,比如包名写错、CMakeLists.txt 没配置——这些坑现在踩了,后面就顺了。