NoeticMaze 在 STM32 上实现激光雷达 SLAM 与自主导航的工程拆解

声明: NoeticMaze 是一个开源项目,作者为 dokdo2013。本文是阅读该项目源码和文档后整理的学习笔记,用于理解嵌入式 SLAM 的工程实现方式。本文作者不是该项目的开发者,未参与该项目的任何代码贡献。 文中所有工程细节均来自对开源代码的分析,不代表本文作者的设计决策。

NoeticMaze 是一个基于 STM32F446RE 的室内自主导航项目。它把激光雷达建图、ICP 定位、A* 路径规划、路径跟踪、动态重规划和蓝牙遥测放进了一个资源受限的 MCU 工程。

这篇文章按照技术报告的完整度展开,不把项目压缩成一句“STM32 跑 SLAM”。真正有价值的部分在工程边界:地图怎么存,点云怎么传,ICP 怎么收敛,A* 怎么在小内存里搜索,FreeRTOS 任务怎么隔离实时控制和重计算。

1. 感知
雷达 DMA 组帧,IMU 和编码器给运动预测。
2. 定位与建图
ICP 输出位姿,Bresenham 写入占据栅格。
3. 规划
2cm 地图降采样到 10cm,A* 搜索低代价路径。
4. 控制
MotionTask 100Hz 跟踪路径并输出左右轮 PWM。

核心结论

NoeticMaze 在 STM32F446RE 上实现了基于激光雷达的局部室内建图与自主导航闭环。系统不依赖 ROS,不依赖上位机做核心算法,也不使用预先导入的地图。

完整导航能力包括:

它的适用范围也很明确:固定 4m x 4m 局部地图、小型室内或迷宫场景、相对静态环境。它没有回环检测,不适合长距离、大范围、动态人群环境。

几个需要先厘清的技术边界

理解 NoeticMaze 前,需要先厘清几个技术边界。它们直接影响后面对定位、建图、路径规划和避障机制的判断。

第一,地图是在线构建的。机器人开机后从零开始扫描环境,通过 ICP 估计位姿,再用 Bresenham 光线追踪把扫描结果写入占据栅格。用户是在实时构建的地图上点选目标。

第二,ICP 采用帧到帧匹配。系统维护参考扫描帧,用当前扫描和参考帧做点到线匹配,估计相对运动,再把定位结果用于建图。

第三,A* 结果应描述为低代价可行路径。项目在 10cm 规划图上做 8 连通搜索,并叠加障碍膨胀、未知区策略、旧路径亲和度和路径平滑。

第四,当前雷达任务没有真正按车体运动状态做跳帧。代码中有运动学跳帧相关宏和设计意图,但实际数据流是每完成一圈 360 度扫描后投递给 SLAM;队列满时复用当前缓冲。

第五,路径规划结果通过全局路径快照和事件标志通知运动线程。系统里存在 RespQueueHandle,但最终路径发布机制是全局路径公告板。

硬件平台与资源约束

NoeticMaze 使用 NUCLEO-F446RE,核心 MCU 是 STM32F446RE。

参数 规格
内核 ARM Cortex-M4F,带单精度 FPU
主频 180MHz
Flash 512KB
SRAM 128KB
FPU fpv4-sp-d16,支持单精度浮点
RTOS FreeRTOS v10.3.1,CMSIS-RTOS v2 API
FreeRTOS 堆 30720 Bytes,约 30KB
开发板 NUCLEO-F446RE

外围传感器和通信接口如下。

模块 型号或规格 接口 用途
激光雷达 460800 波特率,84 字节/帧,40 点/帧 USART1 + DMA 360 度环境距离扫描
IMU MPU6500,3 轴陀螺仪 + 3 轴加速度计 SPI2 Yaw 和角速度
编码器 MC520P30_12V,30:1 减速比,4 倍频 1560 ticks/圈 TIM3/TIM4 正交解码 轮速与里程计
蓝牙 透传模块 USART3 + DMA,912600 波特率 上位机遥测和目标点指令

底盘参数如下。

参数
驱动方式 双轮差速驱动
轮径 70mm
轮距 180mm
最大线速度 0.8m/s
巡航线速度 0.35m/s
最大角速度 60deg/s
电机 PWM 频率 45kHz

128KB SRAM 是最紧的资源。系统同时需要维护地图、雷达帧、点云、A* 队列、任务栈、蓝牙缓冲和各种状态快照。

关键内存压力包括:

这些数字解释了项目为什么大量使用静态内存、打包存储、零拷贝队列和降采样规划图。桌面端常见的宽松数据结构,在这个平台上很快会把 RAM 用完。

总体数据流

系统可以按以下链路理解:

激光雷达 USART1 + DMA
  -> LidarRouteTask 解析并组装 360 度扫描
  -> LidarQueueHandle 投递 LidarMap_t 指针
  -> AlgorithmBrain 执行 ICP、建图、降采样、重规划判断
  -> ReqQueueHandle 向 PlannerTask 请求路径
  -> PlannerTask 在 10cm 地图上运行 A* 和路径平滑
  -> 全局路径公告板 + PathEventHandle 发布路径
  -> MotionTask 100Hz 跟踪路径并控制电机
  -> TaskPrint 通过蓝牙发送地图、路径、位姿和调试事件

高频运动控制和低频规划分开。A* 可以按需运行,MotionTask 必须稳定周期运行。雷达、SLAM、Planner 和蓝牙也通过队列和事件解耦,避免单个模块卡住整个控制链。

激光雷达数据采集与预处理

激光雷达数据通过 USART1 + DMA 循环接收。UART 空闲中断触发解析逻辑,LidarRouteTask 将原始帧组装为完整的 360 度扫描。

协议结构要点:

代码采用动态插值解算器。解析时根据相邻两帧起始角度差计算精确步长,补偿雷达电机转速波动。

距离过滤也分层处理。雷达模块有效距离宏为 30mm ~ 10000mm,SLAM 转换点云时使用 30mm ~ 8000mm 过滤,无效点标记为 0。

雷达坐标系还有方向适配问题。该雷达角度方向是顺时针为正,而常见 SLAM 坐标系按逆时针为正。极坐标转笛卡尔坐标时需要做角度方向转换,并叠加雷达安装角补偿。

零拷贝雷达缓冲池

系统维护 3 个静态 LidarMap_t 缓冲区,通过 FreeRTOS 队列实现借还机制。

空闲池 -> 雷达任务填充 -> 就绪队列 -> SLAM 任务处理 -> 归还空闲池

队列里传的是指针,不复制整帧数据。当前结构体每个约 736B,三块缓冲区可以接受;如果每帧复制,会浪费 CPU 和 SRAM。

运动学跳帧的当前状态

代码中定义了直行跳帧、转弯全投递、角速度阈值等宏,设计意图是转弯时全量处理,直行或静止时降低投递频率。

当前 StartLidarRouteTask() 没有读取角速度,也没有使用这些宏丢弃直行帧。实际行为是每完成一圈 360 度扫描后,队列可用就投递给 SLAM;队列满时复用当前缓冲。

因此,文章里不能把“直行时每 10 帧只处理 1 帧”写成已生效优化,只能写成预留设计。

IMU 与轮式里程计

MPU6500 使用 DMP 做姿态解算。DMP 在传感器内部输出四元数,STM32 通过 SPI2 读取结果,减少 MCU 上的姿态计算负担。

参数
IMU MPU6500
通信 SPI2
DMP 输出 6 轴低功耗四元数
采样率 200Hz
主要输出 Yaw、角速度

差速底盘的里程计来自左右轮编码器。系统根据左右轮位移计算线位移和角位移,给 ICP 提供初始猜测。轮式里程计会因为打滑、地面不平和积分误差逐步漂移,但短时间运动预测很有价值。

ICP 如果从零初值开始匹配,容易收敛慢或落到错误局部最优。里程计和 IMU 预测让 ICP 从一个接近真实运动的位置开始迭代,减少计算量,也提高匹配稳定性。

ICP 定位:帧到帧点到线匹配

室内机器人需要持续回答“我在哪里”。编码器可以推算位移,但误差会积累;室内没有 GPS;磁力计在电机和结构件附近也不可靠。因此 NoeticMaze 用激光雷达扫描之间的几何关系修正位姿。

ICP 的直观含义是:当前扫描得到一圈环境轮廓,参考帧保存上一关键帧的环境轮廓。将当前轮廓平移、旋转,直到它和参考轮廓尽量重合。最终平移和旋转量就是机器人相对参考帧的运动。

NoeticMaze 使用帧到帧模式。

ICP 输入
当前 360 度扫描、参考帧点云、编码器和 IMU 给出的初始位姿预测。
ICP 输出
机器人本帧位姿增量,以及用于修正里程计漂移的 map-odom 变换。
建图输入
ICP 位姿和当前雷达命中点,用 Bresenham 标记空闲格和占据格。
模式 匹配对象 NoeticMaze 是否使用
帧到帧 当前扫描 vs 最近参考扫描
帧到地图 当前扫描 vs 已建地图
预存地图定位 当前扫描 vs 预先导入地图

地图由定位结果驱动建出来。ICP 负责估计机器人移动了多少,建图模块用这个位姿把激光扫描写入栅格。

参考帧维护

ICP 需要参考帧。系统将第一次有效激光扫描作为初始参考帧,之后根据运动阈值更新。

参考帧存在全局数组 ref_scan[360] 中,同时预计算每个点附近的表面法向量 ref_normals[360],供点到线误差计算使用。

ICP 完整流程

一帧扫描到来后,定位流程可以拆成以下步骤。

  1. 极坐标转笛卡尔坐标
    雷达每个角度槽有一个距离值。系统将距离和角度转换为 XY 点,同时处理顺时针角度方向和安装角补偿。

  2. 运动畸变补偿
    一圈扫描跨越一段时间。机器人扫描过程中仍在运动,因此不同角度点处于不同采样时刻。系统用线速度和角速度把每个点补偿到扫描结束时刻。

  3. 里程计初始猜测
    编码器和 IMU 给出本帧位姿预测,作为 ICP 初始估计。

  4. 迭代匹配
    对当前帧每个有效点,按当前估计位姿变换到参考坐标系,在参考帧角度窗口内搜索最近线段,并计算点到线误差。

  5. 构造最小二乘问题
    每个匹配点贡献一行方程,未知量是 dxdydtheta

  6. 求解 3 x 3 线性方程组
    使用高斯消元、部分主元和岭回归,得到本轮位姿增量。

  7. 更新位姿并判断收敛
    若增量足够小则退出,否则继续迭代,最多迭代 10 次。

点到线误差的数学形式

点到线 ICP 每次迭代求解的是最小二乘问题。对第 i 个匹配点:

pi=R(θ)pi+tp'_i = R(\theta) \cdot p_i + t
ei=(refipi)nie_i = (\text{ref}_i - p'_i) \cdot n_i

其中:

线性化后得到关于 dxdxdydydθd\theta 的超定方程组。有效匹配对通常有数百个,但未知量只有 3 个。

(ATA+λI)x=ATb(A^T A + \lambda I)\, x = A^T b
x=[dxdydθ],λ=104x = \begin{bmatrix} dx \\ dy \\ d\theta \end{bmatrix}, \quad \lambda = 10^{-4}

岭回归项用于防止矩阵病态或接近奇异。

为什么使用点到线 ICP

激光雷达扫到的是离散点,但室内墙面、走廊和家具边缘常常形成连续线段。点到点 ICP 只关心离散最近点,当前扫描和参考扫描角度稍有错位时,最近点未必是真正对应点。

点到线 ICP 把相邻参考点形成的局部表面当作线段,计算当前点到表面的垂直距离。对墙面和走廊这类结构,误差方向更稳定。

法向量通过相邻点计算:

t=point[next]point[prev]t = \text{point}[\text{next}] - \text{point}[\text{prev}]
nx=tyt,ny=txtn_x = -\frac{t_y}{\|t\|}, \quad n_y = \frac{t_x}{\|t\|}

环形边界用模运算处理:0 度点的左邻居是 359 度,359 度点的右邻居是 0 度。

法向量计算还包含保护条件:

ICP 参数表

参数 说明
扫描点数 360 每度 1 个槽位
最大迭代次数 10 平衡精度和 CPU 开销
匹配距离阈值 0.20m 超过距离的匹配对丢弃
搜索窗口 ±30 度 在参考帧中搜索候选点
求解器 高斯消元 + 部分主元 + 岭回归 模拟稳定的最小二乘求解
关键帧更新 位移 > 10cm 或旋转 > 5.7 度 更新参考帧
ZUPT 死区 位移 < 2mm 且旋转 < 0.14 度 静止时跳过 ICP
增量限制 平移 ±0.4m,旋转 ±0.3rad 防止单次迭代发散
最小匹配数 10 匹配不足时判定失败

运动畸变补偿

激光雷达完成一圈扫描需要时间。机器人在这一段时间内可能前进或转弯,导致早期采集的点和后期采集的点不在同一车体坐标系。

补偿思路是把每个点变换到扫描结束时刻。

for each point i in scan:
    dt = i * (scan_time / 360) - scan_time
    dtheta = omega * dt

    if (fabs(omega) > 1e-4) {
        dx = (v / omega) * sin(dtheta);
        dy = (v / omega) * (1 - cos(dtheta));
    } else {
        dx = v * dt;
        dy = 0;
    }

    point[i] = rotate(point[i], dtheta) + [dx, dy];

dt 为负值是合理的:早期点距离扫描结束时刻更远,需要补偿更多运动。代码将 scan_time / 360 提到循环外,用乘法替代重复除法,减少 360 次循环中的开销。

map-odom 变换

ICP 输出的是相对更可信的地图坐标位姿,里程计输出的是高频但会漂移的 odom 位姿。系统维护 map <- odom 变换,将二者连接起来。

tf_theta = icp_theta - odom_theta;
tf_x = icp_x - (cos(tf_theta) * odom_x - sin(tf_theta) * odom_y);
tf_y = icp_y - (sin(tf_theta) * odom_x + cos(tf_theta) * odom_y);

global_x = tf_x + cos(tf_theta) * odom_x - sin(tf_theta) * odom_y;
global_y = tf_y + sin(tf_theta) * odom_x + cos(tf_theta) * odom_y;
global_theta = tf_theta + odom_theta;

这样 100Hz 运动线程可以使用高频里程计,同时通过 ICP 结果修正长期漂移。

占据栅格建图

定位回答“机器人在哪”,建图回答“环境里有什么”。NoeticMaze 使用占据栅格表示局部地图。

SLAM 高精图参数:

参数
分辨率 2cm
尺寸 200 x 200
覆盖范围 4m x 4m
存储 2bit/cell,共 10000 字节
状态 未知、空闲、占据

每次有激光点命中障碍,系统从机器人所在栅格到命中点栅格做 Bresenham 光线追踪:

for each lidar hit point:
    ray = bresenham_line(robot_pos, hit_point);
    for each cell in ray excluding endpoint:
        mark cell as FREE;
    mark endpoint as OCCUPIED;

Bresenham 只需要整数加减和比较,很适合 MCU。沿途格子标为空闲,终点格子标为占据,这正是占据栅格建图的核心。

2bit 打包存储

每个格子只需要 2 bit 表示 3 种状态:

00 = 未知
01 = 空闲
10 = 占据

一个字节存 4 个格子:

bit[7:6] = cell3
bit[5:4] = cell2
bit[3:2] = cell1
bit[1:0] = cell0

40000 个格子如果用 1 字节表示需要 40KB;2bit 打包后约 10KB。对 128KB SRAM 来说,这个优化非常关键。

地图写保护和增量传输

项目通过 map_core_write.h 的宏定义控制写权限,确保只有 SLAM 线程写地图,降低并发写导致数据损坏的风险。

蓝牙发送地图时只发送变化的格子增量。报告中提到最多记录 512 个 diff,这能显著降低蓝牙带宽压力。

双地图结构:2cm 建图,10cm 规划

SLAM 高精图适合建图和显示,但不适合直接跑 A*。200 x 200 网格有 40000 个节点,搜索空间过大。

SLAM 高精图
2cm,200 x 200,2bit/cell。用于精细建图、显示和地图增量。
Planner 规划图
10cm,40 x 40,4bit/cell。用于 A*、碰撞检测和重规划。

系统将 2cm 图降采样为 10cm 规划图。

地图 分辨率 尺寸 覆盖范围 存储 用途
SLAM 高精图 2cm 200 x 200 4m x 4m 2bit/cell,约 10KB 建图、显示、增量传输
Planner 规划图 10cm 40 x 40 4m x 4m 4bit/cell,800B x 2 A*、碰撞检测、重规划

一个 10cm 规划格对应 5 x 5 个 2cm 细格。降采样采用保守规则:

这个规则可能牺牲一点通行空间,但能降低漏检障碍的风险。

A* 路径规划

PlannerTask 在 10cm、40 x 40 的规划图上运行 A*。使用 8 连通网格,允许上下左右和 4 个对角方向移动。

参数 说明
网格连通 8 连通 正交 + 对角
正交代价 10 整数缩放,避免浮点
对角代价 14 约等于 10 x sqrt(2)
启发函数 Octile 距离 适合 8 连通网格
目标容差 1.5 格 不要求精确到目标格
优先队列 最小堆,容量 2500 打包 f 值和索引
最大路径长度 400 点 限制路径缓存大小

A* 的目标是在当前代价模型下找可行且代价较低的路径。代价模型中包含障碍膨胀、未知区策略和路径亲和度。

障碍膨胀

机器人有物理尺寸,路径不能贴着障碍物走。规划图对障碍周围区域设置多级惩罚。

级别 膨胀半径 惩罚值 作用
L1 0.15m 1000 接近物理碰撞边界,A* 极力避免
L2 0.22m 150 安全缓冲区
L3 0.28m 30 轻微惩罚,鼓励远离障碍

物理碰撞半径约 0.10m。膨胀半径让路径在可通行区域中留出安全余量。

路径亲和度

重规划时,如果新路径和旧路径完全无关,机器人可能在局部代价变化中来回跳。NoeticMaze 对旧路径附近区域降低代价,让新路径倾向于延续旧路径。

报告中整理的策略是:旧路径附近约 0.3m 半径内代价获得折扣。这样能降低重规划振荡,尤其在未知区域和 U 型结构附近更有用。

路径平滑

A* 输出的是栅格折线,直接跟踪会造成频繁转向。系统做两步平滑:

  1. 双向拉线
    从路径首尾尝试用直线连接更远路径点,用 Bresenham 射线检测是否穿墙。若直线安全,就替代中间折线。

  2. 安全 Bezier 圆角
    在转折处用 Bezier 曲线做圆角。生成圆角时持续做碰撞检测,若碰撞则缩小圆角半径。

平滑后的路径更适合预瞄点跟踪,也能减少差速底盘频繁急转。

路径跟踪与运动控制

MotionTask 以 100Hz 运行。它读取全局路径公告板中的路径,选择前方预瞄点,生成线速度和角速度,再换算为左右轮速度。

核心思想如下:

lookahead_point = find_lookahead(path, robot_pos, L = 0.18m);
heading_error = angle_to(lookahead_point) - robot_yaw;

yaw_rate_cmd = Kp * heading_error;

if (fabs(heading_error) > 10deg) {
    linear_speed *= speed_scale;
}
if (fabs(heading_error) > 60deg) {
    linear_speed = 0;
}

v_left  = linear_speed - yaw_rate_cmd * wheel_track / 2;
v_right = linear_speed + yaw_rate_cmd * wheel_track / 2;

这套方法使用前方预瞄点,但没有使用经典 Pure Pursuit 的曲率公式。因此更准确的描述是“预瞄点航向控制”。

底层电机控制采用轮速 PID。

控制环 参数 说明
轮速 PID Kp=950, Ki=250, Kd=0 使用 CMSIS-DSP arm_pid_f32(),100Hz 更新
角速度控制 Kp=0.0035, Kd=0.0005 生成角速度修正
加速度限制 1.60m/s^2 防止电机突然加减速

到达目标附近后系统减速停车。若启用自动返航,到达目标后会将初始位置 (1.05, 3.15) 设为新目标并规划返航路径。

动态避障与重规划

NoeticMaze 的避障机制是持续建图和重规划。系统检测到新障碍后先写入地图,再根据当前地图重新规划路径。

重规划触发条件如下。

# 触发条件 阈值 说明
1 目标变化 移动超过 1 个规划格 用户通过蓝牙下发新目标
2 无有效路径 路径长度为 0 当前没有可跟踪路径
3 起点漂移 偏离超过 5 个规划格 机器人位置和旧路径起点偏差过大
4 路径阻断 连续 4 帧以上检测到路径穿障 新障碍挡住当前路径
5 防抖间隔 两次规划间隔至少 300ms 避免高频重规划振荡

避障链路如下:

激光雷达持续扫描
  -> SLAM 更新占据栅格
  -> 检查当前路径是否穿过新障碍
  -> 触发重规划请求
  -> PlannerTask 基于新地图运行 A*
  -> MotionTask 切换到新路径

这套机制适合静态或缓慢变化的室内环境。快速移动的人或物体可能被写入地图,导致临时虚假障碍或重规划抖动。

FreeRTOS 任务拓扑

NoeticMaze 由多个 FreeRTOS 任务协同运行。任务频率和优先级体现了实时系统设计:运动控制优先,规划按需,蓝牙遥测低优先级。

任务 优先级 频率 栈大小 职责
MotionTask 最高 100Hz 1KB 电机 PID + 路径跟踪
ImuTask 200Hz 2KB MPU6500 DMP 读取和 Yaw 解算
LidarRouteTask 较高 事件驱动 1KB DMA 数据解码和 360 度帧组装
AlgorithmBrain 跟随激光帧 4KB ICP、建图、降采样、重规划判断
PlannerTask 中低 按需 4KB A* 和路径平滑
TaskPrint 事件/周期 4KB 蓝牙遥测发送和指令接收
defaultTask 空闲 0.5KB 默认空闲任务

任务间通信机制如下。

机制 名称 用途
消息队列 LidarQueueHandle,深度 3 激光帧指针传递,零拷贝
消息队列 ReqQueueHandle,深度 3 SLAM 向 Planner 发重规划请求
消息队列 RespQueueHandle,深度 2 当前版本保留创建,规划结果不靠它返回
事件标志 PathEventHandle 广播路径更新
互斥锁 MapDataMutexHandle 保护地图数据访问
互斥锁 PrintfMutexHandle 保护调试输出

系统数据流可以简化为:

LidarRouteTask
  -> LidarQueueHandle
  -> AlgorithmBrain
     -> ICP 位姿
     -> 2cm 栅格更新
     -> 10cm 规划图更新
     -> ReqQueueHandle
  -> PlannerTask
     -> A* 路径
     -> 平滑路径
     -> 全局路径公告板
     -> PathEventHandle
  -> MotionTask
     -> 预瞄点跟踪
     -> 左右轮速度
     -> PWM

这个结构避免了把所有逻辑塞进一个大循环。高频控制不等待 A*,SLAM 不等待蓝牙,上位机遥测也不能阻塞运动控制。

用户交互流程

从用户角度看,流程分为 4 个阶段。

阶段 1:开机初始化

阶段 2:自主探索建图

阶段 3:用户下发目标点

用户在上位机地图上点击目标位置,上位机通过蓝牙发送目标点。报告中整理的目标点包是 GoalPacket (0x07),包含 goal_xgoal_y

STM32 通过 USART3 DMA 接收并解析包,调用目标点设置逻辑。SLAM 线程检测到目标变化后,向 PlannerTask 发起规划请求。

阶段 4:规划和自动行驶

这里的用户输入是目标点,系统自行完成定位、建图、路径搜索和轨迹跟踪。

项目亮点

NoeticMaze 的亮点集中在工程实现。

第一,完整链路在 MCU 端闭环。激光雷达、IMU、编码器、SLAM、A*、路径跟踪和蓝牙遥测都在 STM32 上运行。

第二,内存设计有明确约束。2bit 地图压缩、静态缓冲池、零拷贝队列、规划图降采样,都是为 128KB SRAM 服务的。

第三,算法选型贴合场景。室内墙面环境适合点到线 ICP,局部地图适合占据栅格,固定小范围导航适合 40 x 40 规划图。

第四,实时调度边界清晰。高频控制和低频规划分开,重计算不会直接压在运动控制周期上。

第五,通用库依赖少。项目没有使用 Cartographer、GMapping 这类 ROS SLAM 库,ICP、地图、规划和调度逻辑都针对 MCU 资源约束自研。

已知边界

这套系统的边界同样需要写清楚。

没有回环检测。机器人回到之前到过的位置时,系统不会识别并全局校正地图。长时间运行后,位姿和地图会累积漂移。

地图范围固定为 4m x 4m。它适合迷宫、小房间、教学和竞赛场景,不适合整层楼或大范围导航。

环境假设偏静态。移动的人或物体可能被写入地图,造成临时障碍和重规划抖动。

定位精度依赖硬件标定。轮径、轮距、编码器、IMU 零偏、雷达安装角和底盘机械误差都会影响结果。

A* 在未知区域和 U 型结构附近可能振荡。路径亲和度、最小重规划间隔和路径穿障连续帧判断可以缓解,但不能从根本上消除所有局部规划问题。

结论

NoeticMaze 展示了一种典型的嵌入式导航解法:放弃通用框架,把问题限制在明确范围内,并在这个范围内完成闭环。

在 128KB RAM 的 STM32F446RE 上,完整导航系统能跑起来,依赖一组连续的工程取舍:雷达零拷贝、点到线 ICP、2bit 占据栅格、2cm 到 10cm 的双地图、带代价约束的 A*、预瞄点航向控制,以及 FreeRTOS 下的任务隔离。

这也是 NoeticMaze 最值得学习的地方。它是一个约束明确、链路完整、适合学习嵌入式 SLAM 和室内导航的工程样本。

本文由 AI 辅助生成,可能存在错误或遗漏,请以实际资料和官方文档为准。