ROS(Robot Operating System)全面深入详解,入门学习ROS和ROS2

在这里插入图片描述

目录


一、ROS 概述与背景

1.1 什么是 ROS

ROS(Robot Operating System)并不是一个传统意义上的操作系统,而是一个机器人软件开发框架。它提供了一套标准的通信机制、开发工具、库函数和约定,使得开发者可以更加高效地构建复杂的机器人应用。

通俗理解:如果说操作系统(如 Windows/Linux)管理的是计算机硬件资源,那么 ROS 管理的就是机器人各组件之间的协作。它就像是机器人的"神经系统",负责连接感知模块(眼睛/耳朵)、决策模块(大脑)和执行模块(手脚)。

1.2 ROS 的诞生与发展

时间节点事件
2007年Willow Garage 公司开始开发 ROS
2010年ROS 1.0 正式发布(基于 Ubuntu)
2012年ROS 成为机器人领域事实上的标准框架
2017年ROS2 的第一个正式版本发布
2020年ROS1 Noetic 发布(最后一个 ROS1 版本,2025年 EOL)
2022年ROS2 Humble 发布(LTS 长期支持版本)
2023年ROS2 Iron 发布
2024年ROS2 Jazzy 发布(LTS 长期支持版本)

1.3 为什么选择 ROS

  1. 标准化:提供了统一的通信协议和接口标准,不同团队开发的模块可以无缝集成
  2. 模块化:采用松耦合架构,每个功能模块可以独立开发、测试和替换
  3. 生态丰富:拥有数千个开源功能包,涵盖导航、SLAM、机械臂控制、视觉处理等
  4. 社区活跃:全球最大的机器人开发者社区,文档、教程、问答资源丰富
  5. 跨平台:ROS2 支持 Linux、Windows、macOS 甚至嵌入式系统
  6. 工业级应用:被 NASA、波士顿动力、Fetch Robotics 等广泛采用

1.4 ROS 适用场景

  • 移动机器人(AGV、AMR)导航与避障
  • 机械臂运动规划与控制
  • 无人驾驶(与 Autoware 等结合)
  • 无人机飞控与编队
  • 人形机器人控制
  • 巡检、配送、清洁等服务机器人
  • 工业自动化与智能制造
  • 学术研究与教育

二、ROS 的核心哲学与设计思想

2.1 点对点设计(Peer-to-Peer)

ROS 采用点对点的通信架构。每个功能模块(节点)可以独立运行在不同的计算机上,通过网络直接通信,而不是依赖一个中心服务器。

┌──────────────┐    Topic: /camera/image    ┌──────────────┐
│   相机节点    │ ==========================> │  视觉处理节点  │
│  (电脑A)     │                            │   (电脑B)    │
└──────────────┘                            └──────────────┘

2.2 不重复造轮子(Re-use)

ROS 鼓励代码复用。已有的功能包可以直接拿来使用,开发者只需要关注自己需要创新的部分。

2.3 语言无关性

ROS 的核心通信层是用 C++ 实现的,但它提供了 Python、C++、Java、Lua 等多语言的客户端接口(Client Library)。

2.4 松耦合架构

节点之间通过消息(Message)进行通信,而不是直接调用彼此的函数。这意味着:

  • 节点可以随时启动或关闭,不影响其他节点
  • 可以方便地替换任何节点的实现
  • 可以在不同语言编写的节点之间通信

2.5 工具化思维

ROS 提供了大量的可视化、调试和分析工具,而不是将这些功能嵌入到框架本身,保持了核心的轻量化。


三、ROS 核心概念详解

3.1 节点(Node)

节点是 ROS 中最基本的执行单元,每个节点负责机器人的一项具体功能。

一个机器人系统可能包含的节点:
├── /base_controller     → 底盘运动控制
├── /lidar_driver        → 激光雷达驱动
├── /camera_driver       → 相机驱动
├── /slam_node           → SLAM建图
├── /navigation_node     → 导航规划
├── /obstacle_detector   → 障碍物检测
└── /battery_monitor     → 电池监控

节点的特点

  • 一个节点是一个进程(Process)
  • 一个节点通常只负责一件事(单一职责原则)
  • 节点可以在同一台机器上运行,也可以分布在不同机器上
  • 每个节点在 ROS 网络中有一个唯一的名称

节点生命周期

创建 → 初始化(注册到Master) → 运行(发布/订阅/服务) → 关闭(注销)

3.2 消息(Message)

消息是节点之间通信的数据载体,定义了传输数据的结构。

消息文件(.msg)定义示例:

# geometry_msgs/Point.msg
float64 x
float64 y
float64 z
# sensor_msgs/LaserScan.msg
std_msgs/Header header
float32 angle_min
float32 angle_max
float32 angle_increment
float32 time_increment
float32 scan_time
float32 range_min
float32 range_max
float32[] ranges
float32[] intensities

常见消息类型分类

包名用途常见消息
std_msgs基础数据类型Int32, Float64, String, Bool
geometry_msgs几何信息Twist, Pose, Point, Quaternion
sensor_msgs传感器数据LaserScan, Image, Imu, PointCloud2
nav_msgs导航相关Odometry, OccupancyGrid, Path
actionlib_msgs动作相关GoalID, GoalStatus

消息的嵌套与组合

# geometry_msgs/Pose.msg  (嵌套了Point和Quaternion)
geometry_msgs/Point position
geometry_msgs/Quaternion orientation

# geometry_msgs/Twist.msg  (嵌套了Vector3)
geometry_msgs/Vector3 linear
geometry_msgs/Vector3 angular

3.3 话题(Topic)

话题是 ROS 中最常用的异步通信机制,基于发布-订阅(Publish-Subscribe)模型。

发布者                              订阅者
┌─────┐                           ┌──────────┐
│节点A│ ──发布──> [/chatter] ──> │ 节点B     │
└─────┘            │             └──────────┘
                   │              ┌──────────┐
                   └──────────>  │ 节点C     │
                                 └──────────┘

话题的关键特性

  • 异步通信:发布者发送消息后不等待订阅者接收
  • 一对多:一个话题可以有多个订阅者
  • 多对一:多个发布者可以向同一个话题发布消息
  • 解耦:发布者和订阅者不需要知道对方的存在
  • 有类型:每个话题只能传输一种类型的消息

话题 vs 消息的区别

  • 话题是通信的通道(管道)
  • 消息是通道中传输的数据(水)

3.4 服务(Service)

服务是 ROS 中的同步通信机制,基于请求-响应(Request-Response)模型。

客户端(Client)                服务端(Server)
┌─────┐                       ┌─────┐
│节点A│ ──请求(Request)──>    │节点B│
│     │ <──响应(Response)──   │     │
└─────┘                       └─────┘

服务 vs 话题的对比

特性话题(Topic)服务(Service)
通信模式发布-订阅(异步)请求-响应(同步)
实时性持续发送一次请求一次响应
适用场景传感器数据流、状态广播查询状态、触发动作
多对多支持一对一
流量可能很大(如30Hz图像)通常很小

服务文件(.srv)示例

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

上面的 --- 分隔了请求和响应部分。

3.5 动作(Action)

动作是 ROS 中用于长时间任务的通信机制,基于目标-反馈-结果模型。

客户端                              服务端
┌─────┐                           ┌─────┐
│     │ ──目标(Goal)──────>      │     │
│     │ <──反馈(Feedback)── ···  │     │  ← 可以持续发送多次反馈
│     │ <──反馈(Feedback)── ···  │     │
│     │ <──结果(Result)────      │     │
└─────┘                           └─────┘

动作的三部分

  1. Goal(目标):客户端发送任务目标
  2. Feedback(反馈):服务端周期性反馈执行进度
  3. Result(结果):任务完成后返回最终结果

动作 vs 服务的对比

特性服务动作
执行时间短(毫秒级)长(秒到分钟)
反馈有周期性反馈
取消不支持支持取消
典型应用查询电池电量导航到目标点

动作文件(.action)示例

# NavigateToGoal.action
geometry_msgs/PoseStamped target_pose
---
geometry_msgs/PoseStamped final_pose
---
float32 distance_remaining
float32 progress_percent

三个 --- 分隔了目标(Goal)、结果(Result)和反馈(Feedback)。

3.6 参数(Parameter)

参数是 ROS 中的全局共享数据存储机制,类似于一个分布式的键值数据库。

# 设置参数
rosparam set /robot_name "turtlebot3"

# 获取参数
rosparam get /robot_name

# 列出所有参数
rosparam list

# 从 YAML 文件加载参数
rosparam load params.yaml

参数的用途

  • 存储机器人配置(如轮距、最大速度)
  • 存储传感器校准数据
  • 存储算法超参数
  • 存储坐标变换参数

参数服务器的特性

  • 全局共享:所有节点都可以读取
  • 可以存储基本类型(int, float, string, bool)以及列表、字典
  • 运行时可以动态修改(不需要重启节点)
  • 由 ROS Master 管理

3.7 坐标系与 TF 变换

TF(Transform)是 ROS 中管理多坐标系关系的系统

为什么需要 TF?
一个机器人有多个传感器和执行器,每个都有自己的坐标系:

         /map(地图坐标系)
            │
     /odom(里程计坐标系)
            │
    /base_link(机器人本体坐标系)
       ╱    │    ╲
/arm_base  /laser  /camera_link

TF 的核心功能

  • 实时维护多个坐标系之间的变换关系
  • 查询任意两个坐标系之间的变换(旋转 + 平移)
  • 支持时间回溯(查询历史时刻的变换)

TF 广播示例(Python)

import tf2_ros
import geometry_msgs.msg

# 广播 TF 变换
broadcaster = tf2_ros.TransformBroadcaster()
transform = geometry_msgs.msg.TransformStamped()
transform.header.stamp = rospy.Time.now()
transform.header.frame_id = "odom"
transform.child_frame_id = "base_link"
transform.transform.translation.x = 1.0
transform.transform.translation.y = 0.5
transform.transform.rotation.w = 1.0
broadcaster.sendTransform(transform)

TF 监听示例(Python)

import tf2_ros

# 监听 TF 变换
tf_buffer = tf2_ros.Buffer()
listener = tf2_ros.TransformListener(tf_buffer)

# 查询 base_link 在 odom 坐标系下的位姿
transform = tf_buffer.lookup_transform("odom", "base_link", rospy.Time(0))

3.8 Launch 文件

Launch 文件用于同时启动多个节点以及配置相关参数

XML 格式(ROS1 传统格式)

<launch>
    <!-- 设置参数 -->
    <param name="robot_name" value="my_robot"/>

    <!-- 启动节点 -->
    <node pkg="turtlesim" type="turtlesim_node" name="turtle_sim" output="screen"/>
    <node pkg="turtlesim" type="turtle_teleop_key" name="turtle_teleop" output="screen"/>

    <!-- 包含其他 launch 文件 -->
    <include file="$(find my_robot)/launch/base.launch"/>

    <!-- 条件启动 -->
    <arg name="use_rviz" default="true"/>
    <node pkg="rviz" type="rviz" name="rviz" if="$(arg use_rviz)"/>
</launch>

Python 格式(ROS2 推荐)

from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration

def generate_launch_description():
    return LaunchDescription([
        DeclareLaunchArgument('use_rviz', default_value='true'),
        Node(
            package='turtlesim',
            executable='turtlesim_node',
            name='turtle_sim',
            output='screen'
        ),
        Node(
            package='rviz2',
            executable='rviz2',
            name='rviz',
            condition=IfCondition(LaunchConfiguration('use_rviz'))
        ),
    ])

3.9 功能包(Package)

功能包是 ROS 中代码组织的基本单位,类似于项目/模块。

功能包的目录结构

my_robot_package/
├── CMakeLists.txt          # 编译配置(必须)
├── package.xml             # 包描述文件(必须)
├── launch/                 # launch 文件目录
│   └── bringup.launch.py
├── config/                 # 配置文件目录
│   └── params.yaml
├── src/                    # C++ 源代码
│   └── my_node.cpp
├── scripts/                # Python 脚本
│   └── my_node.py
├── msg/                    # 自定义消息
│   └── MyMessage.msg
├── srv/                    # 自定义服务
│   └── MyService.srv
├── action/                 # 自定义动作
│   └── MyAction.action
├── urdf/                   # 机器人模型文件
│   └── robot.urdf
└── worlds/                 # 仿真世界文件
    └── empty.world

package.xml 示例

<?xml version="1.0"?>
<package format="2">
  <name>my_robot_package</name>
  <version>0.1.0</version>
  <description>我的机器人功能包</description>
  <maintainer email="me@example.com">myname</maintainer>
  <license>Apache-2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <depend>rclcpp</depend>
  <depend>std_msgs</depend>
  <depend>geometry_msgs</depend>
  <exec_depend>robot_state_publisher</exec_depend>
  <test_depend>ament_lint_auto</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

四、ROS 架构体系

4.1 三层架构

ROS 的架构可以分为三个层次:

┌─────────────────────────────────────────────────┐
│              社区级 (Community Level)              │
│   ROS Wiki · 软件包仓库 · 邮件列表 · 论坛         │
├─────────────────────────────────────────────────┤
│              计算图级 (Computation Graph Level)    │
│   Master · Node · Topic · Service · Action       │
│   Parameter Server · TF                           │
├─────────────────────────────────────────────────┤
│              文件系统级 (Filesystem Level)          │
│   Workspace · Package · Message · Service File    │
│   Action File · Launch File · URDF                │
└─────────────────────────────────────────────────┘

4.2 文件系统级

工作空间(Workspace)结构

catkin_ws/  (ROS1 工作空间)
├── src/                 # 源代码空间
│   ├── CMakeLists.txt   # 由 catkin_init_workspace 生成
│   ├── package_a/
│   └── package_b/
├── build/               # 编译空间(自动生成)
├── devel/               # 开发空间(自动生成)
└── install/             # 安装空间(可选)
ros2_ws/  (ROS2 工作空间)
├── src/
│   ├── package_a/
│   └── package_b/
├── build/               # 编译空间
├── install/             # 安装空间
└── log/                 # 日志空间

4.3 计算图级(ROS1)

ROS1 中有一个重要的角色——ROS Master(主节点)

┌──────────────────────────────────────────────────────┐
│                     ROS Master                        │
│         (维护节点注册表和话题/服务查找)                  │
│                                                       │
│   注册信息:                                            │
│   - /talker 节点 → 发布 /chatter 话题                  │
│   - /listener 节点 → 订阅 /chatter 话题                │
│                                                       │
└───────────┬──────────────────────────┬───────────────┘
            │ 注册/查找                │ 注册/查找
            ▼                          ▼
    ┌───────────┐              ┌───────────┐
    │  /talker  │  ──消息──>   │ /listener │
    │  (发布者)  │   (P2P通信)  │ (订阅者)   │
    └───────────┘              └───────────┘

ROS Master 的作用

  1. 节点注册:每个节点启动时向 Master 注册自己的信息
  2. 名称服务:帮助节点找到其他节点的网络地址
  3. 参数管理:存储和管理全局参数

重要:Master 只负责"牵线搭桥",实际的数据传输是节点之间直接进行的。

4.4 ROS2 架构的变化

ROS2 移除了中心化的 Master,采用了更加分布式的架构:

ROS1: 节点 → Master → 节点(需要Master中转)
ROS2: 节点 ←→ 节点(通过DDS直接发现和通信)

ROS2 使用 DDS(Data Distribution Service) 作为底层通信中间件,实现了:

  • 去中心化的节点自动发现
  • 更好的实时性
  • 跨网络通信
  • 更高的可靠性

五、ROS 通信机制深度解析

5.1 话题通信详细流程(ROS1)

步骤1: Talker 启动,向 Master 注册
       Talker → Master: "我是 /talker,我要发布 /chatter 话题"

步骤2: Listener 启动,向 Master 注册
       Listener → Master: "我是 /listener,我要订阅 /chatter 话题"

步骤3: Master 通知 Listener
       Master → Listener: "/talker 有你要的 /chatter 话题,它的地址是 xxx"

步骤4: Listener 向 Talker 发起连接
       Listener → Talker: "我要订阅 /chatter"

步骤5: Talker 确认连接,开始发送数据
       Talker → Listener: [消息数据流...]

步骤6: 之后的数据传输不再经过 Master(P2P通信)

代码示例 - 发布者(Python)

#!/usr/bin/env python3
import rospy
from std_msgs.msg import String

def talker():
    rospy.init_node('talker', anonymous=True)
    pub = rospy.Publisher('chatter', String, queue_size=10)
    rate = rospy.Rate(10)  # 10Hz

    while not rospy.is_shutdown():
        hello_str = "hello world %s" % rospy.get_time()
        rospy.loginfo(hello_str)
        pub.publish(hello_str)
        rate.sleep()

if __name__ == '__main__':
    talker()

代码示例 - 订阅者(Python)

#!/usr/bin/env python3
import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo("I heard: %s", data.data)

def listener():
    rospy.init_node('listener', anonymous=True)
    rospy.Subscriber('chatter', String, callback)
    rospy.spin()  # 保持节点运行

if __name__ == '__main__':
    listener()

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.publisher_ = self.create_publisher(String, 'chatter', 10)
        timer_period = 0.1  # 10Hz
        self.timer = self.create_timer(timer_period, self.timer_callback)

    def timer_callback(self):
        msg = String()
        msg.data = f'Hello World: {self.get_clock().now()}'
        self.publisher_.publish(msg)
        self.get_logger().info(f'Publishing: "{msg.data}"')

def main():
    rclpy.init()
    node = Talker()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

ROS2 版本 - 订阅者(Python)

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class Listener(Node):
    def __init__(self):
        super().__init__('listener')
        self.subscription = self.create_subscription(
            String, 'chatter', self.callback, 10)

    def callback(self, msg):
        self.get_logger().info(f'I heard: "{msg.data}"')

def main():
    rclpy.init()
    node = Listener()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

5.2 服务通信详细流程

步骤1: Server 启动,向 Master 注册服务
步骤2: Client 向 Master 查询服务地址
步骤3: Client 向 Server 发送请求
步骤4: Server 处理请求,返回响应
步骤5: Client 收到响应

服务端代码示例(ROS2 Python)

import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts

class AddTwoIntsServer(Node):
    def __init__(self):
        super().__init__('add_two_ints_server')
        self.srv = self.create_service(
            AddTwoInts, 'add_two_ints', self.callback)
        self.get_logger().info('Service ready.')

    def callback(self, request, response):
        response.sum = request.a + request.b
        self.get_logger().info(
            f'Request: {request.a} + {request.b} = {response.sum}')
        return response

def main():
    rclpy.init()
    node = AddTwoIntsServer()
    rclpy.spin(node)
    rclpy.shutdown()

客户端代码示例(ROS2 Python)

import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts

class AddTwoIntsClient(Node):
    def __init__(self):
        super().__init__('add_two_ints_client')
        self.client = self.create_client(AddTwoInts, 'add_two_ints')
        while not self.client.wait_for_service(timeout_sec=1.0):
            self.get_logger().info('Waiting for service...')
        self.request = AddTwoInts.Request()

    def send_request(self, a, b):
        self.request.a = a
        self.request.b = b
        return self.client.call_async(self.request)

def main():
    rclpy.init()
    node = AddTwoIntsClient()
    future = node.send_request(3, 5)
    rclpy.spin_until_future_complete(node, future)
    result = future.result()
    node.get_logger().info(f'Result: {result.sum}')
    node.destroy_node()
    rclpy.shutdown()

5.3 动作通信详细流程

动作端代码示例(ROS2 Python - Server)

import rclpy
from rclpy.node import Node
from rclpy.action import ActionServer
from example_interfaces.action import Fibonacci

class FibonacciServer(Node):
    def __init__(self):
        super().__init__('fibonacci_server')
        self._action_server = ActionServer(
            self, Fibonacci, 'fibonacci', self.execute_callback)

    async 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[-1] + feedback_msg.sequence[-2])
            goal_handle.publish_feedback(feedback_msg)
            self.get_logger().info(f'Feedback: {feedback_msg.sequence}')

        goal_handle.succeed()
        result = Fibonacci.Result()
        result.sequence = feedback_msg.sequence
        return result

5.4 参数通信

ROS2 参数使用示例

class MyNode(Node):
    def __init__(self):
        super().__init__('param_node')
        # 声明参数(带默认值)
        self.declare_parameter('speed', 1.0)
        self.declare_parameter('robot_name', 'default_robot')

        # 获取参数值
        speed = self.get_parameter('speed').value
        name = self.get_parameter('robot_name').value

        # 动态参数回调
        self.add_on_set_parameters_callback(self.param_callback)

    def param_callback(self, params):
        for param in params:
            if param.name == 'speed':
                self.get_logger().info(f'Speed changed to: {param.value}')
        return SetParametersResult(successful=True)

六、ROS 开发环境搭建

6.1 操作系统选择

操作系统ROS版本推荐度
Ubuntu 20.04ROS1 Noetic / ROS2 Foxy⭐⭐⭐⭐
Ubuntu 22.04ROS2 Humble(LTS)⭐⭐⭐⭐⭐
Ubuntu 24.04ROS2 Jazzy(LTS)⭐⭐⭐⭐⭐
Windows 10/11ROS2(部分支持)⭐⭐⭐
macOSROS2(实验性)⭐⭐

推荐:新手建议使用 Ubuntu 22.04 + ROS2 HumbleUbuntu 24.04 + ROS2 Jazzy

6.2 安装 ROS2 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
export 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
sudo apt update
sudo apt upgrade
sudo apt install ros-humble-desktop

# 4. 设置环境变量
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc

# 5. 安装开发工具
sudo apt install python3-colcon-common-extensions python3-rosdep2

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

6.3 安装 ROS1 Noetic(Ubuntu 20.04)

# 1. 添加 ROS 软件源
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -

# 2. 安装 ROS
sudo apt update
sudo apt install ros-noetic-desktop-full

# 3. 设置环境变量
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc

# 4. 安装依赖管理工具
sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential
sudo rosdep init
rosdep update

6.4 使用 Docker 环境(推荐新手尝试)

# 拉取 ROS2 Humble 镜像
docker pull ros:humble-desktop

# 运行容器(带 GUI 支持)
docker run -it \
  --env DISPLAY=$DISPLAY \
  --volume /tmp/.X11-unix:/tmp/.X11-unix \
  --net=host \
  ros:humble-desktop

# 在容器中测试
ros2 run demo_nodes_cpp talker

6.5 使用虚拟机

对于不想更换操作系统的用户:

  1. 安装 VirtualBox 或 VMware
  2. 下载 Ubuntu 22.04 ISO
  3. 创建虚拟机(建议分配 4GB+ 内存,20GB+ 硬盘)
  4. 在虚拟机中安装 ROS2

注意:虚拟机中无法使用 GPU 加速,性能会有限制。对于图形化仿真(如 Gazebo)可能体验较差。

6.6 WSL2 方案(Windows 用户)

# 在 Windows PowerShell 中执行
wsl --install -d Ubuntu-22.04

# 重启后进入 Ubuntu,按上述步骤安装 ROS2 Humble
# 注意:WSL2 中 GUI 支持需要安装 WSLg(Windows 11 自带)

七、ROS 核心工具链

7.1 命令行工具

ROS1 命令行工具
# ===== 节点相关 =====
rosnode list                    # 列出所有运行中的节点
rosnode info /node_name         # 查看节点信息
rosnode ping /node_name         # 测试节点连接
rosnode kill /node_name         # 杀死节点

# ===== 话题相关 =====
rostopic list                   # 列出所有活跃话题
rostopic info /topic_name       # 查看话题信息(类型、发布者、订阅者)
rostopic echo /topic_name       # 实时打印话题消息
rostopic hz /topic_name         # 查看话题发布频率
rostopic pub /topic_name msg_type "data: 'hello'"  # 手动发布消息
rostopic type /topic_name       # 查看消息类型

# ===== 服务相关 =====
rosservice list                 # 列出所有服务
rosservice info /service_name   # 查看服务信息
rosservice call /service_name "args"  # 调用服务

# ===== 参数相关 =====
rosparam list                   # 列出所有参数
rosparam get /param_name        # 获取参数值
rosparam set /param_name value  # 设置参数值
rosparam dump params.yaml       # 导出参数到文件
rosparam load params.yaml       # 从文件加载参数

# ===== 消息/服务类型 =====
rosmsg show msg_type            # 查看消息结构
rossrv show srv_type            # 查看服务结构
rosmsg list                     # 列出所有消息类型

# ===== 日志 =====
roscd log                       # 进入日志目录
rqt_console                     # 图形化日志查看器

# ===== 系统 =====
rospack find package_name       # 查找功能包路径
rosls package_name              # 列出功能包内容
roscd package_name              # 进入功能包目录
ROS2 命令行工具
# ===== 节点相关 =====
ros2 node list                  # 列出所有节点
ros2 node info /node_name       # 查看节点信息

# ===== 话题相关 =====
ros2 topic list                 # 列出所有话题
ros2 topic list -t              # 列出话题及其类型
ros2 topic info /topic_name     # 查看话题信息
ros2 topic echo /topic_name     # 打印话题消息
ros2 topic hz /topic_name       # 查看发布频率
ros2 topic pub /topic_name msg_type "{data: hello}"  # 发布消息

# ===== 服务相关 =====
ros2 service list               # 列出所有服务
ros2 service list -t            # 列出服务及其类型
ros2 service call /service_name type_name "{args}"  # 调用服务

# ===== 动作相关 =====
ros2 action list                # 列出所有动作
ros2 action info /action_name   # 查看动作信息
ros2 action send_goal /action_name type_name "{args}"  # 发送动作目标

# ===== 参数相关 =====
ros2 param list                 # 列出所有参数
ros2 param get /node_name param_name  # 获取参数
ros2 param set /node_name param_name value  # 设置参数
ros2 param dump /node_name      # 导出节点参数

# ===== 包管理 =====
ros2 pkg list                   # 列出所有已安装的包
ros2 pkg executables package_name  # 列出包的可执行文件
ros2 pkg create my_package      # 创建新功能包

# ===== 运行节点 =====
ros2 run package_name executable  # 运行节点
ros2 launch package_name launch_file.py  # 运行launch文件

# ===== 调试工具 =====
ros2 doctor                     # 系统诊断
ros2 bag record /topic_name     # 录制话题数据
ros2 bag play bag_file          # 回放数据

7.2 可视化工具

RViz(Robot Visualization)

RViz 是 ROS 中最重要的3D可视化工具,用于显示:

  • 机器人模型(URDF)
  • 激光雷达点云
  • 相机图像
  • 地图
  • 路径规划
  • 坐标系变换
  • 标记(Markers)
# ROS1
rviz

# ROS2
ros2 run rviz2 rviz2

RViz 常用显示类型

显示类型用途
RobotModel显示机器人3D模型
LaserScan显示激光雷达数据
PointCloud2显示3D点云
Image显示相机图像
Map显示2D栅格地图
Path显示路径
Pose显示位姿
Marker显示自定义标记
TF显示坐标系关系
Odometry显示里程计轨迹
RQt 工具集

RQt 是基于 Qt 的图形化工具集:

rqt                        # 启动 rqt 综合界面
rqt_graph                  # 显示节点之间的连接关系图
rqt_plot                   # 实时绘制数据曲线
rqt_console                # 日志查看器
rqt_tf_tree                # 查看 TF 坐标树
rqt_image_view             # 查看图像话题
rqt_reconfigure            # 动态参数调节
Foxglove Studio(ROS2 推荐)

Foxglove Studio 是一个现代化的 ROS 可视化工具,支持 Web 端使用:

  • 3D 面板
  • 图表面板
  • 图像面板
  • 地图面板
  • 支持 ROS bag 回放
  • 支持远程连接

7.3 数据录制与回放(rosbag)

rosbag 是 ROS 的数据录制工具,可以录制话题数据并回放,用于:

  • 记录实验数据
  • 离线调试算法
  • 数据集创建
# ROS1
rosbag record -a                    # 录制所有话题
rosbag record /camera/image /scan   # 录制指定话题
rosbag play data.bag                # 回放数据
rosbag info data.bag                # 查看 bag 文件信息

# ROS2
ros2 bag record -a                  # 录制所有话题
ros2 bag record /camera/image /scan # 录制指定话题
ros2 bag play data/                 # 回放数据(ROS2 bag 是目录格式)
ros2 bag info data/                 # 查看信息

7.4 构建工具

构建工具ROS版本说明
catkinROS1基于 CMake 的构建系统
colconROS2通用构建工具,支持 CMake/Python
amentROS2ROS2 的构建框架
# catkin(ROS1)
mkdir -p catkin_ws/src
cd catkin_ws/src
catkin_init_workspace
cd ..
catkin_make
source devel/setup.bash

# colcon(ROS2)
mkdir -p ros2_ws/src
cd ros2_ws/src
ros2 pkg create my_package --build-type ament_cmake
cd ..
colcon build
source install/setup.bash

# 只编译特定包
colcon build --packages-select my_package

# 编译并开启并行
colcon build --parallel-workers 4

# 编译后自动 source
colcon build && source install/setup.bash

八、ROS 常用功能包与生态

8.1 导航相关

功能包说明
navigation / nav2ROS1/ROS2 导航栈(路径规划、避障)
gmapping基于激光雷达的 SLAM
cartographerGoogle 开源的 SLAM
slam_toolboxROS2 推荐的 SLAM 工具
amcl自适应蒙特卡洛定位
robot_localization多传感器融合定位
teb_local_planner局部路径规划器

8.2 机械臂控制

功能包说明
moveit / moveit2机械臂运动规划框架
ros_control / ros2_control硬件抽象和控制框架
kdl运动学/动力学求解器
trac_ik逆运动学求解器
ros_industrial工业机器人接口

8.3 仿真

功能包说明
gazebo / gazebo_classic / ignition物理仿真器
turtlebot3_simulationsTurtleBot3 仿真
turtlebot4_simulatorTurtleBot4 仿真
carla自动驾驶仿真
air_sim无人机仿真
webotsWebots 仿真器 ROS 接口

8.4 感知与视觉

功能包说明
cv_bridgeOpenCV 与 ROS 图像消息转换
image_transport高效图像传输
vision_opencv计算机视觉工具
darknet_rosYOLO 目标检测
perception_pcl点云处理
aruco_rosArUco 标记检测
depthai_rosOAK 相机驱动

8.5 常用机器人平台

平台说明
TurtleBot3/4最流行的 ROS 学习/研究平台
PR2Willow Garage 的研究机器人
Fetch仓库机器人
Universal Robots (UR)工业机械臂
Franka Emika协作机械臂
DJI Robomaster教育机器人

8.6 URDF 与机器人建模

URDF(Unified Robot Description Format) 是 ROS 中描述机器人模型的标准 XML 格式。

<?xml version="1.0"?>
<robot name="my_robot">

  <!-- 基座连杆 -->
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.5 0.3 0.1"/>
      </geometry>
      <material name="blue">
        <color rgba="0 0 0.8 1"/>
      </material>
    </visual>
    <collision>
      <geometry>
        <box size="0.5 0.3 0.1"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="10.0"/>
      <inertia ixx="0.1" ixy="0" ixz="0"
               iyy="0.1" iyz="0" izz="0.1"/>
    </inertial>
  </link>

  <!-- 左轮连杆 -->
  <link name="left_wheel">
    <visual>
      <geometry>
        <cylinder radius="0.1" length="0.05"/>
      </geometry>
    </visual>
  </link>

  <!-- 左轮关节(连续旋转) -->
  <joint name="left_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="left_wheel"/>
    <origin xyz="0 0.175 0" rpy="-1.5708 0 0"/>
    <axis xyz="0 0 1"/>
  </joint>

</robot>

URDF 关节类型

类型说明
fixed固定关节,不可移动
revolute旋转关节,有角度限制
continuous连续旋转关节,无角度限制
prismatic棱柱形关节(滑动)
floating浮动关节(6自由度)
planar平面关节

URDF 查看工具

# 可视化 URDF
ros2 run urdf_tutorial display model.launch.py model:=my_robot.urdf

# 使用 RViz 查看
ros2 run robot_state_publisher robot_state_publisher --ros-args -p robot_description:="$(cat my_robot.urdf)"

Xacro(XML Macro) 是 URDF 的增强版,支持宏定义、变量、条件判断:

<!-- 定义变量 -->
<xacro:property name="wheel_radius" value="0.1"/>

<!-- 定义宏 -->
<xacro:macro name="wheel" params="name parent y_offset">
  <link name="${name}">
    <visual>
      <geometry>
        <cylinder radius="${wheel_radius}" length="0.05"/>
      </geometry>
    </visual>
  </link>
  <joint name="${name}_joint" type="continuous">
    <parent link="${parent}"/>
    <child link="${name}"/>
    <origin xyz="0 ${y_offset} 0" rpy="-1.5708 0 0"/>
    <axis xyz="0 0 1"/>
  </joint>
</xacro:macro>

<!-- 使用宏 -->
<xacro:wheel name="left_wheel" parent="base_link" y_offset="0.175"/>
<xacro:wheel name="right_wheel" parent="base_link" y_offset="-0.175"/>

九、ROS2 vs ROS1:为什么需要 ROS2

9.1 ROS1 的局限性

问题详细说明
依赖 Master单点故障,Master 挂了整个系统不工作
实时性差基于 TCP/UDP,无法保证实时性
不支持跨网络同一局域网内通信,难以实现云机器人
构建系统不规范catkin 构建系统复杂,新手学习成本高
不支持多机器人多机器人系统需要复杂的命名空间配置
安全性差没有认证和加密机制
仅支持 Linux主要支持 Ubuntu,跨平台困难

9.2 ROS2 的改进

改进项详细说明
去中心化使用 DDS 实现节点自动发现,无需 Master
实时支持支持实时调度,满足工业级实时要求
跨平台支持 Linux、Windows、macOS、嵌入式
安全性支持 SROS2(安全 ROS2),提供认证和加密
多机器人原生支持多机器人系统和命名空间
构建系统使用 colcon + ament,更简洁标准
生命周期管理节点支持生命周期状态机
QoS 策略灵活的服务质量控制
组件化支持节点组件(Component),可在同进程组合

9.3 ROS1 与 ROS2 对照表

概念ROS1ROS2
构建工具catkin_make / catkin_toolscolcon
包格式package.xml format 1/2package.xml format 3
构建系统catkinament_cmake / ament_python
Masterroscore无(DDS自动发现)
节点rospy / roscpprclpy / rclcpp
参数rosparamros2 param
启动roslaunchros2 launch(Python格式)
包管理rosdeprosdep
录制rosbagros2 bag
环境source devel/setup.bashsource install/setup.bash
通信库自定义TCP/UDPDDS(中间件)
命名/global/topic可配置命名空间

9.4 ROS1 到 ROS2 的迁移

迁移工具

  • ros1_bridge:ROS1 和 ROS2 之间的消息桥接
  • 逐步迁移策略:核心模块迁移到 ROS2,其余通过 bridge 通信
# 安装 ros1_bridge
sudo apt install ros-humble-ros1-bridge

# 使用 bridge
ros2 run ros1_bridge dynamic_bridge

代码迁移对比

# ROS1 写法
import rospy
from std_msgs.msg import String

rospy.init_node('my_node')
pub = rospy.Publisher('topic', String, queue_size=10)
rospy.loginfo("Hello")

# ROS2 写法
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

rclpy.init()
node = Node('my_node')
pub = node.create_publisher(String, 'topic', 10)
node.get_logger().info("Hello")

十、ROS2 核心概念与新特性

10.1 节点生命周期(Lifecycle Node)

ROS2 引入了生命周期管理,节点可以有以下状态:

         ┌──────────┐
         │Unconfigured│
         └─────┬────┘
          configure│
               ▼
         ┌──────────┐
    ┌───>│ Inactive  │<───┐
    │    └─────┬────┘    │
activate│      │ deactivate│
    │         ▼           │
    │    ┌──────────┐    │
    │    │  Active   │────┘
    │    └─────┬────┘
    │    cleanup│
    │         ▼
    │    ┌──────────┐
    │    │Finalized  │
    │    └──────────┘
    │
    │  error (任何状态)
    └──────────────────

生命周期状态说明

状态说明
Unconfigured未配置,初始状态
Inactive已配置但未激活,不发布数据
Active激活状态,正常工作
Finalized已终结,准备销毁

用途:实现系统启动时序控制,确保传感器先初始化再启动算法。

10.2 QoS(Quality of Service)策略

QoS 是 ROS2 的重要特性,允许精细控制通信行为。

QoS 配置文件

策略选项说明
ReliabilityRELIABLE / BEST_EFFORT可靠传输 vs 尽力传输
DurabilityTRANSIENT_LOCAL / VOLATILE保留最后消息 vs 不保留
HistoryKEEP_LAST(n) / KEEP_ALL保留最近n条 / 保留全部
Depth数值队列深度
Deadline期限预期消息到达间隔
LivelinessAUTOMATIC / MANUAL存活检测方式

QoS 预设

from rclpy.qos import QoSProfile, ReliabilityPolicy, DurabilityPolicy

# 传感器数据(实时性优先,允许丢帧)
sensor_qos = QoSProfile(
    reliability=ReliabilityPolicy.BEST_EFFORT,
    durability=DurabilityPolicy.VOLATILE,
    depth=5
)

# 控制指令(可靠性优先)
control_qos = QoSProfile(
    reliability=ReliabilityPolicy.RELIABLE,
    durability=DurabilityPolicy.TRANSIENT_LOCAL,
    depth=10
)

# 使用 QoS
pub = node.create_publisher(String, 'topic', qos_profile=sensor_qos)

10.3 组件(Component)与进程内通信

ROS2 支持将多个节点组合到同一个进程中,实现零拷贝的高效通信。

# 组件节点(继承自 Node,但不调用 rclpy.init)
import rclpy
from rclpy.node import Node

class MyComponent(Node):
    def __init__(self):
        super().__init__('my_component')
        self.pub = self.create_publisher(String, 'output', 10)
        self.sub = self.create_subscription(
            String, 'input', self.callback, 10)

    def callback(self, msg):
        out = String()
        out.data = f"Processed: {msg.data}"
        self.pub.publish(out)

# 注册为组件
from rclpy.executors import ExternalShutdownException
def main():
    rclpy.spin(MyComponent())

组合的好处

  • 减少进程间通信开销
  • 共享内存(零拷贝传输)
  • 更好的确定性和实时性

10.4 Executor 执行器

ROS2 使用 Executor 来管理回调的执行:

# 单线程执行器(默认)
executor = rclpy.executors.SingleThreadedExecutor()
executor.add_node(node)
executor.spin()

# 多线程执行器
executor = rclpy.executors.MultiThreadedExecutor(num_threads=4)
executor.add_node(node1)
executor.add_node(node2)
executor.spin()

# 静态单线程执行器(更高性能)
executor = rclpy.executors.StaticSingleThreadedExecutor()
executor.add_node(node)
executor.spin()

10.5 命名空间与重映射

# 命名空间
ros2 run my_package my_node --ros-args -r __ns:=/robot1
# 效果:节点的所有话题都加上 /robot1/ 前缀

# 话题重映射
ros2 run my_package my_node --ros-args -r /input:=/camera/image_raw
# 效果:节点订阅的 /input 话题被重映射为 /camera/image_raw

# 节点名称重映射
ros2 run my_package my_node --ros-args -r __node:=my_renamed_node

十一、ROS2 通信机制详解

11.1 DDS(Data Distribution Service)

DDS 是 ROS2 的底层通信中间件,是一种工业级的实时发布-订阅通信标准。

DDS 厂商

厂商包名许可证
eProsima Fast DDSrmw_fastrtps_cppApache 2.0(默认)
RTI Connext DDSrmw_connextdds商业/研究
Eclipse Cyclone DDSrmw_cyclonedds_cppEPL 2.0
# 切换 DDS 实现
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp

DDS 通信流程

1. 节点启动 → DDS Participant 创建
2. 创建 Publisher/Subscriber → DDS DataWriter/DataReader
3. DDS 自动发现其他 Participant(无需 Master)
4. 数据通过 DDS 中间件传输
5. 支持 TCP/UDP/Shared Memory 传输

11.2 ROS2 消息 IDL

ROS2 使用 IDL(Interface Description Language)定义接口:

# msg/MyMessage.idl
module my_package {
  module msg {
    struct MyMessage {
      string name;
      float64 value;
      sequence<uint8> data;
    };
  };
};

同时仍然支持传统的 .msg、.srv、.action 文件格式。

11.3 ROS2 完整发布订阅示例

自定义消息定义

# msg/RobotStatus.msg
string robot_name
float64 battery_level
geometry_msgs/Pose current_pose
bool is_moving

CMakeLists.txt 配置

find_package(rosidl_default_generators REQUIRED)
find_package(geometry_msgs REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/RobotStatus.msg"
  DEPENDENCIES geometry_msgs
)

十二、ROS2 开发环境与工具

12.1 创建 ROS2 工作空间

# 创建工作空间
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src

# 创建功能包(C++)
ros2 pkg create --build-type ament_cmake my_cpp_package \
  --dependencies rclcpp std_msgs

# 创建功能包(Python)
ros2 pkg create --build-type ament_python my_py_package \
  --dependencies rclpy std_msgs

# 编译
cd ~/ros2_ws
colcon build

# 加载环境
source install/setup.bash

12.2 Python 功能包结构

my_py_package/
├── package.xml
├── setup.py
├── setup.cfg
├── resource/
│   └── my_py_package
├── my_py_package/
│   ├── __init__.py
│   ├── my_publisher.py      # 发布者节点
│   ├── my_subscriber.py     # 订阅者节点
│   └── my_service.py        # 服务节点
└── test/
    ├── test_copyright.py
    ├── test_flake8.py
    └── test_pep257.py

setup.py

from setuptools import setup

package_name = 'my_py_package'

setup(
    name=package_name,
    version='0.1.0',
    packages=[package_name],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='yourname',
    maintainer_email='your@email.com',
    description='My ROS2 Python package',
    license='Apache-2.0',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'my_publisher = my_py_package.my_publisher:main',
            'my_subscriber = my_py_package.my_subscriber:main',
            'my_service = my_py_package.my_service:main',
        ],
    },
)

12.3 C++ 功能包结构

my_cpp_package/
├── CMakeLists.txt
├── package.xml
├── include/
│   └── my_cpp_package/
│       └── my_node.hpp
├── src/
│   ├── my_publisher.cpp
│   ├── my_subscriber.cpp
│   └── my_service.cpp
└── test/
    └── test_my_node.cpp

CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
project(my_cpp_package)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(my_publisher src/my_publisher.cpp)
ament_target_dependencies(my_publisher rclcpp std_msgs)

add_executable(my_subscriber src/my_subscriber.cpp)
ament_target_dependencies(my_subscriber rclcpp std_msgs)

install(TARGETS
  my_publisher
  my_subscriber
  DESTINATION lib/${PROJECT_NAME})

ament_package()

12.4 Launch 文件详解(ROS2)

# launch/my_system.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node, ComposableNodeContainer
from launch.actions import (
    DeclareLaunchArgument, IncludeLaunchDescription,
    GroupAction, TimerAction
)
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
from launch.conditions import IfCondition

def generate_launch_description():
    # 声明启动参数
    use_sim_time_arg = DeclareLaunchArgument(
        'use_sim_time', default_value='false'
    )
    robot_name_arg = DeclareLaunchArgument(
        'robot_name', default_value='my_robot'
    )

    return LaunchDescription([
        use_sim_time_arg,
        robot_name_arg,

        # 启动节点
        Node(
            package='robot_state_publisher',
            executable='robot_state_publisher',
            name='robot_state_publisher',
            parameters=[{
                'robot_description': open(
                    '/path/to/robot.urdf').read(),
                'use_sim_time': LaunchConfiguration('use_sim_time'),
            }],
            output='screen',
        ),

        # 延迟启动
        TimerAction(
            period=3.0,
            actions=[
                Node(
                    package='nav2_bringup',
                    executable='bringup_launch.py',
                    name='navigation',
                ),
            ]
        ),

        # 包含其他 launch 文件
        IncludeLaunchDescription(
            PathJoinSubstitution([
                FindPackageShare('my_package'),
                'launch',
                'sensors.launch.py'
            ])
        ),
    ])

12.5 ros2_control 硬件抽象框架

ros2_control 是 ROS2 的硬件抽象和控制框架:

# config/controllers.yaml
controller_manager:
  ros__parameters:
    update_rate: 100
    joint_state_broadcaster:
      type: joint_state_broadcaster/JointStateBroadcaster
    diff_drive_controller:
      type: diff_drive_controller/DiffDriveController

diff_drive_controller:
  ros__parameters:
    left_wheel_names: ["left_wheel_joint"]
    right_wheel_names: ["right_wheel_joint"]
    wheel_separation: 0.35
    wheel_radius: 0.1
    publish_rate: 50.0

十三、SLAM(同时定位与地图构建)

13.1 SLAM 基本概念

SLAM(Simultaneous Localization and Mapping) 是机器人在未知环境中同时完成:

  1. 定位(Localization):确定自己在环境中的位置
  2. 建图(Mapping):构建环境的地图
                    ┌────────────┐
    传感器数据 ───> │ SLAM 算法  │ ───> 地图 + 机器人位姿
  (激光/相机/IMU)   └────────────┘
                         ▲
                         │
                    里程计数据
                   (编码器/IMU)

13.2 地图类型

地图类型说明适用场景
栅格地图(Grid Map)2D 平面网格,每个格子有占用概率室内导航(最常用)
点云地图(Point Cloud)3D 空间中的点集合3D 环境感知
拓扑地图(Topological)节点+边的图结构大规模导航
语义地图(Semantic)带物体标签的地图高级任务规划

13.3 常用 SLAM 算法

13.3.1 Gmapping(2D 激光 SLAM)
# 安装
sudo apt install ros-humble-gmapping

# 运行
ros2 launch slam_toolbox online_async_launch.py \
  params_file:=./config/mapper_params_online_async.yaml

Gmapping 参数配置

# mapper_params.yaml
slam_toolbox:
  ros__parameters:
    resolution: 0.05          # 地图分辨率(米/格)
    max_laser_range: 12.0     # 激光雷达最大范围
    minimum_score: 50         # 匹配最小得分
    use_scan_matching: true
    use_scan_barycenter: true
    maxUrange: 12.0
    sigma: 0.05
    kernelSize: 1
    lstep: 0.05
    astep: 0.05
    iterations: 5
    lsigma: 0.075
    ogain: 3.0
    lskip: 0
    srr: 0.1
    srt: 0.2
    str: 0.1
    stt: 0.2
    linearUpdate: 1.0
    angularUpdate: 0.5
    temporalUpdate: 3.0
    occ_thresh: 0.25
13.3.2 Cartographer(Google)

Cartographer 是 Google 开源的跨平台 SLAM 系统:

# 安装
sudo apt install ros-humble-cartographer
sudo apt install ros-humble-cartographer-ros

# 运行2D SLAM
ros2 launch cartographer_ros demo_backpack_2d.launch.py

# 运行3D SLAM
ros2 launch cartographer_ros demo_backpack_3d.launch.py

Cartographer 的特点

  • 支持 2D 和 3D SLAM
  • 累积误差小
  • 支持多传感器融合(激光+IMU+里程计)
  • Google 背书,工程质量高
13.3.3 SLAM Toolbox(ROS2 推荐)

SLAM Toolbox 是 ROS2 中最推荐的 2D SLAM 方案:

# 安装
sudo apt install ros-humble-slam-toolbox

# 在线建图
ros2 launch slam_toolbox online_async_launch.py

# 离线处理
ros2 launch slam_toolbox offline.launch.py

# 继续之前的地图继续建图
ros2 launch slam_toolbox online_async_launch.py \
  use_sim_time:=true
13.3.4 ORB-SLAM3(视觉 SLAM)

ORB-SLAM3 是目前最先进的视觉 SLAM 系统:

# 需要从源码编译
git clone https://github.com/UZ-SLAMLab/ORB_SLAM3.git
cd ORB_SLAM3
./build.sh

特点

  • 支持单目、双目、RGB-D 相机
  • 支持视觉+IMU 融合
  • 支持多地图系统
  • 实时性能优秀

13.4 保存和加载地图

# 保存地图(nav2_map_server)
ros2 run nav2_map_server map_saver_cli -f my_map

# 生成文件:
# my_map.pgm  → 地图图像(灰度图)
# my_map.yaml → 地图元数据

# 加载地图
ros2 run nav2_map_server map_server --ros-args \
  -p yaml_filename:=my_map.yaml -p use_sim_time:=true

地图文件(.yaml)示例

image: my_map.pgm
resolution: 0.05          # 米/像素
origin: [-13.8, -12.6, 0] # 地图原点(x, y, yaw)
negate: 0
occupied_thresh: 0.65      # 占用阈值
free_thresh: 0.196         # 空闲阈值

十四、Navigation 导航栈

14.1 Nav2 概述(ROS2)

Nav2 是 ROS2 的标准导航框架,是 ROS1 Navigation Stack 的升级版。

┌────────────────────────────────────────────────────┐
│                    Nav2 架构                         │
│                                                      │
│  ┌──────────────┐    ┌──────────────────────────┐   │
│  │ BT Navigator │    │    Controller Server     │   │
│  │(行为树导航)  │    │   (控制器/局部规划)      │   │
│  └──────┬───────┘    └────────────┬─────────────┘   │
│         │                         │                  │
│  ┌──────┴───────┐    ┌────────────┴─────────────┐   │
│  │ Planner Server│    │  Recovery Server         │   │
│  │ (全局规划)   │    │  (恢复行为)             │   │
│  └──────────────┘    └──────────────────────────┘   │
│                                                      │
│  ┌──────────────┐    ┌──────────────────────────┐   │
│  │  Map Server  │    │  Costmap 2D              │   │
│  │ (地图服务)   │    │  (代价地图)             │   │
│  └──────────────┘    └──────────────────────────┘   │
└────────────────────────────────────────────────────┘

14.2 Nav2 核心组件

全局路径规划器(Global Planner)
规划器说明
NavFn PlannerDijkstra/A* 算法
Smac Planner2D/Hybrid-A*/Lattice 算法
Theta* Planner任意角度路径
局部控制器(Local Controller)
控制器说明
DWB Controller动态窗口法(DWA改进)
TEB Controller时间弹性带算法
MPPI Controller模型预测路径积分
RPP Controller拦截点追踪(Regulated Pure Pursuit)
Rotation Shim旋转辅助控制器
行为树(Behavior Tree)

Nav2 使用行为树来组织复杂的导航逻辑:

<!-- 导航行为树 -->
<root main_tree_to_execute="MainTree">
  <BehaviorTree ID="MainTree">
    <Sequence name="NavigateWithRecovery">
      <!-- 全局路径规划 -->
      <ComputePathToPose goal="{goal}" path="{path}"/>
      <!-- 局部控制跟随路径 -->
      <FollowPath path="{path}"/>
    </Sequence>
    <!-- 恢复行为 -->
    <RecoveryNode number_of_retries="3">
      <SequenceStar name="RecoveryActions">
        <ClearEntireCostmap service_name="global_costmap/clear"/>
        <Wait wait_duration="2"/>
        <Spin spin_dist="1.57"/>
        <BackUp backup_dist="0.3" backup_speed="0.1"/>
      </SequenceStar>
    </RecoveryNode>
  </BehaviorTree>
</root>
代价地图(Costmap)
┌─────────────────────────────┐
│          代价地图             │
│  ┌───────────────────────┐  │
│  │    静态层 (Static)     │  │  ← 来自地图
│  │    障碍物层 (Obstacle)  │  │  ← 来自传感器
│  │    膨胀层 (Inflation)   │  │  ← 障碍物膨胀
│  │    安全距离             │  │
│  └───────────────────────┘  │
└─────────────────────────────┘

14.3 Nav2 配置

典型 Nav2 配置文件

# nav2_params.yaml
bt_navigator:
  ros__parameters:
    default_nav_to_pose_bt_xml: nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
    plugin_lib_names:
      - nav2_compute_path_to_pose_action_bt_node
      - nav2_follow_path_action_bt_node
      - nav2_rate_controller_bt_node

controller_server:
  ros__parameters:
    controller_frequency: 20.0
    min_x_velocity_threshold: 0.001
    min_y_velocity_threshold: 0.5
    min_theta_velocity_threshold: 0.001
    progress_checker_plugins: ["progress_checker"]
    goal_checker_plugins: ["goal_checker"]
    controller_plugins: ["FollowPath"]
    progress_checker:
      plugin: "nav2_controller::SimpleProgressChecker"
      required_movement_radius: 0.5
      movement_time_allowance: 10.0
    goal_checker:
      plugin: "nav2_controller::SimpleGoalChecker"
      xy_goal_tolerance: 0.25
      yaw_goal_tolerance: 0.25
    FollowPath:
      plugin: "dwb_core::DWBLocalPlanner"

planner_server:
  ros__parameters:
    expected_planner_frequency: 20.0
    planner_plugins: ["GridBased"]
    GridBased:
      plugin: "nav2_navfn_planner/NavfnPlanner"
      tolerance: 0.5
      use_astar: false

local_costmap:
  local_costmap:
    ros__parameters:
      update_frequency: 5.0
      publish_frequency: 2.0
      global_frame: odom
      robot_base_frame: base_link
      rolling_window: true
      width: 3
      height: 3
      resolution: 0.05
      robot_radius: 0.22
      plugins: ["obstacle_layer", "inflation_layer"]
      obstacle_layer:
        plugin: "nav2_costmap_2d::ObstacleLayer"
        enabled: True
        observation_sources: scan
        scan:
          topic: /scan
          max_obstacle_height: 2.0
          clearing: True
          marking: True
      inflation_layer:
        plugin: "nav2_costmap_2d::InflationLayer"
        cost_scaling_factor: 10.0
        inflation_radius: 0.55

global_costmap:
  global_costmap:
    ros__parameters:
      update_frequency: 1.0
      publish_frequency: 1.0
      global_frame: map
      robot_base_frame: base_link
      robot_radius: 0.22
      plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
      static_layer:
        plugin: "nav2_costmap_2d::StaticLayer"
        map_subscribe_transient_local: True

14.4 运行 Nav2

# 启动完整导航栈
ros2 launch nav2_bringup navigation_launch.py \
  use_sim_time:=true \
  map:=/path/to/my_map.yaml

# 通过 RViz 发送导航目标
# 或通过代码发送
ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose \
  "{pose: {header: {frame_id: 'map'}, pose: {position: {x: 2.0, y: 1.0}}}}"

14.5 自定义导航行为

# 通过代码发送导航目标
import rclpy
from rclpy.node import Node
from rclpy.action import ActionClient
from nav2_msgs.action import NavigateToPose
from geometry_msgs.msg import PoseStamped

class NavClient(Node):
    def __init__(self):
        super().__init__('nav_client')
        self._action_client = ActionClient(
            self, NavigateToPose, 'navigate_to_pose')

    def send_goal(self, x, y, yaw=0.0):
        goal_msg = NavigateToPose.Goal()
        goal_msg.pose.header.frame_id = 'map'
        goal_msg.pose.pose.position.x = x
        goal_msg.pose.pose.position.y = y

        self._action_client.wait_for_server()
        return self._action_client.send_goal_async(
            goal_msg, feedback_callback=self.feedback_callback)

    def feedback_callback(self, feedback_msg):
        remaining = feedback_msg.feedback.distance_remaining
        self.get_logger().info(f'Distance remaining: {remaining:.2f}m')

十五、机械臂控制与 MoveIt

15.1 MoveIt 概述

MoveIt 是 ROS 中最流行的运动规划框架,支持:

  • 运动学求解(正/逆运动学)
  • 运动规划(RRT、PRM 等算法)
  • 碰撞检测
  • 抓取规划
  • 轨迹执行

15.2 MoveIt 架构

┌──────────────────────────────────────────────┐
│                MoveIt2 架构                    │
│                                                │
│  ┌──────────────┐    ┌──────────────────┐     │
│  │  Move Group   │    │  Planning Scene  │     │
│  │  (运动组)    │    │  (规划场景)     │     │
│  └──────┬───────┘    └────────┬─────────┘     │
│         │                     │                │
│  ┌──────┴───────┐    ┌────────┴─────────┐     │
│  │Motion Planner│    │ Collision Check  │     │
│  │(运动规划器) │    │ (碰撞检测)      │     │
│  └──────────────┘    └──────────────────┘     │
│                                                │
│  ┌──────────────┐    ┌──────────────────┐     │
│  │   Kinematics │    │   ros2_control   │     │
│  │  (运动学)   │    │ (硬件控制)      │     │
│  └──────────────┘    └──────────────────┘     │
└──────────────────────────────────────────────┘

15.3 使用 MoveIt Setup Assistant

# 启动 MoveIt 配置助手(图形化配置工具)
ros2 launch moveit_setup_assistant setup_assistant.launch.py

Setup Assistant 步骤

  1. 加载 URDF 模型
  2. 生成自碰撞矩阵
  3. 定义虚拟关节
  4. 规划组(Planning Group)配置
  5. 定义机器人姿态(预设位姿)
  6. 配置末端执行器
  7. 生成配置文件

15.4 MoveIt 运动规划代码示例

import rclpy
from rclpy.node import Node
from moveit_msgs.action import MoveGroup
from moveit_msgs.msg import (
    MotionPlanRequest, Constraints, JointConstraint,
    PositionConstraint, OrientationConstraint
)
from geometry_msgs.msg import PoseStamped
import moveit_commander

class MoveItDemo(Node):
    def __init__(self):
        super().__init__('moveit_demo')
        moveit_commander.roscpp_initialize([])
        self.robot = moveit_commander.RobotCommander()
        self.scene = moveit_commander.PlanningSceneInterface()
        self.group = moveit_commander.MoveGroupCommander("arm")

    def move_to_joint_goal(self):
        """移动到关节目标"""
        joint_goal = self.group.get_current_joint_values()
        joint_goal[0] = 0.0
        joint_goal[1] = -0.5
        joint_goal[2] = 0.5
        joint_goal[3] = -1.0
        joint_goal[4] = 0.0
        joint_goal[5] = 0.5
        self.group.go(joint_goal, wait=True)
        self.group.stop()

    def move_to_pose_goal(self):
        """移动到笛卡尔目标"""
        pose_goal = PoseStamped()
        pose_goal.header.frame_id = "base_link"
        pose_goal.pose.position.x = 0.4
        pose_goal.pose.position.y = 0.0
        pose_goal.pose.position.z = 0.4
        pose_goal.pose.orientation.w = 1.0
        self.group.set_pose_target(pose_goal)
        plan = self.group.go(wait=True)
        self.group.stop()
        self.group.clear_pose_targets()

    def plan_cartesian_path(self):
        """笛卡尔路径规划(直线运动)"""
        waypoints = []
        wpose = self.group.get_current_pose().pose
        wpose.position.z += 0.1
        waypoints.append(wpose)
        wpose.position.x += 0.2
        waypoints.append(wpose)

        (plan, fraction) = self.group.compute_cartesian_path(
            waypoints, 0.01, 0.0)  # 步长1cm
        self.group.execute(plan, wait=True)

15.5 常用运动规划算法

算法类型特点
RRT单查询快速,但路径可能不优
RRT*单查询RRT 改进,渐近最优
PRM多查询适合静态环境反复查询
KPIECE单查询适合高维空间
BKPIECE单查询双向 KPIECE
LBKPIECE单查询有界的双向 KPIECE
TRRT单查询适合有梯度的环境

十六、仿真环境

16.1 Gazebo

Gazebo 是 ROS 生态中最主流的物理仿真器。

# Gazebo Classic(ROS1 常用)
gazebo --verbose worlds/empty.world

# Gazebo Fortress/Harmonic(ROS2 新版)
# 安装
sudo apt install ros-humble-gazebo-ros-pkgs

# 运行
ros2 launch gazebo_ros gazebo.launch.py

Gazebo 能做什么

  • 物理引擎仿真(重力、摩擦、碰撞)
  • 传感器仿真(激光雷达、相机、IMU、GPS)
  • 机器人模型加载(URDF/SDF)
  • 自定义环境(建筑、地形、物体)
  • 插件系统(自定义行为)

Gazebo 插件示例

<!-- 在 URDF/SDF 中添加 Gazebo 插件 -->
<gazebo>
  <plugin name="differential_drive" filename="libgazebo_ros_diff_drive.so">
    <ros>
      <namespace>/</namespace>
    </ros>
    <update_rate>30</update_rate>
    <left_joint>left_wheel_joint</left_joint>
    <right_joint>right_wheel_joint</right_joint>
    <wheel_separation>0.35</wheel_separation>
    <wheel_diameter>0.2</wheel_diameter>
    <publish_odom>true</publish_odom>
    <publish_odom_tf>true</publish_odom_tf>
  </plugin>
</gazebo>

16.2 TurtleBot3 仿真(入门推荐)

# 安装
sudo apt install ros-humble-turtlebot3*
sudo apt install ros-humble-turtlebot3-simulations

# 设置模型
export TURTLEBOT3_MODEL=burger

# 启动 Gazebo 仿真
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py

# 启动导航
ros2 launch turtlebot3_navigation2 navigation2.launch.py \
  use_sim_time:=true map:=$HOME/map.yaml

# 启动 SLAM
ros2 launch turtlebot3_cartographer cartographer.launch.py \
  use_sim_time:=true

16.3 Webots

Webots 是另一个流行的开源仿真器:

sudo apt install ros-humble-webots-ros2
ros2 launch webots_ros2_universal_robot multirobot_launch.py

16.4 Isaac Sim(NVIDIA)

NVIDIA Isaac Sim 是基于 Omniverse 的高保真机器人仿真平台:

  • 物理级真实的渲染
  • 域随机化(Domain Randomization)
  • 支持 ROS2 接口
  • GPU 加速
  • 适合深度学习训练

16.5 仿真与实际机器人的关系

仿真环境                              真实环境
┌────────────┐                      ┌────────────┐
│  Gazebo    │                      │  真实机器人  │
│            │                      │            │
│ /cmd_vel ──┤                      │ /cmd_vel ──┤
│ /scan    ──┤  ← 接口完全相同 →    │ /scan    ──┤
│ /odom    ──┤                      │ /odom    ──┤
│ /camera  ──┤                      │ /camera  ──┤
│            │                      │            │
│ use_sim_time:=true               │ use_sim_time:=false
└────────────┘                      └────────────┘

Sim2Real(从仿真到现实)的关键:

  • 使用 use_sim_time 参数区分仿真和真实时间
  • 话题和消息接口保持一致
  • 仿真中测试通过后,只需要更换硬件驱动即可部署到真实机器人

十七、ROS 与传感器

17.1 激光雷达(LiDAR)

# 常用激光雷达驱动
sudo apt install ros-humble-rplidar-ros       # RPLidar A1/A2/A3
sudo apt install ros-humble-velodyne-driver    # Velodyne 3D 雷达
sudo apt install ros-humble-hls-lfcd-lidar-driver  # LDS-01(TurtleBot3)

# 查看激光数据
ros2 topic echo /scan
ros2 run rviz2 rviz2  # 添加 LaserScan 显示

LaserScan 消息结构

Header header
float32 angle_min       # 扫描起始角度
float32 angle_max       # 扫描结束角度
float32 angle_increment # 角度分辨率
float32 time_increment  # 测量时间间隔
float32 scan_time       # 完整扫描时间
float32 range_min       # 最小测量距离
float32 range_max       # 最大测量距离
float32[] ranges        # 距离数据数组
float32[] intensities   # 强度数据数组

17.2 相机

# USB 相机
sudo apt install ros-humble-usb-cam
ros2 run usb_cam usb_cam_node_exe --ros-args -p video_device:=/dev/video0

# Intel RealSense 深度相机
sudo apt install ros-humble-realsense2-camera
ros2 launch realsense2_camera rs_launch.py

# 查看图像
ros2 run rqt_image_view rqt_image_view

常用图像话题

话题类型说明
/camera/image_rawsensor_msgs/Image原始图像
/camera/image_colorsensor_msgs/Image彩色图像
/camera/image_depthsensor_msgs/Image深度图像
/camera/pointssensor_msgs/PointCloud2点云数据
/camera/camera_infosensor_msgs/CameraInfo相机内参

17.3 IMU(惯性测量单元)

# 查看 IMU 数据
ros2 topic echo /imu

Imu 消息结构

Header header
geometry_msgs/Quaternion orientation
float64[9] orientation_covariance
geometry_msgs/Vector3 angular_velocity
float64[9] angular_velocity_covariance
geometry_msgs/Vector3 linear_acceleration
float64[9] linear_acceleration_covariance

17.4 GPS

# u-blox GPS
sudo apt install ros-humble-ublox-gps

17.5 多传感器融合

# robot_localization 包 - 扩展卡尔曼滤波融合
sudo apt install ros-humble-robot-localization

融合配置示例(ekf.yaml)

ekf_filter_node:
  ros__parameters:
    frequency: 30.0
    two_d_mode: false
    odom0: /odom
    odom0_config: [true, true, false,
                   false, false, true,
                   true, true, false,
                   false, false, true,
                   false, false, false]
    imu0: /imu
    imu0_config: [false, false, false,
                  true, true, true,
                  false, false, false,
                  true, true, true,
                  true, true, true]
    odom0_differential: false
    imu0_differential: false

十八、ROS 与视觉/AI

18.1 OpenCV 与 ROS 集成

import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import cv2

class VisionNode(Node):
    def __init__(self):
        super().__init__('vision_node')
        self.bridge = CvBridge()
        self.sub = self.create_subscription(
            Image, '/camera/image_raw', self.image_callback, 10)
        self.pub = self.create_publisher(Image, '/processed_image', 10)

    def image_callback(self, msg):
        # ROS Image → OpenCV
        cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")

        # 图像处理
        gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)
        edges = cv2.Canny(gray, 50, 150)

        # OpenCV → ROS Image
        ros_image = self.bridge.cv2_to_imgmsg(edges, "mono8")
        self.pub.publish(ros_image)

18.2 目标检测集成

# 使用 YOLO + ROS2
import torch
from ultralytics import YOLO

class YOLONode(Node):
    def __init__(self):
        super().__init__('yolo_node')
        self.model = YOLO('yolov8n.pt')
        self.bridge = CvBridge()
        self.sub = self.create_subscription(
            Image, '/camera/image_raw', self.detect, 10)

    def detect(self, msg):
        cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")
        results = self.model(cv_image)

        for result in results:
            boxes = result.boxes
            for box in boxes:
                cls = int(box.cls[0])
                conf = float(box.conf[0])
                name = result.names[cls]
                self.get_logger().info(
                    f'Detected: {name} ({conf:.2f})')

18.3 点云处理

import rclpy
from sensor_msgs.msg import PointCloud2
import sensor_msgs_py.point_cloud2 as pc2

class PointCloudNode(Node):
    def __init__(self):
        super().__init__('pointcloud_node')
        self.sub = self.create_subscription(
            PointCloud2, '/points', self.callback, 10)

    def callback(self, msg):
        # 读取点云数据
        points = list(pc2.read_points(msg,
            field_names=("x", "y", "z"), skip_nans=True))

        self.get_logger().info(
            f'Point cloud: {len(points)} points')

18.4 Nav2 路径跟随中的视觉应用

结合视觉和导航的典型场景:

  • 视觉 SLAM(VSLAM)
  • 二维码/ArUco 定位
  • 视觉避障
  • 物体识别后的目标导航

十九、ROS 项目实战开发流程

20.1 典型项目流程

阶段1: 需求分析与系统设计
├── 确定机器人类型(移动/机械臂/复合)
├── 确定传感器配置
├── 确定功能需求(导航/抓取/巡检等)
└── 系统架构设计

阶段2: 模型建立
├── 创建 URDF/Xacro 模型
├── 在 RViz 中验证模型
├── 添加物理属性(惯性、碰撞)
└── 配置 ros2_control 硬件接口

阶段3: 仿真验证
├── 在 Gazebo 中搭建环境
├── 加载机器人模型
├── 测试传感器数据
└── 调试基础运动

阶段4: 功能开发
├── 实现传感器驱动
├── 实现 SLAM 建图
├── 实现导航功能
├── 实现任务逻辑
└── 集成 AI/视觉算法

阶段5: 测试与优化
├── 仿真环境全面测试
├── 性能优化
├── 异常处理
└── 参数调优

阶段6: 部署与调试
├── 迁移到真实机器人
├── 实机调试
├── 参数标定
└── 现场测试

20.2 项目目录结构示例

my_robot_project/
├── src/
│   ├── my_robot_description/         # 机器人描述
│   │   ├── urdf/
│   │   │   ├── robot.urdf.xacro
│   │   │   └── robot.gazebo.xacro
│   │   ├── meshes/
│   │   │   ├── base_link.stl
│   │   │   └── wheel.stl
│   │   ├── config/
│   │   │   └── robot_state_publisher.yaml
│   │   └── launch/
│   │       └── display.launch.py
│   │
│   ├── my_robot_bringup/             # 系统启动
│   │   ├── launch/
│   │   │   ├── robot.launch.py       # 真实机器人启动
│   │   │   ├── sim.launch.py         # 仿真启动
│   │   │   └── nav.launch.py         # 导航启动
│   │   └── config/
│   │       └── nav2_params.yaml
│   │
│   ├── my_robot_navigation/          # 导航功能
│   │   ├── src/
│   │   ├── config/
│   │   └── launch/
│   │
│   ├── my_robot_perception/          # 感知功能
│   │   ├── src/
│   │   │   ├── obstacle_detector.py
│   │   │   └── object_tracker.py
│   │   └── launch/
│   │
│   └── my_robot_bringup/             # 任务逻辑
│       ├── src/
│       │   ├── patrol_node.py        # 巡逻节点
│       │   └── task_manager.py       # 任务管理器
│       └── config/
│           └── patrol_points.yaml
│
└── maps/
    ├── warehouse_map.yaml
    └── warehouse_map.pgm

20.3 实际项目示例:巡检机器人

# patrol_node.py
import rclpy
from rclpy.node import Node
from rclpy.action import ActionClient
from nav2_msgs.action import NavigateToPose
from geometry_msgs.msg import PoseStamped
import yaml

class PatrolNode(Node):
    def __init__(self):
        super().__init__('patrol_node')
        self._nav_client = ActionClient(
            self, NavigateToPose, 'navigate_to_pose')

        # 加载巡检点
        self.patrol_points = self.load_patrol_points()
        self.current_index = 0

        # 定时器:每30秒检查一次
        self.timer = self.create_timer(30.0, self.patrol_callback)

    def load_patrol_points(self):
        with open('/config/patrol_points.yaml', 'r') as f:
            data = yaml.safe_load(f)
        return data['points']

    def patrol_callback(self):
        if not self._nav_client.wait_for_server(timeout_sec=1.0):
            self.get_logger().warn('Navigation server not available')
            return

        point = self.patrol_points[self.current_index]
        goal = NavigateToPose.Goal()
        goal.pose.header.frame_id = 'map'
        goal.pose.pose.position.x = point['x']
        goal.pose.pose.position.y = point['y']
        goal.pose.pose.orientation.w = 1.0

        self.get_logger().info(
            f'Navigating to point {self.current_index}: '
            f'({point["x"]}, {point["y"]})')

        future = self._nav_client.send_goal_async(
            goal, feedback_callback=self.feedback_cb)
        future.add_done_callback(self.goal_response_cb)

    def goal_response_cb(self, future):
        goal_handle = future.result()
        if not goal_handle.accepted:
            self.get_logger().warn('Goal rejected')
            return
        result_future = goal_handle.get_result_async()
        result_future.add_done_callback(self.result_cb)

    def result_cb(self, future):
        self.get_logger().info('Goal reached!')
        self.current_index = (
            self.current_index + 1) % len(self.patrol_points)

    def feedback_cb(self, feedback_msg):
        dist = feedback_msg.feedback.distance_remaining
        self.get_logger().debug(f'Remaining: {dist:.1f}m')

二十、学习路线图与资源推荐

20.1 学习路线图

阶段 0: 基础准备(2-4周)
├── Linux 基础命令
├── Python 基础(或 C++ 基础)
├── 线性代数基础(矩阵、坐标变换)
└── 软件安装与环境配置

阶段 1: ROS 基础入门(4-6周)
├── ROS 核心概念(节点/话题/服务)
├── 命令行工具使用
├── 编写简单发布者/订阅者
├── 编写简单服务端/客户端
├── Launch 文件编写
├── 功能包创建与管理
└── RViz 可视化入门

阶段 2: ROS 进阶(4-6周)
├── TF 坐标变换系统
├── URDF 机器人建模
├── 自定义消息/服务/动作
├── 参数使用与管理
├── rosbag 数据录制与回放
├── RQt 工具使用
└── Gazebo 仿真入门

阶段 3: 机器人功能模块(6-8周)
├── SLAM 建图
├── Navigation 导航
├── MoveIt 机械臂控制
├── 传感器集成(激光/相机/IMU)
├── 多传感器融合
└── 视觉处理与目标检测

阶段 4: 项目实战(8-12周)
├── 完整机器人系统集成
├── 仿真到实际部署
├── 性能优化与调试
├── 多机器人系统
└── 安全与异常处理

阶段 5: 高级主题(持续学习)
├── ROS2 DDS 深入
├── 实时系统与 ros2_control
├── AI 与 ROS 集成
├── 嵌入式 ROS2(micro-ROS)
├── 云机器人
└── 开源贡献

20.2 推荐学习资源

官方资源
资源链接说明
ROS Wikiwiki.ros.orgROS1 官方文档
ROS2 文档docs.ros.orgROS2 官方文档
ROS Discoursediscourse.ros.org官方论坛
ROS Answersanswers.ros.org问答社区
GitHubgithub.com/ros2源码仓库
教程与课程
资源说明
ROS2 官方教程docs.ros.org/en/humble/Tutorials.html
The Construct(在线课程)付费/免费 ROS 课程
古月居(中文社区)古月居 ROS 教程
fishros(中文工具)一键安装脚本和教程
鱼香ROSfishros.com
推荐书籍
书名说明
《ROS Robot Programming》ROS 入门经典
《Programming Robots with ROS》O’Reilly ROS 实战
《Learning ROS for Robotics Programming》ROS 学习指南
《ROS2 Navigation》Nav2 导航详解
《Mastering ROS for Robotics Programming》ROS 进阶
推荐硬件平台
平台价格适合
TurtleBot3 Burger~$500入门学习
TurtleBot4~$2000进阶学习
JetBot~$200AI + 机器人
OpenManipulator~$500机械臂学习
自制小车 + RPLidar~$300-500动手能力强

20.3 快速上手练习

练习 1:Hello ROS
# 安装 demo 包
sudo apt install ros-humble-demo-nodes-cpp ros-humble-demo-nodes-py

# 运行发布者(终端1)
ros2 run demo_nodes_cpp talker

# 运行订阅者(终端2)
ros2 run demo_nodes_py listener

# 使用 rqt_graph 查看节点关系
rqt_graph
练习 2:Turtle 仿真
# 安装
sudo apt install ros-humble-turtlesim

# 启动小乌龟
ros2 run turtlesim turtlesim_node

# 键盘控制
ros2 run turtlesim turtle_teleop_key

# 查看话题
ros2 topic list
ros2 topic echo /turtle1/pose
练习 3:自己写一个控制乌龟的节点
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist

class TurtleController(Node):
    def __init__(self):
        super().__init__('turtle_controller')
        self.pub = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
        self.timer = self.create_timer(0.1, self.move_turtle)
        self.angle = 0.0

    def move_turtle(self):
        msg = Twist()
        msg.linear.x = 1.0
        msg.angular.z = 0.5
        self.pub.publish(msg)

def main():
    rclpy.init()
    node = TurtleController()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

附录:常见问题与排错

A1. 常见错误及解决方案

错误原因解决方案
ros2: command not found未 source 环境source /opt/ros/humble/setup.bash
Package not found包未安装或未 sourcesudo apt install ros-humble-<pkg> 或 source workspace
Could not find the GUIDocker/WSL 中没有显示设置 DISPLAY 环境变量
Node already exists节点名重复更换节点名或使用 anonymous:=True
QoS incompatible发布者和订阅者 QoS 不匹配确保 reliability/durability 兼容
Failed to load robot descriptionURDF 文件路径错误检查文件路径和 launch 参数
Transform lookup failedTF 树不完整检查 TF 广播器是否运行
colcon build 失败依赖缺失rosdep install --from-paths src --ignore-src -r -y

A2. 调试技巧

# 1. 系统诊断
ros2 doctor --report

# 2. 查看节点通信图
rqt_graph

# 3. 查看 TF 树
ros2 run tf2_tools view_frames

# 4. 查看话题数据流
ros2 topic info /topic_name -v

# 5. 检查节点参数
ros2 param dump /node_name

# 6. 查看日志
ros2 log list
ros2 log /tmp/ros2_latest/my_node.log

# 7. GDB 调试
ros2 run --prefix 'gdb -ex run' my_package my_node

# 8. 性能分析
ros2 run rqt_top rqt_top

A3. 性能优化建议

  1. 使用组件(Component):将多个节点组合到同一进程,减少进程间通信开销
  2. 选择合适的 QoS:传感器数据使用 BEST_EFFORT,控制指令使用 RELIABLE
  3. 使用共享内存:ROS2 支持零拷贝的共享内存传输
  4. 减少消息频率:只在必要时使用高频话题
  5. 使用 Intra-Process Communication:同一进程内的节点可以直接传递指针

A4. 环境变量速查

# ROS1
export ROS_MASTER_URI=http://localhost:11311
export ROS_HOSTNAME=localhost
export ROS_IP=192.168.1.100

# ROS2
export ROS_DOMAIN_ID=0          # 域ID(多机器人隔离)
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp  # DDS实现
export ROS_LOCALHOST_ONLY=1     # 仅本机通信
export CYCLONEDDS_URI=file:///path/to/cyclonedds.xml  # DDS配置

# 通用
export TURTLEBOT3_MODEL=burger  # TurtleBot3 模型选择
export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/path/to/models

A5. 多机器人配置

# 机器人1
export ROS_DOMAIN_ID=0
ros2 run my_package robot_node --ros-args -r __ns:=/robot1

# 机器人2
export ROS_DOMAIN_ID=0
ros2 run my_package robot_node --ros-args -r __ns:=/robot2

A6. micro-ROS(嵌入式 ROS2)

micro-ROS 是 ROS2 的嵌入式版本,支持在微控制器上运行:

  • 支持平台:ESP32、STM32、Arduino 等
  • 通信协议:与 ROS2 完全兼容
  • 适用场景:传感器节点、执行器节点
# 创建 micro-ROS 固件
ros2 run micro_ros_setup create_firmware_ws.sh host
ros2 run micro_ros_setup create_agent_ws.sh

# 构建 micro-ROS agent
ros2 run micro_ros_setup build_agent.sh
ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyUSB0

总结

本文档从零基础出发,系统梳理了 ROS 和 ROS2 的核心知识体系:

  1. 概念层面:理解了 ROS 的节点、话题、服务、动作、参数等核心概念
  2. 架构层面:理解了 ROS 的三层架构和 ROS2 去中心化的设计
  3. 工具层面:掌握了命令行工具、RViz、Gazebo 等核心工具
  4. 应用层面:了解了 SLAM、导航、机械臂控制、视觉处理等应用
  5. 实践层面:掌握了从建模到仿真实战的完整开发流程

给新手的建议

  • 先 ROS2:ROS1 即将 EOL,建议直接学习 ROS2
  • 多动手:每学一个概念就写代码验证
  • 从小项目开始:先用 TurtleBot3 仿真,再尝试真实机器人
  • 善用社区:遇到问题搜索 ROS Answers、GitHub Issues
  • 系统学习:跟着官方教程走一遍基础教程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Together_CZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值