ROS2 Humble 2026 最新安装指南及多机器人仿真实战
ROS2 Humble 2026 年最新安装教程,涵盖 Ubuntu 22.04 环境配置、多机器人仿真(Gazebo Harmonic)、ROS2 Control、Nav2 集成,附完整代码示例
ROS2 Humble 2026 最新安装指南及多机器人仿真实战
ROS2 Humble Hawksbill 是当前 LTS 版本,支持到 2027 年 5 月。2026 年,随着 Gazebo Harmonic 的成熟和 ROS2 Control 生态的完善,ROS2 已经成为机器人开发的事实标准。
本文基于 2026 年最新实践,系统讲解 ROS2 Humble 的安装配置、多机器人仿真架构、ROS2 Control 集成,以及与 Nav2 导航框架的配合使用。无论你是 ROS1 迁移用户还是 ROS2 新手,都能快速上手。
你将学到:
- ✅ ROS2 Humble 2026 最新安装方式(apt / Docker / 源码)
- ✅ Gazebo Harmonic 多机器人仿真配置
- ✅ ROS2 Control 硬件抽象层实战
- ✅ 多机器人命名空间与 TF 管理
- ✅ 与 Nav2、MoveIt2 集成
1. ROS2 Humble 安装
1.1 系统要求
- 操作系统:Ubuntu 22.04 LTS(推荐)
- 替代系统:RHEL 9、Windows 10/11、macOS(部分功能)
- 内存:8GB+(推荐 16GB)
- 存储:20GB+(含 Gazebo 和依赖)
- GPU:可选,用于 Gazebo 渲染加速
1.2 安装方式对比
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| apt 安装 | 简单快速,自动依赖 | 版本固定,不易定制 | 生产环境 |
| Docker | 隔离环境,可复现 | 图形界面需额外配置 | CI/CD、多版本共存 |
| 源码编译 | 完全可控,最新功能 | 编译时间长(1-2 小时) | 开发/贡献代码 |
1.3 apt 安装(推荐)
# 设置 locale
sudo apt update && sudo apt install locales -y
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
# 添加 ROS2 软件源
sudo apt install software-properties-common -y
sudo add-apt-repository universe -y
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
-o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] \
http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | \
sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
# 安装 ROS2 Humble Desktop
sudo apt update
sudo apt install ros-humble-desktop -y
# 安装开发工具
sudo apt install ros-dev-tools -y
# 配置环境
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc
# 验证安装
ros2 --version
# 应输出:ros2 1.0.0 或更高
# 运行示例
ros2 run demo_nodes_cpp talker &
ros2 run demo_nodes_py listener
# 应看到消息传递
1.4 Docker 安装
# 拉取官方镜像
docker pull osrf/ros:humble-desktop-full
# 运行容器(带图形界面)
docker run -it --rm \
--net=host \
--privileged \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
osrf/ros:humble-desktop-full \
bash
# 或使用 rocker(推荐,自动处理 X11)
pip install rocker
rocker --x11 --nvidia osrf/ros:humble-desktop-full
1.5 源码编译(高级)
# 安装依赖
sudo apt update && sudo apt install -y \
python3-flake8 python3-pytest python3-pip \
python3-rosdep python3-colcon-common-extensions
# 初始化 rosdep
sudo rosdep init
rosdep update
# 创建工作空间
mkdir -p ~/ros2_humble/src
cd ~/ros2_humble
# 下载源码
wget https://raw.githubusercontent.com/ros2/ros2/humble/ros2_humble.repos
vcs import src < ros2_humble.repos
# 安装依赖
rosdep install --from-paths src --ignore-src --rosdistro humble -y
# 编译
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release
# 配置环境
echo "source ~/ros2_humble/install/setup.bash" >> ~/.bashrc
2. Gazebo Harmonic 多机器人仿真
2.1 Gazebo 版本选择
2026 年,Gazebo 有两个主要版本:
- Gazebo Classic(11.x):已停止维护,仅用于旧项目
- Gazebo Harmonic(新版):推荐,与 ROS2 深度集成
# 安装 Gazebo Harmonic
sudo apt install ros-humble-gz-harmonic -y
# 或安装完整桌面版
sudo apt install ros-humble-gz-harmonic-desktop -y
2.2 创建机器人 URDF
<!-- urdf/simple_robot.urdf.xacro -->
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="simple_robot">
<!-- 参数 -->
<xacro:arg name="namespace" default="robot1"/>
<xacro:arg name="robot_id" default="1"/>
<!-- 基础属性 -->
<xacro:property name="ns" value="$(arg namespace)"/>
<xacro:property name="id" value="$(arg robot_id)"/>
<!-- 底盘 -->
<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="5.0"/>
<inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.1"/>
</inertial>
</link>
<!-- 轮子 -->
<xacro:macro name="wheel" params="prefix x y">
<link name="${prefix}_wheel">
<visual>
<geometry>
<cylinder radius="0.05" length="0.02"/>
</geometry>
<material name="black"/>
</visual>
<collision>
<geometry>
<cylinder radius="0.05" length="0.02"/>
</geometry>
</collision>
<inertial>
<mass value="0.5"/>
<inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
</inertial>
</link>
<joint name="${prefix}_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="${prefix}_wheel"/>
<origin xyz="${x} ${y} -0.05" rpy="${-pi/2} 0 0"/>
<axis xyz="0 0 1"/>
</joint>
</xacro:macro>
<xacro:wheel prefix="left" x="-0.15" y="0.18"/>
<xacro:wheel prefix="right" x="-0.15" y="-0.18"/>
<!-- 激光雷达 -->
<link name="lidar_link">
<visual>
<geometry>
<cylinder radius="0.05" length="0.05"/>
</geometry>
</visual>
</link>
<joint name="lidar_joint" type="fixed">
<parent link="base_link"/>
<child link="lidar_link"/>
<origin xyz="0.2 0 0.1" rpy="0 0 0"/>
</joint>
<!-- Gazebo 插件 -->
<gazebo>
<plugin filename="libgz-sim-ros2-system.so" name="gz::sim::systems::Ros2">
<namespace>${ns}</namespace>
</plugin>
</gazebo>
<gazebo reference="lidar_link">
<sensor type="gpu_lidar" name="lidar">
<pose>0 0 0 0 0 0</pose>
<topic>${ns}/scan</topic>
<update_rate>10</update_rate>
<ray>
<scan>
<horizontal>
<samples>360</samples>
<resolution>1</resolution>
<min_angle>-3.14159</min_angle>
<max_angle>3.14159</max_angle>
</horizontal>
</scan>
<range>
<min>0.1</min>
<max>10.0</max>
</range>
</ray>
</sensor>
</gazebo>
<!-- 差速驱动插件 -->
<gazebo>
<plugin filename="libgz-sim-diff-drive-system.so" name="gz::sim::systems::DiffDrive">
<left_joint>left_wheel_joint</left_joint>
<right_joint>right_wheel_joint</right_joint>
<wheel_separation>0.36</wheel_separation>
<wheel_radius>0.05</wheel_radius>
<odom_publish_frequency>10</odom_publish_frequency>
<topic>${ns}/cmd_vel</topic>
<odom_topic>${ns}/odom</odom_topic>
<tf_topic>${ns}/tf</tf_topic>
<frame_id>${ns}/odom</frame_id>
<child_frame_id>${ns}/base_link</child_frame_id>
</plugin>
</gazebo>
</robot>
2.3 多机器人启动文件
# launch/multi_robot.launch.py
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, GroupAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import PathJoinSubstitution, LaunchConfiguration
from launch_ros.actions import Node, PushRosNamespace
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
pkg_gz_sim = get_package_share_directory('gz_sim')
pkg_robot = get_package_share_directory('my_robot')
# Gazebo 世界文件
world_file = os.path.join(pkg_robot, 'worlds', 'empty.world')
# 启动 Gazebo
gz_sim = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(pkg_gz_sim, 'launch', 'gz_sim.launch.py')
),
launch_arguments={'world': world_file}.items()
)
# 生成多个机器人
robots = []
num_robots = 3
for i in range(num_robots):
namespace = f'robot{i+1}'
x_pos = i * 2.0
# 启动单个机器人
robot_group = GroupAction([
PushRosNamespace(namespace),
# Robot State Publisher
Node(
package='robot_state_publisher',
executable='robot_state_publisher',
name='robot_state_publisher',
parameters=[{
'robot_description':
open(os.path.join(pkg_robot, 'urdf', 'simple_robot.urdf.xacro')).read(),
'frame_prefix': f'{namespace}/'
}]
),
# 在 Gazebo 中生成机器人
Node(
package='gz_sim',
executable='create',
arguments=[
'-name', namespace,
'-x', str(x_pos),
'-y', '0.0',
'-z', '0.1',
'-file', os.path.join(pkg_robot, 'urdf', 'simple_robot.urdf.xacro'),
'--ros-args', '-p', f'namespace:={namespace}'
]
),
])
robots.append(robot_group)
return LaunchDescription([
gz_sim,
*robots,
# RViz
Node(
package='rviz2',
executable='rviz2',
name='rviz2',
arguments=['-d', os.path.join(pkg_robot, 'rviz', 'multi_robot.rviz')]
)
])
2.4 命名空间与 TF 管理
多机器人系统的关键是命名空间隔离和 TF 树管理。
命名空间规则:
- 每个机器人有独立的命名空间:
robot1/,robot2/, … - 所有话题、服务、动作都在命名空间下
- TF 帧使用
frame_prefix区分:robot1/base_link,robot2/base_link
TF 树结构:
world
├── robot1/odom
│ └── robot1/base_link
│ ├── robot1/lidar_link
│ └── robot1/camera_link
├── robot2/odom
│ └── robot2/base_link
│ └── ...
└── robot3/odom
└── robot3/base_link
└── ...
Robot State Publisher 配置:
Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{
'robot_description': robot_desc,
'frame_prefix': 'robot1/', # 关键:添加前缀
'use_sim_time': True
}]
)
3. ROS2 Control 集成
3.1 ROS2 Control 架构
ROS2 Control 是 ROS2 的硬件抽象层,提供统一的控制接口:
┌─────────────────────────────────────────┐
│ Controller Manager │
│ ┌──────────┐ ┌──────────┐ │
│ │ Joint │ │ Diff │ │
│ │ Position │ │ Drive │ ... │
│ │ Controller│ │Controller│ │
│ └──────────┘ └──────────┘ │
├─────────────────────────────────────────┤
│ Hardware Interface │
│ ┌──────────────────────────────────┐ │
│ │ System / Actuator / Sensor │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────┘
3.2 配置 ROS2 Control
<!-- urdf/robot_ros2_control.xacro -->
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<ros2_control name="GazeboSimSystem" type="system">
<hardware>
<plugin>gz_ros2_control/GazeboSimSystem</plugin>
</hardware>
<joint name="left_wheel_joint">
<command_interface name="velocity">
<param name="min">-10</param>
<param name="max">10</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
<joint name="right_wheel_joint">
<command_interface name="velocity">
<param name="min">-10</param>
<param name="max">10</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
</ros2_control>
<gazebo>
<plugin filename="libgz_ros2_control-system.so" name="gz::sim::systems::GzRos2Control">
<parameters>$(find my_robot)/config/controllers.yaml</parameters>
<namespace>$(arg namespace)</namespace>
</plugin>
</gazebo>
</robot>
控制器配置:
# config/controllers.yaml
controller_manager:
ros__parameters:
update_rate: 100 # Hz
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.36
wheel_radius: 0.05
# 里程计
publish_rate: 50.0
base_frame_id: "base_link"
odom_frame_id: "odom"
enable_odom_tf: true
# 速度限制
linear:
x:
has_velocity_limits: true
has_acceleration_limits: true
max_velocity: 1.0
min_velocity: -1.0
max_acceleration: 1.0
angular:
z:
has_velocity_limits: true
has_acceleration_limits: true
max_velocity: 1.5
min_velocity: -1.5
max_acceleration: 2.0
3.3 启动控制器
# launch/controllers.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
# Controller Manager
Node(
package='controller_manager',
executable='ros2_control_node',
parameters=[
os.path.join(get_package_share_directory('my_robot'), 'config', 'controllers.yaml')
],
output='screen'
),
# 加载控制器
Node(
package='controller_manager',
executable='spawner',
arguments=['joint_state_broadcaster', '--controller-manager', '/controller_manager'],
output='screen'
),
Node(
package='controller_manager',
executable='spawner',
arguments=['diff_drive_controller', '--controller-manager', '/controller_manager'],
output='screen'
),
])
4. 多机器人导航
4.1 为每个机器人启动 Nav2
# launch/multi_robot_nav2.launch.py
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, GroupAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
robots = ['robot1', 'robot2', 'robot3']
robot_groups = []
for robot_name in robots:
# 每个机器人独立的 Nav2 实例
nav2_group = GroupAction([
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')
),
launch_arguments={
'namespace': robot_name,
'map': os.path.join(get_package_share_directory('my_robot'), 'maps', 'map.yaml'),
'params_file': os.path.join(get_package_share_directory('my_robot'), 'config', f'nav2_{robot_name}.yaml'),
'use_sim_time': 'true',
'autostart': 'true',
'use_composition': 'false',
'use_respawn': 'true',
}.items()
)
])
robot_groups.append(nav2_group)
return LaunchDescription([
*robot_groups,
])
4.2 多机器人任务分配
#!/usr/bin/env python3
"""多机器人任务分配器"""
import rclpy
from rclpy.node import Node
from nav2_simple_commander.robot_navigator import BasicNavigator
from geometry_msgs.msg import PoseStamped
import tf_transformations
class MultiRobotCoordinator(Node):
def __init__(self):
super().__init__('multi_robot_coordinator')
# 为每个机器人创建导航器
self.robots = {}
for i in range(1, 4):
name = f'robot{i}'
self.robots[name] = BasicNavigator(f'{name}_navigator', namespace=name)
# 等待所有机器人就绪
for name, nav in self.robots.items():
nav.waitUntilNav2Active()
self.get_logger().info(f'{name} 就绪')
# 分配任务
self.assign_tasks()
def assign_tasks(self):
"""分配任务给各机器人"""
tasks = {
'robot1': (2.0, 0.0, 0.0),
'robot2': (0.0, 2.0, 1.57),
'robot3': (-2.0, 0.0, 3.14),
}
for robot_name, (x, y, theta) in tasks.items():
nav = self.robots[robot_name]
goal_pose = PoseStamped()
goal_pose.header.frame_id = 'map'
goal_pose.header.stamp = self.get_clock().now().to_msg()
goal_pose.pose.position.x = x
goal_pose.pose.position.y = y
q = tf_transformations.quaternion_from_euler(0, 0, theta)
goal_pose.pose.orientation.x = q[0]
goal_pose.pose.orientation.y = q[1]
goal_pose.pose.orientation.z = q[2]
goal_pose.pose.orientation.w = q[3]
self.get_logger().info(f'{robot_name} 前往 ({x}, {y})')
nav.goToPose(goal_pose)
# 监控任务进度
self.monitor_tasks()
def monitor_tasks(self):
"""监控所有任务进度"""
rate = self.create_rate(1.0)
while rclpy.ok():
all_done = True
for name, nav in self.robots.items():
if not nav.isTaskComplete():
all_done = False
feedback = nav.getFeedback()
if feedback:
self.get_logger().info(
f'{name}: 距离 {feedback.distance_remaining:.2f}m'
)
if all_done:
self.get_logger().info('所有任务完成!')
break
rate.sleep()
def main():
rclpy.init()
coordinator = MultiRobotCoordinator()
rclpy.spin(coordinator)
rclpy.shutdown()
if __name__ == '__main__':
main()
5. 常见问题与优化
5.1 多机器人 TF 冲突
问题: 多个机器人的 TF 帧名称冲突
解决: 确保每个机器人的 frame_prefix 正确设置
# Robot State Publisher
Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{
'frame_prefix': f'robot{i}/' # 每个机器人不同的前缀
}]
)
5.2 Gazebo 性能优化
问题: 多机器人仿真帧率低
解决:
# 降低物理引擎频率
gz sim -r --physics-engine DART --iterations-per-second 100
# 减少传感器频率
# 激光雷达:10 Hz → 5 Hz
# 相机:30 Hz → 15 Hz
# 使用 headless 模式(无渲染)
gz sim -s -r world.sdf # 服务器模式
5.3 命名空间话题调试
# 查看所有话题
ros2 topic list
# 查看特定机器人的话题
ros2 topic list | grep robot1
# 监听特定话题
ros2 topic echo /robot1/cmd_vel
# 查看 TF 树
ros2 run tf2_tools view_frames
evince frames.pdf
6. FAQ 常见问题
Q1: ROS2 Humble 支持到什么时候?
ROS2 Humble Hawksbill 是 LTS 版本,官方支持到 2027 年 5 月。2026 年仍然是生产环境的推荐选择。
Q2: Gazebo Classic 和 Gazebo Harmonic 有什么区别?
Gazebo Classic(11.x)已停止维护,仅用于旧项目兼容。Gazebo Harmonic 是新版,与 ROS2 深度集成,支持 SDF 1.9、更好的物理引擎、插件系统重构。新项目必须使用 Harmonic。
Q3: 多机器人仿真中 TF 帧冲突怎么解决?
关键是为每个机器人的 robot_state_publisher 设置不同的 frame_prefix。例如 robot1/、robot2/,这样 TF 帧会自动命名为 robot1/base_link、robot2/base_link,避免冲突。
Q4: ROS2 Control 和直接发布 cmd_vel 有什么区别?
直接发布 cmd_vel 是开环控制,无法精确控制轮子转速。ROS2 Control 提供闭环控制、硬件抽象、统一的控制器接口,支持差速驱动、全向轮、机械臂等多种硬件,是生产环境的最佳实践。
Q5: 多机器人导航需要为每个机器人单独启动 Nav2 吗?
是的。每个机器人需要独立的 Nav2 实例,使用不同的命名空间、参数文件(nav2_robot1.yaml、nav2_robot2.yaml)。本文第 4 节提供了完整的启动代码。
7. 相关资源
站内文章:
- ROS2 入门:为机器人搭建通信骨架
- 具身智能是什么?从概念到技术栈全解析
- 具身智能技术栈:传感器、算法、硬件选型指南
- 宇树 Go2 机器狗 ROS2 开发完全指南 2026
- 宇树 Go2 SLAM 导航实战:从建图到自主避障
外部资源:
8. SEO 优化
Title: ROS2 Humble 2026 最新安装指南及多机器人仿真实战
Description: ROS2 Humble 2026 年最新安装教程,涵盖 Ubuntu 22.04 环境配置、多机器人仿真(Gazebo Harmonic)、ROS2 Control、Nav2 集成,附完整代码示例
FAQ Schema:
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "ROS2 Humble 支持到什么时候?",
"acceptedAnswer": {
"@type": "Answer",
"text": "ROS2 Humble Hawksbill 是 LTS 版本,官方支持到 2027 年 5 月。2026 年仍然是生产环境的推荐选择。"
}
},
{
"@type": "Question",
"name": "Gazebo Classic 和 Gazebo Harmonic 有什么区别?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Gazebo Classic(11.x)已停止维护,仅用于旧项目兼容。Gazebo Harmonic 是新版,与 ROS2 深度集成,支持 SDF 1.9、更好的物理引擎、插件系统重构。新项目必须使用 Harmonic。"
}
},
{
"@type": "Question",
"name": "多机器人仿真中 TF 帧冲突怎么解决?",
"acceptedAnswer": {
"@type": "Answer",
"text": "关键是为每个机器人的 robot_state_publisher 设置不同的 frame_prefix。例如 robot1/、robot2/,这样 TF 帧会自动命名为 robot1/base_link、robot2/base_link,避免冲突。"
}
},
{
"@type": "Question",
"name": "ROS2 Control 和直接发布 cmd_vel 有什么区别?",
"acceptedAnswer": {
"@type": "Answer",
"text": "直接发布 cmd_vel 是开环控制,无法精确控制轮子转速。ROS2 Control 提供闭环控制、硬件抽象、统一的控制器接口,支持差速驱动、全向轮、机械臂等多种硬件,是生产环境的最佳实践。"
}
},
{
"@type": "Question",
"name": "多机器人导航需要为每个机器人单独启动 Nav2 吗?",
"acceptedAnswer": {
"@type": "Answer",
"text": "是的。每个机器人需要独立的 Nav2 实例,使用不同的命名空间、参数文件(nav2_robot1.yaml、nav2_robot2.yaml)。"
}
}
]
}
总结
本文系统讲解了 ROS2 Humble 2026 最新实践:
- ✅ 三种安装方式(apt / Docker / 源码)
- ✅ Gazebo Harmonic 多机器人仿真
- ✅ ROS2 Control 硬件抽象层
- ✅ 多机器人命名空间与 TF 管理
- ✅ Nav2 多机器人导航集成
下一步:
- 集成 MoveIt2 实现多机器人操作
- 探索 ROS2 实时性优化(PREEMPT_RT)
- 部署到真实硬件(见 宇树 Go2 ROS2 开发指南、Go2 SLAM 导航实战)
实践出真知,动手试试吧!如果遇到问题,欢迎在评论区交流。