这个基于STM32F103ZE的扫地机器人项目,是我去年花了三个月时间完成的智能家居实验项目。作为一个嵌入式开发老手,我一直想打造一个真正实用的自主清扫设备,而不是简单的玩具。这个机器人实现了三大核心功能:精准的弓字形路径规划、多传感器融合避障,以及通过WiFi实现的远程控制与状态监控。
选择STM32F103ZE作为主控是经过深思熟虑的。这款Cortex-M3内核的MCU具有72MHz主频和512KB Flash,足够处理路径算法和传感器数据融合。更重要的是它的丰富外设接口——3个USART、2个SPI和2个I2C,可以轻松连接各类传感器和WiFi模块。
主控板采用STM32F103ZET6最小系统,通过L298N电机驱动模块控制两个直流减速电机。这里有个关键细节:我在电机输出端加了LM393比较器构成的速度反馈电路,通过编码器信号实现闭环控制。实测表明,这种设计能让机器人在不同地面材质上保持恒定的行进速度,误差小于5%。
电源管理部分使用了两套方案:主电路由7.4V 5200mAh锂电池供电,通过AMS1117-3.3转换为控制电路电压;电机驱动则直接使用电池电压,避免稳压芯片过热。这个双电源设计让系统连续工作时间达到了120分钟以上。
避障系统采用了多传感器融合方案:
特别要说明的是传感器安装角度。经过多次测试,我将超声波模块向下倾斜15度安装,这样既能检测前方障碍,又能识别地面凹陷。红外对管则呈30度夹角向外安装,形成了有效的立体检测区域。
先来看最基础的运动控制。通过PWM控制两个电机的转速差实现转向,具体参数如下:
c复制#define PWM_PERIOD 7200 // 100kHz PWM
#define BASE_SPEED 3600 // 50%占空比
void SetMotorSpeed(int left, int right) {
TIM1->CCR1 = BASE_SPEED + left;
TIM1->CCR2 = BASE_SPEED + right;
}
这里使用TIM1的CH1和CH2输出PWM,通过调节相对于基准速度的偏移量实现差速控制。
弓字形清扫的核心是状态机设计。我定义了6种运动状态:
c复制typedef enum {
STATE_FORWARD,
STATE_TURN_90,
STATE_SIDE_STEP,
STATE_OBSTACLE_AVOID,
STATE_RETURN_HOME,
STATE_IDLE
} RobotState;
主控制循环中维护着一个二维数组map[20][20],每个元素代表10cm×10cm的区域。当机器人检测到已清扫区域时,对应数组元素置1。路径规划算法会优先选择相邻的未清扫区域前进。
关键技巧:在实际编码中发现,纯弓字形路径在复杂家居环境中效率很低。后来改进为"弓字形+沿边清扫"的混合策略——先沿墙走一圈记录房间轮廓,再在内部区域执行标准弓字形清扫。
避障系统的核心是建立一个基于加权投票的决策机制。每个传感器提供的数据被赋予不同的置信度:
| 传感器类型 | 检测范围 | 权重 | 更新频率 |
|---|---|---|---|
| 超声波 | 20-400cm | 0.6 | 10Hz |
| 红外 | 2-30cm | 0.3 | 20Hz |
| 碰撞开关 | 接触式 | 0.1 | 即时 |
当任一传感器的威胁值超过阈值时,触发避障行为:
c复制if((sonar_weight*sonar_danger + ir_weight*ir_danger) > 0.5) {
CurrentState = STATE_OBSTACLE_AVOID;
}
避障采用三级响应机制:
实测中发现,单纯依赖距离检测会导致机器人在桌椅腿丛中陷入死循环。后来增加了历史路径记忆功能,对重复经过的区域自动提高威胁等级,有效解决了这个问题。
选用ESP8266作为WiFi模块,通过USART3与STM32通信。接线方式如下:
code复制STM32 ESP8266
PA10 TXD
PA9 RXD
3.3V VCC
GND GND
定义了一套简单的ASCII协议,格式为:
code复制[命令类型][参数1],[参数2],...\n
例如:
在STM32端使用DMA+空闲中断实现高效串口接收:
c复制// USART3初始化片段
USART3->CR3 |= USART_CR3_DMAR;
DMA1_Channel3->CNDTR = BUF_SIZE;
DMA1_Channel3->CMAR = (uint32_t)uart3_rx_buf;
DMA1_Channel3->CPAR = (uint32_t)&USART3->DR;
DMA1_Channel3->CCR |= DMA_CCR_EN;
开发了简易的Android控制APP,主要功能包括:
数据传输使用JSON格式,例如:
json复制{
"x": 125,
"y": 342,
"battery": 78,
"state": "cleaning"
}
采用航位推测法(Dead Reckoning)记录位置:
c复制typedef struct {
float x; // 单位:cm
float y;
float theta; // 朝向角度
} Pose;
void UpdatePose(int left_ticks, int right_ticks) {
float dl = left_ticks * CM_PER_TICK;
float dr = right_ticks * CM_PER_TICK;
float d = (dl + dr) / 2;
current_pose.theta += (dr - dl) / WHEEL_BASE;
current_pose.x += d * cos(current_pose.theta);
current_pose.y += d * sin(current_pose.theta);
}
其中CM_PER_TICK需要通过实际测量标定,我的机器人每个编码器脉冲对应0.087cm。
返回过程分为三个阶段:
关键代码逻辑:
c复制float dx = HOME_X - current_pose.x;
float dy = HOME_Y - current_pose.y;
float target_angle = atan2(dy, dx);
// 先转向目标方向
TurnToAngle(target_angle);
// 然后直线前进
float distance = sqrt(dx*dx + dy*dy);
MoveStraight(distance);
在FreeRTOS中划分了三个关键任务:
每个任务都设置了合理的堆栈大小,并通过互斥锁保护共享资源。特别要注意的是,超声波模块的测量会阻塞约60ms,必须放在低优先级任务中。
实现了三级电量检测:
当电量低于15%时,机器人会自动暂停清扫任务并返回充电座。这里有个重要细节:返回过程中要关闭所有非必要外设(包括WiFi),仅保留最基本的传感器和电机控制。
分享几个关键参数的调试经验:
我开发了一个简单的上位机工具,通过WiFi实时调整参数并绘制运动轨迹,大大提高了调试效率。
现象:行进过程中电机突然剧烈抖动
排查步骤:
最终发现是编码器信号线受到电机干扰,增加磁环后问题解决。
可能原因:
解决方案:
c复制// 在WiFi任务中添加心跳检测
if(xTaskGetTickCount() - last_ack > 5000) {
ESP_Reset();
}
航位推测法不可避免会产生累积误差,我采用了以下补偿措施:
实测表明,结合这些方法可以将位置误差控制在5%以内,足够满足家庭清扫需求。