1. STM32F103在智能扫地机器人中的核心作用
STM32F103系列单片机作为智能扫地机器人的"大脑",其重要性怎么强调都不为过。这款基于ARM Cortex-M3内核的微控制器,主频最高可达72MHz,内置128KB Flash和20KB SRAM,完全能够胜任实时控制任务。我在实际项目中测试过,即使在同时处理红外传感器数据、电机控制和风扇调速的情况下,CPU占用率也从未超过60%。
选择STM32F103C8T6这款具体型号有几个实际考量:首先是它的TSSOP20封装尺寸小巧,非常适合空间受限的机器人设计;其次是它具备丰富的外设接口,我们需要的定时器、ADC、GPIO等一应俱全;最重要的是它的性价比极高,批量采购单价可以控制在10元以内,这对消费级产品至关重要。
在电路设计中,我习惯将单片机核心板独立出来,通过排针与其他模块连接。这样做有两个好处:一是调试时可以单独测试核心板功能;二是后续升级换代时只需更换核心板,其他模块可以复用。核心板上除了单片机最小系统外,我还增加了以下关键电路:
- USB转串口芯片(如CH340G),方便通过电脑调试
- 复位电路和boot选择跳线
- 用户按键和LED指示灯
- 所有IO口通过排针引出
2. 红外循迹模块的实战设计
红外循迹是扫地机器人的基础功能,我测试过市面上常见的几种方案,最终选择了HW-201红外对管模块。这个模块最大的特点是自带电位器调节灵敏度,实测在2-30cm范围内检测稳定性很好。模块输出数字信号(检测到障碍时输出低电平),直接连接STM32的GPIO即可。
在电路连接上有个细节需要注意:红外模块最好单独供电,不要与电机共用电源。我在早期版本中就遇到过电机启动时电流突变导致红外模块误触发的问题。后来改进的方案是:
- 锂电池电压先经过LM2596降压到5V
- 5V再分两路:一路给电机驱动,另一路经AMS1117-3.3V给单片机
- 红外模块直接使用5V供电
红外循迹的软件算法其实可以做得很有趣。我实现的方案包含三个层次:
// 基础检测 if(LEFT_IR == LOW) { // 左侧检测到障碍 turnRight(30); // 右转30度 } if(RIGHT_IR == LOW) { // 右侧检测到障碍 turnLeft(30); // 左转30度 } // 进阶处理 - 防卡死 static uint32_t obstacleTimer = 0; if((LEFT_IR == LOW) || (RIGHT_IR == LOW)) { if(obstacleTimer++ > 100) { // 持续检测到障碍 moveBackward(100); // 后退 turnAround(); // 调头 obstacleTimer = 0; } } // 高级优化 - 动态灵敏度 void adjustIRSensitivity() { // 根据环境光强度动态调整阈值 uint16_t ambientLight = readADC(LIGHT_SENSOR); setIRThreshold(ambientLight/10 + 50); }3. 红外避障传感器的选型与优化
避障功能我推荐使用E18-D80NK这款光电传感器,它比普通红外对管性能强很多。实测在80cm距离内对白色墙壁检测稳定,而且自带透镜可以聚焦红外光,抗干扰能力出色。这个模块有三个引脚:棕色接VCC,蓝色接GND,黑色是信号输出。
在安装位置上有讲究:我通常会在机器人前部呈扇形布置3个避障传感器:
- 左侧传感器角度偏左30度
- 中间传感器正对前方
- 右侧传感器角度偏右30度
这样布置的好处是可以提前感知侧方障碍物,给转向决策留出更多时间。对应的检测代码可以这样写:
void obstacleAvoidance() { uint8_t left = readSensor(LEFT_OBSTACLE); uint8_t center = readSensor(CENTER_OBSTACLE); uint8_t right = readSensor(RIGHT_OBSTACLE); if(center) { // 正前方有障碍 if(left && !right) { turnRight(45); } else if(!left && right) { turnLeft(45); } else { // 正前方完全堵住 moveBackward(200); turnAround(); } } else if(left) { // 只有左侧有障碍 adjustRight(10); } else if(right) { // 只有右侧有障碍 adjustLeft(10); } }传感器的安装高度也需要注意。经过多次测试,我发现距离地面5-8cm是最佳高度,既能检测到低矮障碍物,又不容易被地面纹理误触发。可以用尼龙柱固定传感器,方便调节高度。
4. 电机驱动与运动控制
我习惯使用L298N作为电机驱动芯片,虽然效率不如新一代的DRV8833,但它的驱动能力更强(峰值3A),而且价格便宜。在PCB布局时,电机驱动部分要特别注意以下几点:
- 电源走线要足够宽,建议至少2mm
- 在电机电源输入端加装470μF以上的电解电容
- 每个电机引脚都要加续流二极管(如1N5819)
- 散热片要足够大,必要时可以涂抹散热硅脂
运动控制算法我采用了PID调节,下面是一个简化版的实现:
typedef struct { float Kp, Ki, Kd; float error, lastError, integral; } PID; void PID_Init(PID* pid, float Kp, float Ki, float Kd) { pid->Kp = Kp; pid->Ki = Ki; pid->Kd = Kd; pid->error = pid->lastError = pid->integral = 0; } float PID_Update(PID* pid, float setpoint, float input) { pid->lastError = pid->error; pid->error = setpoint - input; pid->integral += pid->error; return pid->Kp * pid->error + pid->Ki * pid->integral + pid->Kd * (pid->error - pid->lastError); } // 实际调用示例 PID leftPID, rightPID; PID_Init(&leftPID, 0.8, 0.01, 0.05); PID_Init(&rightPID, 0.8, 0.01, 0.05); while(1) { float leftSpeed = getLeftEncoderSpeed(); float rightSpeed = getRightEncoderSpeed(); float leftPWM = PID_Update(&leftPID, targetSpeed, leftSpeed); float rightPWM = PID_Update(&rightPID, targetSpeed, rightSpeed); setMotorPWM(MOTOR_LEFT, leftPWM); setMotorPWM(MOTOR_RIGHT, rightPWM); delay_ms(10); }对于扫地机器人来说,运动模式至少要实现以下几种:
- 直线前进/后退
- 原地左转/右转
- 弧线转弯
- 螺旋清扫模式
- 沿边清扫模式
5. PCB布局的关键技巧
经过多个版本的迭代,我总结出几个PCB布局的黄金法则:
电源分区布局:将PCB划分为数字区、模拟区、电机驱动区。数字区放置单片机等逻辑器件,模拟区放置传感器接口,电机驱动区单独放在一侧。
地平面处理:采用星型接地,所有模块的地线最终汇集到电源输入端的接地点。电机驱动部分的地要与其他部分地单点连接。
关键信号线处理:
- 电机PWM信号线要尽量短
- 红外传感器信号线要走差分对
- 晶振下方不要走其他信号线
元器件布局示例表格:
| 模块 | 推荐位置 | 注意事项 |
|---|---|---|
| STM32单片机 | PCB中心位置 | 远离电机和电源 |
| 电机驱动芯片 | 靠近电机接口 | 加强散热设计 |
| 红外传感器接口 | 板子边缘 | 信号线加RC滤波 |
| 电源模块 | 靠近电源输入口 | 输入输出电容要足够 |
- 实际布线时有个小技巧:先布电源线,再布关键信号线,最后布普通IO线。电源线宽度可以用下面公式估算: 线宽(mm) = 电流(A) / (铜厚(oz)1.378) * 1.1 比如1oz铜厚、2A电流,线宽应该不小于2/(11.378)*1.1≈1.6mm
6. 系统供电设计
供电系统是很多初学者容易忽视的部分,但实际项目中大部分稳定性问题都出在电源上。我的设计方案是:
- 采用7.4V锂电池供电,容量建议2000mAh以上
- 第一级降压:LM2596将7.4V降为5V,供电机驱动和部分传感器使用
- 第二级降压:AMS1117-3.3V给单片机和数字电路供电
- 充电管理:使用TP4056充电芯片,支持MicroUSB充电
特别要注意的是要在电池输入端加装自恢复保险丝,我一般用1812封装的2A保险丝。另外所有电源输入端都要加滤波电容,推荐值:
- 电池输入端:100μF电解电容 + 100nF陶瓷电容
- 5V输出端:47μF电解电容 + 100nF陶瓷电容
- 3.3V输出端:22μF电解电容 + 100nF陶瓷电容
7. 风扇驱动与灰尘收集
扫地机器人的吸尘风扇通常需要较高电压(12V),所以需要升压电路。我设计的方案是:
- 使用MT3608升压芯片将锂电池电压升到12V
- 风扇控制采用MOSFET(如AO3400)进行PWM调速
- 在风扇电源端并联一个100μF电容减少噪声
风扇转速控制可以根据灰尘量自动调节:
void fanControl() { static uint16_t dustLevel = 0; // 通过红外灰尘传感器获取灰尘浓度 dustLevel = (dustLevel * 0.7) + (readDustSensor() * 0.3); // 映射灰尘浓度到PWM占空比(30%-100%) uint8_t pwm = 30 + (dustLevel * 70 / 1024); setFanSpeed(pwm); }8. 软件架构与任务调度
对于这种实时控制系统,我推荐使用简单的状态机+定时中断架构,而不是上RTOS。下面是我的典型设计:
typedef enum { MODE_IDLE, MODE_CLEANING, MODE_CHARGING, MODE_ERROR } RobotMode; volatile RobotMode currentMode = MODE_IDLE; volatile uint32_t systemTicks = 0; void TIM4_IRQHandler(void) { // 1ms定时中断 if(TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update); systemTicks++; // 每10ms执行一次控制任务 if(systemTicks % 10 == 0) { controlTask(); } // 每100ms执行一次传感器读取 if(systemTicks % 100 == 0) { sensorTask(); } // 每1000ms执行一次状态更新 if(systemTicks % 1000 == 0) { statusUpdateTask(); } } } void controlTask() { switch(currentMode) { case MODE_CLEANING: obstacleAvoidance(); pathPlanning(); motorControl(); fanControl(); break; // 其他模式处理... } }这种架构既保证了实时性,又避免了RTOS的复杂性。所有时间敏感的操作(如电机PWM)放在中断中处理,其他任务在主循环中按优先级执行。
9. 常见问题与调试技巧
在开发过程中我遇到过不少"坑",这里分享几个典型问题的解决方法:
电机干扰单片机复位现象:电机启动时单片机随机复位 解决:
- 电机电源与逻辑电源完全隔离
- 在单片机复位引脚加0.1μF电容
- 确保所有地线连接可靠
红外传感器误触发现象:没有障碍物时传感器偶尔误报 解决:
- 在传感器输出端加上拉电阻(10kΩ)
- 软件上添加消抖逻辑:
uint8_t readIRSensor() { static uint8_t history = 0xFF; history = (history << 1) | (GPIO_ReadInputDataBit(IR_PORT, IR_PIN)); return (history == 0x00); // 连续8次低电平才认为有效 }
电池电量检测不准现象:电量显示跳动大 解决:
- 在ADC输入端加RC滤波(10kΩ+1μF)
- 软件采用滑动平均滤波:
#define ADC_SAMPLES 16 uint16_t adcBuffer[ADC_SAMPLES]; uint8_t adcIndex = 0; uint16_t readBattery() { adcBuffer[adcIndex++] = readADC(BAT_ADC); if(adcIndex >= ADC_SAMPLES) adcIndex = 0; uint32_t sum = 0; for(int i=0; i<ADC_SAMPLES; i++) { sum += adcBuffer[i]; } return sum / ADC_SAMPLES; }
PID参数整定技巧
- 先设Ki=0,Kd=0,逐渐增大Kp直到系统开始振荡
- 取振荡时Kp值的60%作为最终Kp
- 逐渐增大Ki直到静差消除,但不要太大以免超调
- 最后加入Kd抑制超调,通常Kd=Kp/10
10. 进阶优化方向
当基础功能实现后,可以考虑以下几个优化方向:
路径规划算法
- 实现回字形清扫模式
- 增加房间记忆功能
- 动态调整清扫路线
能耗优化
- 根据电量自动调整清扫强度
- 低电量时自动返回充电座
- 睡眠模式下关闭非必要外设
扩展功能
- 增加WIFI模块实现手机控制
- 添加摄像头实现视觉识别
- 支持语音控制
结构优化
- 采用模块化设计方便维护
- 优化重心分布提高越障能力
- 增加防缠绕设计
下面是一个简单的回字形路径规划示例代码:
void zigzagClean() { static uint8_t step = 0; static uint16_t distance = 0; switch(step) { case 0: // 前进1米 if(moveForward(1000)) { distance += 1000; step = 1; } break; case 1: // 右转90度 if(turnRight(90)) { step = 2; } break; case 2: // 前进0.3米 if(moveForward(300)) { step = 3; } break; case 3: // 右转90度 if(turnRight(90)) { step = 0; if(distance >= 5000) { // 清扫5米后切换方向 step = 4; } } break; // 反向清扫... } }在项目开发过程中,建议使用版本控制系统(如Git)管理代码,每个功能模块单独分支开发,测试稳定后再合并到主分支。硬件方面可以制作多个版本的PCB,通过实际测试比较不同方案的优劣。