一、系统总体方案
1.1 功能定义
- 循迹:红外对管 / 灰度传感器,沿黑线自动行驶
- 避障:超声波 + 红外,遇到障碍物自动减速 / 转向
- 蓝牙遥控:手机 APP(SPP 串口协议)远程控制
- 自动 / 手动模式切换
1.2 系统框图
┌─────────────┐
│ 蓝牙 APP │
│ (Android) │
└─────▲───────┘
│ UART
┌─────┴───────┐
│ STM32F103ZET6│
│ 主控 MCU │
└─────▲───────┘
│ PWM / GPIO
┌──────────┼──────────┐
│ │ │
┌──▼───┐ ┌──▼───┐ ┌──▼───┐
│ 左电机 │ │ 右电机 │ │ 舵机 │
│ TB6612 │ │ TB6612 │ │ SG90 │
└──────┘ └──────┘ └──────┘
▲ ▲
└────┬─────┘
│
┌───────┴───────┐
│ 循迹 / 避障 │
│ 红外 / 超声 │
└───────────────┘
二、硬件设计(可直接打板)
2.1 核心器件选型
| 模块 |
型号 |
说明 |
| MCU |
STM32F103ZET6 |
512KB Flash,足够复杂逻辑 |
| 电机驱动 |
TB6612FNG |
双路直流,PWM调速 |
| 循迹 |
TCRT5000 × 5 |
模拟/数字均可 |
| 避障 |
HC-SR04 + IR |
超声测距 + 红外防跌落 |
| 蓝牙 |
HC-05 / JDY-31 |
串口 SPP |
| 电源 |
18650 × 2 |
7.4V → 5V/3.3V |
| 稳压 |
LM2596 + AMS1117 |
7.4→5V→3.3V |
2.2 引脚分配
电机控制
/* 左电机 */
#define MOTOR_L_IN1 GPIO_PIN_0 // PA0
#define MOTOR_L_IN2 GPIO_PIN_1 // PA1
#define MOTOR_L_PWM TIM2_CH1 // PA0
/* 右电机 */
#define MOTOR_R_IN1 GPIO_PIN_2 // PA2
#define MOTOR_R_IN2 GPIO_PIN_3 // PA3
#define MOTOR_R_PWM TIM2_CH2 // PA1
循迹传感器(5路)
#define TRACK_S1 GPIO_PIN_4 // PA4
#define TRACK_S2 GPIO_PIN_5 // PA5
#define TRACK_S3 GPIO_PIN_6 // PA6
#define TRACK_S4 GPIO_PIN_7 // PA7
#define TRACK_S5 GPIO_PIN_0 // PB0
避障传感器
#define US_TRIG GPIO_PIN_8 // PB8
#define US_ECHO GPIO_PIN_9 // PB9
#define IR_LEFT GPIO_PIN_10 // PB10
#define IR_RIGHT GPIO_PIN_11 // PB11
蓝牙(UART1)
#define BT_TX GPIO_PIN_9 // PA9
#define BT_RX GPIO_PIN_10 // PA10
三、STM32CubeMX 配置
3.1 时钟
- HSE:8 MHz
- PLL:×9 → 72 MHz
3.2 TIM 定时器
| TIM |
用途 |
频率 |
| TIM2 |
电机 PWM |
10 kHz |
| TIM3 |
舵机 PWM |
50 Hz |
| TIM4 |
超声波计时 |
1 MHz |
3.3 USART
- USART1:蓝牙(115200, 8N1)
- DMA:接收(减少中断)
四、核心软件实现(C 语言)
4.1 电机驱动(motor.c)
#include "motor.h"
void Motor_Init(void)
{
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
}
void Motor_SetSpeed(uint8_t left, uint8_t right)
{
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, left);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, right);
}
void Motor_Forward(uint8_t speed)
{
HAL_GPIO_WritePin(GPIOA, MOTOR_L_IN1, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, MOTOR_L_IN2, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, MOTOR_R_IN1, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, MOTOR_R_IN2, GPIO_PIN_RESET);
Motor_SetSpeed(speed, speed);
}
void Motor_TurnLeft(uint8_t speed)
{
HAL_GPIO_WritePin(GPIOA, MOTOR_L_IN1, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, MOTOR_L_IN2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, MOTOR_R_IN1, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, MOTOR_R_IN2, GPIO_PIN_RESET);
Motor_SetSpeed(speed, speed);
}
4.2 循迹算法(track.c)
#include "track.h"
uint8_t Track_Read(void)
{
uint8_t status = 0;
status |= HAL_GPIO_ReadPin(GPIOA, TRACK_S1) << 0;
status |= HAL_GPIO_ReadPin(GPIOA, TRACK_S2) << 1;
status |= HAL_GPIO_ReadPin(GPIOA, TRACK_S3) << 2;
status |= HAL_GPIO_ReadPin(GPIOA, TRACK_S4) << 3;
status |= HAL_GPIO_ReadPin(GPIOB, TRACK_S5) << 4;
return status; // bit0~4:1=黑线
}
void Track_Control(void)
{
uint8_t track = Track_Read();
switch(track)
{
case 0b00100: // 正中
Motor_Forward(70);
break;
case 0b01100: // 偏右
Motor_TurnLeft(60);
break;
case 0b00011: // 偏左
Motor_TurnRight(60);
break;
case 0b11111: // 十字路口
Motor_Forward(80);
HAL_Delay(500);
break;
default:
Motor_Stop();
break;
}
}
4.3 避障算法(avoid.c)
#include "avoid.h"
float Ultrasonic_GetDistance(void)
{
uint32_t time_us;
HAL_GPIO_WritePin(GPIOB, US_TRIG, GPIO_PIN_SET);
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOB, US_TRIG, GPIO_PIN_RESET);
while(HAL_GPIO_ReadPin(GPIOB, US_ECHO) == GPIO_PIN_RESET);
__HAL_TIM_SET_COUNTER(&htim4, 0);
while(HAL_GPIO_ReadPin(GPIOB, US_ECHO) == GPIO_PIN_SET);
time_us = __HAL_TIM_GET_COUNTER(&htim4);
return (time_us / 58.0f); // cm
}
void Avoid_Control(void)
{
float dist = Ultrasonic_GetDistance();
uint8_t ir_left = HAL_GPIO_ReadPin(GPIOB, IR_LEFT);
uint8_t ir_right = HAL_GPIO_ReadPin(GPIOB, IR_RIGHT);
if(dist < 20 || ir_left == 0 || ir_right == 0)
{
// 后退 + 转向
Motor_Backward(60);
HAL_Delay(300);
Motor_TurnLeft(70);
HAL_Delay(400);
}
}
4.4 蓝牙协议(bluetooth.c)
#include "bluetooth.h"
uint8_t bt_rx_buf[32];
uint8_t bt_rx_len = 0;
void Bluetooth_Parse(uint8_t *data, uint8_t len)
{
if(len < 2) return;
if(data[0] == 0xA5 && data[len-1] == 0x5A)
{
uint8_t cmd = data[1];
uint8_t val = data[2];
switch(cmd)
{
case 0x01: // 前进
Motor_Forward(val);
break;
case 0x02: // 后退
Motor_Backward(val);
break;
case 0x03: // 左转
Motor_TurnLeft(val);
break;
case 0x04: // 右转
Motor_TurnRight(val);
break;
case 0x05: // 自动模式
system_mode = MODE_AUTO;
break;
case 0x06: // 手动模式
system_mode = MODE_MANUAL;
break;
}
}
}
void USART1_IRQHandler(void)
{
uint8_t ch;
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
{
ch = USART1->DR;
if(bt_rx_len < sizeof(bt_rx_buf))
bt_rx_buf[bt_rx_len++] = ch;
}
}
五、主循环逻辑(main.c)
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
Motor_Init();
Bluetooth_Init();
while(1)
{
if(system_mode == MODE_AUTO)
{
Track_Control();
Avoid_Control();
}
else if(system_mode == MODE_MANUAL)
{
// 蓝牙控制已在中断中处理
}
HAL_Delay(10);
}
}
参考代码 基于STM32F103ZET6实现循轨避障蓝牙小车 www.youwenfan.com/contentcsv/115832.html
六、手机蓝牙 APP(通用协议)
| 功能 |
发送数据 |
说明 |
| 前进 |
A5 01 64 5A |
速度 100 |
| 后退 |
A5 02 64 5A |
速度 100 |
| 左转 |
A5 03 50 5A |
速度 80 |
| 自动 |
A5 05 01 5A |
进入自动 |
| 手动 |
A5 06 01 5A |
进入手动 |
可用 蓝牙串口助手 或 App Inventor 自制 APP。
七、调试与常见问题
7.1 电机抖动
- PWM 频率太低 → 设为 10 kHz
- 电源不足 → 18650 单独供电
7.2 循迹不准
- 传感器间距 2~3 cm
- 黑线宽度 1.5~2 cm
- 增加软件滤波(连续 3 次确认)
7.3 蓝牙断连