扫地机器人嵌入式编程:实现路径规划算法在MCU上的轻量化运行
扫地机路径规划在MCU上不是把ROS的move_base搬过来。低端机型RAM几KB到上百KB,跑不动激光SLAM后端;正确思路是低分辨率栅格地图+弓字形覆盖+局部A*避障+里程计闭环。全局建图留给上位机或高端芯片,下位机只做“这块地扫没扫、前面有障碍怎么绕”。
一、地图别用float栅格
每格用1字节状态位就够了:
#define CELL_FREE 0
#define CELL_OBS 1
#define CELL_CLEAN 2
#define CELL_UNKNOWN 3
uint8_t grid[GRID_W*GRID_H];
栅格尺寸按机身直径取,家用机10~20cm一格最稳。 传感器探到障碍后在外扩1格“阴影区”,避免贴边误判死区。
坐标换算:
static inline int cell_idx(int x_cm,int y_cm){
int gx = x_cm / CELL_CM;
int gy = y_cm / CELL_CM;
return gy*GRID_W + gx;
}
地图常驻RAM吃紧就分块滚动加载,只保留当前房间栅格。
二、覆盖主逻辑:弓字形
家装场景90%是矩形房间,弓字形(回字形)覆盖最省算力,不用图搜索:
typedef enum { DIR_E, DIR_S, DIR_W, DIR_N } dir_t;
static dir_t g_dir = DIR_E;
static int g_row_turn = 0;
void cover_step(pose_t *p){
int idx = cell_idx(p->x, p->y);
grid[idx] = CELL_CLEAN;
if(front_cell_blocked(p, g_dir)){ // 前方障碍/边界
turn_right_or_left(p); // 贴边换向
g_dir = (g_dir+1)%4;
return;
}
move_forward_one_cell(p);
}
规则:
每行扫到头换向
遇到障碍走“沿边一步”再回弓字
已清扫格可踩,但不重复启动滚刷
覆盖率不够再补“未清扫格就近跳转”
纯随机清扫只放超低配机型,正经量产至少加弓字形+已扫标记。
三、局部重规划用A*,不全图搜
回充、跨房间、前方大障碍才调A*;而且只搜前方窗口,不搜全图。
typedef struct {
int x,y,g,f,parent;
uint8_t state; // 0空 1开 2关
} node_t;
int astar_local(int sx,int sy,int gx,int gy){
open_clear();
node_put(sx,sy,0,heur(sx,sy,gx,gy));
int expanded=0;
while(!open_empty()){
node_t*c=open_pop_min();
if(c->x==gx && c->y==gy) return rebuild();
if(++expanded > MAX_EXPAND) return -1; // 兜底放弃
for(int d=0;d<4;d++){
int nx=c->x+dx[d], ny=c->y+dy[d];
if(cell_blocked(nx,ny)) continue;
int ng=c->g+1;
node_t*n=node_get(nx,ny);
if(n->state==2) continue;
if(n->state==0 || ng<n->g){
n->g=ng; n->f=ng+heur(nx,ny,gx,gy);
n->parent=c->y*GRID_W+c->x;
node_put(nx,ny,n->g,n->f);
}
}
}
return -1;
}
MCU优化要点:
静态数组,别malloc,避免碎片HardFault
启发函数用曼哈顿距离,整数运算
开放列表线性扫描或小顶堆,地图小的时候线性扫描反而快
MAX_EXPAND限节点数,超时放弃走沿边
栅格状态用位域压内存
四、传感器融合:轻量就行
别在M3/M4上跑AMCL粒子滤波,那是Linux侧干的活。 MCU层做:
编码器积分位姿(高频)
IMU补转角漂移
红外/ToF/超声波更新障碍栅格
碰撞开关触发“重标障碍+后退”
pose_t odom_step(pose_t p, int enc_l,int enc_r,float gyro_z){
float d = (enc_l+enc_r)*0.5f*ENC_CM;
p.head += gyro_z * DT; // 陀螺仪补航向
p.x += d*cosf(p.head);
p.y += d*sinf(p.head);
return p;
}
激光雷达每帧噪点别直接重规划,变动栅格≥几格再触发,否则主控被占满。
五、任务分层(裸机/RTOS都适用)
10ms -> 编码器/IMU采样,里程计更新
20ms -> 传感器融合,栅格更新
50ms -> 覆盖状态机走一步
按需 -> 障碍挡路调astar_local
100ms -> 上报上位机位姿/电量
路径规划别进中断,别在主循环里阻塞等A*结果;算不完就沿边兜底。
六、落坑清单
全图A*每次都搜→M3直接卡死
malloc节点→跑几天HardFault
浮点欧氏距离→没FPU的核耗时翻倍
激光每帧重规划→底盘控制周期被挤
纯随机清扫无已扫标记→重复率飙
里程计不补IMU→打滑漂移撞墙
障碍不外延栅格→机身卡边
弓字形不处理凹角→角落漏扫
回充用弓字形找→绕半天,该A*
七、落地顺序
定栅格尺寸(10~20cm),建uint8地图
编码器+IMU出里程计位姿
红外/ToF写障碍栅格,外扩1格
弓字形覆盖主状态机
障碍挡路→局部A*重规划
MAX_EXPAND限节点,超时走沿边
回充/跨房走全局A*(地图块级)
变动栅格阈值防抖,别帧帧重算
MCU上扫地机路径规划的本质不是“算法多高级”,是覆盖用规则、避障用局部搜索、定位靠里程计闭环。弓字形管“扫得全”,局部A*管“绕得开”,栅格地图管“记得住”;SLAM建图、粒子定位留给Linux侧或上位机。低端STM32F1也能跑稳,前提是别贪——全图A*、粒子滤波、动态窗口法一股脑塞进MCU,都是量产翻车起点。





