基于 STM32F103C8T6,实现 红外循迹 + 超声波避障 + 编码器码盘测速 + PID 调速,是电子竞赛 / 课程设计 / 毕业设计的成熟方案。
一、硬件系统框图
┌──────────────┐
│ STM32F103 │
│ (主控) │
└──────┬───────┘
│
┌─────────────┼──────────────────┐
│ │ │
┌──▼──┐ ┌────▼────┐ ┌──────▼─────┐
│ TB6612 │ │ 红外循迹 │ │ 超声波 HC-SR04 │
│ 电机驱动│ │ 5路 TCRT5000│ │ 避障 │
└──┬──┘ └────┬────┘ └──────┬─────┘
│ │ │
┌──▼─────────────▼──────────────────▼──┐
│ 直流减速电机 + 霍尔/光电码盘 │
└──────────────────────────────────────┘
二、硬件选型清单
| 模块 | 型号 | 说明 |
|---|---|---|
| MCU | STM32F103C8T6 | 72MHz,资源丰富 |
| 电机驱动 | TB6612 / DRV8833 | 双路直流电机 |
| 循迹 | TCRT5000 × 5 | 黑线检测 |
| 避障 | HC‑SR04 | 超声波测距 |
| 测速 | 霍尔/光电码盘 | 每路电机 |
| 电源 | 18650 × 2 | 7.4V → 5V/3.3V |
三、引脚分配(标准)
| 功能 | 引脚 |
|---|---|
| 左电机 PWM | PA8 / TIM1_CH1 |
| 右电机 PWM | PA9 / TIM1_CH2 |
| 左电机 DIR | PB12 / PB13 |
| 右电机 DIR | PB14 / PB15 |
| 左编码器 A/B | PA0 / PA1 |
| 右编码器 A/B | PB6 / PB7 |
| 红外循迹 | PC0~PC4 |
| 超声波 Trig | PB8 |
| 超声波 Echo | PB9 |
四、核心源码
1、 电机 & PWM 驱动
void Motor_Init(void)
{
__HAL_RCC_TIM1_CLK_ENABLE();
TIM_OC_InitTypeDef sConfig = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 72 - 1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 1000 - 1;
HAL_TIM_PWM_Init(&htim1);
sConfig.OCMode = TIM_OCMODE_PWM1;
sConfig.Pulse = 0;
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfig, TIM_CHANNEL_1);
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfig, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
}
void Motor_SetSpeed(uint8_t left, uint8_t right)
{
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, left);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, right);
}
2、 编码器码盘测速
volatile int32_t left_cnt = 0;
volatile int32_t right_cnt = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
left_cnt++;
if(htim->Instance == TIM3)
right_cnt++;
}
float Get_Speed(void)
{
float speed = (abs(left_cnt) + abs(right_cnt)) / 2.0;
left_cnt = right_cnt = 0;
return speed;
}
3、 红外循迹逻辑
uint8_t Track_Read(void)
{
return (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_0) << 4) |
(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_1) << 3) |
(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_2) << 2) |
(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_3) << 1) |
(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_4));
}
4、 超声波避障
float HCSR04_Read(void)
{
uint32_t time = 0;
HAL_GPIO_WritePin(TRIG_GPIO, TRIG_PIN, GPIO_PIN_SET);
HAL_Delay(10);
HAL_GPIO_WritePin(TRIG_GPIO, TRIG_PIN, GPIO_PIN_RESET);
while(HAL_GPIO_ReadPin(ECHO_GPIO, ECHO_PIN) == GPIO_PIN_RESET);
while(HAL_GPIO_ReadPin(ECHO_GPIO, ECHO_PIN) == GPIO_PIN_SET)
time++;
return time * 0.017;
}
5、 PID 调速
typedef struct
{
float Kp, Ki, Kd;
float error, last_error, integral;
} PID_t;
float PID_Calc(PID_t *pid, float target, float feedback)
{
pid->error = target - feedback;
pid->integral += pid->error;
float out = pid->Kp * pid->error +
pid->Ki * pid->integral +
pid->Kd * (pid->error - pid->last_error);
pid->last_error = pid->error;
return out;
}
6、 主控制逻辑
void Car_Control(void)
{
uint8_t track = Track_Read();
float dist = HCSR04_Read();
float speed = Get_Speed();
if(dist < 20) // 避障
{
Motor_SetSpeed(0, 0);
return;
}
if(track == 0b00100) // 直行
Motor_SetSpeed(600, 600);
else if(track == 0b01110) // 十字
Motor_SetSpeed(600, 600);
else if(track & 0b00011) // 偏左
Motor_SetSpeed(400, 700);
else if(track & 0b11000) // 偏右
Motor_SetSpeed(700, 400);
}
参考代码 STM32智能红外循迹小车,包括完整码盘测速,蔽障,等功能 www.youwenfan.com/contentcst/182042.html
五、功能总结
红外 5 路循迹
超声波自动避障
编码器码盘测速
PID 闭环调速
差速转向
停车保护
六、调试建议
| 问题 | 解决 |
|---|---|
| 跑偏 | 调整 PID 参数 |
| 循迹抖动 | 降低电机速度 |
| 测速不准 | 提高编码器采样频率 |
| 避障误判 | 多次滤波取平均值 |