3、激光雷达基础:工作原理、数据格式、常用型号介绍
各位同学,欢迎来到第三章。从这一章开始,我们正式进入传感器的世界。激光雷达,英文叫 LiDAR,是咱们这套联调系统的「眼睛」。没有它,机械臂就是个瞎子。
我最早接触激光雷达是在 2016 年做 AGV 导航项目的时候。当时被一堆点云数据搞得晕头转向,后来才慢慢摸清门道。今天我就把这些经验掰开揉碎讲给你听。
3.1 工作原理:它到底是怎么看见世界的?
说白了,激光雷达的原理特别简单:发射激光 → 碰到物体 → 反射回来 → 计算时间差 → 算出距离。就这么个循环,每秒重复几万到几十万次。
你想想看,光速是固定的(约 3×10⁸ m/s),只要测出激光从发射到返回的时间 Δt,距离 d 就能算出来:
d = c × Δt / 2
除以 2 是因为光走了个来回。这个公式看着简单,但实际工程里坑不少。我曾在室外强光环境下吃过亏——太阳光里的红外成分会干扰接收器,导致测距跳变。后来加了带通滤光片才解决。
根据扫描方式,市面上主流方案分三种:
| 类型 | 原理 | 典型代表 | 我个人的看法 |
|---|---|---|---|
| 机械旋转式 | 激光收发模块整体旋转 | Velodyne VLP-16 | 技术成熟,但怕震动 |
| 固态式(MEMS) | 微振镜反射激光 | Livox Mid-40 | 体积小,寿命长 |
| 固态式(Flash) | 面阵闪光一次成像 | LeddarTech 系列 | 适合短距,无运动畸变 |
3.2 数据格式:ROS 里的两种「语言」
激光雷达把原始数据发出来,ROS 节点要能读懂。ROS 里封装了两种标准消息类型:LaserScan 和 PointCloud2。我习惯把前者叫「一维扫描线」,后者叫「三维点云」。两者可以互相转换。
3.2.1 LaserScan:单线雷达的「母语」
这种消息结构特别直观。想象你站在一个圆心上,朝四周均匀发射激光,每个角度测一个距离值。把这些距离按角度顺序排成一列,就是 LaserScan。
核心字段如下:
Header header # 时间戳和坐标系ID
float32 angle_min # 扫描起始角度(弧度)
float32 angle_max # 扫描结束角度(弧度)
float32 angle_increment # 角度分辨率(弧度)
float32 time_increment # 每束激光的时间间隔(秒)
float32 scan_time # 整圈扫描时间(秒)
float32 range_min # 最小有效测距(米)
float32 range_max # 最大有效测距(米)
float32[] ranges # 距离数组,长度 = (angle_max - angle_min) / angle_increment
float32[] intensities # 反射强度数组(可选)
避坑指南:我曾经在调试时发现 rviz 里显示的距离全是 inf(无穷大)。查了半天,原来是 frame_id 写成了 laser 而不是 laser_frame。ROS 的 TF 树找不到这个坐标系,直接罢工。记住:header.frame_id 必须和 TF 树里发布的坐标系名称完全一致。
3.2.2 PointCloud2:三维世界的「通用语言」
单线雷达只能扫一个平面。但如果你用多线雷达(比如 16 线、32 线),或者想把多次扫描拼成三维数据,就得用 PointCloud2。
它的结构比 LaserScan 复杂一点:
Header header
uint32 height # 点云高度(1 表示无序点云)
uint32 width # 点云宽度(点数)
PointField[] fields # 每个点的字段定义(x, y, z, intensity...)
bool is_bigendian # 字节序
uint32 point_step # 单个点的字节数
uint32 row_step # 一行点的字节数
uint8[] data # 原始二进制数据
bool is_dense # 是否包含无效点
你可能会问:为什么不用 LaserScan 直接传三维数据?嗯,这里有个历史原因。LaserScan 设计时假设雷达是「单线 + 水平旋转」,数据天然是一维数组。多线雷达的数据是二维网格(线号 × 角度),硬塞进一维数组会丢失结构信息。
3.2.3 两者互转:laser_geometry 包
ROS 官方提供了 laser_geometry 包,里面有个 LaserProjection 类,能把 LaserScan 转成 PointCloud2。用法很简单:
#include <laser_geometry/laser_geometry.h>
laser_geometry::LaserProjection projector;
sensor_msgs::PointCloud2 cloud;
projector.projectLaser(scan_msg, cloud);
但要注意:这个转换默认假设激光束是从原点发出的。如果你的雷达安装位置有偏移,记得先设置好 TF 变换,否则点云位置会错位。我见过有人把雷达装在机械臂末端,没配 TF,结果点云全飘到天花板上了。
3.3 常用型号介绍:选对雷达,少走弯路
市面上的激光雷达品牌多如牛毛。我按应用场景给你梳理几个典型型号,都是 ROS 生态里验证过的:
| 型号 | 线数 | 测距 | 精度 | ROS 支持 | 适合场景 |
|---|---|---|---|---|---|
| RPLIDAR A1/A2 | 单线 | 12m / 16m | ±1% | 官方驱动 | 室内建图、避障 |
| YDLIDAR X4 | 单线 | 10m | ±2% | 社区驱动 | 入门学习、低成本方案 |
| Velodyne VLP-16 | 16 线 | 100m | ±3cm | 官方驱动 | 室外导航、三维感知 |
| Livox Mid-40 | 固态 | 90m | ±2cm | 官方驱动 | 高动态、抗震动 |
| Hesai PandarXT-16 | 16 线 | 80m | ±2cm | 官方驱动 | 工业场景、户外作业 |
3.4 知识体系总览
下面这张图把本章的核心脉络串起来了。你可以把它当作一个「雷达知识地图」:
3.5 动手验证:在 ROS 里「听」雷达说话
光看理论不过瘾。我建议你立刻打开终端,跑一下这个命令(假设你已经接好雷达并启动了驱动):
# 查看 LaserScan 消息
rostopic echo /scan
# 查看 PointCloud2 消息(如果驱动支持)
rostopic echo /velodyne_points
你会看到终端里刷刷刷地往外蹦数据。注意观察 ranges 数组的长度和数值变化。拿本书在雷达前面晃一晃,距离值会跟着变。这就是激光雷达在「看见」物体。
rostopic hz /scan 看看雷达的发布频率。如果是 10Hz,说明每 0.1 秒扫一圈。如果频率突然掉到 5Hz 以下,检查一下 CPU 占用——我遇到过 USB 带宽不够导致丢包的情况,换个 USB 3.0 口就好了。