Gazebo是一个强大的机器人仿真平台,提供物理引擎、传感器模拟和3D可视化。它允许在虚拟环境中测试机器人算法,无需真实硬件。
# 安装Gazebo与ROS2桥接
sudo apt install ros-humble-gazebo-ros-pkgs
# 启动空世界
ros2 launch gazebo_ros gazebo.launch.py
# 启动自定义世界
ros2 launch gazebo_ros gazebo.launch.py world:=/path/to/my_world.world
# Gazebo经典版(Gazebo 11)
gazebo --verbose
<?xml version="1.0" ?>
<!-- my_world.world - 自定义仿真世界 -->
<sdf version="1.6">
<world name="my_robot_world">
<physics type="ode">
<max_step_size>0.001</max_step_size>
<real_time_factor>1.0</real_time_factor>
</physics>
<include>
<uri>model://sun</uri>
</include>
<include>
<uri>model://ground_plane</uri>
</include>
<model name="box_obstacle">
<static>true</static>
<pose>3 2 0.5 0 0 0</pose>
<link name="link">
<collision name="collision">
<geometry>
<box><size>1 1 1</size></box>
</geometry>
</collision>
<visual name="visual">
<geometry>
<box><size>1 1 1</size></box>
</geometry>
<material>
<ambient>0.8 0.2 0.2 1</ambient>
</material>
</visual>
</link>
</model>
<model name="cylinder_obstacle">
<static>true</static>
<pose>-2 3 0.75 0 0 0</pose>
<link name="link">
<collision name="collision">
<geometry>
<cylinder><radius>0.5</radius><length>1.5</length></cylinder>
</geometry>
</collision>
<visual name="visual">
<geometry>
<cylinder><radius>0.5</radius><length>1.5</length></cylinder>
</geometry>
<material>
<ambient>0.2 0.2 0.8 1</ambient>
</material>
</visual>
</link>
</model>
<model name="wall_1">
<static>true</static>
<pose>0 5 0.5 0 0 0</pose>
<link name="link">
<collision name="collision">
<geometry><box><size>10 0.2 1</size></box></geometry>
</collision>
<visual name="visual">
<geometry><box><size>10 0.2 1</size></box></geometry>
<material><ambient>0.5 0.5 0.5 1</ambient></material>
</visual>
</link>
</model>
</world>
</sdf>
<?xml version="1.0"?>
<!-- 差速驱动机器人URDF (含Gazebo插件) -->
<robot name="diff_bot">
<link name="base_link">
<visual>
<geometry><box size="0.5 0.3 0.15"/></geometry>
<material name="blue"><color rgba="0 0 0.8 1"/></material>
</visual>
<collision>
<geometry><box size="0.5 0.3 0.15"/></geometry>
</collision>
<inertial>
<mass value="5.0"/>
<inertia ixx="0.1" ixy="0" ixz="0" iyy="0.2" iyz="0" izz="0.2"/>
</inertial>
</link>
<link name="left_wheel">
<visual>
<geometry><cylinder radius="0.075" length="0.05"/></geometry>
<material name="black"/>
</visual>
<collision>
<geometry><cylinder radius="0.075" length="0.05"/></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="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="0 0.175 0" rpy="-1.5707 0 0"/>
<axis xyz="0 0 1"/>
</joint>
<link name="right_wheel">
<visual>
<geometry><cylinder radius="0.075" length="0.05"/></geometry>
</visual>
<collision>
<geometry><cylinder radius="0.075" length="0.05"/></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="right_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="right_wheel"/>
<origin xyz="0 -0.175 0" rpy="-1.5707 0 0"/>
<axis xyz="0 0 1"/>
</joint>
<link name="laser_link">
<visual>
<geometry><cylinder radius="0.05" length="0.04"/></geometry>
</visual>
</link>
<joint name="laser_joint" type="fixed">
<parent link="base_link"/>
<child link="laser_link"/>
<origin xyz="0.25 0 0.1"/>
</joint>
<gazebo>
<plugin name="diff_drive" filename="libgazebo_ros_diff_drive.so">
<alwaysOn>true</alwaysOn>
<update_rate>50</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.15</wheel_diameter>
<command_topic>cmd_vel</command_topic>
<odometry_topic>odom</odometry_topic>
<odometry_frame>odom</odometry_frame>
<base_frame_id>base_link</base_frame_id>
<publish_odom>true</publish_odom>
<publish_odom_tf>true</publish_odom_tf>
<publish_wheel_tf>true</publish_wheel_tf>
<max_wheel_torque>20</max_wheel_torque>
<max_wheel_acceleration>1.0</max_wheel_acceleration>
</plugin>
</gazebo>
<gazebo reference="laser_link">
<sensor name="laser" type="ray">
<pose>0 0 0 0 0 0</pose>
<visualize>true</visualize>
<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>
<resolution>0.01</resolution>
</range>
</ray>
<plugin name="laser_plugin" filename="libgazebo_ros_ray_sensor.so">
<ros>
<namespace>/</namespace>
<remapping>~/out:=scan</remapping>
</ros>
<output_type>sensor_msgs/LaserScan</output_type>
</plugin>
</sensor>
</gazebo>
</robot>
#!/usr/bin/env python3
"""在Gazebo中Spawn机器人"""
import os
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, ExecuteProcess
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
pkg_dir = get_package_share_directory('my_robot_pkg')
urdf_file = os.path.join(pkg_dir, 'urdf', 'diff_bot.urdf')
# 启动Gazebo
gazebo = IncludeLaunchDescription(
PythonLaunchDescriptionSource([
os.path.join(get_package_share_directory('gazebo_ros'),
'launch', 'gazebo.launch.py')
]),
launch_arguments={'world': os.path.join(
pkg_dir, 'worlds', 'my_world.world')}.items()
)
# 发布机器人描述
robot_state_publisher = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{'robot_description': open(urdf_file).read()}],
output='screen'
)
# Spawn机器人到Gazebo
spawn_entity = Node(
package='gazebo_ros',
executable='spawn_entity.py',
arguments=[
'-entity', 'diff_bot',
'-file', urdf_file,
'-x', '0', '-y', '0', '-z', '0.1',
],
output='screen'
)
return LaunchDescription([
gazebo,
robot_state_publisher,
spawn_entity,
])
# 键盘控制
ros2 run teleop_twist_keyboard teleop_twist_keyboard
# 命令行发布速度
ros2 topic pub /cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 0.5}, angular: {z: 0.3}}"
# 查看里程计
ros2 topic echo /odom
# 查看激光扫描
ros2 topic echo /scan --once
创建一个包含墙壁、箱子和圆柱的Gazebo世界,在其中Spawn差速驱动机器人。
在URDF中添加IMU传感器插件,查看 /imu 话题数据。
使用teleop_twist_keyboard在Gazebo中遥控机器人,观察里程计和激光数据变化。
经验值:+200 XP