第1章:ROS2基础与环境搭建
各位同学好,我是你们这门课的老朋友。今天咱们从最基础的东西开始——把ROS2 Humble装好,把开发环境搭起来。
说实话,我见过太多同学一上来就急着写代码,结果编译报错、通信不通,折腾半天发现是环境没配好。嗯,咱们先把地基打牢,后面才能跑得快。
1.1 ROS2 Humble安装——别踩我踩过的坑
ROS2的版本很多,为什么我选Humble?因为它是个长期支持版(LTS),到2027年都有人维护。我在项目里吃过非LTS版本的亏——升级后一堆包不兼容,那叫一个酸爽。
安装前,先确认你的Ubuntu版本。Humble对应的是Ubuntu 22.04。如果你用20.04,那就装Galactic;用24.04就装Jazzy。别搞混了,否则依赖会出问题。
安装步骤其实很简单,我习惯用脚本一键搞定:
# 设置locale
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
# 安装开发工具
sudo apt install ros-dev-tools
装完后,别忘了配置环境变量。我个人习惯把它写到.bashrc里:
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc
验证一下是否装好了:
ros2 run demo_nodes_cpp talker
再开一个终端,运行:
ros2 run demo_nodes_py listener
如果你看到两个节点在互相发消息,恭喜你,环境搭好了。
1.2 工作空间创建——你的代码仓库
ROS2里,所有代码都放在工作空间里。说白了,就是一个有特定结构的文件夹。我建议你建一个专门的目录来放所有ROS2项目:
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
这个src文件夹放你的代码包,build和install是编译后自动生成的,不用手动管。
你想想看,如果你把所有代码都堆在桌面上,找起来多费劲。工作空间就是帮你把项目组织得井井有条。
1.3 Colcon编译工具——从catkin到colcon的进化
用过ROS1的同学可能记得catkin。ROS2换成了colcon,速度更快,功能更强。我个人觉得colcon最大的好处是——它不会把编译中间文件搞得乱七八糟。
安装colcon:
sudo apt install python3-colcon-common-extensions
编译工作空间:
cd ~/ros2_ws
colcon build
常用的编译选项:
| 命令 | 作用 | 我什么时候用 |
|---|---|---|
colcon build |
完整编译 | 第一次编译,或改了多个包 |
colcon build --packages-select 包名 |
只编译指定包 | 只改了一个包时,省时间 |
colcon build --symlink-install |
符号链接安装 | 开发Python节点时,改代码不用重新编译 |
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release |
Release模式编译 | 性能测试时用 |
重点: 每次编译后,记得source一下:source install/setup.bash。我刚开始经常忘了这步,然后发现找不到自己写的节点,急得团团转。
1.4 话题通信——ROS2的血管
话题(Topic)是ROS2里最常用的通信方式。一个节点发布消息,另一个节点订阅,就这么简单。
我举个例子。在Nav2导航里,机器人会发布/odom话题(里程计数据),机械臂会订阅这个数据来调整自己的位置。这就是典型的话题通信。
创建一个发布者:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalPublisher(Node):
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 10)
timer_period = 0.5 # 每秒发两次
self.timer = self.create_timer(timer_period, self.timer_callback)
def timer_callback(self):
msg = String()
msg.data = 'Hello, Nav2!'
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
创建一个订阅者:
class MinimalSubscriber(Node):
def __init__(self):
super().__init__('minimal_subscriber')
self.subscription = self.create_subscription(
String, 'topic', self.listener_callback, 10)
def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
为什么用话题?因为它是一对多、异步的。一个传感器数据可以被导航模块、机械臂、可视化工具同时使用,互不干扰。
1.5 服务通信——有来有往
话题是单向的,服务(Service)是双向的。客户端发请求,服务端回响应。我在做机械臂抓取时,经常用服务来查询当前关节角度——问一次,答一次,干净利落。
定义服务接口(srv文件):
# GetJointAngle.srv
string joint_name
---
float64 angle
服务端实现:
class JointAngleServer(Node):
def __init__(self):
super().__init__('joint_angle_server')
self.srv = self.create_service(
GetJointAngle, 'get_joint_angle', self.get_angle_callback)
def get_angle_callback(self, request, response):
# 假设从硬件读取角度
response.angle = 45.0
return response
客户端调用:
client = self.create_client(GetJointAngle, 'get_joint_angle')
while not client.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting...')
req = GetJointAngle.Request()
req.joint_name = 'shoulder'
future = client.call_async(req)
1.6 动作通信——干大事的
动作(Action)是ROS2里最复杂的通信方式,但也是最强大的。它适合长时间的任务,比如让机械臂从A点移动到B点——你可以随时查询进度,也可以中途取消。
动作有三个要素:目标(Goal)、反馈(Feedback)、结果(Result)。
定义动作接口:
# MoveArm.action
# 目标
geometry_msgs/Pose target_pose
---
# 结果
bool success
string message
---
# 反馈
float32 progress
动作服务端:
class MoveArmServer(Node):
def __init__(self):
super().__init__('move_arm_server')
self.action_server = ActionServer(
self, MoveArm, 'move_arm', self.execute_callback)
def execute_callback(self, goal_handle):
feedback_msg = MoveArm.Feedback()
# 模拟移动过程
for i in range(10):
feedback_msg.progress = (i + 1) * 10.0
goal_handle.publish_feedback(feedback_msg)
time.sleep(0.5)
goal_handle.succeed()
result = MoveArm.Result()
result.success = True
return result
动作客户端:
class MoveArmClient(Node):
def __init__(self):
super().__init__('move_arm_client')
self.action_client = ActionClient(self, MoveArm, 'move_arm')
def send_goal(self, pose):
goal_msg = MoveArm.Goal()
goal_msg.target_pose = pose
self.action_client.send_goal_async(goal_msg)
1.7 本章知识体系
下面这张图是我自己画的,把本章的核心内容串起来了。你保存好,后面学Nav2和机械臂时经常要回来翻。
这张图你看懂了吗?从上到下,先搭环境,再学通信,最后应用到Nav2和机械臂上。每一步都是下一步的基础。
好了,这一章的内容就到这里。环境搭好了,通信机制也回顾了,后面咱们就可以开始玩真的了——让机器人动起来!