用STM32和HC-SR04做个智能小车避障,代码和接线图都给你准备好了 STM32与HC-SR04构建智能小车避障系统实战指南1. 项目概述与核心组件选型智能小车避障系统是嵌入式开发中极具实用价值的练手项目它能综合考察开发者对传感器数据采集、电机控制和简单算法的掌握程度。这个项目的核心在于如何让小车自主感知环境并做出避障决策而超声波模块HC-SR04正是实现这一功能的经济实惠之选。系统主要组件包括STM32F103C8T6最小系统板Blue Pill性价比极高的ARM Cortex-M3内核控制器L298N电机驱动模块可同时驱动两个直流电机支持PWM调速HC-SR04超声波模块测距范围2cm-400cm精度满足避障需求7.4V锂电池组为电机提供充足动力同时通过降压模块为控制系统供电四轮小车底盘建议选择带有编码器接口的型号以便后期扩展提示初学者常犯的错误是直接使用开发板的5V输出驱动电机这可能导致电源不稳定。正确的做法是单独为电机供电并确保控制信号地线与电机电源地线相连。2. 硬件系统搭建与电路设计2.1 超声波模块布局策略避障效果很大程度上取决于超声波模块的安装位置和数量。对于基础版本建议采用前向双模块布局左模块安装位置小车前部左侧倾斜15°朝外 右模块安装位置小车前部右侧倾斜15°朝外 主模块安装位置小车正前方用于检测正前方障碍物这种布局可以检测约120°范围内的障碍物三个模块的检测范围如下表所示模块位置检测角度最佳检测距离主要用途左前侧15°-45°20-150cm检测左侧障碍正前方-15°~15°10-200cm检测正前方障碍右前侧-45°~-15°20-150cm检测右侧障碍2.2 电路连接详解STM32与各外设的连接需要特别注意信号电平和电流需求。以下是推荐连接方式// GPIO引脚分配示例 #define LEFT_TRIG_PIN GPIO_Pin_0 // PA0 #define LEFT_ECHO_PIN GPIO_Pin_1 // PA1 #define FRONT_TRIG_PIN GPIO_Pin_2 // PA2 #define FRONT_ECHO_PIN GPIO_Pin_3 // PA3 #define RIGHT_TRIG_PIN GPIO_Pin_4 // PA4 #define RIGHT_ECHO_PIN GPIO_Pin_5 // PA5 #define MOTOR_PWM_PIN GPIO_Pin_6 // PA6 (TIM3_CH1)电源部分连接要点使用AMS1117-3.3V为STM32供电L298N的12V输入接锂电池正极HC-SR04的VCC接5V稳压输出所有GND必须共地3. 软件架构与核心算法实现3.1 多超声波模块协同工作传统的单模块轮询方式在避障场景下响应速度不足。我们采用定时器中断触发所有模块同时工作void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); static uint8_t state 0; switch(state) { case 0: TriggerSensor(LEFT_TRIG_PIN); break; case 1: distance_left GetDistance(LEFT_ECHO_PIN); TriggerSensor(FRONT_TRIG_PIN); break; case 2: distance_front GetDistance(FRONT_ECHO_PIN); TriggerSensor(RIGHT_TRIG_PIN); break; case 3: distance_right GetDistance(RIGHT_ECHO_PIN); ObstacleAvoidance(); state -1; // 重置状态机 break; } state; } }3.2 避障决策算法基于三路超声波数据我们实现了一个带记忆功能的避障算法typedef enum { SAFE, WARNING, DANGER } ObstacleStatus; ObstacleStatus CheckArea(float distance) { if(distance 10.0) return DANGER; else if(distance 30.0) return WARNING; else return SAFE; } void ObstacleAvoidance() { static uint8_t last_direction 0; ObstacleStatus left CheckArea(distance_left); ObstacleStatus front CheckArea(distance_front); ObstacleStatus right CheckArea(distance_right); if(front DANGER) { if(left SAFE right SAFE) { // 随机选择转向方向以避免陷入振荡 if(rand() % 2) TurnLeft(90); else TurnRight(90); } else if(left SAFE) TurnLeft(45); else if(right SAFE) TurnRight(45); else { // 三面都有障碍执行后退转向 MoveBackward(100); Delay_ms(500); TurnLeft(90); } } else if(front WARNING) { ReduceSpeed(50); // 减速通过危险区域 } else { MoveForward(100); // 全速前进 } }4. 系统优化与调试技巧4.1 超声波数据滤波处理原始超声波数据存在波动需要采用合适的滤波算法#define FILTER_WINDOW 5 float MedianFilter(float new_value) { static float buffer[FILTER_WINDOW] {0}; static uint8_t index 0; float temp_buffer[FILTER_WINDOW]; // 更新环形缓冲区 buffer[index] new_value; index (index 1) % FILTER_WINDOW; // 复制到临时数组进行排序 memcpy(temp_buffer, buffer, sizeof(buffer)); // 冒泡排序 for(int i0; iFILTER_WINDOW-1; i) { for(int j0; jFILTER_WINDOW-i-1; j) { if(temp_buffer[j] temp_buffer[j1]) { float temp temp_buffer[j]; temp_buffer[j] temp_buffer[j1]; temp_buffer[j1] temp; } } } // 返回中值 return temp_buffer[FILTER_WINDOW/2]; }4.2 常见问题排查问题1超声波模块偶尔返回极大值解决方案增加超时检测当测量时间超过对应400cm的距离时丢弃该次测量#define TIMEOUT 23200 // 400cm对应的时间值(us) float GetDistance(uint16_t echo_pin) { uint32_t start_time, end_time; // 等待回波信号变高 while(GPIO_ReadInputDataBit(GPIOA, echo_pin) 0); start_time TIM_GetCounter(TIM2); // 等待回波信号变低同时检测超时 uint32_t timeout start_time TIMEOUT; while(GPIO_ReadInputDataBit(GPIOA, echo_pin) 1) { if(TIM_GetCounter(TIM2) timeout) { return 999.9; // 返回无效值 } } end_time TIM_GetCounter(TIM2); return (end_time - start_time) * 0.017; // 换算为cm }问题2电机干扰导致超声波测量不准解决方案在电机电源线上加装104电容滤波测量时短暂停止PWM输出使用独立的5V稳压芯片为超声波模块供电5. 进阶功能扩展5.1 融合红外传感器数据在复杂光照环境下可以增加红外避障传感器作为超声波模块的补充#define IR_LEFT_PIN GPIO_Pin_6 // PA6 #define IR_RIGHT_PIN GPIO_Pin_7 // PA7 void ReadIRSensors() { ir_left GPIO_ReadInputDataBit(GPIOA, IR_LEFT_PIN); ir_right GPIO_ReadInputDataBit(GPIOA, IR_RIGHT_PIN); // 红外传感器通常低电平有效 if(!ir_left) distance_left 5.0; // 强制设为近距离障碍 if(!ir_right) distance_right 5.0; }5.2 实现SLAM基础功能通过记录小车运动轨迹和障碍物位置可以构建简单的环境地图typedef struct { float x; float y; float theta; } Pose; typedef struct { float distance; float angle; uint32_t timestamp; } Obstacle; Pose current_pose {0, 0, 0}; Obstacle obstacles[100]; uint8_t obstacle_count 0; void UpdatePose(float linear, float angular, float dt) { current_pose.theta angular * dt; current_pose.x linear * cos(current_pose.theta) * dt; current_pose.y linear * sin(current_pose.theta) * dt; // 记录障碍物位置 if(obstacle_count 100) { obstacles[obstacle_count].distance distance_front; obstacles[obstacle_count].angle current_pose.theta; obstacles[obstacle_count].timestamp HAL_GetTick(); obstacle_count; } }在实际项目中我发现超声波模块对软质障碍物如窗帘的检测效果较差这时配合红外传感器就能显著提高可靠性。另一个实用技巧是给小车安装一个简单的蜂鸣器在不同避障状态下发出不同频率的提示音这对调试非常有帮助。