STM32F103智能小车开发实战:硬件搭建与核心算法

发布时间:2026/7/16 17:39:44
STM32F103智能小车开发实战:硬件搭建与核心算法 1. STM32F103开发板与智能小车项目概述在嵌入式开发领域STM32系列MCU因其出色的性价比和丰富的生态资源成为工程师和爱好者的首选平台。我最近基于STM32F103C8T6开发板完成了一个智能小车的二次开发项目这个蓝色的小板子虽然只有拇指大小但通过合理的外设扩展和编程可以实现相当复杂的移动机器人功能。STM32F103系列属于ARM Cortex-M3内核主频可达72MHz具有128KB Flash和20KB SRAM对于智能小车这类需要实时控制的项目完全够用。开发板通常自带USB转串口、复位电路、用户按键和LED等基础外设而智能小车项目需要额外扩展电机驱动、传感器等模块。我选择这款开发板主要考虑三点首先是社区支持完善遇到问题容易找到解决方案其次是外设接口丰富GPIO、PWM、ADC等资源充足最后是开发工具链成熟Keil、IAR、STM32CubeIDE等多种选择。这个项目的核心目标是通过二次开发让基础开发板具备智能小车的完整功能。所谓二次开发就是在现有硬件基础上进行功能扩展和软件优化区别于从零设计PCB的一次开发。对于大多数爱好者来说二次开发既能降低硬件门槛又能快速实现创意是性价比极高的选择。2. 硬件搭建与关键模块选型2.1 基础车体结构与电机驱动智能小车的机械结构看似简单实则每个细节都影响最终性能。我选用的是常见的四轮小车底盘两个主动轮配合两个万向轮的方案。主动轮采用N20减速电机工作电压3-6V减速比1:120这种电机体积小、扭矩适中非常适合实验室环境下的智能小车。电机驱动模块的选择至关重要常见方案有L298N、TB6612FNG和DRV8833等。经过实测对比我最终选择了TB6612FNG原因有三首先它的效率高达90%以上远高于L298N的60%其次支持1.2A持续电流峰值3.2A足够驱动N20电机最后是体积小巧便于安装在紧凑的车体上。接线时需要注意电机的PWM输入信号需要连接到STM32的定时器通道我使用的是TIM3的CH1和CH2。重要提示电机与开发板必须共地否则会出现控制异常。同时建议在电源输入端加装470μF以上的电解电容防止电机启动时的电压波动导致MCU复位。2.2 核心传感器配置方案智能小车的智能很大程度上取决于传感器系统的设计。基于功能需求和成本考虑我的配置方案如下超声波模块HC-SR04用于避障和距离测量精度±3mm测量范围2-400cm。通过STM32的GPIO触发和输入捕获功能实现测距。红外循迹模块TCRT5000五路数字输出用于黑线识别。灵敏度可调通过STM32的普通GPIO读取状态。六轴姿态传感器MPU6050通过I2C接口连接提供加速度和角速度数据用于车身姿态校正。红外接收模块VS1838B配合普通遥控器实现无线控制使用STM32的外部中断捕获信号。传感器布局需要特别注意超声波传感器应安装在小车正前方高度约10-15cm红外循迹模块安装在小车底部距地面0.5-1cmMPU6050应尽量靠近车体中心位置减少振动干扰2.3 电源系统设计稳定的电源是系统可靠运行的基础。我的方案采用两路独立供电主控部分通过开发板的Micro USB接口供电5V动力部分18650锂电池3.7V经升压模块输出至6V这种设计有三个优点一是避免了电机噪声对MCU的干扰二是可以单独调试控制系统三是延长了电池使用时间。实测表明2000mAh的18650电池可以支持小车连续运行约2小时。3. 开发环境搭建与基础配置3.1 工具链安装与配置对于STM32开发Keil MDK-ARM是最常用的IDE之一。安装时需要注意先安装Keil软件主体建议v5.25以上版本安装STM32F1xx_DFP芯片支持包当前最新为2.3.0安装ST-Link驱动用于程序下载调试配置工程时关键点选择正确的设备型号STM32F103C8Tx设置正确的时钟源HSE 8MHz配置调试接口为SWD模式勾选Use MicroLIB以减小代码体积3.2 HAL库与硬件抽象层初始化STM32CubeMX是ST官方提供的图形化配置工具可以大幅提升开发效率。我的配置流程如下新建工程选择STM32F103C8Tx配置时钟树HSE→PLL→系统时钟72MHz启用必要的外设GPIO超声波Trig/Echo、红外接收等TIM3通道1/2用于电机PWM通道4用于超声波测距I2C1MPU6050通信USART1调试信息输出生成代码时选择生成独立的.c/.h文件经验分享在CubeMX中配置外设时建议为每个功能模块创建单独的GPIO组并添加有意义的标签这样生成的代码可读性更好。例如将超声波相关引脚标记为USONIC_TRIG和USONIC_ECHO。3.3 基础驱动实现3.3.1 电机PWM驱动// 电机初始化 void Motor_Init(void) { HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); // 左电机PWM HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_2); // 右电机PWM // 设置初始占空比为0 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, 0); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_2, 0); } // 设置电机速度 void Motor_SetSpeed(int16_t left, int16_t right) { // 限幅处理 left (left 1000) ? 1000 : ((left -1000) ? -1000 : left); right (right 1000) ? 1000 : ((right -1000) ? -1000 : right); // 设置PWM占空比 if(left 0) { HAL_GPIO_WritePin(MOTOR_L_DIR_GPIO_Port, MOTOR_L_DIR_Pin, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, left); } else { HAL_GPIO_WritePin(MOTOR_L_DIR_GPIO_Port, MOTOR_L_DIR_Pin, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, -left); } // 右电机同理 ... }3.3.2 超声波测距实现超声波测距的关键是精确测量高电平持续时间。我采用输入捕获方式实现// 超声波测距函数 float USonic_GetDistance(void) { static uint32_t start_time 0, end_time 0; float distance 0; // 发送10us的触发信号 HAL_GPIO_WritePin(USONIC_TRIG_GPIO_Port, USONIC_TRIG_Pin, GPIO_PIN_SET); delay_us(10); HAL_GPIO_WritePin(USONIC_TRIG_GPIO_Port, USONIC_TRIG_Pin, GPIO_PIN_RESET); // 等待回波信号 while(HAL_GPIO_ReadPin(USONIC_ECHO_GPIO_Port, USONIC_ECHO_Pin) GPIO_PIN_RESET); start_time HAL_GetTick(); while(HAL_GPIO_ReadPin(USONIC_ECHO_GPIO_Port, USONIC_ECHO_Pin) GPIO_PIN_SET); end_time HAL_GetTick(); // 计算距离声速340m/s distance (end_time - start_time) * 340.0 / 2.0 / 1000.0; return distance; }4. 智能小车核心功能实现4.1 多模式控制框架设计为了让小车支持多种控制方式我设计了一个状态机框架typedef enum { MODE_MANUAL, // 手动遥控模式 MODE_LINE_TRACE,// 循迹模式 MODE_AVOID, // 避障模式 MODE_COUNT } RunMode_t; RunMode_t current_mode MODE_MANUAL; void MainControlLoop(void) { static uint32_t last_ticks 0; if(HAL_GetTick() - last_ticks 50) return; // 20Hz控制频率 last_ticks HAL_GetTick(); switch(current_mode) { case MODE_MANUAL: ManualMode_Handler(); break; case MODE_LINE_TRACE: LineTrace_Handler(); break; case MODE_AVOID: Avoid_Handler(); break; default: break; } }4.2 红外循迹算法实现五路红外循迹模块的布局为左2、中1、右2算法设计如下#define LINE_SENSOR_COUNT 5 #define BASE_SPEED 600 void LineTrace_Handler(void) { uint8_t sensor_state[LINE_SENSOR_COUNT]; int16_t left_speed BASE_SPEED, right_speed BASE_SPEED; // 读取传感器状态 sensor_state[0] HAL_GPIO_ReadPin(LINE_L2_GPIO_Port, LINE_L2_Pin); sensor_state[1] HAL_GPIO_ReadPin(LINE_L1_GPIO_Port, LINE_L1_Pin); sensor_state[2] HAL_GPIO_ReadPin(LINE_M_GPIO_Port, LINE_M_Pin); sensor_state[3] HAL_GPIO_ReadPin(LINE_R1_GPIO_Port, LINE_R1_Pin); sensor_state[4] HAL_GPIO_ReadPin(LINE_R2_GPIO_Port, LINE_R2_Pin); // PID控制算法 static float last_error 0, integral 0; float error 0; // 计算偏差 if(!sensor_state[2]) { // 中间传感器在黑线上 error 0; } else if(!sensor_state[1]) { error -1; } else if(!sensor_state[3]) { error 1; } else if(!sensor_state[0]) { error -2; } else if(!sensor_state[4]) { error 2; } // PID计算 float Kp 150.0, Ki 0.5, Kd 80.0; integral error; float output Kp * error Ki * integral Kd * (error - last_error); last_error error; // 应用控制量 left_speed output; right_speed - output; Motor_SetSpeed(left_speed, right_speed); }4.3 超声波避障策略避障模式采用有限状态机设计包含前进、避障、旋转三个状态typedef enum { AVOID_FORWARD, AVOID_BACK_OFF, AVOID_TURN } AvoidState_t; AvoidState_t avoid_state AVOID_FORWARD; uint32_t avoid_timer 0; void Avoid_Handler(void) { float distance USonic_GetDistance(); switch(avoid_state) { case AVOID_FORWARD: if(distance 20.0) { // 检测到障碍物 Motor_SetSpeed(-400, -400); // 后退 avoid_state AVOID_BACK_OFF; avoid_timer HAL_GetTick(); } else { Motor_SetSpeed(600, 600); // 前进 } break; case AVOID_BACK_OFF: if(HAL_GetTick() - avoid_timer 500) { // 后退500ms Motor_SetSpeed(400, -400); // 左转 avoid_state AVOID_TURN; avoid_timer HAL_GetTick(); } break; case AVOID_TURN: if(HAL_GetTick() - avoid_timer 800) { // 旋转800ms avoid_state AVOID_FORWARD; } break; } }5. 系统优化与调试技巧5.1 电源噪声抑制实践在调试过程中电机启停经常导致系统复位经过排查发现是电源问题。采取的解决方案在电机电源输入端并联470μF电解电容和0.1μF陶瓷电容组合为STM32的VDDA引脚增加LC滤波电路10μH电感1μF电容在软件上实现电机软启动避免电流突变// 电机软启动实现 void Motor_SoftStart(int16_t target_left, int16_t target_right, uint16_t duration_ms) { int16_t step_left target_left / (duration_ms / 10); int16_t step_right target_right / (duration_ms / 10); int16_t current_left 0, current_right 0; while(abs(current_left) abs(target_left) || abs(current_right) abs(target_right)) { current_left step_left; current_right step_right; Motor_SetSpeed(current_left, current_right); HAL_Delay(10); } }5.2 传感器数据滤波处理原始传感器数据往往包含噪声需要进行滤波处理。对于不同类型的传感器我采用不同的滤波策略超声波测距中值滤波滑动平均#define USONIC_FILTER_SIZE 5 float USonic_FilteredDistance(void) { static float buffer[USONIC_FILTER_SIZE] {0}; static uint8_t index 0; float temp; // 获取新数据 buffer[index] USonic_GetDistance(); index (index 1) % USONIC_FILTER_SIZE; // 排序找中值 for(uint8_t i0; iUSONIC_FILTER_SIZE-1; i) { for(uint8_t ji1; jUSONIC_FILTER_SIZE; j) { if(buffer[i] buffer[j]) { temp buffer[i]; buffer[i] buffer[j]; buffer[j] temp; } } } return buffer[USONIC_FILTER_SIZE/2]; }MPU6050姿态数据互补滤波void MPU6050_GetAngle(float *pitch, float *roll) { static float angle_pitch 0, angle_roll 0; float accel[3], gyro[3]; float dt 0.02; // 50Hz采样率 // 读取原始数据 MPU6050_ReadAccel(accel); MPU6050_ReadGyro(gyro); // 加速度计计算角度 float acc_pitch atan2(accel[1], accel[2]) * 180/PI; float acc_roll atan2(-accel[0], sqrt(accel[1]*accel[1] accel[2]*accel[2])) * 180/PI; // 互补滤波 angle_pitch 0.98 * (angle_pitch gyro[0]*dt) 0.02 * acc_pitch; angle_roll 0.98 * (angle_roll gyro[1]*dt) 0.02 * acc_roll; *pitch angle_pitch; *roll angle_roll; }5.3 无线调试技巧为了便于现场调试我实现了基于HC-05蓝牙模块的无线调试功能硬件连接HC-05的TXD→STM32的USART2_RXHC-05的RXD→STM32的USART2_TX软件配置// 初始化USART2 void DebugUART_Init(void) { huart2.Instance USART2; huart2.Init.BaudRate 9600; huart2.Init.WordLength UART_WORDLENGTH_8B; huart2.Init.StopBits UART_STOPBITS_1; huart2.Init.Parity UART_PARITY_NONE; huart2.Init.Mode UART_MODE_TX_RX; huart2.Init.HwFlowCtl UART_HWCONTROL_NONE; huart2.Init.OverSampling UART_OVERSAMPLING_16; HAL_UART_Init(huart2); } // 发送调试信息 void Debug_Printf(const char *fmt, ...) { char buffer[128]; va_list args; va_start(args, fmt); vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); HAL_UART_Transmit(huart2, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY); }使用时可以通过手机蓝牙串口APP实时查看传感器数据和控制参数极大提高了调试效率。