用STM32F103C8T6+L298N+蓝牙,手把手教你做个带PID调速的智能小车电机驱动(附完整代码)
2026/5/23 22:11:56 网站建设 项目流程

从零打造智能小车:STM32F103C8T6与L298N的PID电机控制实战

1. 项目概述与核心组件选型

智能小车作为嵌入式学习的经典项目,融合了硬件设计、电机控制、传感器集成和无线通信等多领域技术。本项目采用STM32F103C8T6作为主控芯片,配合L298N电机驱动模块和蓝牙通信,实现带PID调速功能的智能小车系统。

核心组件特性对比:

组件型号关键参数项目中的作用
主控芯片STM32F103C8T6Cortex-M3内核,72MHz主频,64KB Flash系统控制核心,运行PID算法
电机驱动L298N最大46V/2A双路输出,逻辑电压5V驱动直流电机,支持PWM调速
蓝牙模块JDY-31蓝牙4.2,串口透传,10米传输距离无线控制指令传输
直流电机25GA37012V,370转/分,带霍尔编码器提供动力并反馈转速

提示:初学者建议选择带有编码器的直流电机,便于实现闭环速度控制。无编码器的开环控制难以达到精确调速效果。

2. 硬件系统搭建与电路设计

2.1 电源系统设计

智能小车的电源系统需要为不同组件提供合适的电压:

  • 12V锂电池:直接为L298N和电机供电
  • 5V稳压电路:为STM32和蓝牙模块供电
  • 3.3V LDO:为编码器等外围器件供电

典型接线步骤:

  1. 将锂电池正负极接入L298N的12V和GND端子
  2. 从L298N的5V输出端引出电源线到STM32的5V引脚
  3. 确保所有模块的GND共地连接
  4. 为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参数整定方法

手动调参步骤:

  1. 先将Ki和Kd设为0,逐渐增大Kp直到系统出现持续振荡
  2. 取振荡时Kp值的50%作为初始比例系数
  3. 逐渐增加Ki,消除稳态误差但避免积分饱和
  4. 最后加入Kd抑制超调,通常为Kp的10%-20%

注意:实际调试时应先确保电机能正常启停和转向,再逐步加入PID控制。调试过程中建议通过OLED实时显示转速和PWM占空比。

4. 蓝牙遥控与系统集成

4.1 蓝牙通信协议设计

JDY-31模块通过串口与STM32通信,可定义简单协议格式:

帧头(1B) | 命令类型(1B) | 数据(2B) | 校验(1B)

典型控制命令示例:

  • 0xA1:设置目标速度
  • 0xB1:紧急停止
  • 0xC1:PID参数调整
// 蓝牙数据接收处理 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 系统整合与测试

完整的控制流程如下:

  1. 手机APP发送速度指令
  2. STM32通过蓝牙接收并解析指令
  3. 编码器实时反馈当前转速
  4. PID控制器计算PWM输出
  5. 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 i=0; i<FILTER_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的发热问题不容忽视,长时间运行时最好加装散热片。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询