激光导航全流程:从激光测距到机器人运动的完整闭环

激光导航不是一个单一算法,而是一条从光子到车轮的完整数据流水线:激光打出一个光子 → 测出距离 → 拼成点云 → 建成地图 → 算出路径 → 下发速度 → 轮子转动 → 传感器再感知,形成闭环。

下面按数据流动的顺序,逐环节拆解,每个环节讲清楚 "输入是什么、做了什么、输出是什么"。


全流程总览

plaintext

①激光测距 → ②点云预处理 → ③SLAM建图/定位 → ④代价地图 → ⑤路径规划 → ⑥运动控制 → ⑦底盘执行 → 传感器反馈
     ↑                                                                              ↓
     └──────────────────────────── 闭环反馈 ────────────────────────────────────────┘

① 激光测距:从光子到一个距离值

输入:FPGA 触发激光二极管发射脉冲 输出:一个方向上的距离值 d

过程(你之前已经深入了解过):

  1. FPGA 发触发脉冲 → 激光驱动芯片输出大电流 → LD 发出 905nm/1550nm 激光脉冲;
  2. 同步信号 SYNC 给 TDC 的 START,标记 "发射时刻";
  3. 激光打到物体反射回来 → APD/SPAD 接收 → TIA 放大 → 比较器整形 → TDC 的 STOP;
  4. TDC 测飞行时间 t → 距离 d = c × t / 2;
  5. 扫描机构(旋转电机 / MEMS)让激光扫过不同角度,每个角度得到一个 (d, θ, φ)。

输出:一帧 3D 点云(几万个点,每个点含 X/Y/Z/ 反射强度)。

这一步是硬件层,决定了整个导航系统的 "感知上限"—— 测距不准,后面所有算法都白搭。


② 点云预处理:把原始点云变 "干净"

输入:原始 3D 点云

输出:去畸变、去噪、降采样后的可用点云

原始点云不能直接用,有几个问题必须处理:

2.1 运动畸变补偿

  • 激光雷达转一圈需要 50~100ms(10~20Hz),这段时间机器人在运动;
  • 一帧点云的开头和结尾,机器人位置已经变了,导致点云 "拖影";
  • 用 IMU / 里程计记录每个时刻的位姿,把每个点 "投影回" 同一时刻的坐标系,消除畸变。

2.2 去噪

  • 点云里有离群噪点(灰尘、雨滴、激光多径反射产生的假点);
  • 统计滤波:每个点看周围邻居数量,邻居太少的判定为噪点删除;
  • 半径滤波:指定半径内点数少于阈值就删除。

2.3 降采样(体素滤波)

  • 一帧点云几十万点,全处理太慢;
  • 把空间分成小立方体(体素,如 5cm×5cm×5cm),每个立方体内只保留一个点(重心);
  • 点数减少 5~10 倍,精度损失很小,计算速度大幅提升。

2.4 地面分割

  • 地面点占点云的 30~50%,但对避障和建图是 "背景";
  • 用 RANSAC 平面拟合或角度特征,把地面点和非地面点分开;
  • 地面点用来估计 "可通行区域",非地面点用来检测障碍物。

输出:干净、稀疏、分好类的点云,送给 SLAM 和避障模块。


③ SLAM 建图与定位:我在哪?周围什么样?

SLAM = 同步定位与建图,同时回答两个问题:

  • 定位:我现在在什么位置?(x, y, θ)
  • 建图:周围环境长什么样?(地图)

3.1 前端:激光里程计(帧间配准)

输入:当前帧点云 + 上一帧点云 / 子地图 输出:两帧之间的机器人位姿变化(Δx, Δy, Δθ)

方法

  • ICP(迭代最近点):找两帧点云中对应的点对,最小化点对距离,解出位姿变换;
  • NDT(正态分布变换):把点云分成体素,每个体素用正态分布描述,匹配分布而非点,比 ICP 更鲁棒、更快;
  • 特征匹配:提取角点、边线等特征,匹配特征点计算位姿(如 LOAM 系列)。

通俗理解:就像你走路时,靠 "上一秒看到的树和这一秒看到的树位置变化" 来估算自己走了多远、转了多少。

3.2 后端:位姿图优化

输入:前端输出的一连串位姿(有累积误差) 输出:全局优化后的位姿序列

  • 前端帧间配准会有误差,走得越远误差累积越大(漂移);
  • 后端把所有位姿和约束建成一个 "图",用图优化(g2o、Ceres)全局求解,让整体误差最小;
  • 类似把一串有误差的尺子读数,整体拉平到最合理的位置。

3.3 回环检测

输入:当前帧点云 + 历史所有帧 输出:"我回到了以前来过的地方" 的判定

  • 机器人转了一圈回到起点,前端累积误差可能显示 "离起点还有 2m";
  • 回环检测识别出 "这个地方我来过",添加一个闭环约束;
  • 后端根据这个约束,把整条轨迹 "掰正",消除累积漂移。

方法

  • Scan Context:把点云转成环形描述子,快速匹配历史帧;
  • 词袋模型:提取特征点,用视觉词袋匹配(适合视觉,激光也有类似方法)。

3.4 地图输出

输入:优化后的位姿序列 + 所有帧点云 输出

  • 2D 占据栅格地图(pgm+yaml):每个格子标记 "占用 / 空闲 / 未知",供导航用;
  • 3D 点云地图(pcd):完整三维环境,供 3D 导航 / 数字孪生用;
  • OctoMap(.ot):八叉树 3D 地图,高效存储。

建图阶段通常是 "先开着机器人走一圈,把地图建好保存下来";之后导航时不再建图,只用已建好的地图 + 定位。

3.5 定位(AMCL)

输入:已建好的地图 + 当前激光点云 输出:机器人在地图中的精确位姿 (x, y, θ)

  • 建图完成后,导航阶段不需要再建图,只需要 "在已知地图中找到自己在哪";
  • AMCL(自适应蒙特卡洛定位):用粒子滤波,撒一堆 "可能的位置" 粒子,每个粒子对比 "如果我在这个位置,激光应该看到什么" 和 "实际激光看到了什么",相似度高的粒子保留,低的淘汰,最终收敛到真实位置;
  • 通俗理解:就像你在商场里,看一眼周围的店铺,对照商场地图,就知道自己站在哪。

④ 代价地图:把地图变成 "能不能走" 的评分

输入:静态地图 + 实时传感器数据(激光点云) 输出:全局代价地图 + 局部代价地图

光有地图还不够,导航需要知道 "哪里能走、哪里不能走、哪里走起来代价高"。

4.1 全局代价地图

  • 基于静态地图,给每个格子打分;
  • 障碍物本身:代价最高(不可通行);
  • 障碍物附近:膨胀一层(代价较高,避免贴太近);
  • 开阔区域:代价低(优先走)。

4.2 局部代价地图

  • 以机器人为中心的一个小窗口(如 5m×5m),实时更新;
  • 把激光雷达实时看到的动态障碍物(人、车、突然出现的箱子)标记进去;
  • 全局地图是 "静态的世界",局部代价地图是 "实时的世界"。

这一步是避障的基础 —— 规划器看着代价地图,就知道 "该绕开哪里"。


⑤ 路径规划:从 A 到 B 怎么走

输入:起点(当前定位)、终点(目标点)、代价地图 输出一条无碰撞的路径 + 实时速度指令

规划分两层:全局规划(大方向)+ 局部规划(实时避障)。

5.1 全局规划器(Global Planner)

作用:在全局代价地图上,算一条从起点到终点的 "最优路径"。

算法

  • A*:启发式搜索,用 "到终点的估计距离" 引导搜索方向,比 Dijkstra 快;
  • Dijkstra:无启发式,保证找到最短路径,但慢;
  • 输出:一串路径点(waypoints),类似导航 APP 给的路线。

全局规划只算一次(或目标变了重算),不处理动态障碍物。

5.2 局部规划器(Local Planner)

作用:沿着全局路径走,同时实时避开动态障碍物,输出速度指令。

算法

  • DWA(动态窗口法):在速度空间 (v, ω) 里采样一组可能的速度,模拟未来几秒的轨迹,选一条 "不撞障碍物、离全局路径近、速度快" 的轨迹,输出对应的 /cmd_vel;
  • TEB(时间弹性带):把路径当成一条有弹性的带子,在障碍物约束下优化形状和时间,轨迹更平滑,适合差速 / 全向底盘。

输出/cmd_vel 话题,包含线速度 v(m/s)和角速度 ω(rad/s)。

通俗理解:全局规划是 "导航 APP 给的路线",局部规划是 "你开车时实时打方向盘躲行人"。


⑥ 运动控制:把速度指令变成轮子转动

输入:/cmd_vel(线速度 v, 角速度 ω) 输出:左右轮电机 PWM 信号

6.1 差速底盘运动学解算

对于两轮差速小车(你的 ROSMASTER M1 就是这种):

plaintext

左轮速度 = v - ω × L / 2
右轮速度 = v + ω × L / 2
(L = 左右轮间距)

6.2 PID 速度闭环

  • 上面算出的是 "目标轮速",但电机实际转速受负载、电压、地面摩擦影响;
  • 编码器反馈实际轮速 → PID 控制器对比目标和实际 → 调整 PWM 占空比;
  • P(比例):差多少补多少;I(积分):消除稳态误差;D(微分):抑制超调。

6.3 电机驱动

  • MCU 输出 PWM 到电机驱动芯片(TB6612/DRV8833);
  • 驱动芯片放大电流,驱动直流电机转动;
  • 编码器实时反馈转速,形成闭环。

这一步是 "大脑的指令变成肌肉的动作"。


⑦ 避障:三层防护机制

激光导航的避障不是单一模块,而是三层冗余防护

表格

层级 机制 作用 触发条件
第一层 局部规划器避障 正常绕行动态障碍物 激光看到障碍物,局部规划器重新规划轨迹
第二层 代价地图膨胀 保持安全距离 障碍物进入膨胀区,规划器自动绕开
第三层 紧急制动(安全层) 急停 障碍物进入安全距离内,直接发零速度,不管规划器说什么

第三层安全层非常重要 —— 哪怕规划器出 bug,只要激光检测到前方很近有障碍物,直接强制停车,保证安全。


完整闭环数据流(ROS2 视角)

plaintext

激光雷达硬件
    ↓ /scan 或 /pointcloud2
点云预处理节点
    ↓
┌─────────────┬──────────────┐
↓             ↓              ↓
SLAM节点     避障节点      定位节点AMCL
(建图时用)   (更新代价地图)  (导航时用)
↓             ↓              ↓
地图话题    代价地图话题    位姿话题 /tf
└─────────────┴──────────────┘
              ↓
        Nav2规划服务器
        (全局规划+局部规划)
              ↓ /cmd_vel
        底盘控制节点(MCU)
              ↓ PWM
        电机驱动 → 轮子转动
              ↓
        编码器反馈 → 里程计 /odom
              ↓
        机器人移动 → 激光看到新环境 → 回到第一步

各环节常用算法 / 工具汇总

表格

环节 主流算法 / 工具 ROS2 对应包
激光测距 TDC+APD/SPAD 硬件层
点云预处理 体素滤波、统计滤波、RANSAC PCL、pcl_ros
SLAM 前端 ICP、NDT、LOAM 特征 slam_toolbox、cartographer
SLAM 后端 图优化 (g2o/Ceres) slam_toolbox 内置
回环检测 Scan Context、词袋 slam_toolbox 内置
定位 AMCL 粒子滤波 nav2_amcl
全局规划 A*、Dijkstra nav2_planner
局部规划 DWA、TEB nav2_controller
代价地图 膨胀层、障碍物层 nav2_costmap_2d
运动控制 PID、差速运动学 MCU 固件、diff_drive_controller
避障 局部规划 + 安全层 nav2_obstacle_layer、safety_controller

一句话总结

激光导航的全流程 = 激光雷达测距 (硬件) → 点云变干净 (预处理) → 边走边建图同时定位 (SLAM) → 把地图变成可走 / 不可走的评分 (代价地图) → 全局算路线 + 局部躲障碍 (规划) → 速度指令换算成轮子转动 (运动控制) → 传感器再感知形成闭环。每一环都有误差,靠闭环反馈不断修正,最终实现 "指哪走哪、不撞东西"。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐