简介: 本文基于 Ubuntu20.04 + ROS Noetic + Gazebo11,打造零基础保姆级 机器人传感器仿真教程。分为激光雷达、摄像头两个独立完整实验,手把手实现 URDF 机器人建模、Gazebo 传感器插件配置、RViz 可视化、ROS 话题数据分析,适合 ROS 新手入门、高校课程实验、移动机器人实训。
第12天-:传感器+摄像头仿真实验
一、环境前置说明
1. 软硬件环境
-
操作系统:Ubuntu 20.04 LTS
-
ROS 版本:ROS Noetic Ninjemys
-
仿真工具:Gazebo 11(ROS 官方适配仿真器)
提前安装全部依赖包,终端执行:
bash
sudo apt install ros-noetic-desktop-full ros-noetic-gazebo-ros-pkgs
sudo apt install ros-noetic-urdf ros-noetic-rviz
2. 实验设计规范
本文包含两个完全独立的仿真实验 :分别创建独立工作空间、独立功能包、独立 URDF 模型,互不干扰,新手可以单独练习、分步掌握。文末附带激光雷达+摄像头多传感器融合拓展方案。
二、实验1:Gazebo 360°激光雷达仿真
预计耗时:45分钟
2.1 实验目标
-
使用 URDF 搭建简易小车底盘,挂载 360° 激光雷达模型;
-
通过 Gazebo 官方插件实现雷达仿真,自动发布
/scan激光话题; -
RViz 实时可视化激光点云、障碍物测距效果;
-
使用
rostopic工具解析LaserScan原始数据; -
掌握 Gazebo 仿真引擎与 ROS 消息总线的数据流转逻辑。
2.2 核心原理:数据流转链路
Gazebo 物理仿真器负责模拟真实激光测距,通过桥接插件转为 ROS 标准话题,供可视化与算法
bash
Gazebo物理仿真引擎(模拟激光测距)
↓ libgazebo_ros_laser.so 桥接插件
发布 sensor_msgs/LaserScan 消息 → ROS话题 /scan
↙ ↘
RViz可视化显示 终端/自定义节点订阅解析
LaserScan 消息字段详解
bash
header 时间戳 + 雷达坐标系 frame_id
angle_min 扫描起始弧度(-π = -180°)
angle_max 扫描终止弧度(π = 180°)
angle_increment 单束激光角度间隔
range_min 最小探测距离(m)
range_max 最大探测距离(m)
ranges[] 全方位距离数组,inf 代表无障碍物
2.3 完整实操步骤
步骤1:新建独立雷达工作空间 laser_ws
bash
# 创建工作空间与src目录
mkdir -p ~/laser_ws/src
cd ~/laser_ws/src
# 创建功能包,依赖 urdf、gazebo_ros
catkin_create_pkg laser_demo urdf gazebo_ros
# 返回工作空间编译
cd ~/laser_ws
catkin_make
source devel/setup.bash
步骤2:编写带激光雷达的 URDF 模型
文件路径:~/laser_ws/src/laser_demo/urdf/robot_laser.urdf
bash
<?xml version="1.0"?>
<robot name="laser_robot">
<!-- ========== 机器人底盘 ========== -->
<link name="base_link">
<visual>
<geometry>
<!-- 一个简单的盒子作为底盘,长0.3m 宽0.2m 高0.1m -->
<box size="0.3 0.2 0.1"/>
</geometry>
<material name="blue">
<color rgba="0.0 0.0 1.0 1.0"/>
</material>
</visual>
<!-- 碰撞体积,Gazebo 物理引擎用这个计算碰撞 -->
<collision>
<geometry>
<box size="0.3 0.2 0.1"/>
</geometry>
</collision>
<!-- 惯性参数,Gazebo 仿真必须有,否则会报警告 -->
<inertial>
<mass value="1.0"/>
<inertia ixx="0.01" ixy="0" ixz="0"
iyy="0.01" iyz="0" izz="0.01"/>
</inertial>
</link>
<!-- ========== 激光雷达 Link ========== -->
<!-- 激光雷达是一个小圆柱体,安装在底盘顶部中央 -->
<link name="laser_link">
<visual>
<geometry>
<cylinder radius="0.05" length="0.04"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 1.0"/>
</material>
</visual>
<collision>
<geometry>
<cylinder radius="0.05" length="0.04"/>
</geometry>
</collision>
<inertial>
<mass value="0.1"/>
<inertia ixx="0.001" ixy="0" ixz="0"
iyy="0.001" iyz="0" izz="0.001"/>
</inertial>
</link>
<!-- ========== 激光雷达与底盘的关节 ========== -->
<!-- 固定关节:激光雷达相对底盘不动 -->
<!-- xyz="0 0 0.07" 表示激光雷达安装在底盘上方 0.07m 处 -->
<joint name="laser_joint" type="fixed">
<parent link="base_link"/>
<child link="laser_link"/>
<origin xyz="0 0 0.07" rpy="0 0 0"/>
</joint>
<!-- ========== Gazebo 激光雷达插件 ========== -->
<!--
这是整个实验的核心部分!
libgazebo_ros_laser.so 插件让 Gazebo 模拟激光雷达,
并把数据发布到 ROS Topic。
-->
<gazebo reference="laser_link">
<sensor type="ray" name="laser_sensor">
<!-- 传感器安装位置(相对于 laser_link 的偏移) -->
<pose>0 0 0 0 0 0</pose>
<visualize>true</visualize> <!-- 在 Gazebo 中显示激光束(绿线) -->
<update_rate>10</update_rate> <!-- 每秒更新10次 -->
<ray>
<scan>
<horizontal>
<!-- 一共360束激光,覆盖360度 -->
<samples>360</samples>
<resolution>1</resolution>
<min_angle>-3.14159</min_angle> <!-- -180度,单位弧度 -->
<max_angle>3.14159</max_angle> <!-- +180度,单位弧度 -->
</horizontal>
</scan>
<range>
<min>0.1</min> <!-- 最小测量距离:10cm -->
<max>10.0</max> <!-- 最大测量距离:10m -->
</range>
</ray>
<!-- 加载 ROS 激光雷达插件,这个插件完成 Gazebo→ROS 的数据桥接 -->
<plugin name="gazebo_ros_laser" filename="libgazebo_ros_laser.so">
<!-- 数据发布到这个 Topic,RViz 和你的节点都从这里订阅 -->
<topicName>/scan</topicName>
<!-- 坐标系名称,必须与 laser_link 一致 -->
<frameName>laser_link</frameName>
</plugin>
</sensor>
</gazebo>
</robot>
步骤3:编写启动文件 laser_demo.launch
文件路径:~/laser_ws/src/laser_demo/launch/laser_demo.launch
bash
<launch>
<!-- 将URDF加载到参数服务器robot_description -->
<param name="robot_description" textfile="$(find laser_demo)/urdf/robot_laser.urdf" />
<!-- 启动Gazebo空白仿真世界 -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="paused" value="false"/>
<arg name="use_sim_time" value="true"/>
<arg name="gui" value="true"/>
</include>
<!-- 将机器人模型生成到Gazebo -->
<node pkg="gazebo_ros" type="spawn_model" name="spawn_robot"
args="-urdf -model laser_robot -param robot_description" output="screen"/>
<!-- TF坐标转换发布,RViz必备 -->
<node pkg="robot_state_publisher" type="robot_state_publisher" name="state_pub"/>
<!-- 自动启动RViz可视化 -->
<node pkg="rviz" type="rviz" name="rviz_gui" output="screen"/>
</launch>
步骤4:编译并启动仿真
bash
cd ~/laser_ws
catkin_make
source devel/setup.bash
roslaunch laser_demo laser_demo.launch
启动成功后,自动弹出Gazebo仿真窗口 和 RViz可视化窗口。
步骤5:RViz配置激光点云显示
-
RViz 左下角
Add→By Topic; -
找到
/scan下的LaserScan并添加; -
左侧面板将
Fixed Frame修改为base_link; -
在 Gazebo 中拖拽障碍物,RViz 实时刷新红色激光点云测距数据。



步骤6:终端查看激光话题原始数据
bash
source ~/laser_ws/devel/setup.bash
# 1. 查看所有活跃话题
rostopic list
# 2. 查看/scan消息类型
rostopic info /scan
# 3. 实时打印激光测距数据
rostopic echo /scan
# 4. 查看话题发布频率
rostopic hz /scan
关键字段说明:
-
frame_id:laser_link:数据所属传感器坐标系; -
ranges数组:存储360°全方位距离,inf表示无障碍物遮挡。
2.4 实验小结
Gazebo 通过 ray 传感器模拟激光测距,依托 libgazebo_ros_laser.so 桥接插件,将测距数据封装为 ROS 标准 LaserScan 消息对外发布,可直接用于 RViz 可视化、导航避障、算法开发。

三、实验2:Gazebo 摄像头视觉仿真
预计耗时:45分钟
3.1 实验目标
-
独立搭建带前置摄像头的 URDF 小车模型;
-
仿真相机输出图像话题
/camera/image_raw、内参话题/camera/camera_info; -
RViz 实时预览仿真相机画面;
-
掌握 ROS 标准
sensor_msgs/Image图像消息结构。
3.2 雷达 & 相机仿真核心对比
| 对比项 | 激光雷达 | 仿真摄像头 |
|---|---|---|
| 桥接插件 | libgazebo_ros_laser.so | libgazebo_ros_camera.so |
| 核心话题 | /scan | /camera/image_raw |
| 消息类型 | sensor_msgs/LaserScan | sensor_msgs/Image |
| 核心数据 | 距离浮点数组 | RGB 像素字节流 |
| 附属话题 | 无 | /camera/camera_info(相机内参) |
Image 图像消息结构
bash
header 时间戳+相机坐标系
height 图像高度像素
width 图像宽度像素
encoding 像素格式(RGB8/BGR8)
step 单行总字节数(width×3)
data[] 全部像素原始字节流
3.3 完整实操步骤
步骤1:新建相机独立工作空间 camera_ws
bash
mkdir -p ~/camera_ws/src
cd ~/camera_ws/src
catkin_create_pkg camera_demo urdf gazebo_ros
cd ~/camera_ws
catkin_make
source devel/setup.bash
步骤2:相机 URDF 模型
路径:~/camera_ws/src/camera_demo/urdf/robot_camera.urdf
bash
<?xml version="1.0"?>
<robot name="camera_robot">
<!-- ========== 机器人底盘 ========== -->
<link name="base_link">
<visual>
<geometry>
<box size="0.3 0.2 0.1"/>
</geometry>
<material name="green">
<color rgba="0.0 1.0 0.0 1.0"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.3 0.2 0.1"/>
</geometry>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.01" ixy="0" ixz="0"
iyy="0.01" iyz="0" izz="0.01"/>
</inertial>
</link>
<!-- ========== 摄像头 Link ========== -->
<!-- 摄像头用一个小盒子表示,安装在底盘前方顶部 -->
<link name="camera_link">
<visual>
<geometry>
<box size="0.05 0.05 0.05"/>
</geometry>
<material name="red">
<color rgba="1.0 0.0 0.0 1.0"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.05 0.05 0.05"/>
</geometry>
</collision>
<inertial>
<mass value="0.05"/>
<inertia ixx="0.0001" ixy="0" ixz="0"
iyy="0.0001" iyz="0" izz="0.0001"/>
</inertial>
</link>
<!-- ========== 摄像头安装关节 ========== -->
<!--
xyz="0.15 0 0.08":
x=0.15 → 安装在底盘前方 15cm 处
z=0.08 → 高于底盘中心 8cm
-->
<joint name="camera_joint" type="fixed">
<parent link="base_link"/>
<child link="camera_link"/>
<origin xyz="0.15 0 0.08" rpy="0 0 0"/>
</joint>
<!-- ========== Gazebo 摄像头插件 ========== -->
<!--
libgazebo_ros_camera.so 是摄像头的桥接插件。
Gazebo 渲染引擎把摄像头"看到"的画面打包成
sensor_msgs/Image 消息,发布到 ROS Topic。
-->
<gazebo reference="camera_link">
<sensor type="camera" name="camera_sensor">
<update_rate>30.0</update_rate> <!-- 30帧/秒 -->
<visualize>true</visualize>
<camera name="head_camera">
<!-- 水平视场角:80度 -->
<horizontal_fov>1.3962634</horizontal_fov>
<image>
<width>640</width> <!-- 图像宽度(像素) -->
<height>480</height> <!-- 图像高度(像素) -->
<format>R8G8B8</format> <!-- RGB格式,每通道8位 -->
</image>
<clip>
<near>0.02</near> <!-- 近裁剪面:2cm 以内看不到 -->
<far>300</far> <!-- 远裁剪面:300m 以外看不到 -->
</clip>
</camera>
<!-- 加载摄像头插件 -->
<plugin name="gazebo_ros_camera" filename="libgazebo_ros_camera.so">
<!-- 命名空间:Topic 会以此为前缀 -->
<cameraName>camera</cameraName>
<!-- 图像 Topic 名:完整名为 /camera/image_raw -->
<imageTopicName>image_raw</imageTopicName>
<!-- 相机内参 Topic:/camera/camera_info -->
<cameraInfoTopicName>camera_info</cameraInfoTopicName>
<!-- 坐标系名称 -->
<frameName>camera_link</frameName>
</plugin>
</sensor>
</gazebo>
</robot>
步骤3:相机启动文件 camera_demo.launch
路径:~/camera_ws/src/camera_demo/launch/camera_demo.launch
bash
<launch>
<param name="robot_description" textfile="$(find camera_demo)/urdf/robot_camera.urdf" />
<!-- 启动Gazebo空世界 -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="paused" value="false"/>
<arg name="use_sim_time" value="true"/>
<arg name="gui" value="true"/>
</include>
<!-- 生成小车模型 -->
<node pkg="gazebo_ros" type="spawn_model" name="spawn_car"
args="-urdf -model camera_robot -param robot_description" output="screen"/>
<node pkg="robot_state_publisher" type="robot_state_publisher" name="tf_pub"/>
<node pkg="rviz" type="rviz" name="rviz_cam" output="screen"/>
</launch>
步骤4:启动相机仿真
bash
cd ~/camera_ws
catkin_make
source devel/setup.bash
roslaunch camera_demo camera_demo.launch
步骤5:RViz 显示相机画面
-
RViz
Add→By Topic,选中/camera/image_raw的 Image 组件; -
在 Gazebo 小车前方放置任意模型,RViz 实时渲染相机成像画面。



步骤6:终端查看图像话题数据
bash
source ~/camera_ws/devel/setup.bash
# 查看所有相机相关话题
rostopic list | grep camera
# 查看图像消息类型
rostopic info /camera/image_raw
# 仅打印图像元数据,屏蔽超长像素数组
rostopic echo /camera/image_raw --noarr
# 查看相机输出帧率
rostopic hz /camera/image_raw
3.4 实验小结
Gazebo 相机插件将仿真渲染画面转为标准 ROS 图像消息,并同步输出相机内参,可直接用于目标检测、视觉识别、视觉 SLAM 算法仿真开发。
四、拓展实验:激光雷达+摄像头多传感器融合仿真思路
基于以上两个独立实验,可快速搭建多传感器融合机器人模型,实现激光测距+视觉成像同步感知:
-
新建综合工作空间,编写融合版 URDF,同时包含底盘、雷达、相机 link 与固定关节;
-
在同一个 URDF 中同时配置
ray雷达插件与camera相机插件; -
复用 launch 启动文件,仅替换融合模型;
-
RViz 同时添加 LaserScan、Image 组件,同步观测距离数据与视觉画面;
-
编写 Python 订阅节点,同时订阅
/scan与/camera/image_raw,实现视觉+激光多传感器融合。
五、全文总结
本文从零完成了 Gazebo 激光雷达、摄像头双传感器仿真,覆盖 URDF 机器人建模、Gazebo 插件配置、RViz 可视化、ROS 话题数据分析全流程,是新手入门 ROS 机器人仿真的完整闭环教程。
核心学习要点:
-
Gazebo 插件是连接仿真物理引擎与 ROS 消息总线的核心桥梁;
-
激光、相机传感器对应 ROS 标准消息固定,可无缝对接导航、视觉、避障算法;
-
双工作空间独立设计,低耦合、易练习、易拓展;
-
通过 rostopic 工具可快速解析传感器数据,适配算法调试与实验分析。