基于STM32的避障寻迹小车设计

基于STM32的避障寻迹小车设计

一、系统概述与核心功能

1. 系统定位

基于STM32的避障寻迹小车以“环境感知-路径规划-运动控制-状态反馈”为核心,融合红外寻迹(沿黑线行驶)与超声波避障(自动绕开障碍物)功能,支持手动/自动模式切换、实时状态显示及低功耗续航,适用于教育实训、智能仓储、家庭服务等场景。

2. 核心功能模块

模块 功能描述
环境感知 红外寻迹(5路TCRT5000检测黑线)、超声波避障(HC-SR04检测前方障碍物,测距2-400cm)
运动控制 双直流电机驱动(L298N/TB6612FNG),PWM调速(0-100%占空比),支持前进/后退/转向
路径规划 寻迹:PID算法调整电机差速(直线/弯道跟踪);避障:遇障时“停止-转向-绕行-回归轨迹”
状态反馈 OLED显示(传感器数据、模式、电池电压),LED指示(电源/故障),蜂鸣器报警(避障触发)
人机交互 按键切换模式(寻迹/避障/手动),蓝牙模块(HC-05)手机APP控制,预留扩展接口(摄像头/陀螺仪)
低功耗设计 待机时关闭非必要模块(传感器/显示),STM32进入STOP模式,续航≥2小时(7.4V/2000mAh电池)

二、硬件设计方案

1. 核心硬件选型

模块 型号 关键参数 接口方式
主控MCU STM32F103C8T6 72MHz Cortex-M3,64KB Flash,20KB RAM,3个定时器(PWM),2个UART,2个I2C 核心控制器
寻迹传感器 TCRT5000×5 红外反射式,检测距离0-3cm,输出数字信号(黑线低电平,白底高电平) GPIO(PB0-PB4,数字输入)
避障传感器 HC-SR04 超声波测距,Trig(输出)、Echo(输入),测距精度±3mm,探测角度15° Trig=PA0,Echo=PA1(定时器输入捕获)
电机驱动 L298N 双H桥驱动,支持2路直流电机(5-35V,2A/路),内置续流二极管 IN1=PA2,IN2=PA3(左电机);IN3=PA4,IN4=PA5(右电机);ENA=PA6(PWM),ENB=PA7(PWM)
直流电机 37GB520直流减速电机 减速比1:30,额定电压6V,空载转速120rpm,扭矩0.5N·m 配橡胶轮(直径65mm)
电源模块 7.4V锂电池+LM2596+AMS1117 7.4V/2000mAh锂电池→LM2596降压至5V(驱动模块)→AMS1117-3.3V(STM32/传感器) 供电一体化(带过充保护)
显示模块 OLED 12864(I2C) 0.96寸,128×64像素,低功耗(<10mA),显示传感器数据、模式、电压 I2C(PB6=SCL,PB7=SDA)
通信模块 HC-05蓝牙模块 蓝牙2.0+EDR,UART接口,10m距离,支持AT指令配置 UART1(PA9=TX,PA10=RX)
辅助模块 按键×3+LED×2+蜂鸣器 按键(模式切换/启停),LED(电源/故障),有源蜂鸣器(5V,避障报警) GPIO(PC0-PC2按键,PC3-PC4 LED,PC5蜂鸣器)

2. 硬件电路设计要点

2.1 核心电路连接

2.2 抗干扰设计

三、软件设计与核心代码

1. 系统架构(FreeRTOS多任务调度)

采用FreeRTOS实时操作系统,划分5个核心任务(优先级从高到低):

  1. 传感器采集任务(优先级4):周期性读取红外寻迹(5路)和超声波(测距)数据,滤波后发送至消息队列。
  2. 寻迹控制任务(优先级3):解析寻迹数据,通过PID算法计算电机差速,生成电机控制指令。
  3. 避障控制任务(优先级3):判断超声波距离,触发避障逻辑(转向/绕行),发送临时控制指令。
  4. 电机驱动任务(优先级2):解析控制指令,通过PWM驱动电机(正转/反转/调速)。
  5. 人机交互任务(优先级1):更新OLED显示(传感器数据、模式),处理按键/蓝牙指令(模式切换、启停)。

2. 核心代码实现(基于HAL库)

2.1 主程序框架(FreeRTOS初始化)

#include "stm32f1xx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "motor.h"
#include "sensor.h"
#include "oled.h"
#include "bluetooth.h"

// 全局变量定义
uint8_t system_mode = 0;  // 0-待机,1-寻迹,2-避障,3-手动
uint16_t trace_data[5];   // 5路红外寻迹数据(0=白底,1=黑线)
uint16_t distance = 0;    // 超声波测距结果(cm)

int main(void) {
  HAL_Init();
  SystemClock_Config();  // 72MHz
  MX_GPIO_Init();        // GPIO初始化(传感器、电机、按键)
  MX_TIM3_Init();        // TIM3初始化(PWM输出,控制电机)
  MX_TIM2_Init();        // TIM2初始化(超声波Echo输入捕获)
  MX_I2C1_Init();        // I2C1初始化(OLED)
  MX_USART1_Init();      // USART1初始化(蓝牙)
  
  OLED_Init();           // OLED显示初始化
  Motor_Init();          // 电机驱动初始化
  Sensor_Init();         // 传感器初始化(红外、超声波)
  Bluetooth_Init();      // 蓝牙模块初始化
  
  // FreeRTOS任务创建
  xTaskCreate(Sensor_Task, "Sensor", 256, NULL, 4, NULL);    // 传感器采集
  xTaskCreate(Trace_Task, "Trace", 256, NULL, 3, NULL);      // 寻迹控制
  xTaskCreate(Avoid_Task, "Avoid", 256, NULL, 3, NULL);      // 避障控制
  xTaskCreate(Motor_Task, "Motor", 256, NULL, 2, NULL);      // 电机驱动
  xTaskCreate(UI_Task, "UI", 128, NULL, 1, NULL);            // 人机交互
  
  vTaskStartScheduler();  // 启动调度器
  while (1);
}

2.2 传感器采集任务(红外寻迹+超声波)

// 传感器数据结构体
typedef struct {
  uint16_t trace[5];  // 5路红外数据(0/1)
  uint16_t distance;  // 超声波距离(cm)
} SensorData_t;

SensorData_t sensor_data;

void Sensor_Task(void *pvParameters) {
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = pdMS_TO_TICKS(20);  // 20ms采集一次(50Hz)
  
  while (1) {
    // 1. 读取红外寻迹数据(5路TCRT5000,PB0-PB4)
    for (uint8_t i = 0; i < 5; i++) {
      sensor_data.trace[i] = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0 + i);  // 0=白底,1=黑线
    }
    
    // 2. 读取超声波距离(HC-SR04,TIM2输入捕获)
    distance = HCSR04_ReadDistance();  // 返回cm(简化函数,实际需定时器测脉宽)
    
    // 3. 发送数据至消息队列
    xQueueSend(sensor_queue, &sensor_data, 0);
    xQueueSend(distance_queue, &distance, 0);
    
    vTaskDelayUntil(&xLastWakeTime, xFrequency);
  }
}

2.3 寻迹控制任务(PID算法)

// PID参数(需调试优化)
#define KP 2.5f   // 比例系数
#define KI 0.1f   // 积分系数
#define KD 0.5f   // 微分系数

// 寻迹控制逻辑
void Trace_Task(void *pvParameters) {
  SensorData_t data;
  int16_t error = 0, last_error = 0, integral = 0, derivative = 0;
  int16_t motor_left = 0, motor_right = 0;  // 电机速度(-100~100,负=反转)
  
  while (1) {
    if (xQueueReceive(sensor_queue, &data, portMAX_DELAY) == pdPASS) {
      if (system_mode == 1) {  // 寻迹模式
        // 1. 计算偏差(黑线位置:0=左,1=中左,2=中,3=中右,4=右)
        error = 0;
        uint8_t active_count = 0;  // 检测到黑线的传感器数量
        for (uint8_t i = 0; i < 5; i++) {
          if (data.trace[i]) {  // 黑线(低电平,此处假设1=黑线,需根据实际调整)
            error += (i - 2);  // 以中间传感器(2号)为基准,偏差范围-2~+2
            active_count++;
          }
        }
        if (active_count == 0) error = last_error;  // 无黑线时保持上次偏差
        else error /= active_count;  // 平均偏差
        
        // 2. PID计算
        integral += error;
        if (integral > 100) integral = 100;    // 积分限幅
        if (integral < -100) integral = -100;
        derivative = error - last_error;
        int16_t pid_output = KP*error + KI*integral + KD*derivative;
        last_error = error;
        
        // 3. 计算电机速度(基础速度50,差速由PID输出调整)
        motor_left = 50 - pid_output;
        motor_right = 50 + pid_output;
        
        // 4. 限幅(-100~100)
        motor_left = (motor_left > 100) ? 100 : (motor_left < -100) ? -100 : motor_left;
        motor_right = (motor_right > 100) ? 100 : (motor_right < -100) ? -100 : motor_right;
        
        // 5. 发送控制指令至电机任务
        xQueueSend(motor_queue, &motor_left, 0);
        xQueueSend(motor_queue, &motor_right, 0);
      }
    }
  }
}

2.4 避障控制任务(遇障转向)

void Avoid_Task(void *pvParameters) {
  uint16_t dist;
  while (1) {
    if (xQueueReceive(distance_queue, &dist, portMAX_DELAY) == pdPASS) {
      if (system_mode == 2 && dist < 20) {  // 避障模式,距离<20cm触发
        // 1. 停止电机
        Motor_Control(0, 0);
        HAL_Delay(200);
        
        // 2. 左转90°(假设左转时间500ms)
        Motor_Control(-30, 30);  // 左轮反转,右轮正转
        HAL_Delay(500);
        
        // 3. 前进绕行(200ms)
        Motor_Control(50, 50);
        HAL_Delay(200);
        
        // 4. 右转90°回归原方向
        Motor_Control(30, -30);
        HAL_Delay(500);
        
        // 5. 恢复前进
        Motor_Control(50, 50);
        
        // 6. 蜂鸣器报警
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_Delay(100);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_RESET);
      }
    }
  }
}

2.5 电机驱动任务(PWM控制)

// 电机控制函数(速度-100~100,负=反转)
void Motor_Control(int16_t left, int16_t right) {
  // 左电机方向(IN1/IN2)
  if (left > 0) {  // 正转
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
  } else if (left < 0) {  // 反转
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
  } else {  // 停止
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
  }
  
  // 右电机方向(IN3/IN4)
  if (right > 0) {
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
  } else if (right < 0) {
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
  } else {
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
  }
  
  // PWM调速(占空比0-100%对应速度-100~100,实际需映射)
  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, abs(left) * 10);  // PA6,左电机PWM
  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, abs(right) * 10); // PA7,右电机PWM
}

四、关键技术与优化

1. 寻迹算法优化

2. 避障逻辑优化

3. 低功耗设计

参考代码 基于STM32设计避障寻迹小车 www.youwenfan.com/contentcst/133708.html

五、系统调试与扩展

1. 调试步骤

阶段 操作 工具
硬件调试 测量电源电压(7.4V/5V/3.3V),示波器检查PWM波形 万用表、示波器(TIM3_CH1/CH2)
传感器校准 红外对管对准黑线/白底,验证输出电平;超声波测已知距离(如20cm) 串口打印原始数据
电机测试 单独控制左右电机正反转、调速,验证L298N驱动逻辑 逻辑分析仪(GPIO输出)
联调 铺设黑线轨迹,测试寻迹流畅度;放置障碍物测试避障成功率 高速摄像机(拍摄小车轨迹)

2. 扩展功能

专注于matlab/simulink,电子电路,编程