第二章:ROS2基础与环境搭建
各位同学,欢迎来到第二章。上一章我们聊了课程的整体脉络,这一章咱们得把家伙事儿先支棱起来。
ROS2 Humble,是目前做机器人导航和机械臂控制最稳的版本之一。我个人习惯用Ubuntu 22.04搭配Humble,这套组合在工业项目里验证得最多,坑也最少。
2.1 ROS2 Humble 安装
安装其实不复杂,但有几个细节我吃过亏,得跟你念叨念叨。
ROS2 Humble 只支持 Ubuntu 22.04 (Jammy)。如果你用的是20.04或者24.04,要么换系统,要么选对应的ROS2版本(Galactic或Rolling)。别硬装,会出各种奇奇怪怪的依赖问题。
安装步骤,我习惯分三步走:
- 设置语言环境:确保系统支持UTF-8。
- 添加ROS2源:把官方软件源加进来,顺便把公钥导进去。
- 安装核心包:我建议装
ros-humble-desktop,这个包包含了导航、可视化、仿真等常用工具。如果你硬盘吃紧,装ros-humble-ros-base也行,但后面很多例子跑不起来。
# 设置语言环境
sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
# 添加ROS2源
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
# 安装ROS2 Humble桌面版
sudo apt update
sudo apt install ros-humble-desktop
装完之后,记得把
source /opt/ros/humble/setup.bash 加到 ~/.bashrc 里。不然每次开终端都要手动source,很烦。我曾经因为这个忘了source,排查了半小时找不到节点,后来发现是环境变量没加载。
2.2 工作空间配置
工作空间,说白了就是你放代码、编译、运行的地方。ROS2里最常用的是colcon工作空间。
我一般这样建:
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
colcon build
建好之后,你会看到三个新文件夹:build、install、log。
- src:放你的源码包。
- build:编译中间文件,别手动改。
- install:编译产物,相当于安装目录。
- log:编译日志,出错了来这里翻。
每次编译完,记得
source install/setup.bash。不然你新写的节点ROS2找不到。我建议也把这个加到 ~/.bashrc 里,但要注意顺序:先source ROS2基础环境,再source你的工作空间。
2.3 Colcon编译工具
Colcon是ROS2的官方编译工具。它比ROS1时代的catkin快不少,而且支持并行编译。
常用命令就这几个:
| 命令 | 作用 | 我的备注 |
|---|---|---|
colcon build | 编译所有包 | 最常用,不加参数就是全量编译 |
colcon build --packages-select 包名 | 只编译指定包 | 改了一个包就用这个,省时间 |
colcon build --symlink-install | 使用符号链接安装 | 我强烈推荐!改Python脚本不用重新编译 |
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release | Release模式编译 | 性能敏感时用,比如导航算法 |
我曾经在编译一个大型项目时,直接
colcon build 跑了半小时。后来发现只需要改一个Python文件,但因为没有加 --symlink-install,每次都要重新编译。从那以后,我新建工作空间第一件事就是加这个参数。
2.4 关键通信机制
ROS2的核心,说白了就是节点之间怎么说话。主要有三种方式:话题、服务、动作。咱们后面做导航和MoveIt协同,这三种都会用到。
2.4.1 话题(Topic)
话题是ROS2里最常用的通信方式。它是异步、单向、多对多的。一个节点发布数据,其他节点订阅接收。
举个例子,激光雷达一直在发扫描数据,导航节点订阅了就能实时拿到。这种场景用话题最合适。
# 发布者示例(Python)
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Talker(Node):
def __init__(self):
super().__init__('talker')
self.pub = self.create_publisher(String, 'chatter', 10)
self.timer = self.create_timer(1.0, self.timer_callback)
def timer_callback(self):
msg = String()
msg.data = 'Hello, ROS2!'
self.pub.publish(msg)
# 订阅者示例(Python)
class Listener(Node):
def __init__(self):
super().__init__('listener')
self.sub = self.create_subscription(String, 'chatter', self.callback, 10)
def callback(self, msg):
self.get_logger().info(f'I heard: {msg.data}')
话题里的那个数字
10 是QoS深度。我建议别用默认值,要根据场景调。比如激光雷达数据丢了就丢了,用 SensorDataQoS;但控制指令必须可靠,用 ReliableQoS。这个细节在导航里特别重要,后面章节会展开。
2.4.2 服务(Service)
服务是同步、双向、一对一的。客户端发请求,服务端回响应。适合那种「问一下,等结果」的场景。
比如你让机械臂去抓一个物体,先问一下「当前关节角度是多少?」——这就是服务调用。
# 服务端示例
from example_interfaces.srv import AddTwoInts
class Adder(Node):
def __init__(self):
super().__init__('adder')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_callback)
def add_callback(self, request, response):
response.sum = request.a + request.b
return response
# 客户端示例
class AdderClient(Node):
def __init__(self):
super().__init__('adder_client')
self.cli = self.create_client(AddTwoInts, 'add_two_ints')
while not self.cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting...')
req = AddTwoInts.Request()
req.a = 3
req.b = 5
self.future = self.cli.call_async(req)
服务调用是阻塞的。如果你在回调函数里调服务,小心死锁。我一般用
call_async 异步调用,配合 spin_until_future_complete 来等结果。
2.4.3 动作(Action)
动作是ROS2里最复杂的通信方式,但也是最强大的。它结合了话题和服务的特点:
- 客户端发目标(Goal)
- 服务端持续反馈状态(Feedback)
- 完成后返回结果(Result)
- 支持取消操作
你想想看,导航机器人从A点到B点,是不是正好符合这个模型?发一个目标点,途中不断反馈当前位置,到了告诉你结果。如果中途想改目的地,还能取消当前任务。
# 动作服务端骨架
from nav2_msgs.action import NavigateToPose
class NavActionServer(Node):
def __init__(self):
super().__init__('nav_action_server')
self.action_server = ActionServer(
self,
NavigateToPose,
'navigate_to_pose',
self.execute_callback
)
async def execute_callback(self, goal_handle):
# 接收目标
target_pose = goal_handle.request.pose
# 持续反馈
feedback_msg = NavigateToPose.Feedback()
feedback_msg.distance_remaining = 10.0
goal_handle.publish_feedback(feedback_msg)
# 返回结果
result = NavigateToPose.Result()
return result
初学者容易搞混。记住一句话:需要反馈的用动作,不需要的用服务。比如「打开灯」用服务,因为开没开一下就知道了;「走到门口」用动作,因为路上需要知道走到哪了。
2.5 本章知识体系
下面这张图,把咱们这章的核心内容串起来了。你可以看到,从系统安装到工作空间,再到三种通信机制,它们之间是层层递进的关系。
嗯,这一章的内容就这些。环境搭好了,通信机制理解了,后面咱们就能直接上手做导航和机械臂的协同了。记住,动手敲代码比看十遍书都管用。去把环境装好,跑通那几个示例节点,咱们下章见。