从江科大学到寒假作品:一个单片机新手的寻迹小车开发全记录(附STM32代码)
从零到一STM32寻迹小车开发实战全解析记得第一次看到实验室学长演示寻迹小车时那种精准的轨迹跟踪让我目瞪口呆。作为电子工程专业的大二学生我决定利用寒假挑战这个看似简单却暗藏玄机的项目。三个月后当我的小车成功跑完复杂赛道时那种成就感至今难忘。本文将完整分享这段从零开始的开发历程特别适合刚接触STM32和嵌入式开发的同好参考。1. 硬件选型与系统架构选择STM32F103C8T6作为主控芯片是经过深思熟虑的。这款被称为蓝色药丸的开发板价格亲民约20元却拥有72MHz主频和丰富的外设资源完全能满足寻迹小车的需求。硬件系统主要由以下模块构成模块型号功能成本主控STM32F103C8T6核心控制20元电机驱动L298N双路直流电机控制15元传感器TCRT5000红外反射式检测3元/个电机TT马达动力输出10元/个电源18650电池系统供电15元传感器布局方案采用了经典的三角排列中间传感器(I)略微突出约5mm左右传感器(L/R)对称分布间距约3cm安装高度距离地面1-1.5cm为最佳检测距离提示TCRT5000的红外发射管需要串联220Ω限流电阻接收端建议加上10kΩ上拉电阻2. 开发环境搭建与基础驱动Keil MDK是STM32开发的经典选择但对我这样的新手来说配置过程颇为复杂。经过多次尝试最终确定的开发环境如下// 简单的GPIO初始化示例 void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置PA6,PA7为推挽输出电机控制 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 配置PB0,PB1,PB2为上拉输入传感器输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOB, GPIO_InitStructure); }电机驱动采用PWM调速关键配置参数定时器频率10kHzPWM分辨率8位(0-255)死区时间1μs防止H桥直通3. 传感器数据处理与状态机设计三个红外传感器理论上可以识别7种不同的轨道状态。经过实测我将状态简化为5种典型情况全白状态000保持上一状态或低速前进仅左检测100急左转左轮反转左中检测110缓左转仅中检测010直行中右检测011缓右转仅右检测001急右转右轮反转状态判断代码优化版本#define SENSOR_L GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) #define SENSOR_I GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) #define SENSOR_R GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) uint8_t get_track_state(void) { static uint8_t last_state STATE_FORWARD; if(!SENSOR_L !SENSOR_I !SENSOR_R) return last_state; // 保持上一状态 if(SENSOR_L !SENSOR_I !SENSOR_R) { last_state STATE_HARD_LEFT; return STATE_HARD_LEFT; } // 其他状态判断... return STATE_FORWARD; // 默认返回直行状态 }4. 运动控制算法优化基础的速度控制采用查表法针对不同状态预设速度值状态左轮速度右轮速度适用场景急左转-7070直角弯、锐角弯缓左转3070大半径弯道直行6060直线段缓右转7030大半径弯道急右转70-70直角弯、锐角弯速度平滑过渡技巧每次速度变化不超过±20添加50ms延时防止突变引入简易PID算法修正偏差// 带平滑处理的速度设置函数 void set_motor_smooth(int8_t left, int8_t right) { static int8_t current_left 0, current_right 0; // 左轮平滑过渡 while(current_left ! left) { if(current_left left) current_left 20; else current_left - 20; Motor_L_SetSpeed(current_left); Delay_ms(50); } // 右轮平滑过渡同上 // ... }5. 特殊赛道处理技巧直角弯道是三个传感器方案的难点。通过将中间传感器前突5mm的设计实现了独特的检测时序进入直角前LIR000全白接触直角LIR100检测到左侧通过直角LIR101左右同时检测离开直角LIR001检测到右侧针对急弯的特殊处理if(last_state STATE_HARD_LEFT current_state STATE_HARD_RIGHT) { // 检测到急转弯增大转向力度 Motor_L_SetSpeed(-90); Motor_R_SetSpeed(90); Delay_ms(200); // 保持转向一段时间 }6. 系统调试与性能优化调试过程中总结的宝贵经验硬件调试要点传感器灵敏度调节电位器旋至刚好能检测黑线电机供电电压7.4V两节18650为最佳轮子摩擦力橡胶轮胎比塑料轮更稳定软件调试技巧添加状态指示灯LED串口打印实时传感器数据使用逻辑分析仪捕捉PWM波形# 简单的调试命令示例 printf(L:%d I:%d R:%d | Lspd:%d Rspd:%d\n, SENSOR_L, SENSOR_I, SENSOR_R, left_speed, right_speed);7. 完整项目代码架构最终项目的代码结构如下/Project ├── CMSIS # 内核支持文件 ├── Drivers │ ├── motor.c # 电机驱动 │ ├── sensor.c # 传感器处理 │ └── pwm.c # PWM生成 ├── Application │ ├── main.c # 主循环 │ └── control.c # 运动控制 └── Utilities # 延时等工具函数关键的主循环逻辑while(1) { track_state get_track_state(); switch(track_state) { case STATE_HARD_LEFT: set_motor_smooth(-70, 70); break; // 其他状态处理... default: set_motor_smooth(60, 60); } Delay_ms(10); // 控制循环周期 }记得第一次成功跑完全程时小车在终点线前打了个转像在庆祝。这三个月里烧录了上百次程序调试到凌晨成为常态但看到最终成果时所有的付出都值得了。对于想尝试的后来者我的建议是先从最基础的单传感器开始逐步增加复杂度多打印调试信息最重要的是——保持耐心每个问题都是进步的机会。