尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

51单片机闭环直流电机控制系统:从PWM到PID的完整实现

51单片机闭环直流电机控制系统:从PWM到PID的完整实现 简介基于51单片机的闭环直流电机控制系统资料包面向电子设计学习者与竞赛选手适合课程设计、毕业设计及项目入门。方案以AT89C51为核心通过P1.0输出占空比可调PWM用4个独立按键设定速度并用两个4位共阴数码管分别显示设定值与实时转速配合P3.2外部中断采集测速传感器脉冲形成速度反馈并实现闭环调速。资源共36个文件压缩包约381KB主要包含Keil工程源代码、Proteus仿真文件、原理图、流程图、物料清单等类型便于按设计流程查看和二次开发。已有757人学习/下载。资料不仅给出可运行的PWM调速程序还从引脚分配、按键逻辑、中断测速、数码管动态显示等环节完整梳理闭环控制实现思路适合需要快速搭建直流电机调速系统或理解反馈控制基础的读者参考。1. 51单片机闭环直流电机控制系统先跨过速度反馈这道坎在课程设计答辩现场最常被问的一个问题是PWM占空比明明给的是50%为什么电机实测转速不是空载转速的一半因为直流电机的转速不仅由PWM平均电压决定还受负载转矩、电枢电阻和反电动势影响。开环系统里你给多少占空比心里没底电机堵转你也不知道。闭环直流电机控制系统要做的事是把“设定转速”和“实测转速”放在同一个循环里做差差值经PWM控制算法换算成新的占空比让转速在负载变化时仍然逼近期望值。这套基于51单片机的方案在Proteus里完成原理图、仿真图和源码级验证再用配套物料清单落到实物是课程设计和入门工程训练最常见的一条完整路径。接下来按“选型→建图→写代码→调参→整理交付”展开每一处引脚、参数和代码都能直接抄走。2. 51单片机闭环系统的方案与选型反馈、PWM、驱动三者的边界2.1 反馈方式什么信号能让51单片机“看见”转速51单片机没有内部ADC也没有硬件正交解码器选反馈方式时先看输出信号类型。实际项目中主要用三种光电码盘、霍尔传感器、测速发电机。光电码盘把圆形码盘装在电机轴上红外对管透过码盘缝隙输出脉冲每转脉冲数PPR由码盘刻度决定。霍尔方案在轴上粘一对磁钢用A3144霍尔开关输出方波。这两种信号都是TTL电平脉冲可以直接接到P3.2或P3.3外部中断引脚上用中断计数换算转速。测速发电机输出的是与转速成正比的模拟电压需要再经过ADC0832或PCF8591采样麻烦一点但适合模拟量采集练习。选择原则可以压成一句话脉冲型反馈适合51单片机因为外部中断天然就是计数器电压型反馈需要先问自己“板上有没有多余AD通道”。Proteus仿真里脉冲型反馈可以直接用DCLOCK信号源替代编码器输出这也是后续仿真图里最贴近实物的做法。反馈方式输出信号51单片机接线适用场景光电码盘TTL脉冲P3.2外部中断0精度要求高仿真与实物一致霍尔磁钢TTL脉冲P3.2外部中断0结构简单、成本低测速发电机模拟电压经ADC0832转数字量Proteus中模拟反馈电压方便这里有一个必须提前算清的参数反馈脉冲最大频率。假设电机额定转速3000rpmPPR20则每秒脉冲数3000/60×201000即1kHz。外部中断0处理1kHz信号完全没问题但如果PPR提高到200脉冲频率就是10kHz中断里要尽量少做事避免丢脉冲。2.2 PWM的占空比和频率怎么定PWM的平均电压决定电机端电压占空比分辨率决定转速调节精度PWM频率决定电流纹波和噪声。51单片机用定时器中断翻转I/O口产生PWM载波频率不能太高否则中断占用率过高也不能太低否则电机转起来会有明显顿挫。工程上常见的做法是PWM频率取10kHz到20kHz但那是针对带硬件PWM的STM3251单片机用软件PWM一般把载波频率放在100Hz到1kHz之间。直流电机本身的惯性就是低通滤波器输出力矩的纹波不明显100Hz到1kHz完全够用。载波频率计算公式f 1 / (定时器溢出周期 × PWM周期计数值)。比如定时器溢出周期250μsPWM周期计数100次则f 1/(250μs×100)40Hz。40Hz对直流电机来说转速纹波已经很大不建议再低。还有一个容易被忽略的问题占空比从0跳到100、再从100跳回0的速度。51单片机I/O口驱动能力有限直接驱动L298N的使能端没有问题但L298N输入引脚要有上拉否则复位期间电平不确定电机会瞬动。2.3 驱动芯片选L298N还是ULN2003ULN2003是达林顿管阵列只能做单向驱动集电极输出灌电流适合驱动小功率电机和步进电机L298N是双H桥能双向调速、能耗制动。闭环直流电机控制系统的题目里如果只要求单向调速ULN2003更省事如果要求正反转直接用L298N。我一般选L298N原因有三个内置续流二极管、逻辑电源和电机电源分开、输入引脚兼容TTL电平。Proteus库里的元件名是L298界面上同样有ENA、IN1、IN2、VS、VSS引脚。引脚映射关系建议按下表固化成工程规范代码、原理图和实物三处保持一致。功能51单片机引脚L298N/外部元件说明PWM输出P1.2ENA高电平使能速度由占空比决定方向控制P1.0IN1固定一个方向时P1.00方向控制P1.1IN2P1.11正转反向则取反反馈脉冲输入P3.2编码器/霍尔输出外部中断0计数目标转速加P2.0按键到GND每按一次目标10rpm目标转速减P2.1按键到GND每按一次目标-10rpm晶振用12MHz因为机器周期正好是1μs定时器初值好算。复位电路用10μF电解电容10kΩ电阻EA引脚接VCC。3. 在Proteus 8里绘制原理图和仿真图从元件到闭环反馈3.1 原理图搭建顺序打开Proteus 8 Professional后我习惯按“最小系统→驱动→反馈→显示”的顺序画原理图而不是把元件先全拖到画布再连线。最小系统包括AT89C52、晶振12MHz、两个33pF电容、复位电容和复位电阻。元件搜索关键字AT89C52、CRYSTAL、CAP、CAP-ELEC、RES。把AT89C52放好后先连电源网络和接地网络。Proteus里电源端子放在工具箱的“Terminals”模式下拖出VCC和GND端子不需要给每个引脚画一条长线。晶振接XTAL1和XTAL2两个33pF电容一端接晶振引脚、另一端接GND。复位电路是RESET引脚接10kΩ电阻到GND同时10μF电解电容正极接VCC、负极接RESET引脚。然后放置L298。L298的VSS接VCC网络VS接12V独立电源GND接GND。L298的IN1、IN2接P1.0、P1.1ENA接P1.2。电机用Proteus里的MOTOR-DC元件一端接L298的OUT1另一端接L298的OUT2。3.2 反馈通道在仿真里怎么做这是Proteus仿真里最关键的决策点。真实电机的编码器输出脉冲频率随转速变化但Proteus的MOTOR-DC模型没有机械接口没法直接输出编码脉冲。处理方式有两个我按课程设计的实际需求分别说明。方式一是开环验证法放一个DCLOCK信号发生器到画布输出端接P3.2把方波频率设置成编码器实际输出频率。例如齿轮电机减速比1:30码盘PPR20目标转速600rpm则反馈频率600/60×20200HzDCLOCK就设200Hz。这种方式能验证外部中断计数、速度换算、PID运算和PWM输出整个链路只是外部输入频率是手动设的模拟的是“转子速度已经稳定”的状态。方式二是闭环动态法P1.2的PWM输出经过一级RC低通滤波得到与占空比成正比的模拟电压把这个电压接到Proteus里的压控振荡器VCO控制端VCO输出方波再进P3.2。占空比高→平均电压高→VCO频率高→测得的转速高→PID回调占空比这样仿真里能直观看到PWM变化引起反馈频率变化形成真正的闭环。如果你的Proteus元件库没有VCO用一个直流电压源手动模拟等效电压也行但看不到动态调节过程。这两种方式在工程报告里的定位不同方式一用于验证软件逻辑和PID参数方式二用于展示闭环动态特性。我的建议是仿真图里两者都画用开关切换答辩时对比讲更有说服力。3.3 仿真图上的测量点怎么布置仿真图不只是原理图的复制品还要让波形和参数可视化。P1.2引脚上放一个电压探针运行后点探针看PWM波形L298的OUT1和OUT2之间也放一个探针看电机端电压P3.2引脚放数字探针看反馈脉冲。Proteus的虚拟示波器在工具箱“Virtual Instruments”里A通道接P1.2B通道接P3.2设置好时基后能同时观察PWM和反馈脉冲的时序关系。PWM占空比可以从波形上直接读出来但示波器只能看到高电平时间比例看不到数值。更快的验证办法是在代码里用串口把当前pwm_duty和speed_rpm发出去Proteus里放一个VIRTUAL TERMINAL收串口文本。串口波特率9600定时器2做波特率发生器。这样不用每个数都对着波形数格子。3.4 仿真里最容易踩的三个坑第一个坑是L298的VS和VSS都接了5V。VS是电机电源L298进入饱和压降后电机端电压偏低转速达不到目标PID积分会不断累加。仿真里把VS接12V实物里也照做。第二个坑是DCLOCK的频率设置过高。51单片机外部中断0的中断函数里有脉冲计数操作如果中断频率超过单片机能响应的上限计数会丢失。实测中12MHz晶振下中断频率5kHz以内是安全的DCLOCK设计计算时有意识避开高频段。第三个坑是Proteus仿真速度。仿真器跑500ms仿真时间可能消耗真实时间好几秒PID周期设100ms时波形调节过程看起来会很慢。这不是程序问题把仿真运行速度放到“Faster than Real Time”模式或者把PID采样周期暂时缩短到20ms做快速验证再把参数调回来。4. 从流程图到51单片机源代码PWM中断、PID算法与测速采样4.1 程序流程图对应的模块划分整个系统不复杂但流程必须先理清否则中断嵌套会乱。系统流程分成三个任务PWM产生、速度采集、PID运算。PWM产生由定时器1中断完成中断里只做计数器累加和I/O口置位。速度采集由外部中断0和定时器0配合完成外部中断0只做pulse_count定时器0每100ms计算一次转速并清零脉冲计数。PID运算在主循环里调用但为了保证PID周期稳定我也把它放在定时器0的中断尾部。主循环只处理按键。加速键、减速键每按一次修改目标转速target_rpm按键消抖用20ms延时。LCD显示如果要做也放在主循环里避免中断里做耗时操作。PID运算为什么要放在中断而不是主循环因为主循环里有按键消抖延时PID周期会被拖长且不稳定。放在定时器中断后100ms采样周期是严格保证的曲线也好调。4.2 定时器1产生PWM的代码实现PWM载波频率取1kHz占空比分辨率1%。定时器1溢出周期取100μsPWM_PERIOD10时载波正好1kHz但占空比分辨率只有10%。为了同时兼顾分辨率和频率把溢出周期改为250μsPWM_PERIOD100载波频率40Hz占空比步长1%。直流电机时间常数在几十毫秒量级40Hz载波可以接受。#include reg52.h sbit PWM_PIN P1^2; // 接到L298的ENA #define PWM_PERIOD 100 // 一个PWM周期包含100个中断节拍 unsigned char pwm_duty 50; // 占空比50%0~100 unsigned char pwm_cnt 0; void Timer1_Init(void) { TMOD 0x0F; // 清定时器1模式位 TMOD | 0x10; // 定时器1方式116位定时 TH1 (65536 - 250) / 256; // 250us溢出 TL1 (65536 - 250) % 256; ET1 1; // 开定时器1中断 TR1 1; // 启动定时器1 EA 1; // 开总中断 } void Timer1_ISR(void) interrupt 3 { TH1 (65536 - 250) / 256; // 重装初值 TL1 (65536 - 250) % 256; pwm_cnt; if (pwm_cnt PWM_PERIOD) { pwm_cnt 0; PWM_PIN 1; // 周期起点拉高PWM } else if (pwm_cnt pwm_duty) { PWM_PIN 0; // 计数值超过占空比后拉低 } }注意重装初值用了两次相同的表达式实际工程里可以预处理成宏定义避免代码中多处计算。逻辑说明pwm_cnt从0递增到99计数到pwm_duty时PWM_PIN拉低计数到100时归零并拉高。pwm_duty0时周期开始后第一个中断节拍就满足pwm_cnt0PWM输出一直为低pwm_duty100时pwm_cnt永远不可能大于等于100PWM输出一直为高。所以占空比边界条件天然闭合。4.3 外部中断测速与定时器0定时采样反馈脉冲从P3.2进入外部中断0。外部中断0的触发方式选用下降沿触发还是低电平触发取决于编码器输出波形和霍尔传感器的接法。A3144霍尔开关输出是集电极开路用上拉电阻后产生方波建议用下降沿触发IT01。转速换算公式speed_rpm (pulse_count / PPR) × 60 / sample_time。采样周期0.1sPPR20时speed_rpm pulse_count × 0.05 × 600 pulse_count × 30。volatile unsigned int pulse_count 0; volatile unsigned int speed_rpm 0; unsigned int timer0_cnt 0; void Timer0_Init(void) { TMOD 0xF0; // 清定时器0模式位 TMOD | 0x01; // 定时器0方式116位定时 TH0 (65536 - 1000) / 256; // 1ms中断 TL0 (65536 - 1000) % 256; ET0 1; TR0 1; } void Ext0_Init(void) { IT0 1; // 下降沿触发 EX0 1; // 开外部中断0 } void Timer0_ISR(void) interrupt 1 { TH0 (65536 - 1000) / 256; TL0 (65536 - 1000) % 256; timer0_cnt; if (timer0_cnt 100) // 累计100次即100ms { timer0_cnt 0; speed_rpm pulse_count * 30; // PPR20 pulse_count 0; PID_Update(); // 触发一次闭环运算 } } void Ext0_ISR(void) interrupt 0 { pulse_count; }外部中断0里只做自增不做别的这是高性能测速代码的基本纪律。pulse_count是unsigned int类型100ms内最大可计数65535个脉冲对应转速65535/6≈10922rpm完全够用。如果PPR很大、转速很高可以把pulse_count改成unsigned long但那样中断里要多花几个机器周期没必要。4.4 增量式PID调节代码位置式PID需要累加全部误差容易积分饱和增量式PID只计算本次输出的增量当前占空比在旧值上叠加增量天然适合PWM占空比输出。增量式公式delta_u Kp×(e(k)-e(k-1)) Ki×e(k) Kd×(e(k)-2e(k-1)e(k-2))这里的Ki是积分系数不是积分时间Ti离散化时已经合并了采样周期。51单片机没有浮点单元系数全部用整型为了防止中间变量溢出误差先做除以2的归一化。#define PWM_MAX 100 #define PWM_MIN 0 int target_rpm 600; // 目标转速由按键修改 int err 0; int last_err 0; int prev_err 0; int delta_u 0; int Kp 8; int Ki 2; int Kd 0; void PID_Update(void) { int current_speed (int)speed_rpm; err target_rpm - current_speed; delta_u Kp * (err - last_err) Ki * err Kd * (err - 2 * last_err prev_err); pwm_duty (unsigned char)(delta_u / 2); // 误差归一化系数2 if ((int)pwm_duty PWM_MAX) pwm_duty PWM_MAX; if ((int)pwm_duty PWM_MIN) pwm_duty PWM_MIN; prev_err last_err; last_err err; }这个代码里Kp8、Ki2、Kd0是经验初值不保证直接可用但方向是对的。delta_u除以2是为了让整型运算量级合适相当于把所有系数隐含缩小了一半。要说明的是delta_u为负数时pwm_duty做unsigned char加法可能会被编译器转换成无符号运算所以赋值前先强制转int并做边界判断。5. PID参数调优、PWM故障保护与仿真实物差异5.1 用临界比例法在Proteus里定出Kp、Ki、Kd初值PID参数在仿真里调和在实物上调方法完全一样。第一步只保留比例项Ki和Kd设为0Kp从小往大加。每次加完跑一段仿真观察转速曲线。Proteus里给P3.2的DCLOCK设成固定频率相当于电机在给定占空比下稳定运行然后在定时器0中断里人为把target_rpm改大20%看speed_rpm曲线怎么恢复。当Kp增加到输出PWM出现等幅振荡时记下这个临界Kp为Kc振荡周期为Tc。然后按齐格勒-尼科尔斯经验公式Kp0.6KcKiKp/(0.5Tc)KdKp×0.125Tc。注意这里的积分是Ti要再换算到代码里的KiKp×Ts/TiTs是采样周期100ms。以仿真里常见的Kc20、Tc0.5s为例Kp12Ti0.25sTd0.0625s代码系数Ki12×0.1/0.25≈5Kd12×0.0625/0.1≈7.5取整Kd8。这个整型换算过程是PID调参里最容易被忽略的坑很多人直接把Ti值填进代码结果发现积分项弱得毫无作用。调好初值后做一个负载扰动实验运行仿真到转速稳定把DCLOCK频率瞬时降低10%代表加载观察转速能不能在合理时间内回到设定值。一般要求超调量小于20%调节时间小于2s如果超调太大就加大Kd如果回升太慢就加大Ki。5.2 PWM故障保护与抗积分饱和“PWM故障保护”在51单片机系统里通常指两类问题一类是H桥电路上下桥臂同时导通造成的直通短路另一类是程序跑飞后PWM输出停留在固定占空比导致电机失控。L298N内部已经处理了前一类问题但后一类必须靠软件兜底。可以在代码里加一个看门狗喂狗标志。主循环每运行一次把watchdog_flag置1定时器0中断里如果发现watchdog_flag为0说明主循环卡死立即把pwm_duty清零并拉低PWM_PIN。用STC89C52时可以让硬件看门狗直接复位单片机用AT89S52时就要靠定时器2做软件看门狗因为AT89S52没有独立看门狗模块。抗积分饱和比看门狗更容易被忽略。增量式PID理论上不会长期饱和但pwm_duty一旦到100误差还在累加虽然不会继续增加占空比但积分项会把last_err推离实际值导致退出饱和时响应迟钝。解决办法是积分分离当误差绝对值大于200rpm时把Ki临时改成0。代码里在PID_Update开头加一行判断即可。if (err 200 || err -200) { delta_u Kp * (err - last_err) Kd * (err - 2 * last_err prev_err); } else { delta_u 原积分运算; }5.3 仿真和实物差距在哪Proteus仿真里看不到几个真实因素电机电刷火花、L298N的管压降随电流变化、电源内阻、编码器安装偏心。这些因素会在实物调试时表现为转速波动和反馈脉冲抖动。最典型的是编码器脉冲不均匀。实物码盘安装偏心后每转一圈脉冲宽度会不规则变化导致相邻两个100ms采样周期里pulse_count差几拍转速曲线出现周期性毛刺。处理办法是加大采样周期从100ms改成200ms或者把速度反馈先做一阶低通滤波speed_filtered 0.8×speed_filtered 0.2×speed_rpm。这个滤波系数要放在PID_Update前面防止PWM输出跟着毛刺一起抖。仿真里PWM占空比和电机端电压近似线性关系实物里L298N的饱和导通压降在VDS两端大约1.8V到2.2V12V电源实际到电机端只有不到10V。所以同样的目标转速实物需要的占空比会比仿真高10%以上。如果PID限幅还是0到100去掉限幅后Kp、Ki都可能需要重新微调。6. 物料清单与原理图出图的最后检查附一个验证闭环的收尾技巧6.1 物料清单按三个子电路拆分原理图、仿真相一致物料清单才可追溯。按最小系统、驱动与电源、反馈与交互三块拆分单价估算方便采购也不容易漏。模块物料数量说明最小系统AT89S52或STC89C521DIP40封装12MHz晶振最小系统12MHz晶振1配合33pF负载电容最小系统33pF瓷片电容2晶振负载电容最小系统10μF电解电容、10kΩ电阻各1复位电路驱动与电源L298N模块或芯片1双H桥驱动驱动与电源直流减速电机编码器1建议选带霍尔编码器的12V减速电机反馈霍尔开关A31441开路输出需上拉电阻反馈10kΩ上拉电阻1霍尔输出到P3.2交互按键2目标转速加减交互1602LCD可选1显示目标转速和实测转速电源7805或LM2596模块112V转5V给单片机供电原理图交给生产或焊接前用一个“网络检查法”把原理图缩放到整屏逐一数每个芯片的VCC和GND脚有没有连到电源端子再数每个中断引脚有没有被别的信号占用。P3.2被外部中断0占用就不能再接LCD的R/W脚这是连线阶段最容易犯的错。6.2 关于闭环验证一个更有说服力的收尾方法答辩或验收时不能只说“转速稳定了”。可以做一个阶跃扰动验证运行后等转速稳定在600rpm然后把P3.2的DCLOCK频率从200Hz突然改成180Hz模拟电机负载突然增加。用虚拟示波器同时观察PWM_PIN波形和串口输出的转速值如果看到PWM占空比自动增大、转速回落到接近600rpm就证明闭环有效。输出这个阶跃响应过程时记录占空比从50%升到62%、转速从600降到570再回600的完整曲线比“仿真成功”四个字有说服力得多。本文还有配套的精品资源点击获取
返回列表