基于STM32的遥控小车设计
一、系统概述与核心功能
1. 系统定位
基于STM32的遥控小车以“无线控制-电机驱动-环境感知-智能避障”为核心,支持红外/NRF24L01/蓝牙多种遥控方式,实现前进/后退/转向/调速等基本运动控制,可扩展循迹、避障、WiFi图传等功能,适用于机器人竞赛、智能小车教学、DIY娱乐等场景。
2. 核心功能模块
| 模块 | 功能描述 |
|---|---|
| 遥控通信 | 支持红外遥控(NEC协议)、NRF24L01无线、蓝牙HC-05三种控制方式,可切换 |
| 电机驱动 | 双直流电机驱动(L298N/TB6612),支持PWM无级调速、正反转、刹车 |
| 运动控制 | 基本运动(前进/后退/左转/右转/停止)、速度调节、运动模式切换(竞速/平稳) |
| 环境感知 | 红外避障(E18-D80NK)、循迹(TCRT5000)、超声波测距(HC-SR04)可选 |
| 状态反馈 | OLED显示(速度/模式/电量)、LED指示灯、蜂鸣器提示音 |
| 低功耗设计 | 待机电流<10mA,无操作5分钟自动休眠,遥控唤醒 |
二、硬件设计方案
1. 核心硬件选型
| 模块 | 型号 | 关键参数 | 接口方式 |
|---|---|---|---|
| 主控MCU | STM32F103C8T6 | 72MHz Cortex-M3,64KB Flash,20KB RAM,4路PWM,多路ADC/USART | 核心控制器 |
| 遥控模块 | 红外+蓝牙+NRF24L01 | 红外HS0038B(NEC协议)、蓝牙HC-05、NRF24L01(2.4GHz,100m) | 红外:PA1(输入捕获) 蓝牙:USART1 NRF:SPI1 |
| 电机驱动 | TB6612FNG(优选) | 双H桥,1.2A/路,内置过热保护,效率>90%,体积小 | IN1-4:PB0-PB3 PWMA/B:TIM2 |
| 直流电机 | TT马达+减速箱 | 减速比1:48,额定电压6V,转速200RPM,扭矩1.2kg·cm | 直流电机×2 |
| 电源模块 | 18650锂电池×2 | 7.4V/2200mAh,带充放电保护,续航>2小时 | 7.4V直供电机,5V降压给MCU |
| 避障模块 | E18-D80NK红外避障 | 检测距离3-80cm可调,数字输出,抗干扰强 | GPIO输入(PC0-PC1) |
| 显示模块 | OLED 12864(I2C) | 0.96寸,128×64像素,显示速度/模式/电量 | I2C1(PB6=SCL,PB7=SDA) |
| 车身结构 | 亚克力/铝合金底盘 | 四轮驱动/两轮驱动可选,万向轮×1,电机固定座,电池仓 | 机械结构 |
2. 硬件电路设计要点
2.1 核心电路连接
STM32F103C8T6
├── 电机驱动(TB6612FNG)
│ ├── IN1=PB0, IN2=PB1(左电机方向)
│ ├── IN3=PB2, IN4=PB3(右电机方向)
│ ├── PWMA=PA8(TIM1_CH1,左电机PWM)
│ └── PWMB=PA9(TIM1_CH2,右电机PWM)
├── 遥控模块
│ ├── 红外接收:PA1(TIM2_CH2输入捕获)
│ ├── 蓝牙HC-05:PA9-TX, PA10-RX(USART1)
│ └── NRF24L01:SPI1(PA5-SCK, PA6-MISO, PA7-MOSI, PA4-CS)
├── 传感器
│ ├── 红外避障×2:PC0(左), PC1(右)
│ └── 循迹TCRT5000×3:PC2-PC4
└── 电源系统
├── 7.4V锂电池→电机驱动
└── LM2596降压→5V→AMS1117→3.3V(MCU/传感器)
2.2 电机驱动电路(TB6612FNG)
TB6612FNG引脚连接:
- VM:7.4V(电机电源)
- VCC:5V(逻辑电源)
- GND:共地
- STBY:PB4(高电平使能,低电平休眠)
- AIN1/AIN2:PB0/PB1(左电机方向控制)
- BIN1/BIN2:PB2/PB3(右电机方向控制)
- PWMA/PWMB:PA8/PA9(PWM调速,10kHz)
- AO1/AO2:左电机正负极
- BO1/BO2:右电机正负极
2.3 抗干扰设计
- 电源隔离:电机电源与逻辑电源通过磁珠隔离,电机两端并联104电容。
- 信号滤波:红外接收头电源并联0.1μF电容,PWM信号线远离电机线。
- 接地设计:数字地、模拟地、功率地单点连接。
三、软件设计与核心代码
1. 系统架构(FreeRTOS多任务调度)
采用FreeRTOS实时操作系统,划分5个核心任务(优先级从高到低):
- 遥控接收任务(优先级5):解析红外/NRF/蓝牙指令,生成运动控制命令。
- 电机控制任务(优先级4):执行运动命令,PWM输出,速度闭环控制。
- 避障循迹任务(优先级3):检测障碍物/循迹线,触发避障/循迹算法。
- 状态显示任务(优先级2):更新OLED显示(速度/模式/电量)。
- 低功耗管理任务(优先级1):无操作超时检测,控制休眠/唤醒。
2. 核心代码实现(基于HAL库)
2.1 电机驱动与控制
#include "motor.h"
#include "tim.h"
// 电机方向定义
typedef enum {
MOTOR_STOP = 0,
MOTOR_FORWARD,
MOTOR_BACKWARD
} MotorDir_t;
// 电机结构体
typedef struct {
GPIO_TypeDef* in1_port;
uint16_t in1_pin;
GPIO_TypeDef* in2_port;
uint16_t in2_pin;
TIM_HandleTypeDef* tim_handle;
uint32_t pwm_channel;
int16_t speed; // -100~100,负号表示反向
} Motor_t;
Motor_t motor_left = {
.in1_port = GPIOB, .in1_pin = GPIO_PIN_0,
.in2_port = GPIOB, .in2_pin = GPIO_PIN_1,
.tim_handle = &htim1, .pwm_channel = TIM_CHANNEL_1
};
Motor_t motor_right = {
.in1_port = GPIOB, .in1_pin = GPIO_PIN_2,
.in2_port = GPIOB, .in2_pin = GPIO_PIN_3,
.tim_handle = &htim1, .pwm_channel = TIM_CHANNEL_2
};
// 电机初始化
void Motor_Init(void) {
// 启动PWM
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
// 默认停止
Motor_SetSpeed(&motor_left, 0);
Motor_SetSpeed(&motor_right, 0);
}
// 设置电机速度(-100~100)
void Motor_SetSpeed(Motor_t* motor, int16_t speed) {
motor->speed = speed;
if (speed > 0) {
// 正转
HAL_GPIO_WritePin(motor->in1_port, motor->in1_pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(motor->in2_port, motor->in2_pin, GPIO_PIN_RESET);
} else if (speed < 0) {
// 反转
HAL_GPIO_WritePin(motor->in1_port, motor->in1_pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(motor->in2_port, motor->in2_pin, GPIO_PIN_SET);
speed = -speed; // PWM占空比为正数
} else {
// 停止
HAL_GPIO_WritePin(motor->in1_port, motor->in1_pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(motor->in2_port, motor->in2_pin, GPIO_PIN_RESET);
}
// 设置PWM占空比(0-1000对应0-100%)
uint16_t pwm_val = (speed * 10) > 1000 ? 1000 : (speed * 10);
__HAL_TIM_SET_COMPARE(motor->tim_handle, motor->pwm_channel, pwm_val);
}
2.2 红外遥控解码(NEC协议)
#include "ir_remote.h"
#include "tim.h"
// NEC解码状态
typedef enum {
IR_IDLE = 0,
IR_LEADER_HIGH,
IR_LEADER_LOW,
IR_DATA
} IR_State_t;
volatile IR_State_t ir_state = IR_IDLE;
volatile uint32_t ir_data = 0;
volatile uint8_t ir_bit_count = 0;
volatile uint8_t ir_received = 0;
// TIM2输入捕获中断回调(PA1)
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) {
uint32_t pulse_width = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
switch (ir_state) {
case IR_IDLE:
if (pulse_width > 8500 && pulse_width < 9500) {
ir_state = IR_LEADER_LOW; // 引导码高电平9ms
}
break;
case IR_LEADER_LOW:
if (pulse_width > 4000 && pulse_width < 5000) {
ir_state = IR_DATA; // 引导码低电平4.5ms
ir_data = 0;
ir_bit_count = 0;
} else {
ir_state = IR_IDLE;
}
break;
case IR_DATA:
if (pulse_width > 1400 && pulse_width < 1800) {
ir_data = (ir_data << 1) | 0x01; // 逻辑1
} else if (pulse_width > 400 && pulse_width < 700) {
ir_data = (ir_data << 1); // 逻辑0
}
if (++ir_bit_count >= 32) {
ir_received = 1; // 32位数据接收完成
ir_state = IR_IDLE;
}
break;
}
}
}
// 解析红外键值
uint8_t IR_GetKey(void) {
if (!ir_received) return 0xFF;
ir_received = 0;
uint8_t addr = (ir_data >> 24) & 0xFF; // 地址码
uint8_t addr_inv = (ir_data >> 16) & 0xFF; // 地址反码
uint8_t cmd = (ir_data >> 8) & 0xFF; // 命令码
uint8_t cmd_inv = ir_data & 0xFF; // 命令反码
// 校验
if ((addr ^ addr_inv) == 0xFF && (cmd ^ cmd_inv) == 0xFF) {
return cmd; // 返回命令码
}
return 0xFF; // 校验失败
}
2.3 运动控制逻辑
#include "motion_control.h"
// 运动命令定义
typedef enum {
CMD_STOP = 0,
CMD_FORWARD,
CMD_BACKWARD,
CMD_LEFT,
CMD_RIGHT,
CMD_SPEED_UP,
CMD_SPEED_DOWN
} MotionCmd_t;
// 系统状态
typedef struct {
int16_t base_speed; // 基础速度(0-100)
uint8_t motion_mode; // 0=竞速,1=平稳
uint8_t obstacle_detected; // 障碍物检测标志
} SystemState_t;
SystemState_t sys_state = {50, 0, 0};
// 运动控制任务
void Motion_Control_Task(void *pvParameters) {
uint8_t key;
int16_t left_speed, right_speed;
while (1) {
// 1. 获取遥控命令
key = IR_GetKey();
// 2. 处理避障(优先级最高)
if (sys_state.obstacle_detected && key != CMD_BACKWARD) {
// 检测到障碍物,自动后退
Motor_SetSpeed(&motor_left, -30);
Motor_SetSpeed(&motor_right, -30);
vTaskDelay(pdMS_TO_TICKS(500));
sys_state.obstacle_detected = 0;
continue;
}
// 3. 解析运动命令
switch (key) {
case 0x45: // 电源键 -> 停止
key = CMD_STOP;
break;
case 0x46: // 音量+ -> 前进
key = CMD_FORWARD;
break;
case 0x47: // 音量- -> 后退
key = CMD_BACKWARD;
break;
case 0x44: // 左键 -> 左转
key = CMD_LEFT;
break;
case 0x40: // 右键 -> 右转
key = CMD_RIGHT;
break;
case 0x43: // 上键 -> 加速
key = CMD_SPEED_UP;
break;
case 0x44: // 下键 -> 减速
key = CMD_SPEED_DOWN;
break;
}
// 4. 执行运动控制
switch (key) {
case CMD_STOP:
left_speed = 0;
right_speed = 0;
break;
case CMD_FORWARD:
left_speed = sys_state.base_speed;
right_speed = sys_state.base_speed;
break;
case CMD_BACKWARD:
left_speed = -sys_state.base_speed;
right_speed = -sys_state.base_speed;
break;
case CMD_LEFT:
if (sys_state.motion_mode == 0) { // 竞速模式:差速转向
left_speed = sys_state.base_speed * 0.5;
right_speed = sys_state.base_speed;
} else { // 平稳模式:原地转向
left_speed = -sys_state.base_speed;
right_speed = sys_state.base_speed;
}
break;
case CMD_RIGHT:
if (sys_state.motion_mode == 0) {
left_speed = sys_state.base_speed;
right_speed = sys_state.base_speed * 0.5;
} else {
left_speed = sys_state.base_speed;
right_speed = -sys_state.base_speed;
}
break;
case CMD_SPEED_UP:
sys_state.base_speed = (sys_state.base_speed + 10) > 100 ? 100 : sys_state.base_speed + 10;
break;
case CMD_SPEED_DOWN:
sys_state.base_speed = (sys_state.base_speed - 10) < 0 ? 0 : sys_state.base_speed - 10;
break;
}
// 5. 设置电机速度
Motor_SetSpeed(&motor_left, left_speed);
Motor_SetSpeed(&motor_right, right_speed);
vTaskDelay(pdMS_TO_TICKS(50)); // 50ms控制周期
}
}
2.4 避障循迹功能
#include "obstacle.h"
// 避障检测任务
void Obstacle_Task(void *pvParameters) {
uint8_t left_obstacle, right_obstacle;
while (1) {
// 读取红外避障传感器
left_obstacle = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_0);
right_obstacle = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_1);
// 检测到障碍物
if (left_obstacle == GPIO_PIN_RESET || right_obstacle == GPIO_PIN_RESET) {
sys_state.obstacle_detected = 1;
}
vTaskDelay(pdMS_TO_TICKS(100)); // 100ms检测周期
}
}
2.5 主程序框架
#include "stm32f1xx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "motor.h"
#include "ir_remote.h"
#include "obstacle.h"
#include "oled.h"
int main(void) {
HAL_Init();
SystemClock_Config(); // 72MHz
// 初始化外设
MX_TIM1_Init(); // PWM电机控制
MX_TIM2_Init(); // 红外输入捕获
MX_GPIO_Init(); // GPIO初始化
MX_I2C1_Init(); // OLED I2C
// 初始化模块
Motor_Init();
IR_Init();
OLED_Init();
// 创建FreeRTOS任务
xTaskCreate(Motion_Control_Task, "MotionCtrl", 256, NULL, 4, NULL);
xTaskCreate(Obstacle_Task, "Obstacle", 128, NULL, 3, NULL);
xTaskCreate(Display_Task, "Display", 128, NULL, 2, NULL);
xTaskCreate(LowPower_Task, "LowPower", 128, NULL, 1, NULL);
vTaskStartScheduler();
while (1);
}
参考代码 基于STM32遥控小车设计 www.youwenfan.com/contentcnt/133609.html
四、关键技术与优化
1. 运动控制优化
- 差速转向:竞速模式下左右轮差速,转弯半径小,机动性强。
- 速度闭环:添加编码器反馈,实现速度闭环控制,避免负载变化影响速度。
- 软启动/软停止:速度变化率限制,避免急启急停造成机械冲击。
2. 遥控可靠性优化
- 多协议冗余:同时支持红外/蓝牙/NRF24L01,某一种失效时可切换。
- 指令校验:红外指令增加校验和,防止误触发。
- 心跳检测:遥控端定期发送心跳包,断连时自动停车。
3. 低功耗设计
- 动态调速:无操作时降低PWM频率,减少开关损耗。
- 休眠模式:5分钟无操作进入STOP模式,遥控按键唤醒。
- 传感器间歇工作:避障传感器每100ms检测一次,降低功耗。
五、系统调试与扩展
1. 调试步骤
| 阶段 | 操作 | 工具 |
|---|---|---|
| 硬件调试 | 测量电机驱动电压,检查PWM波形 | 万用表、示波器 |
| 遥控测试 | 验证红外/NRF/蓝牙指令接收和解码 | 串口打印键值 |
| 运动测试 | 测试前进/后退/转向,检查电机响应速度 | 实际运行测试 |
| 避障测试 | 放置障碍物,验证自动避障功能 | 障碍物(书本/墙壁) |
2. 扩展功能
- 循迹功能:添加TCRT5000红外对管,实现自动循迹(黑线跟踪)。
- WiFi图传:添加ESP32-CAM模块,实现实时视频传输到手机。
- 语音控制:集成LD3320语音模块,支持“前进”“后退”等语音指令。
- 手机APP:开发Android APP,通过蓝牙/WiFi控制小车,显示实时视频。