STM32——直流电机PI调速

所需元件

  STM32F103开发板、L298N一个、带编码器的直流电机一个(如下图所示,淘宝上有很多)
在这里插入图片描述

系统框图

在这里插入图片描述
  通过系统框图,我们需要做两件事,一是要测速,二是要调节。测速目前流行的就是通过编码器测速,调节器我采用的时PI调节,PI调节器调节的参数少,而且能够消除静差,当然用PID调节器也行。

编码器

  编码器的结构简化如下图:
在这里插入图片描述
  在电机转轴上安装了一个磁环,在磁环的下方有一个霍尔传感器,在磁环转动过程中就在霍尔传感器的附近产生了变化的磁场,于是霍尔传感器就输出了脉冲信号。我所用的这个直流电机是1:48的减速电机,电机转轴每转动1圈,编码器输出13个脉冲信号,也就是说输出转轴转动1圈,编码器输出13x48=624个脉冲,再通过STM32编码器接口 4 倍频就是 624x4=2496 个脉冲信号,通过STM32定时器的计数值除以2496就是输出转轴转动的圈数。
  所谓4倍频,如下图:
在这里插入图片描述
  编码器中有两个线路,即A相和B相。我们以A相或B相为例,1个上升沿或者下降沿代表1个脉冲信号,由图中可知有2个上升沿或2个下降沿,即2个脉冲信号。而所谓的4倍频,就是把A、B相的上升沿和下降沿都加起来,一共8个,与之前的2个脉冲信号就是4倍,而所以要加起来做成4倍频,可以提高测量转速的精度。另外A、B相之间相差90度,从而可以判断电机的转向。如果电机正转,A相比B相先90度,也就是说A相已经上升沿了B还是低电平。

理论分析

建模

  直流有刷电机可以等效为下图结构。
在这里插入图片描述
在这里插入图片描述
经拉式变换得传递函数
在这里插入图片描述
  由于电枢电感和转子转动惯量都较小,故而传递函数中二次项系数非常小,可忽略二次项做降阶处理,得
在这里插入图片描述

PI调节器

  关于PID算法,可参考STM32——PID恒温控制
PIPIPI调节器表达式如下
在这里插入图片描述
PIPIPI算法省去了微分控制,以损失响应快速性为代价以保证系统的可靠性。

理论计算

  直流电机的相关参数可以测量出来,但由于没有工具只得通过系统辨识的方法获取电机的传递函数
在这里插入图片描述
已知电源电压为12v12v12v,单片机PWM配置中计数值为100时代表100%占空比,周期为10ms,可近似pwm的传递函数为
在这里插入图片描述
输入为占空比DDD,输出为电压,且0<D<1000<D<1000<D<100(因为计数值为100时代表100%100 \%100%占空比,所以可认为占空比就在0<D<1000<D<1000<D<100之间,但实际上真正的占空比数值在0−10-101之间)。令G(s)=GG(s)=GG(s)=Gm(s)∗G(s)*G(s)Gpwm(s)(s)(s),得
在这里插入图片描述
可近似为一阶系统
在这里插入图片描述
此时系统框图如下
在这里插入图片描述

得系统闭环传递函数
在这里插入图片描述
我希望电机响应的超调量很低,调节时间可以长一点,相关参数如下
在这里插入图片描述

在这里插入图片描述
计算出来的参数还不能直接使用,由于单片机控制电机是离散控制,所以实际使用的积分系数KKKi等于计算出的KKKi乘以采样周期TTT。由于传递函数误差或者其他一些原因会造成理论计算的参数并不一定与实际效果高度一致,还需手动微调一下,找到一个更为合适的参数。
仿真结果如下
在这里插入图片描述
通过仿真来看,超调量仍然较大,调节时间在0.7s0.7s0.7s左右,超调量大是由于引入了一个闭环零点,调节时间与理论值有误差是由于理论计算是取近似值等原因造成的。如果要追求更低的超调量,可以在输入端加入一个前置滤波器,该前置滤波器的作用就是用滤波器的极点对消闭环系统的零点,系统框图如下
在这里插入图片描述
仿真结果如下
在这里插入图片描述
从图像上看,调节时间变化不大,但是超调量明显的小了许多。
  这里贴两张速度曲线图:
  1、
在这里插入图片描述
  2、
在这里插入图片描述
  这两组响应的曲线的参数是试凑得来的,与理论计算的参数相差不是很大,理论计算的参数使用效果上略好一些。图1中设定目标值为400,从图上可以看出超调量还是比较小的,调节时间也比较短,调节的效果还是可以的。图2中,目标值每隔一段时间增加100,加到400后又设为100,整体的调节效果还是蛮不错的。

主要程序

TIM_Encoder.c

#include "TIM_Encoder.h"float RPM_1=0;            //存储上一次测速结果void TIM_Encoder_Init(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  TIM_ICInitTypeDef TIM_ICInitStructure;  GPIO_InitTypeDef GPIO_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);        //使能定时器4的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);       //使能PB端口时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	    //端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;       //浮空输入GPIO_Init(GPIOB, &GPIO_InitStructure);					    //根据设定参数初始化GPIOBTIM_TimeBaseStructInit(&TIM_TimeBaseStructure);TIM_TimeBaseStructure.TIM_Prescaler = 0x0;                  // 预分频器 TIM_TimeBaseStructure.TIM_Period = 65535;                   //设定计数器自动重装值TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //选择时钟分频:不分频TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3TIM_ICStructInit(&TIM_ICInitStructure);TIM_ICInitStructure.TIM_ICFilter = 10;TIM_ICInit(TIM4, &TIM_ICInitStructure);TIM_ClearFlag(TIM4, TIM_FLAG_Update);                       //清除TIM的更新标志位TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);//Reset counterTIM_SetCounter(TIM4,0);TIM_Cmd(TIM4, ENABLE);  
}int GetTIMCounter(void)          //获取计数值
{int count=TIM_GetCounter(TIM4);return count;
}float GetRPM(int count)           //计算转速
{
//   int RPM=count/2496*2000+0.5;//30ms计算一次(pid.T=30),60000ms为1min,也就是1min计算了2000次,2496=13*4*48表示转动一圈的脉冲数,48表示1:48的减速比float RPM=count*0.8f;   //等同于上式,2000/2496约等于0.8if(RPM>1000)                 //过滤掉不合理的结果,仍然使用上次的速度,在按键设定速度的时候或者在减速为0时会有非常的大的错误测速结果,具体原因还未查清    {{return RPM_1;}RPM_1=RPM;                   //更新return RPM;
}

PID.c

#include "PID.h"PID pid;
//int time=0;void PID_Init()
{           pid.Sv=400;         //用户设定转速400pid.Kp=0.3;         //比例pid.Ki=0.015;       //积分pid.Kd=0;           //微分pid.pwmcycle=100;   pid.T=30;           //PID计算周期30mspid.OUT0=0;pid.C1ms=0;pid.SEk=0;pid.Ek=0;pid.Ek_1=0;pid.DelEk=0;pid.Dout=0;pid.Iout=0;pid.Pout=0;
}void PID_Calc(float data)        //pid计算
{float out;pid.Pv=data;pid.Ek=pid.Sv-pid.Pv;       //得到当前的偏差值pid.Pout=pid.Kp*pid.Ek;     //比例输出pid.SEk+=pid.Ek;            //历史偏差总和if(pid.SEk<(-50)){pid.SEk=(-50);}pid.DelEk=pid.Ek-pid.Ek_1;  //最近两次偏差之差pid.Iout=pid.Ki*pid.SEk;    //积分输出if(pid.Iout<(-10)){pid.Iout=(-10);}pid.Dout=pid.Kd*pid.DelEk;  //微分输出out= pid.Pout+ pid.Iout+ pid.Dout;if(out>pid.pwmcycle){pid.OUT=pid.pwmcycle;}else if(out<=0){pid.OUT=pid.OUT0; }else {pid.OUT=out+0.5f;       //四舍五入}pid.Ek_1=pid.Ek;            //更新偏差pid.C1ms=0;
}

工程链接
链接:https://pan.baidu.com/s/1dSXgPf0gzSvTdjlMHyOZ7w
提取码:f8h1

  PID调参比较麻烦,这里推荐一个ST官方的软件StmStdio,这个软件网上有很多教程,使用也比较简单。
链接:https://pan.baidu.com/s/1etsrBL80rCe_LouNEE1XEg
提取码:ckve

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545608.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA设计模式--简单介绍

2019独角兽企业重金招聘Python工程师标准>>> &#xfeff;一、简介 Design pattern 是众多软件开发人员经过漫长的试验和错误总结出来的在软件开发过程中面临一般问题的解决方案&#xff0c;代表着最佳实践。使用设计模式是为了重用代码、让代码更容易被他人理解、保…

简单函数

【【【2013-9-13】】】--模糊查询 关键字 like--与通配符联合使用&#xff08;_任意一个字符&#xff1b;%任意长度的字符&#xff1b;转义字符/和关键字escape联用&#xff09;select * from emp where job like %/%% escape /;select * from emp where comm is not null;com…

第 6-3 课:SpringBoot 核心 + 面试题

为什么要用 Spring Boot? Spring Boot 来自于 Spring 大家族,是 Spring 官方团队(Pivotal 团队)提供的全新框架,它的诞生解决了 Spring 框架使用较为繁琐的问题。Spring Boot 的核心思想是约定优于配置,让开发人员不需要配置任何 XML 文件,就可以像 Maven 整合 Jar 包一…

Java ObjectOutputStream writeDouble()方法与示例

ObjectOutputStream类writeDouble()方法 (ObjectOutputStream Class writeDouble() method) writeDouble() method is available in java.io package. java.io包中提供了writeDouble()方法 。 writeDouble() method is used to write the given 8 bytes (i.e. 64 bit) double v…

为什么是PID控制

在进入正式话题之前需要引入四个概念&#xff1a;稳态误差、终值定理、幅角条件和系统稳定的充要条件。 稳态误差&#xff1a;系统达到稳定状态后&#xff0c;系统的实际输出量与系统希望的输出量之间的偏差。 终值定理&#xff1a;设有连续函数f(t)f(t)f(t)&#xff0c;当t趋于…

WinForm C#全局错误捕捉处理【整理】

1 static class Program2 {3 /// <summary>4 /// 应用程序的主入口点。5 /// </summary>6 [STAThread]7 static void Main()8 {9 try 10 { 11 12 //添加事件处理…

第 6-6 课:消息队列面试题汇总

1.消息队列的应用场景有哪些? 答:消息队列的应用场景如下。 应用解耦,比如,用户下单后,订单系统需要通知库存系统,假如库存系统无法访问,则订单减库存将失败,从而导致订单失败。订单系统与库存系统耦合,这个时候如果使用消息队列,可以返回给用户成功,先把消息持久化…

在新建FileInputStream时使用当前相对路径或者绝对路径作为参数的问题

当new一个FileInputStream时&#xff0c;想使用相对路径这样无论我的服务端部署到哪里&#xff0c;都可以一直用一个文件夹而不必修改程序的路径代码&#xff0c;当然首先我用的绝对路径来做实验&#xff0c;保证能够成功通信&#xff0c;使用绝对路径时要注意路径的分隔符可以…

卡尔曼滤波器推导

注&#xff1a;受控制领域大牛CAN博士启发&#xff0c;受益匪浅&#xff0c;作此文以为笔记。 简介 设 卡尔曼滤波器是从测量值ZZZk的平均数开始的。开始推导&#xff1a; 由上式可知   也就是说随着kkk的增大&#xff0c;测量结果Zk不在重要&#xff0c;因为已经获得了足…

第 6-7 课:Java 分布式框架面试题合集

1.什么是 ZooKeeper? 答:ZooKeeper 是一个开源的分布式应用程序协调服务,是一个典型的分布式数据一致性解决方案。设计目的是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的系统,并以一系列简单易用的原子操作提供给用户使用。 2.ZooKeeper 提供了…

Oracle date 插入显示公元前日期

显示公元前日期很简单&#xff1a;--先创建一个表 create table test (d date);--插入一条记录 --插入的日期要注意 --1、“ - ”&#xff08;负号&#xff09; 表示公元前 --2、“ s ” 格式控制符 insert into test values(to_date(-2000-12-2, syyyy-mm-dd));--查询一下 --要…

cocos2dx3.2文件结构和代码结构

既然选定了cocos2dxlua的原生方式来开发&#xff0c;首先要确定的是使用哪个版本的cocos2dx&#xff0c;先看看github上的changelog和releasenote&#xff0c;然后在google里搜索一下&#xff0c;参考了jacky的博客http://zengrong.net/post/2100.htm&#xff0c;最终选择了coc…

java 方法 示例_Java集合的lastlastIndexOfSubList()方法和示例

java 方法 示例集合类lastIndexOfSubList()方法 (Collections Class lastIndexOfSubList() method) lastIndexOfSubList() method is available in java.util package. lastIndexOfSubList()方法在java.util包中可用。 lastIndexOfSubList() method is used to return the star…

Redis 面试题汇总

1.Redis 使用场景有哪些? 答:Redis 使用场景如下: 记录帖子点赞数、点击数、评论数缓存近期热帖缓存文章详情信息记录用户会话信息2.Redis 有哪些功能? 答:Redis 功能如下: 数据缓存功能分布式锁的功能支持数据持久化支持事务支持消息队列3.Redis 支持哪些数据类型? …

改进的PID算法

位置式PID算法 位置式PIDPIDPID算法是一种比较直观的的PIDPIDPID算法&#xff0c;如系统框图中所示&#xff0c;ininin表示设定值&#xff0c;errorerrorerror表示差值&#xff0c;uuu表示控制器输出值&#xff0c;outoutout表示被控量。算法表达式如下&#xff1a; 增量式PI…

奇怪的排序

P6: 奇怪的排序 Description: 春秋战国时期&#xff0c;赵国地大物博&#xff0c;资源非常丰富&#xff0c;人民安居乐业。但许多国家对它虎视眈眈&#xff0c;准备联合起来对赵国发起一场战争。 显然&#xff0c;面对多个国家的部队去作战&#xff0c;赵国的兵力明显处于劣势。…

Oracle 练习题P256

--根据Oracle数据库scott模式下的emp表和dept表&#xff0c;完成下列操作。--&#xff08;1&#xff09;查询20号部门的所有员工信息select * from emp where deptno 20;--&#xff08;2&#xff09;查询所有工种为CLERK的员工的员工号、员工名和部门号select empno,ename,dep…

oracle中dbms_如何在DBMS中找到关系的最高范式?

oracle中dbmsTo find the highest normal form of a relation, you have to first understand the basics of Functional dependency, Candidate keys, and Normal Forms. 要查找关系的最高范式 &#xff0c;您必须首先了解功能依赖项 &#xff0c;候选键和范式的基础。 In re…

MySQL 面试题汇总

1.说一下 MySQL 执行一条查询语句的内部执行过程? 答:MySQL 执行一条查询的流程如下: 客户端先通过连接器连接到 MySQL 服务器;连接器权限验证通过之后,先查询是否有查询缓存,如果有缓存(之前执行过此语句)则直接返回缓存数据,如果没有缓存则进入分析器;分析器会对查…

几种简单电路知识汇总

这篇文章用于记录平时设计电路或者在书中遇到的一些电路方面的知识&#xff0c;会不定期更新。就先从运算放大器开始&#xff0c;对此做个简单的介绍。 运算放大器 说到运算放大器就不得不说两个概念&#xff0c;虚短与虚断。 虚短&#xff1a; 在理想情况下&#xff0c;运算…