
目录
- 一、ROS 概述与背景
- 二、ROS 的核心哲学与设计思想
- 三、ROS 核心概念详解
- 四、ROS 架构体系
- 五、ROS 通信机制深度解析
- 六、ROS 开发环境搭建
- 七、ROS 核心工具链
- 八、ROS 常用功能包与生态
- 九、ROS2 vs ROS1:为什么需要 ROS2
- 十、ROS2 核心概念与新特性
- 十一、ROS2 通信机制详解
- 十二、ROS2 开发环境与工具
- 十三、SLAM(同时定位与地图构建)
- 十四、Navigation 导航栈
- 十五、机械臂控制与 MoveIt
- 十六、仿真环境
- 十七、ROS 与传感器
- 十八、ROS 与视觉/AI
- 十九、ROS 项目实战开发流程
- 二十、学习路线图与资源推荐
- 附录:常见问题与排错
一、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
- 标准化:提供了统一的通信协议和接口标准,不同团队开发的模块可以无缝集成
- 模块化:采用松耦合架构,每个功能模块可以独立开发、测试和替换
- 生态丰富:拥有数千个开源功能包,涵盖导航、SLAM、机械臂控制、视觉处理等
- 社区活跃:全球最大的机器人开发者社区,文档、教程、问答资源丰富
- 跨平台:ROS2 支持 Linux、Windows、macOS 甚至嵌入式系统
- 工业级应用:被 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)──── │ │
└─────┘ └─────┘
动作的三部分:
- Goal(目标):客户端发送任务目标
- Feedback(反馈):服务端周期性反馈执行进度
- 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 的作用:
- 节点注册:每个节点启动时向 Master 注册自己的信息
- 名称服务:帮助节点找到其他节点的网络地址
- 参数管理:存储和管理全局参数
重要: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.04 | ROS1 Noetic / ROS2 Foxy | ⭐⭐⭐⭐ |
| Ubuntu 22.04 | ROS2 Humble(LTS) | ⭐⭐⭐⭐⭐ |
| Ubuntu 24.04 | ROS2 Jazzy(LTS) | ⭐⭐⭐⭐⭐ |
| Windows 10/11 | ROS2(部分支持) | ⭐⭐⭐ |
| macOS | ROS2(实验性) | ⭐⭐ |
推荐:新手建议使用 Ubuntu 22.04 + ROS2 Humble 或 Ubuntu 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 使用虚拟机
对于不想更换操作系统的用户:
- 安装 VirtualBox 或 VMware
- 下载 Ubuntu 22.04 ISO
- 创建虚拟机(建议分配 4GB+ 内存,20GB+ 硬盘)
- 在虚拟机中安装 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版本 | 说明 |
|---|---|---|
| catkin | ROS1 | 基于 CMake 的构建系统 |
| colcon | ROS2 | 通用构建工具,支持 CMake/Python |
| ament | ROS2 | ROS2 的构建框架 |
# 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 / nav2 | ROS1/ROS2 导航栈(路径规划、避障) |
gmapping | 基于激光雷达的 SLAM |
cartographer | Google 开源的 SLAM |
slam_toolbox | ROS2 推荐的 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_simulations | TurtleBot3 仿真 |
turtlebot4_simulator | TurtleBot4 仿真 |
carla | 自动驾驶仿真 |
air_sim | 无人机仿真 |
webots | Webots 仿真器 ROS 接口 |
8.4 感知与视觉
| 功能包 | 说明 |
|---|---|
cv_bridge | OpenCV 与 ROS 图像消息转换 |
image_transport | 高效图像传输 |
vision_opencv | 计算机视觉工具 |
darknet_ros | YOLO 目标检测 |
perception_pcl | 点云处理 |
aruco_ros | ArUco 标记检测 |
depthai_ros | OAK 相机驱动 |
8.5 常用机器人平台
| 平台 | 说明 |
|---|---|
| TurtleBot3/4 | 最流行的 ROS 学习/研究平台 |
| PR2 | Willow 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 对照表
| 概念 | ROS1 | ROS2 |
|---|---|---|
| 构建工具 | catkin_make / catkin_tools | colcon |
| 包格式 | package.xml format 1/2 | package.xml format 3 |
| 构建系统 | catkin | ament_cmake / ament_python |
| Master | roscore | 无(DDS自动发现) |
| 节点 | rospy / roscpp | rclpy / rclcpp |
| 参数 | rosparam | ros2 param |
| 启动 | roslaunch | ros2 launch(Python格式) |
| 包管理 | rosdep | rosdep |
| 录制 | rosbag | ros2 bag |
| 环境 | source devel/setup.bash | source install/setup.bash |
| 通信库 | 自定义TCP/UDP | DDS(中间件) |
| 命名 | /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 配置文件:
| 策略 | 选项 | 说明 |
|---|---|---|
| Reliability | RELIABLE / BEST_EFFORT | 可靠传输 vs 尽力传输 |
| Durability | TRANSIENT_LOCAL / VOLATILE | 保留最后消息 vs 不保留 |
| History | KEEP_LAST(n) / KEEP_ALL | 保留最近n条 / 保留全部 |
| Depth | 数值 | 队列深度 |
| Deadline | 期限 | 预期消息到达间隔 |
| Liveliness | AUTOMATIC / 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 DDS | rmw_fastrtps_cpp | Apache 2.0(默认) |
| RTI Connext DDS | rmw_connextdds | 商业/研究 |
| Eclipse Cyclone DDS | rmw_cyclonedds_cpp | EPL 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) 是机器人在未知环境中同时完成:
- 定位(Localization):确定自己在环境中的位置
- 建图(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 Planner | Dijkstra/A* 算法 |
| Smac Planner | 2D/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 步骤:
- 加载 URDF 模型
- 生成自碰撞矩阵
- 定义虚拟关节
- 规划组(Planning Group)配置
- 定义机器人姿态(预设位姿)
- 配置末端执行器
- 生成配置文件
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_raw | sensor_msgs/Image | 原始图像 |
/camera/image_color | sensor_msgs/Image | 彩色图像 |
/camera/image_depth | sensor_msgs/Image | 深度图像 |
/camera/points | sensor_msgs/PointCloud2 | 点云数据 |
/camera/camera_info | sensor_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 Wiki | wiki.ros.org | ROS1 官方文档 |
| ROS2 文档 | docs.ros.org | ROS2 官方文档 |
| ROS Discourse | discourse.ros.org | 官方论坛 |
| ROS Answers | answers.ros.org | 问答社区 |
| GitHub | github.com/ros2 | 源码仓库 |
教程与课程
| 资源 | 说明 |
|---|---|
| ROS2 官方教程 | docs.ros.org/en/humble/Tutorials.html |
| The Construct(在线课程) | 付费/免费 ROS 课程 |
| 古月居(中文社区) | 古月居 ROS 教程 |
| fishros(中文工具) | 一键安装脚本和教程 |
| 鱼香ROS | fishros.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 | ~$200 | AI + 机器人 |
| 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 | 包未安装或未 source | sudo apt install ros-humble-<pkg> 或 source workspace |
Could not find the GUI | Docker/WSL 中没有显示 | 设置 DISPLAY 环境变量 |
Node already exists | 节点名重复 | 更换节点名或使用 anonymous:=True |
QoS incompatible | 发布者和订阅者 QoS 不匹配 | 确保 reliability/durability 兼容 |
Failed to load robot description | URDF 文件路径错误 | 检查文件路径和 launch 参数 |
Transform lookup failed | TF 树不完整 | 检查 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. 性能优化建议
- 使用组件(Component):将多个节点组合到同一进程,减少进程间通信开销
- 选择合适的 QoS:传感器数据使用 BEST_EFFORT,控制指令使用 RELIABLE
- 使用共享内存:ROS2 支持零拷贝的共享内存传输
- 减少消息频率:只在必要时使用高频话题
- 使用 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 的核心知识体系:
- 概念层面:理解了 ROS 的节点、话题、服务、动作、参数等核心概念
- 架构层面:理解了 ROS 的三层架构和 ROS2 去中心化的设计
- 工具层面:掌握了命令行工具、RViz、Gazebo 等核心工具
- 应用层面:了解了 SLAM、导航、机械臂控制、视觉处理等应用
- 实践层面:掌握了从建模到仿真实战的完整开发流程
给新手的建议:
- 先 ROS2:ROS1 即将 EOL,建议直接学习 ROS2
- 多动手:每学一个概念就写代码验证
- 从小项目开始:先用 TurtleBot3 仿真,再尝试真实机器人
- 善用社区:遇到问题搜索 ROS Answers、GitHub Issues
- 系统学习:跟着官方教程走一遍基础教程
4031

被折叠的 条评论
为什么被折叠?



