一、工程概述
扫地机器人源码框架,适用于STM32F103/F407系列,包含:
- 运动控制(PID速度闭环 + 差速转向)
- 传感器融合(红外避障 + 超声波 + 悬崖检测)
- 清扫算法(随机/弓字形/沿边/定点清扫)
- 回充导航(红外信标对接)
- 电源管理(锂电池充放电 + 低电量保护)
- 通信协议(串口/蓝牙/WiFi远程控制)
二、硬件配置清单
| 模块 | 型号/参数 | 接口 |
|---|---|---|
| 主控 | STM32F103C8T6 / F407ZGT6 | - |
| 电机驱动 | TB6612FNG × 2 | PWM + GPIO |
| 直流电机 | 12V 200RPM 带编码器 | TIM编码器接口 |
| 超声波 | HC-SR04 × 2 | TIM输入捕获 |
| 红外避障 | E18-D80NK × 6 | GPIO输入 |
| 悬崖传感器 | TCRT5000 × 4 | ADC |
| 碰撞开关 | 微动开关 × 2 | GPIO外部中断 |
| 陀螺仪 | MPU6050 | I2C |
| 电池 | 18650 × 3 (11.1V) | ADC电压检测 |
| 充电座 | 红外发射管 + 触点 | GPIO + ADC |
三、核心源码
工程结构
VacuumRobot/
├── Core/
│ ├── Inc/
│ │ ├── main.h
│ │ ├── motor.h # 电机PID控制
│ │ ├── sensor.h # 传感器驱动
│ │ ├── navigation.h # 路径规划
│ │ ├── cleaning.h # 清扫逻辑
│ │ ├── power.h # 电源管理
│ │ ├── protocol.h # 通信协议
│ │ └── robot.h # 状态机
│ └── Src/
│ ├── main.c
│ ├── motor.c
│ ├── sensor.c
│ ├── navigation.c
│ ├── cleaning.c
│ ├── power.c
│ ├── protocol.c
│ └── robot.c
├── Drivers/
└── Middlewares/
main.c(主程序入口)
c
/**
* @file main.c
* @brief 扫地机器人主控程序
* @version 2.0
*/
#include "main.h"
#include "robot.h"
int main(void)
{
/* 1. 系统初始化 */
HAL_Init();
SystemClock_Config(); // 72MHz
MX_GPIO_Init();
MX_TIM_Init(); // PWM + 编码器
MX_ADC_Init(); // 电池电压
MX_I2C_Init(); // MPU6050
MX_USART_Init(); // 蓝牙/WiFi
/* 2. 模块初始化 */
Robot_Init(); // 机器人状态机
Motor_Init(); // 电机PID
Sensor_Init(); // 传感器校准
Navigation_Init(); // 路径规划
Power_Init(); // 电源管理
/* 3. 主循环(10ms任务周期) */
uint32_t last_tick = 0;
while (1)
{
if (HAL_GetTick() - last_tick >= 10)
{
last_tick = HAL_GetTick();
/* 任务调度 */
Sensor_Update(); // ① 传感器扫描
Robot_StateMachine(); // ② 状态机切换
Motor_PID_Control(); // ③ 电机闭环控制
Power_Monitor(); // ④ 电池监控
Protocol_Process(); // ⑤ 通信处理
}
}
}
motor.c(电机PID控制)
c
/**
* @file motor.c
* @brief 双电机PID速度闭环控制
*/
#include "motor.h"
/* 电机结构体 */
typedef struct {
int16_t target_rpm; // 目标转速(RPM)
int16_t current_rpm; // 当前转速
int16_t pwm; // PWM输出值
PID_TypeDef pid; // PID参数
} Motor_t;
static Motor_t motor_left, motor_right;
/* PID参数(根据实际调试修改) */
#define KP 2.5f
#define KI 0.8f
#define KD 0.2f
void Motor_Init(void)
{
/* 初始化PID */
PID_Init(&motor_left.pid, KP, KI, KD, 1000);
PID_Init(&motor_right.pid, KP, KI, KD, 1000);
/* 启动PWM通道 */
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
}
/* 设置电机速度(差速控制) */
void Motor_SetSpeed(int16_t linear, int16_t angular)
{
/* 差速模型:左轮 = 线速度 - 角速度 */
motor_left.target_rpm = linear - angular;
motor_right.target_rpm = linear + angular;
}
/* PID速度闭环(10ms调用) */
void Motor_PID_Control(void)
{
/* 读取编码器 */
motor_left.current_rpm = Encoder_GetRPM(LEFT);
motor_right.current_rpm = Encoder_GetRPM(RIGHT);
/* PID计算 */
motor_left.pwm = PID_Calculate(&motor_left.pid,
motor_left.target_rpm,
motor_left.current_rpm);
motor_right.pwm = PID_Calculate(&motor_right.pid,
motor_right.target_rpm,
motor_right.current_rpm);
/* 输出PWM */
Motor_Output(LEFT, motor_left.pwm);
Motor_Output(RIGHT, motor_right.pwm);
}
sensor.c(传感器融合)
c
/**
* @file sensor.c
* @brief 多传感器数据融合
*/
#include "sensor.h"
Sensor_Data_t sensor;
void Sensor_Init(void)
{
/* 红外避障:GPIO输入 */
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(IR_GPIO_PORT, &GPIO_InitStruct);
/* 超声波:TIM输入捕获 */
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
}
void Sensor_Update(void)
{
/* 1. 红外避障检测 */
sensor.obstacle.front = !HAL_GPIO_ReadPin(IR_FRONT_GPIO_Port, IR_FRONT_Pin);
sensor.obstacle.left = !HAL_GPIO_ReadPin(IR_LEFT_GPIO_Port, IR_LEFT_Pin);
sensor.obstacle.right = !HAL_GPIO_ReadPin(IR_RIGHT_GPIO_Port, IR_RIGHT_Pin);
/* 2. 超声波测距(单位:cm) */
sensor.distance.front = Ultrasonic_GetDistance(FRONT);
sensor.distance.left = Ultrasonic_GetDistance(LEFT);
/* 3. 悬崖检测(ADC值越大越接近地面) */
sensor.cliff.front_left = ADC_GetValue(ADC_CLIFF_FL) > 2000;
sensor.cliff.front_right = ADC_GetValue(ADC_CLIFF_FR) > 2000;
/* 4. 碰撞检测(中断触发) */
if (HAL_GPIO_ReadPin(BUMPER_GPIO_Port, BUMPER_Pin) == GPIO_PIN_RESET)
{
sensor.bumper = 1;
Robot_Stop(); // 立即停止
}
}
navigation.c(路径规划)
c
/**
* @file navigation.c
* @brief 清扫路径规划算法
*/
#include "navigation.h"
/* 清扫模式 */
typedef enum {
MODE_RANDOM = 0, // 随机碰撞
MODE_ZIGZAG, // 弓字形
MODE_EDGE, // 沿边清扫
MODE_SPOT // 定点清扫
} CleanMode_t;
static CleanMode_t current_mode = MODE_RANDOM;
/* 随机碰撞算法(最简单实用) */
void Navigation_Random(void)
{
static uint32_t timer = 0;
if (sensor.obstacle.front) {
/* 前方有障碍 → 后退 + 随机转向 */
Motor_SetSpeed(-200, 0); // 后退
HAL_Delay(500);
if (rand() % 2) {
Motor_SetSpeed(0, 150); // 左转
} else {
Motor_SetSpeed(0, -150); // 右转
}
HAL_Delay(300);
}
else if (sensor.distance.front < 30) {
/* 距离小于30cm → 减速 */
Motor_SetSpeed(100, 0);
}
else {
/* 正常前进 */
Motor_SetSpeed(300, 0);
}
}
/* 弓字形清扫(适合规则房间) */
void Navigation_Zigzag(void)
{
static uint8_t state = 0;
static uint16_t straight_count = 0;
switch (state) {
case 0: // 直线前进
Motor_SetSpeed(300, 0);
if (++straight_count > 100) {
state = 1;
straight_count = 0;
}
break;
case 1: // 右转90°
Motor_SetSpeed(0, 150);
if (++straight_count > 20) {
state = 2;
straight_count = 0;
}
break;
case 2: // 直线前进(反向)
Motor_SetSpeed(-300, 0);
if (++straight_count > 100) {
state = 3;
straight_count = 0;
}
break;
case 3: // 右转90°
Motor_SetSpeed(0, 150);
if (++straight_count > 20) {
state = 0;
straight_count = 0;
}
break;
}
}
power.c(电源管理)
c
/**
* @file power.c
* @brief 锂电池充放电管理
*/
#include "power.h"
Power_Status_t power;
/* 电池电压阈值(mV) */
#define BAT_FULL 12600 // 满电 12.6V
#define BAT_LOW 10500 // 低电量 10.5V
#define BAT_EMPTY 9500 // 关机电压 9.5V
void Power_Init(void)
{
/* ADC初始化 */
HAL_ADC_Start(&hadc1);
}
void Power_Monitor(void)
{
/* 读取电池电压 */
uint32_t adc_val = HAL_ADC_GetValue(&hadc1);
power.voltage = (adc_val * 3300 * 4) / 4095; // 分压比 1:3
/* 电量百分比 */
power.percent = (power.voltage - BAT_EMPTY) * 100 / (BAT_FULL - BAT_EMPTY);
/* 低电量保护 */
if (power.voltage < BAT_LOW) {
Robot_SetMode(RETURN_HOME); // 自动回充
}
if (power.voltage < BAT_EMPTY) {
Robot_PowerOff(); // 强制关机
}
/* 充电检测 */
if (HAL_GPIO_ReadPin(CHARGE_DET_Pin) == GPIO_PIN_SET) {
power.charging = 1;
Charger_Start(); // 启动充电
}
}
protocol.c(通信协议)
c
/**
* @file protocol.c
* @brief 自定义通信协议(兼容小米/石头协议)
*/
#include "protocol.h"
/* 协议格式:0xAA + CMD + LEN + DATA + CRC */
#define PROTOCOL_HEAD 0xAA
void Protocol_Process(void)
{
if (UART_ReceiveComplete()) {
uint8_t *rx_buf = UART_GetBuffer();
if (rx_buf[0] == PROTOCOL_HEAD) {
uint8_t cmd = rx_buf[1];
uint8_t len = rx_buf[2];
switch (cmd) {
case CMD_START_CLEAN:
Robot_StartClean();
break;
case CMD_STOP_CLEAN:
Robot_Stop();
break;
case CMD_SET_MODE:
Robot_SetMode(rx_buf[3]);
break;
case CMD_GET_STATUS:
Protocol_SendStatus();
break;
case CMD_RETURN_HOME:
Robot_ReturnToCharge();
break;
}
}
}
}
/* 发送状态数据(JSON格式,方便手机APP解析) */
void Protocol_SendStatus(void)
{
char json[128];
sprintf(json, "{\"bat\":%d,\"mode\":%d,\"err\":%d}\r\n",
power.percent,
robot.mode,
robot.error);
UART_SendString(json);
}
参考代码 扫地机器人源码及解释 www.youwenfan.com/contentcsu/56299.html
四、Keil工程配置
1、Target设置
Device: STM32F103C8T6
Clock: 8MHz HSE → 72MHz PLL
Code Generation: Use MicroLIB
2、编译优化
Optimization Level: -O1
One ELF Section per Function: ✓
3、链接脚本
RAM: 0x20000000, 20KB
FLASH: 0x08000000, 64KB
五、调试与实测建议
调试技巧
-
先用串口打印日志:
cprintf("[MOTOR] L=%d, R=%d\r\n", left_rpm, right_rpm); -
用示波器看PWM波形,确保电机响应正常
-
先调PID再跑算法:先让电机稳速,再测试转向
安全注意事项
- 首次上电断开电机电源,只测试逻辑
- 悬崖传感器必须接下拉电阻,防止误触发
- 充电电路加保险丝,防止过流
- 所有GPIO加限流电阻(220Ω)
六、扩展功能(可选)
| 功能 | 实现方式 |
|---|---|
| WiFi控制 | ESP8266 AT指令 |
| 激光雷达建图 | 移植Cartographer |
| 语音控制 | LD3320语音模块 |
| OTA升级 | 串口/IAP |