从零打造智能小车STM32F103C8T6与L298N的PID电机控制实战1. 项目概述与核心组件选型智能小车作为嵌入式学习的经典项目融合了硬件设计、电机控制、传感器集成和无线通信等多领域技术。本项目采用STM32F103C8T6作为主控芯片配合L298N电机驱动模块和蓝牙通信实现带PID调速功能的智能小车系统。核心组件特性对比组件型号关键参数项目中的作用主控芯片STM32F103C8T6Cortex-M3内核72MHz主频64KB Flash系统控制核心运行PID算法电机驱动L298N最大46V/2A双路输出逻辑电压5V驱动直流电机支持PWM调速蓝牙模块JDY-31蓝牙4.2串口透传10米传输距离无线控制指令传输直流电机25GA37012V370转/分带霍尔编码器提供动力并反馈转速提示初学者建议选择带有编码器的直流电机便于实现闭环速度控制。无编码器的开环控制难以达到精确调速效果。2. 硬件系统搭建与电路设计2.1 电源系统设计智能小车的电源系统需要为不同组件提供合适的电压12V锂电池直接为L298N和电机供电5V稳压电路为STM32和蓝牙模块供电3.3V LDO为编码器等外围器件供电典型接线步骤将锂电池正负极接入L298N的12V和GND端子从L298N的5V输出端引出电源线到STM32的5V引脚确保所有模块的GND共地连接为STM32添加100μF电容滤波防止电机干扰2.2 电机驱动接口配置L298N模块的电机控制逻辑如下表所示IN1IN2ENA电机A状态00X停止10PWM正转(PWM调速)01PWM反转(PWM调速)11X刹车// STM32 GPIO初始化示例 void Motor_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; // 使能GPIO时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); // 配置IN1(PA4), IN2(PA5)为输出 GPIO_InitStruct.Pin GPIO_PIN_4 | GPIO_PIN_5; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); // 初始化状态电机停止 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); }2.3 编码器接口设计带霍尔编码器的电机可提供转速反馈是实现PID控制的关键。编码器输出两路正交信号可通过STM32的定时器编码器模式读取// 定时器编码器模式配置 void Encoder_TIM_Init(void) { TIM_Encoder_InitTypeDef sConfig {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; htim2.Instance TIM2; htim2.Init.Prescaler 0; htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 65535; htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; sConfig.EncoderMode TIM_ENCODERMODE_TI12; sConfig.IC1Polarity TIM_ICPOLARITY_RISING; sConfig.IC1Selection TIM_ICSELECTION_DIRECTTI; sConfig.IC1Prescaler TIM_ICPSC_DIV1; sConfig.IC1Filter 0; sConfig.IC2Polarity TIM_ICPOLARITY_RISING; sConfig.IC2Selection TIM_ICSELECTION_DIRECTTI; sConfig.IC2Prescaler TIM_ICPSC_DIV1; sConfig.IC2Filter 0; HAL_TIM_Encoder_Init(htim2, sConfig); HAL_TIM_Encoder_Start(htim2, TIM_CHANNEL_ALL); }3. PID控制算法实现与调参3.1 PID算法原理PID控制器由比例(P)、积分(I)、微分(D)三部分组成其离散形式可表示为u(k) Kp*e(k) Ki*∑e(j) Kd*[e(k)-e(k-1)]其中e(k) 目标速度 - 实际速度Kp比例系数决定响应速度Ki积分系数消除稳态误差Kd微分系数抑制超调3.2 增量式PID实现// 增量式PID控制器 int PID_Controller(int Target, int Current) { static int last_error 0, prev_error 0; static int output 0; int error Target - Current; // 增量计算 int delta Kp*(error - last_error) Ki*error Kd*(error - 2*last_error prev_error); output delta; // 输出限幅 if(output MAX_PWM) output MAX_PWM; if(output -MAX_PWM) output -MAX_PWM; // 更新误差记录 prev_error last_error; last_error error; return output; }3.3 PID参数整定方法手动调参步骤先将Ki和Kd设为0逐渐增大Kp直到系统出现持续振荡取振荡时Kp值的50%作为初始比例系数逐渐增加Ki消除稳态误差但避免积分饱和最后加入Kd抑制超调通常为Kp的10%-20%注意实际调试时应先确保电机能正常启停和转向再逐步加入PID控制。调试过程中建议通过OLED实时显示转速和PWM占空比。4. 蓝牙遥控与系统集成4.1 蓝牙通信协议设计JDY-31模块通过串口与STM32通信可定义简单协议格式帧头(1B) | 命令类型(1B) | 数据(2B) | 校验(1B)典型控制命令示例0xA1设置目标速度0xB1紧急停止0xC1PID参数调整// 蓝牙数据接收处理 void Bluetooth_Process(uint8_t* data) { if(data[0] 0xA1) { // 速度设置命令 int speed (data[1] 8) | data[2]; Set_Target_Speed(speed); } else if(data[0] 0xB1) { // 停止命令 Motor_Stop(); } }4.2 手机APP设计要点使用MIT App Inventor或Android Studio开发控制APP时需注意提供速度滑块控制0-100%添加方向控制按钮前进/后退/停止实现PID参数调节界面确保发送数据格式与下位机协议一致4.3 系统整合与测试完整的控制流程如下手机APP发送速度指令STM32通过蓝牙接收并解析指令编码器实时反馈当前转速PID控制器计算PWM输出L298N驱动电机达到目标速度常见问题排查电机不转检查电源连接和使能信号转速波动大调整PID参数或检查编码器接线蓝牙连接不稳定确保模块供电充足避开2.4G干扰源5. 进阶优化方向5.1 速度滤波算法原始编码器数据存在噪声可添加滑动平均滤波#define FILTER_SIZE 5 int speed_filter[FILTER_SIZE] {0}; int Moving_Average_Filter(int new_speed) { static int index 0; int sum 0; speed_filter[index] new_speed; index (index 1) % FILTER_SIZE; for(int i0; iFILTER_SIZE; i) { sum speed_filter[i]; } return sum / FILTER_SIZE; }5.2 自适应PID控制根据系统状态动态调整PID参数void Adaptive_PID(int error) { if(abs(error) 100) { // 大误差范围 Kp 8.0; Ki 0.5; Kd 0.1; } else if(abs(error) 30) { // 中等误差 Kp 5.0; Ki 1.0; Kd 0.5; } else { // 小误差范围 Kp 3.0; Ki 2.0; Kd 1.0; } }5.3 能量回收与制动利用PWM斩波实现电子制动将动能转化为电能回充电容void Motor_Brake(void) { // 设置H桥为短路制动模式 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 短暂保持后释放 HAL_Delay(50); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); }在实际项目中我发现电机的机械特性对PID控制影响很大。相同PID参数在不同负载下表现差异明显因此建议在目标负载条件下进行最终调参。另外L298N的发热问题不容忽视长时间运行时最好加装散热片。