2、ROS2基础与环境搭建:ROS2 Humble安装、工作空间创建、节点与话题通信、服务与动作通信基础
各位同学,欢迎来到第二讲。上一章我们聊了机械臂和导航协同的整体架构,今天咱们得把地基打牢——ROS2环境搭建和基础通信机制。说实话,我见过太多项目因为环境没配好,后面调试时各种莫名其妙的问题。所以这一章,咱们一步一个脚印来。
2.1 ROS2 Humble 安装——别踩我踩过的坑
ROS2的版本很多,为什么我推荐Humble?因为它是个LTS版本,支持到2027年。做机器人项目,稳定性比什么都重要。我记得2021年有个项目用了非LTS版,半年后依赖包全变了,那叫一个酸爽。
安装前,先确认你的Ubuntu版本。Humble对应的是Ubuntu 22.04。别搞错了,否则依赖会打架。
安装步骤其实很简单,核心就这几条命令:
# 1. 设置编码
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
# 2. 添加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
# 3. 安装ROS2 Humble(桌面版,包含可视化工具)
sudo apt update
sudo apt install ros-humble-desktop
# 4. 设置环境变量
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc
ros2 run demo_nodes_cpp talker 和 ros2 run demo_nodes_py listener 测试一下。如果能看到数据在跳动,说明安装成功了。这一步别跳过,我见过有人装完没验证,结果后面写代码才发现问题。
2.2 工作空间创建——你的代码仓库
工作空间,说白了就是放你代码的地方。ROS2用的是colcon编译工具,和ROS1的catkin不太一样。我个人习惯把工作空间命名为 ros2_ws,简单明了。
# 创建工作空间
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
colcon build
嗯,这里要注意。第一次 colcon build 会生成 build、install、log 三个文件夹。很多人会忘记 source 环境变量:
source ~/ros2_ws/install/setup.bash
我建议把这个也加到 .bashrc 里,省得每次都要手动敲。你想想看,每次打开终端都要敲一遍,多烦人。
2.3 节点与话题通信——机器人的"群"
节点是ROS2里最小的执行单元。一个节点可以是一个传感器驱动,也可以是一个算法模块。话题通信,就是节点之间通过"话题"来聊天。
打个比方:你有一个摄像头节点,它不断发布"图像数据"这个话题。另一个视觉处理节点订阅了这个话题,就能拿到图像数据做处理。这就是发布/订阅模式。
咱们写个简单的例子。先创建一个功能包:
cd ~/ros2_ws/src
ros2 pkg create my_pkg --build-type ament_python --dependencies rclpy std_msgs
cd ~/ros2_ws
colcon build
source install/setup.bash
然后写一个发布者节点 publisher.py:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MyPublisher(Node):
def __init__(self):
super().__init__('my_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 10)
self.timer = self.create_timer(0.5, self.timer_callback)
self.i = 0
def timer_callback(self):
msg = String()
msg.data = 'Hello, ROS2! %d' % self.i
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.i += 1
def main(args=None):
rclpy.init(args=args)
my_publisher = MyPublisher()
rclpy.spin(my_publisher)
my_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
再写一个订阅者节点 subscriber.py:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MySubscriber(Node):
def __init__(self):
super().__init__('my_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)
def main(args=None):
rclpy.init(args=args)
my_subscriber = MySubscriber()
rclpy.spin(my_subscriber)
my_subscriber.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
topic 和 Topic 是两个不同的话题。这个坑我踩过,你们别踩。
2.4 服务通信——一问一答的模式
话题通信是"广播式"的,发布者只管发,不管有没有人收。但有些场景需要"一问一答",比如你让机械臂移动到某个位置,你得知道它到底到了没有。这时候就需要服务通信。
服务通信包含客户端和服务端。客户端发请求,服务端处理并返回响应。我举个例子,写一个简单的加法服务:
先定义服务接口文件 AddTwoInts.srv:
int64 a
int64 b
---
int64 sum
服务端代码 service_server.py:
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class MyService(Node):
def __init__(self):
super().__init__('my_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_callback)
def add_callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info('Incoming request: a=%d b=%d' % (request.a, request.b))
return response
def main(args=None):
rclpy.init(args=args)
my_service = MyService()
rclpy.spin(my_service)
my_service.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
客户端代码 service_client.py:
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class MyClient(Node):
def __init__(self):
super().__init__('my_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...')
self.req = AddTwoInts.Request()
def send_request(self, a, b):
self.req.a = a
self.req.b = b
self.future = self.cli.call_async(self.req)
rclpy.spin_until_future_complete(self, self.future)
return self.future.result()
def main(args=None):
rclpy.init(args=args)
my_client = MyClient()
response = my_client.send_request(3, 5)
my_client.get_logger().info('Result: %d' % response.sum)
my_client.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
2.5 动作通信——带反馈的"长任务"
动作通信是ROS2里最强大的通信机制。它结合了话题和服务的特点:你可以发送一个目标(像服务),同时可以收到进度反馈(像话题),还可以随时取消任务。
为什么需要动作?你想想看,让机械臂抓取一个物体,可能需要几秒钟。如果只用服务,客户端得一直等着,不知道进度。用动作,你可以实时看到机械臂走到哪了,甚至可以在中途叫停。
动作通信有三个角色:动作服务器、动作客户端、以及反馈话题。服务器执行任务,客户端发送目标并接收反馈。
下面是一个简单的动作服务器示例,模拟一个倒计时任务:
import rclpy
from rclpy.action import ActionServer
from rclpy.node import Node
from example_interfaces.action import Fibonacci
class MyActionServer(Node):
def __init__(self):
super().__init__('my_action_server')
self._action_server = ActionServer(
self, Fibonacci, 'fibonacci', self.execute_callback)
def execute_callback(self, goal_handle):
self.get_logger().info('Executing goal...')
feedback_msg = Fibonacci.Feedback()
feedback_msg.sequence = [0, 1]
for i in range(1, goal_handle.request.order):
feedback_msg.sequence.append(
feedback_msg.sequence[i] + feedback_msg.sequence[i-1])
self.get_logger().info('Feedback: %s' % feedback_msg.sequence)
goal_handle.publish_feedback(feedback_msg)
time.sleep(1)
goal_handle.succeed()
result = Fibonacci.Result()
result.sequence = feedback_msg.sequence
return result
def main(args=None):
rclpy.init(args=args)
my_action_server = MyActionServer()
rclpy.spin(my_action_server)
rclpy.shutdown()
if __name__ == '__main__':
main()
2.6 本章知识体系总览
为了让你更直观地理解ROS2的通信机制,我画了一张图。这张图展示了节点、话题、服务、动作之间的关系:
从这张图你可以看到,三种通信机制各有特点。话题适合传感器数据这种持续流,服务适合简单的查询,动作适合机械臂控制这种需要反馈和取消的任务。在实际项目中,我经常三种混着用。
2.7 本章小结
这一章我们走完了ROS2的入门流程。从环境搭建到工作空间创建,再到三种通信机制,每一步都是后面做机械臂和导航协同的基础。我个人觉得,理解通信机制比记住命令更重要。命令忘了可以查,但通信模型搞错了,整个系统架构都会出问题。
最后说一句,别怕犯错。我当年学ROS2时,光环境就重装了五次。每次踩坑都是成长,关键是踩完要记住。好了,这一章就到这里,咱们下一章见。
- ROS2 Humble 安装要选对Ubuntu版本,装完一定要验证
- 工作空间用colcon编译,记得source环境变量
- 话题通信是发布/订阅模式,适合持续数据流
- 服务通信是请求/响应模式,适合同步查询
- 动作通信带反馈和取消功能,适合长时间任务