第四章 激光雷达仿真:从原理到实战
各位同学,今天我们来聊聊激光雷达仿真。说实话,这个传感器在自动驾驶和机器人领域,地位相当特殊。它不像摄像头那样受光照影响大,也不像毫米波雷达那样分辨率低。激光雷达,说白了就是「用光来丈量世界」。
我在做第一个自动驾驶项目时,就吃过激光雷达的亏。当时买的16线雷达,跑在校园里好好的,一到雨天就乱跳。后来才明白——仿真环境里不把噪声和遮挡考虑进去,实车测试就是灾难。所以这一章,我会把LiDAR的底裤都扒干净。
4.1 LiDAR工作原理:光速测距的奥秘
激光雷达的核心原理,其实就一句话:发射激光,测量回波时间。你想想看,光速是恒定的3×10⁸ m/s,只要测出激光从发射到返回的时间差Δt,距离d = c × Δt / 2 就出来了。
但实际工程中,没这么简单。我遇到过一个问题:激光打到玻璃上,一部分反射一部分透射,回波信号会分裂成两个峰。这时候单纯用峰值检测,距离就错了。所以仿真里,我们通常用两种模型:
| 测距方式 | 原理 | 仿真精度 | 我踩过的坑 |
|---|---|---|---|
| TOF(飞行时间) | 直接测量脉冲往返时间 | 高(±2cm) | 多径反射会导致虚假回波 |
| FMCW(调频连续波) | 测量发射与回波的频率差 | 极高(±1mm) | 对振动敏感,仿真要加抖动 |
| 三角法 | 基于成像位置计算 | 中(近距离) | 远距离精度急剧下降 |
我个人习惯在仿真中用TOF模型,因为它计算量小,而且大多数真实雷达也是TOF。但注意——仿真里一定要加量化误差。真实ADC采样率有限,比如1GHz采样率,时间分辨率就是1ns,对应距离分辨率15cm。这个不模拟,你的点云会「假干净」。
核心公式(仿真必用):
// 激光雷达点云生成伪代码
for each laser_beam in lidar:
hit_point = raycast(origin, direction, max_range)
if hit_point exists:
distance = norm(hit_point - origin)
// 加高斯噪声
noisy_distance = distance + gaussian_noise(sigma=0.02)
// 量化到雷达分辨率
quantized_distance = round(noisy_distance / resolution) * resolution
point_cloud.append(hit_point)
else:
// 超出范围返回最大距离
point_cloud.append(origin + direction * max_range)
4.2 点云生成算法:从射线到三维世界
点云怎么来的?其实就是一根根激光射线打出去,碰到物体就记录一个点。多线激光雷达,就是多根激光束同时旋转。
我记得第一次写点云生成算法时,犯了个低级错误:没考虑激光束的发散角。真实激光束不是一条线,而是一个圆锥。打到远处,光斑直径可能达到几十厘米。一个点打在一棵树的边缘,回波可能是树叶和树干混合的结果。仿真里如果不模拟这个,点云边缘会过于锐利。
这里我给出一个实用的多线激光雷达配置表:
| 参数 | Velodyne HDL-64E | Ouster OS1-64 | 仿真推荐值 |
|---|---|---|---|
| 线数 | 64 | 64 | 16/32/64 |
| 水平视场角 | 360° | 360° | 360° |
| 垂直视场角 | 26.9° | 45° | 30° |
| 垂直角分辨率 | 0.4° | 0.7° | 0.5°~1° |
| 水平角分辨率 | 0.09° | 0.05° | 0.1°~0.2° |
| 最大测距 | 120m | 120m | 100m |
| 帧率 | 10Hz | 10Hz | 10Hz |
配置多线雷达时,有个技巧:垂直角分辨率决定了你能看到多小的物体。比如一个20cm高的路沿,如果垂直角分辨率是0.4°,在50米外,相邻两线之间的间距是50×tan(0.4°)≈0.35m。也就是说,路沿可能正好落在两条激光线之间,完全看不到。仿真里要复现这个效果,就得按真实角度间隔来发射射线。
我的经验:仿真时别用均匀角度分布。真实雷达的激光线束,中间密、两边疏。比如Velodyne的64线,中间0°附近线间距只有0.2°,边缘却达到1°。你可以在仿真配置里加一个「角度偏移表」,模拟这种非均匀分布。
4.3 多线激光雷达配置:参数调优实战
配置多线激光雷达,说白了就是回答三个问题:多少线?转多快?打多远?
我见过很多新手,一上来就配64线、360°、100米。结果仿真跑起来,一帧点云几十万个点,算法直接卡死。你想想看,真实场景里,64线雷达一帧大约13万个点,如果仿真里每个点都做精确的物理计算,CPU扛不住的。
所以我的建议是:
- 算法验证阶段:用16线,水平分辨率0.2°,帧率5Hz。点云量控制在2万点/帧以内。
- 系统集成阶段:用32线,水平分辨率0.1°,帧率10Hz。点云量约6万点/帧。
- 最终验证阶段:用64线,真实参数。这时候才需要完整的噪声和遮挡模型。
另外,旋转速度很关键。10Hz的旋转频率,意味着每100ms转一圈。如果仿真步长是10ms,那每步雷达只转了3.6°。这意味着——相邻两帧的点云,空间位置是错开的。做点云配准时,这个运动畸变必须考虑。
警告:千万不要在仿真里用「瞬间扫描」模型。真实雷达是一边旋转一边采集的,同一帧内,第一个点和最后一个点的时间差是100ms。如果车辆在高速运动,这100ms里车已经走了好几米。不模拟这个,你的点云配准算法在仿真里跑得飞起,一上实车就废。
4.4 噪声与遮挡模拟:让仿真更真实
这一节,是仿真和实车差距最大的地方。我当年就栽在这里——仿真里点云干净得像艺术品,一上路全是噪点。
噪声主要来自三个方面:
- 测距噪声:高斯分布,标准差约1-3cm。远距离会更大,因为信噪比下降。
- 反射率噪声:低反射率物体(黑色车漆、沥青路面)回波弱,容易丢失点。
- 环境噪声:阳光直射、雨雾、灰尘,会产生大量虚假点。
我在仿真里常用的噪声模型是这样的:
def add_lidar_noise(point, distance, reflectivity):
# 1. 测距噪声:随距离增大
range_noise = np.random.normal(0, 0.01 + 0.001 * distance)
# 2. 反射率影响:低反射率更容易丢失
if reflectivity < 0.1:
if np.random.random() < 0.3: # 30%概率丢失
return None
# 3. 环境噪声:随机虚假点
if np.random.random() < 0.005: # 0.5%概率产生噪点
fake_point = point + np.random.uniform(-5, 5, 3)
return fake_point
return point + range_noise
遮挡模拟更复杂。真实场景里,一个物体可能被另一个物体部分遮挡。比如行人站在车后面,激光可能只打到行人的上半身。仿真里如果只用简单的射线检测,要么完全看不到,要么完全看到。我建议用多射线采样——每个激光束发射多条子射线,统计命中率。比如10条子射线里有7条打中行人,那就生成一个点,强度设为0.7。
遮挡模拟的核心思路:
不要用「是否命中」的二元逻辑,而是用「命中概率」的连续逻辑。每个激光束对应一个圆锥体,圆锥体内均匀采样N条射线,统计命中物体的比例。这样既能模拟边缘遮挡,又能模拟部分反射。
嗯,这里要注意:雨雾模拟是另一个大坑。我建议用衰减模型——激光在雨雾中传播,强度按指数衰减。当回波强度低于检测阈值时,就认为没有回波。这个阈值怎么设?我一般取最大距离处回波强度的10%。
最后,给大家看一张我常用的激光雷达仿真流程图:
这张图我画了很多遍,每次带新人都会拿出来讲。核心就一点:不要跳过噪声和遮挡。很多仿真框架默认不模拟这些,你得自己加。我习惯在碰撞检测之后、点云输出之前,插入一个「物理效应层」,专门处理噪声、遮挡、衰减这些真实世界才有的东西。
好了,这一章的内容就到这里。记住:仿真做得越真实,实车测试的坑就越少。别偷懒,把噪声和遮挡加上去,你的算法会感谢你的。
可可精品资料库