拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32双路避障小车设计:硬件接线、PWM控制与Proteus仿真详解

简介基于意法半导体STM32微控制器设计的两路避障小车项目是一套包含源程序与仿真文件的完整资料面向嵌入式初学者、电子竞赛选手及开发者重点解决如何利用单片机通用输入输出接口、定时器脉宽调制与外部中断实现双路传感器避障的问题。资源包共含九十六个文件其中四十个头文件与三十六个C语言源程序构成完整工程逻辑八个汇编文件配合Keil MDK工程使用另有Proteus仿真文件和可直接烧录的HEX文件便于硬件验证与调试压缩包仅四百四十八KB结构紧凑。目前已有三百八十九人学习下载适合用于课程设计或嵌入式入门实践。资源提供从电路原理图、仿真环境到源码解析的完整链路包含PID控制代码、文件夹说明及工程项目组织方式读者可对照学习超声波或红外避障算法、电机驱动逻辑与中断处理流程为后续更复杂的机器人设计打下基础。1. 双路避障小车两个“双路”先要分清“基于STM32双路避障小车设计”这类资料里“双路”通常同时出现在两个位置驱动端是左右两个电机靠差速实现转向避障端是左右两路传感器常见为红外对管或超声波模块。小车没有地图也没有全局定位它只能根据当前左右传感器的组合状态判断直行、左转、右转还是倒车。这种设计严格来说不是动态路径规划而是反应式避障但足够应付课程设计和毕业设计的验收。拿到源程序和仿真文件以后最容易卡住的其实不是代码本身而是硬件引脚对应关系、源程序里的传感器电平约定、仿真工程和Hex文件的匹配方式。下面按这个顺序把整个项目讲透。2. STM32双路避障小车的硬件拆分电机驱动与传感器接线2.1 两轮差速为什么是最省事的避障方案两轮差速小车的核心在于左右轮独立驱动当左轮转速大于右轮时小车向右转弯反之向左转弯转速差越大转向半径越小两侧完全反向时可以实现原地旋转。避障只需要知道障碍在左还是在右因此双路传感器加双路电机四种状态就能覆盖常见场景。这种方案比舵机转向加单传感器更简单也比三轮全向感知更便宜。对于STM32入门和毕设而言L298N或TB6612驱动板、普通TT直流减速电机、双路红外避障模块就是最小闭环整个系统不涉及编码器闭环可以让刚接触嵌入式的人把注意力集中在GPIO、定时器PWM和状态机逻辑上。2.2 最小硬件接线表STM32、L298N、红外传感器怎么连常见的MCU选择是STM32F103C8T6源程序也大多基于这块芯片编写。电机驱动用L298N时IN1/IN2控制左电机正反转EN_A接PWM调左轮速度IN3/IN4控制右电机EN_B接PWM调右轮速度。传感器用带电位器的红外避障模块输出是TTL电平。功能模块引脚STM32引脚说明左传感器数字输出OUT_LPA0检测到障碍输出低电平右传感器数字输出OUT_RPA1检测到障碍输出低电平左电机PWMEN_APA6TIM3_CH1左电机方向IN1/IN2PB0/PB1高低组合控制正反转右电机PWMEN_BPA7TIM3_CH2右电机方向IN3/IN4PB10/PB11高低组合控制正反转调试串口TX/RXPA9/PA10USART1打印避障状态选择PA6和PA7是因为它们正好是TIM3的PWM通道1和通道2一套定时器就能输出两路频率完全一致的PWM。PB0、PB1、PB10、PB11是普通GPIO用来设置电机方向不参与速度控制。如果只用一个定时器占空比同步性更好两个轮子不会因为定时器“各自为政”产生差拍。下面这段是标准库下的GPIO初始化示例把PWM引脚、方向引脚和传感器引脚一次性配置好void Hardware_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); // 电机方向引脚PB0、PB1、PB10、PB11 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // PWM输出引脚PA6、PA7复用推挽 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_Init(GPIOA, GPIO_InitStructure); // 传感器输入PA0、PA1内部上拉 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOA, GPIO_InitStructure); }这里有两个容易写错的地方PWM引脚必须配置成GPIO_Mode_AF_PP否则定时器信号无法输出到引脚传感器引脚最好配成上拉输入这样模块悬空时读到高电平不会因为电平不确定导致小车乱转。2.3 电源与共地问题仿真里看不出来真车跑起来才致命L298N模块上的5V稳压输出理论上能给STM32和传感器供电但L298N自身压降大电池电压低于7V以后5V输出会明显跌落小车可能出现上电死机或避障误动作。更稳妥的做法是电池直接给L298N供电单独用AMS1117-5V或7805降压给STM32和传感器供电。无论怎么接所有GND必须连在一起L298N的GND、STM32的GND、传感器GND、电池负极缺一个共地PWM信号就工作在悬空状态电机时转时停。红外避障模块的供电通常接5V其数字输出引脚在检测到障碍时拉低未检测时拉高。STM32的PA0和PA1是5V容忍引脚可以直接接收但如果换用别的型号或引脚建议用两个电阻分压后再接入避免造成芯片损伤。3. 双路避障源程序的 STM32 工程组织与避障策略3.1 源程序目录结构先看懂发下来的代码再改拿到“源程序”资料时第一件事不是打开main.c从头读而是先看目录结构。最常见的Keil标准库工程分为几块USERmain.c、stm32f10x_it.c、system_stm32f10x.cHARDWAREmotor.c/h、sensor.c/h、uart.c/hSYSTEMdelay.c/h、sys.c/hSTM32F10x_FWLib标准外设库的源文件如果工程是基于HAL库的目录里会看到Drivers/CMSIS和Drivers/STM32F1xx_HAL_Driver入口函数同样是main.c。两类工程外设初始化方式差异很大但硬件接线是同一套。改引脚时优先改motor.c和sensor.c不要在所有文件里全局搜索引脚号再逐行替换。Keil里新建工程时先选择STM32F103C8芯片再把标准库源文件按USER、HARDWARE、SYSTEM分类添加。若你手里的源程序直接在某个板子上跑过不要随便改Device型号C8是128KB FlashC6是32KB Flash混淆之后下载定位会错乱。烧录时如果报error: no stm32 target found!那不是源码问题而是ST-Link的SWDIO/SWCLK没接对或者STM32处于复位状态。3.2 双路PWM配置TIM3两路通道同时输出双路差速控制的关键是两路PWM频率一致占空比独立可调。使用STM32的TIM3CH1对应PA6控制左轮CH2对应PA7控制右轮。配置代码如下void TIM3_PWM_Init(u16 arr, u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseStructure.TIM_Period arr; // 自动重装值 TIM_TimeBaseStructure.TIM_Prescaler psc; // 预分频 TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM3, TIM_OCInitStructure); TIM_OC2Init(TIM3, TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_Cmd(TIM3, ENABLE); }主频72MHzpsc71时定时器计数频率为1MHzarr999时PWM频率为1kHz。TIM_SetCompare1(TIM3, 650)就代表左轮占空比65%TIM_SetCompare2(TIM3, 650)代表右轮。避障过程中只需要不断修改这两个比较寄存器不需要重新初始化TIM3。3.3 双路传感器读取与避障决策表大多数红外避障模块的低电平代表检测到障碍因此读回0被认为“有障碍”读回1是“无障碍”。决策表如下LEFT_RRIGHT_R障碍情况执行动作11前方通畅两轮同速前进01左前方有障碍右转10右前方有障碍左转00左右都有障碍倒车后转向主循环里的避障函数可以直接写成状态判断void Avoid_Car(void) { uint8_t L GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0); // 0左侧有障碍 uint8_t R GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1); // 0右侧有障碍 if (L 1 R 1) { // 没有障碍双轮同速直行 Motor_SetSpeed(SPEED_STRAIGHT, SPEED_STRAIGHT); Motor_SetDir(FORWARD, FORWARD); } else if (L 0 R 1) { // 左侧障碍右轮降速、左轮保持实现右转 Motor_SetSpeed(SPEED_TURN_FAST, SPEED_TURN_SLOW); Motor_SetDir(FORWARD, FORWARD); } else if (L 1 R 0) { // 右侧障碍左转 Motor_SetSpeed(SPEED_TURN_SLOW, SPEED_TURN_FAST); Motor_SetDir(FORWARD, FORWARD); } else { // 两侧都有先倒车再朝右侧避让 Motor_SetSpeed(SPEED_BACK, SPEED_BACK); Motor_SetDir(BACKWARD, BACKWARD); Delay_ms(BACK_TIME_MS); Motor_SetSpeed(SPEED_TURN_SLOW, SPEED_TURN_FAST); Motor_SetDir(FORWARD, FORWARD); Delay_ms(TURN_TIME_MS); } }这里有个容易被忽略的点转向时两个轮子都保持正转只靠转速差完成转弯速度低的那个轮子仍在前移因此转向半径较大。如果场地空间很窄需要更小的转弯半径应把内侧轮设成反向旋转也就是让左右轮方向相反车体原地旋转。采用哪种方式取决于源程序里Motor_SetSpeed和Motor_SetDir的组合不是只能硬编码成一种。3.4 动态避障路径规划的逻辑边界双路避障小车能解决的只是当前时刻局部避让它没有维护地图也没有记录历史状态所以严格意义上不算“动态避障小车路径规划”。但在答辩时可以把“传感器-状态机-电机动作”分层解释清楚说明这是一个反应式规划器感知层读双路传感器决策层根据组合状态查找动作库执行层把动作映射成左右轮PWM占空比。这样做的好处是新增一路前向超声波时只需在决策层多加一个判断分支。4. 仿真文件怎么用Proteus 里加载 Hex 联调双路避障4.1 仿真文件一般包含哪些内容资料包里的“仿真文件”绝大多数是Proteus工程后缀为.pdsprj也可能含有.DSN旧格式工程。打开后应该看到STM32芯片、L298N驱动模型、两个直流电机模型和两个模拟障碍物的开关或按键。少部分资料会附带Keil软件仿真说明但Keil软件仿真无法模拟电机负载和L298N半桥逻辑只能验证GPIO波形因此主用Proteus更实际。如果Proteus提示工程版本过新无法打开原因是对方用了更高版本的Proteus保存解决办法是安装兼容版本而不是试图用低版本强行打开。4.2 编译源程序生成 hex 文件并加载到 STM32Keil默认可能没有勾选生成Hex。需要按下图路径手动打开Options for Target-Output- 勾选Create HEX File再点Rebuild才能在Obj或Listings目录下生成以工程名命名的.hex文件。查找文件可以用命令行cd KeilProject dir /s /b *.hex然后把Proteus里的STM32芯片“双击”在Program File一栏选择刚才的hex文件也就是...\Obj\双路避障小车.hex。芯片型号建议保持一致源程序是按STM32F103C8编写的仿真工程如果放的是STM32F103R6多数情况下也能跑但引脚数量不同某些端口可能映射有差异。晶振配置也要对齐。如果Proteus原理图里给STM32挂的是8MHz晶体源程序里的SystemInit默认也按HSE8MHz初始化不需要额外改。若源程序使用内部RC时钟Proteus里可以不给晶振但还是建议把晶振画上避免后续下载到真板时发现时钟没起来。4.3 用按键模拟双路传感器用直流电机观察差速Proteus里通常没有和实物完全一致的红外避障模块常见的做法是用两个手动按键或开关模拟传感器输出。把按键一端接到PA0另一端接地并让PA0保持内部上拉输入。按键按下时PA0被拉到低电平模拟“左侧有障碍”松开时恢复高电平模拟“左侧无障碍”。右侧传感器同样处理。电机部分直接用Proteus库里的MOTOR-DC模型输出端接L298N的OUT1/OUT2和OUT3/OUT4。运行后按下左按键可以观察到左轮模型速度变量明显低于右轮小车逻辑执行右转。这样只需通过按键组合就能验证前面那张四状态决策表是否被正确实现。4.4 仿真跑不起来的三个高频原因仿真的坑和真车不完全一样下表列出了最常遇到的情况现象可能原因处理办法电机模型完全不动PWM引脚配置成普通GPIO检查PA6/PA7是否设置成复用推挽按键按下后无反应传感器引脚没有上拉输入PA0/PA1必须配置为GPIO_Mode_IPU芯片一直复位晶振频率和SystemInit不匹配统一改为8MHz外部晶振点击仿真就退出Proteus版本不兼容使用与工程同代或更高版本打开如果仿真里一切正常但真板下载时报error: no stm32 target found! if your product embedsdebug authentication这是ST-Link和芯片之间的连接问题和Proteus工程无关。先检查SWDIO、SWCLK是否接反再把复位引脚手动拉低一次观察是否能识别到芯片确认无误后用ST-Link Utility全片擦除重新烧录。5. 双路避障小车的参数调优与可靠性验证5.1 三个必调参数占空比、转向保持时间、传感器阈值避障小车的动作质量不取决于代码逻辑而取决于几个宏定义参数。把它们集中放在头文件或main.c上方比在避障函数里写魔法数字好改得多#define SPEED_STRAIGHT 650 // 直行占空比 #define SPEED_TURN_FAST 650 // 转弯时外侧轮占空比 #define SPEED_TURN_SLOW 350 // 转弯时内侧轮占空比 #define SPEED_BACK 450 // 倒车占空比 #define BACK_TIME_MS 300 // 倒车持续时间 #define TURN_TIME_MS 300 // 避障转向持续时间参数作用调试建议直行占空比决定基础车速电池满电时650低压时降到500内外轮占空比差决定转向半径差值越大转弯越急但容易原地打转转向保持时间决定转弯角度300ms大约转60到90度视路面调整倒车时间决定脱困深度两侧障碍时建议250到400ms红外模块电位器决定检测距离调到障碍物距离10到15cm时输出翻转调试时不要一次性改多个参数。每次只改一个记录小车是转多、转少还是直行跑偏再决定下一步。常见错误是两侧同时有障碍时倒车时间太短导致小车反复撞击同一个障碍。5.2 用串口打印验证避障决策视觉观察容易主观加一行串口日志能看到完整决策过程。初始化USART1然后重定义fputc输出到串口int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t)ch); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET) ; return ch; }在Avoid_Car函数里打印printf(L%d R%d - , L, R); if (L 1 R 1) printf(GO\r\n); else if (L 0 R 1) printf(TURN_RIGHT\r\n); else if (L 1 R 0) printf(TURN_LEFT\r\n); else printf(BACK_AND_TURN\r\n);串口波特率设115200接USB转TTL模块的TX到PA10RX到PA9GND共地。开机后观察数据是否和实际障碍方向一致尤其要留意红外模块的三针接口有没有焊反因为VCC和OUT一旦接反串口打印出的电平状态会完全反过来。5.3 让真车更可靠的一个细节软件滤波和上电自检红外避障模块在检测边缘会频繁跳变直接读取GPIO可能导致小车左右抖抖索索。最简单的方法是连续读两次间隔2ms只有两次结果一致才算有效。每次避障循环都会调用这个函数uint8_t ReadSensorStable(GPIO_TypeDef *port, uint16_t pin) { uint8_t s1 GPIO_ReadInputDataBit(port, pin); Delay_ms(2); return GPIO_ReadInputDataBit(port, pin) s1; }上电后还要加一段初始化延时让传感器和电机驱动完成上电稳定否则有些红外模块上电瞬间输出低电平小车会认为前方有障碍而原地抖动。在进入while(1)之前调用Delay_ms(1000)顺便给蜂鸣器或其他自检信号留出时间。这最后一个细节往往就是一套双路避障小车从“能跑”到“稳定跑”的分水岭。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门