1. 项目概述:基于STM32的智能避障小车设计
作为一名嵌入式开发者,我最近完成了一个基于STM32F103的智能避障小车项目。这个小车能够自主感知周围环境,在遇到障碍物时做出智能避障决策,实现自主导航功能。整个系统采用模块化设计,硬件成本控制在300元以内,非常适合作为嵌入式学习的实践项目。
这个项目的核心价值在于:它完整呈现了一个典型嵌入式系统的开发流程 - 从硬件选型、电路设计到软件开发和算法实现。通过这个项目,你可以掌握STM32的外设驱动开发、传感器数据采集与处理、电机控制等嵌入式开发的核心技能。更重要的是,它能帮助你理解如何将各种电子模块有机整合,构建一个完整的智能系统。
2. 系统架构设计
2.1 硬件系统组成
整个小车的硬件系统由六大模块构成:
-
主控模块:STM32F103C8T6最小系统板
- 72MHz主频的Cortex-M3内核
- 64KB Flash + 20KB SRAM
- 丰富的外设接口(USART, SPI, I2C, PWM等)
-
驱动模块:L298N电机驱动板
- 驱动电压:5-35V
- 最大输出电流:2A(单路)
- 可同时驱动两个直流电机
-
检测模块:
- HC-SR04超声波传感器(3个)
- 检测距离:2cm-400cm
- 精度:3mm
- TCRT5000红外反射传感器(5个)
- 检测距离:1-8mm
- 用于循迹功能
- HC-SR04超声波传感器(3个)
-
电源模块:
- 18650锂电池(7.4V)
- LM2596S降压模块
- 输入:4.5-40V
- 输出:1.5-35V可调
- 最大电流:3A
-
机械结构:
- 四轮驱动底盘
- 减速电机(带编码器)
- 工作电压:6V
- 减速比:1:48
- 空载转速:200rpm
2.2 软件架构设计
软件系统采用分层架构设计:
code复制应用层:避障算法、循迹算法
中间层:电机控制、传感器驱动
硬件抽象层:GPIO、TIM、USART等外设驱动
开发环境:
- IDE:Keil MDK 5.25
- 编译器:ARMCC V5.06
- 调试工具:ST-Link V2
3. 关键硬件模块详解
3.1 STM32主控电路设计
主控电路设计需要注意以下几个关键点:
-
时钟电路:
- 8MHz外部晶振+22pF负载电容
- 32.768kHz RTC晶振(可选)
-
复位电路:
- 10kΩ上拉电阻+0.1μF电容
- 复位按键
-
电源电路:
- 3.3V LDO稳压
- 0.1μF去耦电容(每个电源引脚)
-
调试接口:
- SWD接口(SWDIO, SWCLK)
- 建议引出USART1用于调试输出
提示:PCB布局时,晶振要尽量靠近MCU,走线要短且对称。电源去耦电容要靠近MCU的电源引脚放置。
3.2 电机驱动电路
L298N驱动电路连接要点:
-
电机电源与逻辑电源隔离
- VM:接7.4V锂电池
- VCC:接5V稳压输出
-
控制信号连接:
- IN1-IN4:接STM32的PWM输出引脚
- ENA/ENB:接PWM用于调速
-
电流检测:
- 可在电机回路串联0.1Ω采样电阻
- 通过运放放大后送ADC检测
典型接线示例:
code复制ENA - PA8(PWM)
IN1 - PB12
IN2 - PB13
ENB - PA9(PWM)
IN3 - PB14
IN4 - PB15
3.3 传感器电路设计
超声波传感器(HC-SR04)
工作流程:
- 触发信号(10μs高电平)
- 模块发送8个40kHz脉冲
- 接收回波并输出高电平
- 高电平持续时间与距离成正比
接线方式:
code复制VCC - 5V
Trig - PB6
Echo - PB7
GND - GND
红外循迹传感器(TCRT5000)
电路设计要点:
- 比较器基准电压可调(用于灵敏度调节)
- 输出加10kΩ上拉电阻
- 建议使用74HC14施密特触发器整形
4. 软件设计与实现
4.1 开发环境搭建
- 安装Keil MDK 5.25
- 安装STM32F1xx Device Family Pack
- 配置工程选项:
- Target: STM32F103C8
- Use MicroLIB勾选
- Optimize: Level 2
- 调试器配置:
- Debug: ST-Link Debugger
- Port: SW
- Max Clock: 4MHz
4.2 关键驱动程序
PWM电机控制
c复制// PWM初始化
void PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 配置PA0为PWM输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 定时器基础配置
TIM_TimeBaseStructure.TIM_Period = 999; // ARR值
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 72MHz/(71+1)=1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// PWM模式配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500; // 初始占空比50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
TIM_CtrlPWMOutputs(TIM2, ENABLE);
}
超声波测距
c复制float Get_Distance(void)
{
float distance;
uint32_t time_us;
// 发送10us触发信号
GPIO_SetBits(TRIG_PORT, TRIG_PIN);
delay_us(10);
GPIO_ResetBits(TRIG_PORT, TRIG_PIN);
// 等待回波信号
while(GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN) == 0);
// 开始计时
TIM_SetCounter(TIM3, 0);
while(GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN) == 1);
// 计算时间
time_us = TIM_GetCounter(TIM3);
distance = (float)time_us * 0.017; // 340m/s = 0.034cm/us, 往返除以2
return distance;
}
4.3 避障算法实现
避障逻辑采用有限状态机(FSM)设计:
c复制typedef enum {
STATE_FORWARD,
STATE_TURN_LEFT,
STATE_TURN_RIGHT,
STATE_BACKWARD,
STATE_STOP
} StateType;
void Obstacle_Avoidance(void)
{
static StateType state = STATE_FORWARD;
float dist_front, dist_left, dist_right;
// 获取传感器数据
dist_front = Get_Distance(FRONT_SENSOR);
dist_left = Get_Distance(LEFT_SENSOR);
dist_right = Get_Distance(RIGHT_SENSOR);
// 状态转移逻辑
switch(state) {
case STATE_FORWARD:
if(dist_front < 20) { // 前方障碍
if(dist_left > 30) {
state = STATE_TURN_LEFT;
} else if(dist_right > 30) {
state = STATE_TURN_RIGHT;
} else {
state = STATE_BACKWARD;
}
}
break;
case STATE_TURN_LEFT:
if(dist_front > 30) {
state = STATE_FORWARD;
}
break;
case STATE_TURN_RIGHT:
if(dist_front > 30) {
state = STATE_FORWARD;
}
break;
case STATE_BACKWARD:
delay_ms(500);
state = STATE_FORWARD;
break;
case STATE_STOP:
// 紧急停止
break;
}
// 执行对应动作
switch(state) {
case STATE_FORWARD:
Motor_Forward(70); // 70%速度前进
break;
case STATE_TURN_LEFT:
Motor_Turn_Left(50);
break;
case STATE_TURN_RIGHT:
Motor_Turn_Right(50);
break;
case STATE_BACKWARD:
Motor_Backward(60);
break;
case STATE_STOP:
Motor_Stop();
break;
}
}
5. 系统调试与优化
5.1 常见问题排查
-
电机不转或转动异常
- 检查L298N使能信号(ENA/ENB)
- 测量电机供电电压
- 确认PWM信号频率(建议1-10kHz)
-
超声波测距不准
- 确保Trig信号宽度≥10μs
- 检查Echo信号是否被干扰
- 校准声速参数(受温度影响)
-
红外循迹误检测
- 调整比较器基准电压
- 增加软件去抖算法
- 优化传感器安装高度(建议8-15mm)
5.2 性能优化技巧
-
传感器数据滤波
- 采用移动平均滤波:
c复制#define FILTER_SIZE 5 float Moving_Average_Filter(float new_data) { static float buffer[FILTER_SIZE] = {0}; static uint8_t index = 0; static float sum = 0; sum -= buffer[index]; buffer[index] = new_data; sum += buffer[index]; index = (index + 1) % FILTER_SIZE; return sum / FILTER_SIZE; } -
电机PID控制
c复制typedef struct { float Kp, Ki, Kd; float integral; float prev_error; } PID_Controller; float PID_Update(PID_Controller* pid, float error, float dt) { float derivative; pid->integral += error * dt; derivative = (error - pid->prev_error) / dt; pid->prev_error = error; return pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative; } -
电源管理优化
- 添加大容量滤波电容(1000μF)
- 电机电源与逻辑电源隔离
- 增加电压监测电路
6. 项目扩展方向
在实际开发中,我发现这个基础平台还有很大的扩展空间:
-
无线遥控功能
- 增加NRF24L01无线模块
- 开发手机APP控制端
- 实现远程监控功能
-
SLAM建图导航
- 添加陀螺仪(MPU6050)
- 实现简单的位置推算
- 开发基于栅格的地图构建
-
机器视觉扩展
- 使用OpenMV摄像头模块
- 实现颜色识别、目标跟踪
- 结合AprilTag进行定位
-
ROS集成
- 移植到STM32F4/F7系列
- 开发ROS底层驱动
- 实现与PC端ROS通信
这个项目最让我满意的是它的可扩展性 - 你可以从最基础的避障功能开始,逐步添加更复杂的功能模块,最终构建出一个功能完善的智能移动机器人平台。