当前位置: 首页 > news >正文

嵌入式PID控制教学系统:双平台直流电机闭环实践

1. 项目概述

本项目实现了一个面向嵌入式初学者的双平台PID控制教学系统,核心目标是通过可复现、可验证、可移植的硬件-软件协同设计,帮助开发者深入理解闭环控制的基本原理与工程实现方法。系统以直流电机为被控对象,采用增量式编码器作为位置/速度反馈传感器,构建完整的定速(恒转速)与定距(恒位移)两种典型控制场景,并通过1.9英寸SPI TFT显示屏与五键人机界面提供直观的操作交互。

项目原始开发基于TI MSPM0G3507微控制器平台,后续完整移植至ST STM32F103C8T6平台。两次实现均严格遵循嵌入式实时系统设计规范:前台任务负责UI刷新、按键响应与状态切换;后台任务由高精度定时器中断驱动,承担编码器采样、PID计算、PWM更新等时间敏感操作。整个架构采用分层设计思想,将应用逻辑、控制算法、外设驱动解耦,为后续功能扩展与跨平台迁移奠定了坚实基础。

该系统并非仅限于理论演示,其硬件设计已通过实际电机负载验证,软件逻辑覆盖了从上电初始化、参数整定、模式切换到异常退出的全生命周期管理。所有关键模块——包括正交编码器信号解析、抗干扰按键扫描、带限幅的PID运算、双路互补PWM电机驱动——均经过实测验证,具备直接用于课程实验、毕业设计或小型机电控制系统原型开发的能力。

2. 系统架构与工作原理

2.1 整体架构设计

系统采用经典的前后台(Foreground-Background)实时架构,兼顾确定性与时序可控性:

  • 前台任务(Main Loop):运行于main()函数的无限while(1)循环中,承担非实时性要求高的任务,包括:

    • 用户界面(UI)周期性刷新(约30Hz)
    • 按键事件的轮询检测与去抖处理
    • 系统状态机的迁移判断与页面跳转
    • 调试信息(如当前转速、目标值、PID参数)的格式化显示
  • 后台任务(Timer Interrupt Service Routine):由20ms周期性定时器中断触发,执行对时序精度要求极高的核心控制任务:

    • 编码器AB相脉冲计数与方向判别
    • 基于20ms采样窗口的速度计算(RPM)
    • PID控制器的误差计算、积分累加、微分估算与输出限幅
    • PWM占空比的实时更新与电机功率输出
    • 系统运行指示灯(LED)的秒级翻转

该架构确保了控制环路的严格周期性(20ms),避免了主循环中UI刷新等耗时操作对控制精度的影响,同时保持了代码结构的清晰性与可维护性。

2.2 控制对象建模与反馈机制

被控对象为一台带1000线增量式编码器的直流有刷电机。其物理模型可简化为一阶惯性环节,存在明显的机械时间常数与电磁时间常数。系统通过以下方式建立闭环:

  • 速度反馈:编码器每转输出2000个脉冲(A/B相各1000个,四倍频后)。在20ms定时中断内统计脉冲增量ΔN,则实际转速为: [ RPM = \frac{\Delta N \times 50 \times 60}{1000} = 3 \times \Delta N ] 其中50为1/20ms的换算系数(50次中断/秒),60为分钟换算系数。该公式隐含了编码器线数为1000的假设,实际应用中需根据所用编码器规格精确标定。

  • 位置反馈:直接读取编码器累计脉冲数count,单位为“脉冲”。设定目标距离target(单位:脉冲)后,系统持续比较counttarget的差值,直至绝对误差小于预设阈值(如5脉冲),即判定到达目标位置并自动停机。

  • 执行机构:BDR6126D双H桥驱动芯片,支持4.5A持续电流。通过BI/FI两路PWM信号控制电机正转(BI高、FI低)、反转(BI低、FI高)与制动(BI/FI同为高或同为低)。硬件设计中未采用主动制动模式,停机时默认将两路PWM置零,依靠电机反电动势与机械摩擦自然减速。

2.3 状态机驱动的人机交互

系统定义了五个核心操作页面,每个页面对应一个独立的状态机,通过五向导航键(上/下/左/右/OK)进行切换与参数修改。状态机设计严格遵循事件驱动原则,所有状态迁移均由按键事件触发,无忙等待或阻塞操作。

  • 首页(Home State):显示嘉立创Logo与常用网址两个静态页面入口,以及“定速”、“定距”两个功能入口。通过ui_flag标志位记忆用户从Logo/网址页返回后的焦点位置,保证操作连贯性。

  • 定速页(Speed Control State):显示当前实测RPM、目标RPM及P/I/D三组参数。用户可通过方向键在“目标值”与“P/I/D参数”间切换焦点,OK键确认进入编辑模式。

  • 定距页(Distance Control State):显示当前累计脉冲数、目标脉冲数及P/I/D参数。操作逻辑与定速页一致,但目标值单位为“脉冲”,反映的是绝对位移量。

  • 设置页(Setting State):提供全局配置选项,如屏幕亮度调节、系统复位等。右键快捷进入当前控制模式(定速/定距)的参数调节子菜单。

  • 调参页(Tuning State):聚焦于PID参数整定。用户可在P、I、D、Target四个字段间循环选择,上下键增减数值,OK键确认修改。target_flag标志位确保在模式切换(如从定速切至定距)后,参数编辑焦点能准确恢复至上次操作的字段。

所有状态机均采用查表法实现状态转移,避免复杂的嵌套条件判断,提升了代码可读性与执行效率。

3. 硬件设计详解

3.1 电源系统设计

系统采用Type-C接口输入标准5V USB电源,通过简洁而稳健的电源分配网络为各功能模块供电:

  • 电机供电(MOTOR_VCC):5V输入经由单刀开关后,直接供给BDR6126D驱动芯片的VM引脚。此路径不经过任何LDO或滤波,旨在为电机提供最大可能的瞬时电流能力,满足启动与堵转工况需求。设计中特别注意PCB走线宽度,确保能承载4.5A峰值电流。

  • 数字电路供电(+5V):5V输入在开关之后,串联一只防倒灌肖特基二极管(如SS34),再经22μF电解电容与100nF陶瓷电容组成的π型滤波网络,最终接入开发板的5V输入引脚。该设计具有双重作用:

    1. 防倒灌保护:当开发板自身5V→3.3V LDO出现故障或外部误接3.3V电源时,二极管可阻止电流反向流入USB端口,保护主机设备。
    2. 储能稳压:22μF电解电容提供低频储能,吸收电机启停瞬间造成的5V母线电压跌落;100nF陶瓷电容则负责高频噪声旁路,抑制BDR6126D开关动作产生的EMI对MCU数字电路的干扰。

开发板内部集成的5V→3.3V LDO(如AMS1117-3.3)为MCU核心、编码器、SPI屏幕逻辑部分提供稳定3.3V电源。该LDO的输入即来自上述+5V滤波输出,因此其输入纹波已得到充分抑制,无需额外增加输入电容。

3.2 电机驱动与编码器接口

3.2.1 BDR6126D驱动电路

BDR6126D是一款集成度高、驱动能力强的双H桥芯片,其关键特性与应用要点如下:

特性参数工程意义
供电电压范围4.5V – 18V完美兼容5V USB输入,留有充足裕量应对电池供电场景
持续输出电流±4.5A远超一般教学用微型电机需求(通常<1A),具备强过载能力
逻辑电平兼容3.3V/5V可直接由MCU GPIO驱动,无需电平转换电路
内置保护过流、过热、欠压锁定显著提升系统鲁棒性,避免因编码器卡死或短路导致芯片损坏

在原理图中,BDR6126D的BO(Bridge Output A)与FO(Bridge Output B)引脚分别连接至电机的两个电极。BI(Bridge Input A)与FI(Bridge Input B)为逻辑输入端,由MCU的两路独立PWM信号控制。为防止电机感性负载在关断瞬间产生高压尖峰损坏芯片,设计中在BO/FOVM之间预留了续流二极管焊盘(虽未在描述中明确安装,但PCB已做冗余设计)。

3.2.2 正交编码器接口

系统采用标准A/B相正交编码器,其信号处理分为硬件与软件两层:

  • 硬件层:编码器A/B相信号直接接入MCU的GPIO引脚,并配置为外部中断(EXTI)输入。为提高抗干扰能力,每个信号线上均放置了10kΩ上拉电阻至3.3V,并在PCB布局时尽量缩短走线长度,远离电机驱动等噪声源。

  • 软件层:采用“边沿触发+状态查询”法实现四倍频计数。以MSPM0G3507为例,A相上升沿中断服务程序中,读取B相当前电平:若B为低,则计数器+1(正转);若B为高,则计数器-1(反转)。同理,在B相上升沿中断中,读取A相电平进行相反判断。此方法充分利用了正交编码器的相位关系,将分辨率提升至原始线数的四倍,显著提高了低速下的位置测量精度。

在STM32F103C8T6移植版本中,该逻辑通过HAL_GPIO_EXTI_Callback()回调函数实现,同样基于A/B相的上升沿触发,并在回调中读取另一相信号电平完成方向判别。

3.3 人机交互外设

3.3.1 五向按键阵列

按键采用标准的“GPIO-上拉-按键-GND”设计,所有按键共用一个GND网络。MCU GPIO配置为浮空输入(Floating Input),内部上拉电阻使未按下时引脚为高电平,按下时被拉至GND,产生低电平有效信号。

  • MSPM0G3507平台:按键分别接入GPIOA8GPIOA9GPIOA28GPIOA31GPIOB4,均为普通GPIO,无特殊复用要求。
  • STM32F103C8T6平台:按键统一接入GPIOB3GPIOB7,利用其连续编号特性,便于在key_scan()函数中通过数组索引高效读取。

按键消抖采用“定时器中断轮询+软件计数”策略:在20ms定时中断中,对每个按键进行一次电平采样,并与前一次采样值比较。只有当连续N次(如3次)采样结果一致时,才认定为有效按键事件。该方法避免了在中断中使用delay()等阻塞函数,符合实时系统设计规范。

3.3.2 1.9英寸SPI TFT显示屏

显示屏采用160x128分辨率、16位RGB565色彩格式的SPI接口TFT。其控制信号定义如下:

信号功能MSPM0G3507引脚STM32F103C8T6引脚驱动方式
SCL(SCK)串行时钟PB13(SPI2_SCK)PB13(SPI2_SCK)硬件SPI
SDA(MOSI)串行数据PB15(SPI2_MOSI)PB15(SPI2_MOSI)硬件SPI
DC数据/命令选择PA0PA0GPIO
CS片选PB12PB12GPIO
RES复位PA2PA2GPIO
BLK背光控制PA1PA1GPIO

驱动代码中,LCD_DC引脚用于区分发送的是指令(DC=0)还是像素数据(DC=1);LCD_CS引脚在每次SPI传输前拉低,传输结束后拉高,实现片选功能;LCD_RES引脚在初始化时产生一个低脉冲,强制屏幕复位;LCD_BLK引脚通过PWM或直接电平控制背光亮度。

值得注意的是,所有GPIO控制宏(如LCD_DC_Set())均被定义为内联函数,避免了函数调用开销,确保了SPI通信时序的严格性。

4. 软件设计与算法实现

4.1 分层软件架构

软件严格遵循分层架构(Layered Architecture),各层职责清晰,依赖关系单向:

Application Layer (app/) ├── app_sys_mode.c/h # 系统模式管理(状态机、页面跳转) ├── app_speed_pid.c/h # 定速控制业务逻辑 ├── app_distance_pid.c/h # 定距控制业务逻辑 ├── app_key_task.c/h # 按键事件分发与响应 └── app_ui.c/h # UI绘制与刷新 Middle Layer (middle/) ├── mid_pid.c/h # PID算法核心(与硬件无关) ├── mid_timer.c/h # 定时器管理与任务调度 ├── mid_button.c/h # 按键扫描与状态机 ├── mid_debug_led.c/h # 运行指示灯控制 └── mid_debug_uart.c/h # 串口调试输出 Hardware Layer (hardware/) ├── hw_motor.c/h # 电机PWM驱动(抽象为set()/stop()接口) ├── hw_encoder.c/h # 编码器计数与速度计算 ├── hw_lcd.c/h # LCD底层驱动(SPI读写、寄存器配置) ├── hw_key.c/h # 按键硬件读取(返回原始电平状态) ├── lcdfont.h # 字模数据 └── pic.h # 图片资源(Logo等)

这种分层设计使得:

  • 应用层完全不关心底层硬件细节,可专注于控制逻辑与用户体验。
  • 中间层封装了通用算法与服务,mid_pid.c中的pid_calc()函数可被任意MCU平台复用。
  • 硬件层实现了平台相关代码,移植时仅需重写该层,上层逻辑几乎无需修改。

4.2 PID控制算法实现

mid_pid.c实现了标准的位置式PID算法,并针对嵌入式环境进行了关键优化:

float pid_calc(PID *pid, float target, float current) { pid->last_error = pid->error; pid->error = target - current; float pout = pid->error; // 比例项 pid->change_i += pid->error; // 积分项累积(离散求和) float dout = pid->error - pid->last_error; // 微分项(后向差分) // 积分限幅:防止积分饱和(Integral Windup) if(pid->change_i > pid->max_change_i) pid->change_i = pid->max_change_i; else if(pid->change_i < -pid->max_change_i) pid->change_i = -pid->max_change_i; // PID输出计算 pid->output = (pid->kp * pout) + (pid->ki * pid->change_i) + (pid->kd * dout); // 输出限幅:保护执行机构 if(pid->output > pid->max_output) pid->output = pid->max_output; else if(pid->output < -pid->max_output) pid->output = -pid->max_output; return pid->output; }

关键工程考量:

  • 积分限幅(Anti-Windup)max_change_i参数限制了积分项的最大累积值。当系统存在大偏差(如电机堵转)时,若不限制积分,change_i会持续增大,一旦偏差消失,积分项仍会维持较大输出,导致严重超调。限幅后,系统响应更平稳。
  • 输出限幅max_output直接约束了最终PWM占空比的绝对值,防止电机因过大的控制量而失控或过热。
  • 数据类型:全部使用float类型,兼顾精度与ARM Cortex-M系列MCU的FPU硬件加速能力(MSPM0G3507与STM32F103均支持单精度浮点运算)。

4.3 定速与定距控制逻辑

4.3.1 定速控制(app_speed_pid.c)

定速模式的核心是将编码器测得的RPM值稳定在用户设定的目标值。其控制流程如下:

  1. 在20ms定时中断中,调用encoder_update_speed()计算当前RPM。
  2. 主循环中,调用app_speed_pid_control()
    • 获取当前RPM作为current
    • 将用户设定的目标RPM作为target
    • 调用mid_pid_calculate()计算PID输出output
    • output(范围[-100, 100])映射为PWM占空比(0-100%),并调用hw_motor_set()驱动电机。
  3. |current - target| < threshold(如±5 RPM)时,认为系统已进入稳态,可维持当前输出;若偏差持续过大,则需检查PID参数或机械连接。
4.3.2 定距控制(app_distance_pid.c)

定距模式的目标是让电机转动一个精确的脉冲数(即角度)。其实现更为直接:

void app_distance_pid_control(void) { if(g_distance_pid_data.enable == 1) { float current_pos = hw_encoder_get_count(); // 获取累计脉冲数 float output = mid_pid_calculate(&g_distance_pid_data.pid, g_distance_pid_data.target, current_pos); hw_motor_set((int16_t)output); // 输出至电机 // 到达目标位置时停止 if(fabs(g_distance_pid_data.target - current_pos) < 5.0) { hw_motor_stop(); g_distance_pid_data.enable = 0; // 关闭控制环 } } }

此处的关键在于hw_encoder_get_count()返回的是自系统上电以来的总脉冲数,是一个绝对位置量。PID控制器将其与目标值比较,输出一个力矩指令,驱动电机向目标靠近。当两者差值小于5个脉冲(约0.018度,假设1000线编码器)时,系统判定已精确定位,立即停机。这种“到位即停”的策略简单有效,适用于对动态性能要求不苛刻的教学场景。

4.4 移植适配关键技术点

从MSPM0G3507到STM32F103C8T6的移植,本质是硬件抽象层(HAL)的替换,而非算法逻辑的修改。关键适配点如下:

模块MSPM0G3507实现STM32F103C8T6实现适配要点
SPI屏幕自定义Bit-Banging SPIHAL库HAL_SPI_Transmit()屏幕驱动函数内部调用HAL API,上层hw_lcd.c接口保持不变
按键读取DL_GPIO_readPins()HAL_GPIO_ReadPin()hw_key.ckey_scan()函数重写,返回结构体KEY_STATUS,内容一致
PWM输出DL_TimerG_setCaptureCompareValue()__HAL_TIM_SetCompare()hw_motor.cset_bi()/set_fi()函数重写,调用HAL TIM API
编码器中断GROUP1_IRQHandler()HAL_GPIO_EXTI_Callback()中断服务逻辑完全相同,仅API调用方式不同
定时器中断TIMER_LED_INST_IRQHandler()HAL_TIM_PeriodElapsedCallback()后台任务逻辑(编码器更新、按键扫描)完全复用,仅中断注册方式不同

移植过程中,中间层(mid_*.c)与应用层(app_*.c)的代码100%复用,证明了分层架构在跨平台开发中的巨大价值。唯一需要关注的是编译器工具链:STM32F103C8T6项目必须使用ARM Compiler 5(AC5),因其对__packed等关键字的支持与AC6存在差异,而AC5是Keil MDK v5.x的默认组件,需单独安装并正确配置路径。

5. BOM清单与器件选型依据

下表列出了项目核心物料及其选型理由,所有器件均为工业级、易采购、成本可控的通用型号:

序号器件名称型号/规格数量选型依据备注
1主控MCUMSPM0G3507R1MT1TI新一代超低功耗Arm Cortex-M0+,集成高性能ADC与多路PWM,开发板已集成所有必要外围原始平台
2主控MCUSTM32F103C8T61ST经典主流Cortex-M3 MCU,生态成熟,资料丰富,性价比极高移植平台
3电机驱动BDR6126D1国产高集成双H桥,4.5A持续电流,内置保护,价格仅为DRV8871的1/3替代方案:DRV8871
4编码器1000线增量式1标准教学用编码器,分辨率适中,易于信号调理与软件解析线数可按需更换
5SPI显示屏1.9" TFT, 160x1281小尺寸、低功耗、SPI接口,驱动IC常见(如ST7735S),配套资源丰富分辨率与接口固定
6电源管理AMS1117-3.31经典LDO,输出3.3V/1A,纹波小,成本低,长期供货稳定必需
7保护二极管SS3413A肖特基二极管,正向压降低(0.55V),反向耐压40V,满足防倒灌需求必需
8滤波电容22μF/16V 电解1为+5V数字电源提供低频储能必需
9滤波电容100nF 陶瓷1为+5V数字电源提供高频旁路必需
10按键贴片轻触开关5标准6x6mm规格,寿命长,手感好,成本低廉必需

所有被动器件(电阻、电容)均选用0805或0603封装,兼顾焊接便利性与PCB空间。BOM总成本控制在百元人民币以内,符合教学项目低成本、易复制的原则。

6. 实际部署与调试经验

6.1 硬件调试要点

  • 电机抖动问题:初次上电时,若电机出现高频抖动,首要检查BDR6126D的VMGND引脚是否接触良好。不良的接地会导致驱动芯片工作异常。其次,确认编码器A/B相是否接反,接反将导致方向判别错误,PID控制器持续发出反向修正指令。

  • 编码器计数不准:若实测RPM与万用表测得的电机转速偏差较大,应重新校准编码器线数。在encoder_update_speed()函数中,将1000.0替换为实际编码器规格书标明的线数(如600、1200),并乘以4(四倍频)。

  • SPI屏幕花屏:多数源于DC引脚电平错误。务必确认在发送指令前DC=0,发送数据前DC=1。可使用逻辑分析仪抓取SCK、MOSI、DC三线波形,验证时序是否符合TFT IC手册要求。

6.2 软件调试技巧

  • PID参数整定:推荐采用“先比例、后积分、最后微分”的Ziegler-Nichols启发式方法:

    1. KiKd置零,缓慢增大Kp,直至系统出现等幅振荡,记录此时的Ku与振荡周期Tu
    2. 设定Kp = 0.6*Ku,Ki = 1.2*Ku/Tu,Kd = 0.075*Ku*Tu作为初始值。
    3. 在此基础上微调,优先保证系统稳定(不振荡),再优化响应速度与超调量。
  • 串口调试:启用mid_debug_uart.c,在关键节点(如PID计算前后、编码器计数更新后)打印变量值。例如,在pid_calc()末尾添加:

    printf("PID: T=%.1f, C=%.1f, Err=%.1f, Out=%.1f\r\n", target, current, pid->error, pid->output);

    通过串口监视器实时观察控制过程,是定位逻辑错误最高效的手段。

  • 状态机可视化:在app_sys_mode.c中,为每个状态定义唯一的ASCII字符(如'H'代表首页,'S'代表定速页),并在主循环开头打印该字符。通过串口输出的字符流,可清晰看到用户操作引发的状态迁移路径,极大简化UI逻辑调试。

6.3 系统可靠性增强措施

  • 看门狗(WDT):虽然原始项目未启用,但在商用化部署时,强烈建议在main()初始化完成后启动独立看门狗(IWDG)。在主循环末尾添加HAL_IWDG_Refresh(&hiwdg),并在所有可能造成死锁的while()循环内定期喂狗。一旦软件跑飞或陷入死循环,WDT将自动复位系统。

  • 电源监控:在+5V输入端增加一个电压检测电路(如TL431+分压电阻),将检测信号接入MCU的ADC通道。当USB电源电压低于4.75V时,系统可主动降低PWM输出上限,避免因供电不足导致电机失步。

  • 电机堵转保护:在app_distance_pid_control()中,增加一个超时计数器。若从启动到enable=0的时间超过预设阈值(如5秒),则强制停机并点亮红色LED报警,提示用户检查机械卡滞。

这些增强措施虽未在基础项目中体现,但它们是将一个教学Demo升级为可靠工程产品的必经之路,体现了嵌入式工程师对系统鲁棒性的深刻理解。

http://www.jsqmd.com/news/473931/

相关文章:

  • Phi-3-mini-128k-instruct实战教程:使用chainlit构建可交互式AI助手前端界面
  • 丹青幻境从零到一:完整创作流程演示,生成你的首幅AI水墨画
  • 3步攻克输入法词库迁移:跨平台输入习惯无缝衔接指南
  • Yi-Coder-1.5B助力C++开发者:智能代码审查工具开发
  • Windows系统下C++恶搞代码实战:从无害玩笑到危险操作(附防护指南)
  • 小白友好:CYBER-VISION零号协议智能助盲眼镜系统一键部署教程
  • Ubuntu下NVIDIA驱动安装全攻略:从报错到完美运行nvidia-smi的完整流程
  • 无源音频信号切换板:高保真对比测试的硬件路由方案
  • 解决小红书内容采集难题的XHS-Downloader:高效无水印批量下载方案
  • 3步实现GitHub全界面汉化:让协作效率提升40%的技术方案
  • UM981高精度组合定位模块在复杂环境下的性能实测与优化策略
  • Kimi-VL-A3B-Thinking环境配置:vLLM启动参数、batch_size、max_model_len详解
  • 基于STM32H7与AD9910的高性能任意波形发生器设计
  • Qwen3-TTS-Tokenizer-12Hz应用场景解析:音频压缩、传输与重建全攻略
  • 进程注入技术实战指南:从原理到复杂场景应用
  • 解锁Better Genshin Impact自动化引擎:打造原神自定义工作流新体验
  • Qwen-Image-2512-ComfyUI 实用指南:三种ControlNet方案对比与选型建议
  • 基于LeCroy Xena Edun-224G的1.6T以太网测试方案:从224G SerDes验证到ASIC与光模块全场景测试
  • Vivado实战:如何将MicroBlaze的bit和elf文件一键整合(附详细步骤)
  • 基于STM32的USB HID隔空翻页PPT嵌入式系统
  • GME-Qwen2-VL-2B与Qt框架结合:开发跨平台桌面端多模态应用
  • CefFlashBrowser:Flash内容访问的兼容性解决方案
  • 电磁V8发动机:机电运动学仿真与多通道同步控制实践
  • CefFlashBrowser:跨越Flash技术鸿沟的全面解决方案
  • 400W多协议桌面电源设计:双路140W PD与SiC宽压DC-DC架构
  • 天空星开发板驱动0.96寸ST7735彩屏:从软件SPI到硬件SPI的完整移植指南
  • Unity脚本中文乱码?3分钟搞定VS+EditorConfig编码统一
  • 飞书文档批量导出创新解决方案:技术架构与企业实践指南
  • Phi-3 Mini部署案例:中小企业知识库问答系统快速构建指南
  • 弦音墨影新手教程:从‘提笔题词’到获取‘千里江山图’式分析结果