在介绍程序之前,简单介绍一些PID。所谓PID指比例、积分、微分控制的总成。PID控制是一种闭环控制,又是用得最广的控制。一方面,PID控制是一种模糊控制,不需要知道系统的状态空间表达式,仅仅需要调整kp,ki,kd的数值,以达到期望的效果。
       PID中的比例控制,代表了系统的快速性,kp越大,响应越快,但是会导致超调增大,又会导致系统震荡。反之,系统响应太慢。ki指的是积分控制,代表了系统的准确性。ki的加入,大大抑制了稳态误差。自然这个数也不能太大,否则有毛刺。至于kd微分控制,表征系统对未来的预判,在延迟较严重下应用,一般不推荐,会导致系统震荡。
      在本例中,采用PID调节来控制BOOST电路电压。BOOST电路采用经典的LC以及绝缘栅场效应管结构。通过分压电阻引入反馈电压,作为PID控制的反馈量。而控制量,是MOS管的通断时间,也就是占空比。BOOST电压与占空比成正比,因此可以通过调整占空比来调整电压。
      本例中修改宏定义“SETVOLTAGE”来设置BOOST输出电压,最高可设置30V,这是理论上的,没试过。输入电压为开发板上的5V接口。在本例中,将5v电压升至18V。
      MCU为ATMEGA328,采用定时器1产生PWM,此定时器是10位的,因此精度较高。采用定时器2溢出中断作为PID积分的步长参考。
      本例之所以为通用PID算法,是因为在本例中,只需要修改函数PIDCtl_Scan(uint16 feedback)中引用的Set_PWM(tPID->s16PIDCtr)子程序以及 该子程序的参数uint16 feedback来移植到其他闭环控制系统中,非常方便。其中feedback为反馈量,tPID->s16PIDCtr为控制量,为PID算法计算得出的当前控制量。控制量可以是多样的,占空比,电流,电压等。
      算法采用查询方式,需要多次调用。在程序前定义了PID的参数,调整这里的kp,ki以及步长。还有输入量范围,输出量范围即可。

本例设置BOOST电压为18V ,可以看到,当控制器工作时,如图所示,电压从5v升到18V,并保持稳定。设置其他电压也是如此。PID控制简单介绍到这里,感兴趣的详细看例程。本例程仅供学习参考,不得用于其他用途。

/****************************************************************************   
//程序可以直接使用,实际工程中与原系统不符,与本人无关,可付费提供技术支持。
//  原     创  : 速浪电子(WEIXIN:haveaseat)

//  设计日期   : 2017-12-25
//  功能描述   : PID通用控制示例 (Atmega328系列) 
                 以通过PWM控制BOOST电路电压为例
//  说    明: 
               单片机      : ATMEGA328P-PU
               晶振        : 片外16Mhz石英晶振
               工作频率    : 16Mhz
               注          : 外置分压电阻为1/6
//========================用到的接口===========================
//              9         PWM端口(PB1)
//              A0        ADC0   (PD0)        
//              5V        BOOST电路输入电压    
********************************************************************************/

#include <Arduino.h>
#include <avr/interrupt.h>

#define SETVOLTAGE   18         //设置电压为12V     
// 数据类型
typedef signed   char sint08;
typedef unsigned char uint08;
typedef signed   int  sint16;
typedef unsigned int  uint16;
typedef unsigned long uint32;
typedef signed   long sint32;
//全局变量
uint16 u16RTICnt;     //定时器计数
// ----------------------------------------------------------------------------
typedef struct {                              // 定时器,单位ms
  uint16 u16TimeStrt;                         // 启动时刻
  uint16 u16TimeNow1;                         // 当前时刻
  uint16 u16Duration;                         // 间隔时间(ms)
} TMyTimer_ms;

typedef struct {                                         // PID 
  sint08      s08PIDDir;                                 // 1:正向;-1:反向
  sint16      s16InpMax, s16InpMin;                      // 输入量的最大/最小值,用于输入的归一化计算
  sint16      s16OutMax, s16OutMin;                      // 控制量的最大/最小值,用于输出限幅
  sint16      s16PIDCmd;                                 // 归一化的命令值,范围为0 ~ 1023
  sint16      s16PIDAcq;                                 // 归一化的实际值,范围为0 ~ 1023
  sint32      s32PIDCtr;                                 // PID(的放大了256倍)的上一次控制量  
  sint16      s16PIDCtr;                                 // PID的控制量
  sint16      s16PID_E0, s16PID_E1;//, s16PID_E2;        // 本次误差, 上次误差,上上次误差
  uint16      u16PID_Kp, u16PID_Ki;//, u16PID_Kd;        // PID参数
  TMyTimer_ms tTimer_ms;                                 // PID定时器  uint16      u16Timer2;  
} TParaPID;
//全局变量设置
TParaPID tPIDMotor;
const TParaPID CstParaPID = {
   1,               // sint08  s08PIDDir;                                 // 1:正向;-1:反向
   512, 307,        // sint16  s16InpMax, s16InpMin;                      // 输入量的最大/最小值,用于输入的归一化计算
   1023 ,0 ,        // sint16  s16OutMax, s16OutMin;                      // 控制量的最大/最小值,用于输出限幅
   0 ,               // sint16  s16PIDCmd;                                 // 归一化的命令值,范围为0 ~ 1023
   0  ,              // sint16  s16PIDAcq;                                 // 归一化的实际值,范围为0 ~ 1023
   0   ,             // sint32  s32PIDCtr;                                 // PID(的放大了256倍)的上一次控制量  
   0   ,             // sint16  s16PIDCtr;                                 // PID的控制量
   0   , 0 ,         // sint16  s16PID_E0, s16PID_E1;//, s16PID_E2;        // 本次误差, 上次误差,上上次误差
   4   ,40 ,         // uint16  u16PID_Kp, u16PID_Ki;//, u16PID_Kd;        // PID参数

{                  // TMyTimer_ms tTimer_ms;                             // PID定时器, PID控制间隔(ms)
     0 ,             //   uint16 u16TimeStrt;                              // 启动时刻
     0 ,             //   uint16 u16TimeNow1;                              // 当前时刻
    10 ,             //   uint08 u16Duration;                              // 间隔时间10ms
  }
};
// ----------------------------------------------------------------------------
void MyTimerStart(TMyTimer_ms *ptMyTime, uint16 u16Duration){             // 初始化定时器
  ptMyTime->u16TimeNow1 = u16RTICnt;                               // 当前时刻
  ptMyTime->u16TimeStrt = ptMyTime->u16TimeNow1;                         // 启动时刻
  ptMyTime->u16Duration = u16Duration;                                   // 间隔时间(ms)
}
// ----------------------------------------------------------------------------
uint08 MyTimerQuery(TMyTimer_ms *ptMyTime){                              // 时间到了返回1,否则返回0
  ptMyTime->u16TimeNow1 = u16RTICnt;
  if(ptMyTime->u16TimeNow1 - ptMyTime->u16TimeStrt < ptMyTime->u16Duration) return 0;
  ptMyTime->u16TimeStrt = ptMyTime->u16TimeNow1;
  return 1;
}
// ----------------------------------------------------------------------------
void InitPID(TParaPID *tpPID01){                                               // 初始化PID参数  
  (void)memcpy(tpPID01, &CstParaPID, sizeof(TParaPID));
  MyTimerStart(&tpPID01->tTimer_ms, tpPID01->tTimer_ms.u16Duration);          // 初始化定时器, 主要用于初始化当前时间

}
// ----------------------------------------------------------------------------
uint16 PID_Sub1(TParaPID *tpPID, sint16 s16V01){                               // 归一化计算,返回佱范围为0 ~ 1023
  uint32 u32Tmp1;
  
  if(tpPID->s16InpMax <= tpPID->s16InpMin) return 0;
  if(s16V01 > tpPID->s16InpMax){
    s16V01 = tpPID->s16InpMax;
  } else if(s16V01 < tpPID->s16InpMin){
    s16V01 = tpPID->s16InpMin;
  }

u32Tmp1 = s16V01 - tpPID->s16InpMin;
  u32Tmp1 = u32Tmp1 * 1023;
  u32Tmp1 = u32Tmp1 / (tpPID->s16InpMax - tpPID->s16InpMin);
  return (uint16)u32Tmp1;
}
// ----------------------------------------------------------------------------
void PID_Cal(TParaPID *tpPID){                                         // 计算PID控制量,只使用了PI控制
  sint32 s32Tmp1, s32Tmp2;

//tpPID->s16PID_E2 = tpPID->s16PID_E1;                               // 上上次误差
  tpPID->s16PID_E1 = tpPID->s16PID_E0;                                 // 上次误差
  tpPID->s16PID_E0 = (tpPID->s16PIDCmd - tpPID->s16PIDAcq)             // 本次误差,命令值与实测值的差
                 * tpPID->s08PIDDir;                                   // 计算误差的方向
  s32Tmp1 = ((sint32)tpPID->u16PID_Kp) 
                   * ((sint32)(tpPID->s16PID_E0 - tpPID->s16PID_E1));  // 比例增量
  s32Tmp2 = ((sint32)tpPID->u16PID_Ki) * ((sint32)tpPID->s16PID_E0);   // 积分增量

tpPID->s32PIDCtr = tpPID->s32PIDCtr + s32Tmp1 + s32Tmp2;             // 控制量

s32Tmp1 = ((sint32)tpPID->s16OutMax) << 16;                          // 控制量的最大值
  s32Tmp2 = ((sint32)tpPID->s16OutMin) << 16;                          // 控制量的最小值
  if       (tpPID->s32PIDCtr > s32Tmp1){  tpPID->s32PIDCtr = s32Tmp1;
  } else if(tpPID->s32PIDCtr < s32Tmp2){  tpPID->s32PIDCtr = s32Tmp2;
  }

tpPID->s16PIDCtr = (sint16)(tpPID->s32PIDCtr >> 16);                 // PID的控制量,只取整数 return (sint16)(tpPID->s32PIDCtr >> 16);                             // 只取整数
}
// ----------------------------------------------------------------------------
void MyPIDMotorSet(TParaPID *ptPID03, sint16 s16P1){                    // 电机的PID控制, 输入要求的值,不需要反复调用
  ptPID03->s16PIDCmd = PID_Sub1(ptPID03, s16P1);                        // 归一化计算,归一化的命令值,返回范围为0 ~ 1023
}
// ----------------------------------------------------------------------------
uint08 MyPIDMotorCtr(TParaPID *ptPID03, sint16 s16P2){                  // 电机的PID控制, 输入实际的值,需要反复调用,这是控制过程
  if(MyTimerQuery(&ptPID03->tTimer_ms) == 0) return 0;                  // 时间到了返回1,否则返回0
  ptPID03->s16PIDAcq = PID_Sub1(ptPID03, s16P2);                        // 归一化计算,返回范围为0 ~ 1023

PID_Cal(ptPID03);                                                     // 计算PID控制量,只使用了PI控制

return 1;
}

//--------------------------------------------------------------------------
//设置PWM占空比
void Set_PWM(uint16 Duty)
{
  OCR1A=Duty;
}
 //--------------------------------------------------------------------------
 //PID控制,查询形式,多次访问
 void  PIDCtl_Scan(uint16 feedback)
 {
 TParaPID *tPID=&tPIDMotor;
  if(MyPIDMotorCtr(tPID, feedback) == 1)
     // PID控制, 输入实际的值,需要反复调用,这是控制过程
          Set_PWM(tPID->s16PIDCtr);                // PID的控制量,此处为占空比
   
 
 }
 //------------------------------------------------------------
//初始化定时器2
void Init_Timer2(void)

  TCCR2A  = 0x00;             //正常模式
  TCCR2B  = 0x04;             //预分频器64分频,每次计数4us
  TIMSK2  |=(1<<TOIE2);       //溢出中断使能,溢出时间1.024ms
}

//------------------------------------------------------------
//初始化PWM,定时器1,16位定时器
void Init_PWM(void)
{
  TCCR1A  = (1<<COM1A1)|(1<<WGM11)|(1<<WGM10); 
  TCCR1B  = (1<<WGM12)|(1<<CS11);             //快速PWM,预分频器8分频
  TCNT1   = 400;                              //计数top值,5kHz
  OCR1A   = 200 ;                             //计数200,占空比50%
  //OCR1B   = 150 ;                           //计数150,占空比50%
}

uint16 u16val;
//--------------------------------------------------------
void setup() {
   TParaPID *tPID=&tPIDMotor;
    pinMode(A0, INPUT);                     // A0 为ADC输入
    DDRB|=0x02;                             // 9端口输出

InitPID(tPID);                           // 初始化PID参数 
   Init_PWM();                              // 初始化PWM
   Init_Timer2();                           //初始化定时器
   u16val=SETVOLTAGE*1024/30;              //6分之一分压
   MyPIDMotorSet(tPID, u16val);            // 电机的PID控制, 输入要求的值,不需要反复调用
   SREG   = 0x80;                          //I位全局中断使能,page10
 //  Serial.begin(19200);
}

//------------------------------------------------------------
void loop() 
{
  u16val=analogRead(A0);                     //读取ADC值,作为电压反馈值
  //Serial.println(u16val);
  PIDCtl_Scan(u16val);
}
//-----------------------------------
//中断服务程序
ISR(TIMER2_OVF_vect) 
{
  TIFR2|=0x01;            //TIFR0_OCF0A=1,A中断标计位清零,page113
  u16RTICnt++;
}
//------------------------------------------------------------------
//This is END.

arduino 版 PID 算法模板(程序可直接套模板实用)相关推荐

  1. Arduino版简单的DS1307程序

    DS1307是一款低功耗,具有56字节非失性RAM的全BCD码时钟日历实时时钟芯片,地址和数据通过两线双向的串行总线的传输,芯片可以提供秒,分,小时等信息,每一个月的天数能自动调整.并且有闰年补偿功能 ...

  2. 二开版彩虹易支付全开源10套模板带风控实名系统源码

    简介: 直接安装即可.彩虹的东西还是非常完善了的. 此版本为彩虹易原作者二开版本,新加了很多功能,新功能:API退款,轮训,网银,京东, 实名认证更多功能自己登录演示后台 再带11套首页模板,比原版不 ...

  3. 【DIY】使用STM32及PID算法实现一个磁悬浮玩具

    今天教大家来做一个磁悬浮玩具,先上两张成品的效果图: 1)磁悬浮的基本原理 磁悬浮有下推式和上拉式两种基本形式. 所谓下推式,就是控制部分在底座上,悬浮的磁铁在上面,依靠底座从下往上的排斥磁力推动磁铁 ...

  4. pwm波程序如何实现_【优秀成果】如何做好算法与程序实现教学的知识储备

    [摘要]<普通高中信息技术课程标准>(2017版)将算法与程序实现作为必修模块一的主要内容,并融入到多个单元和模块中.大部分教师缺少相应的知识储备,需要及时补充.本文结合笔者近3年的教学实 ...

  5. 最新易企秀 微场景制作源码 易企秀去版权源码 带几百套模板

    分享一个最新易企秀 微场景制作源码 易企秀去版权源码,带几百套模板,一键轻松制作H5,开发组合:PHP+MySQL,含详细搭建教程. 企业及个人运用各种H5页面进行营销,用H5搭建的站点与应用可兼容P ...

  6. 苹果cmsv10主题仿V256模板绿色风格免费影视模板

    苹果cmsv10主题仿V256模板主题介绍: 模板程序:苹果cmsv10 模板类型:自适应模板 模板颜色:绿色 模板来源:来自网络收集 下载方式:免费 上架时间:2020-7-23 是否授权:无需授权 ...

  7. 提高开发效率之安卓模板(上面有四种模板的教程,我之前会两种,看完之后还是只会两种2333)

    原文地址: juejin.im/post/5c7339- 在平时的开发工作中,我们经常将一个地方的代码,复制粘贴到另外一个地方,俗称搬砖,搬砖搬多了,作为一个资深的挨踢民工,难免会总结一些提高生产力的 ...

  8. emlog模板-蓝叶博客模板源码-清新大气模板,适合做博客和资源一体!亲测

    简介: emlog模板-蓝叶博客模板源码    这套模板还是挺不错的,挺大气,具体看演示站. 演示地址 http://lanyes.org/    解压密码:www.dkewl.com@lanyefd ...

  9. python算法与程序设计基础第二版-算法与程序设计基础(Python版) - 吴萍

    基本信息 书名:21世纪高等学校计算机基础实用规划教材:算法与程序设计基础(Python版) 定价:39.00元 作者:吴萍21世纪高 出版社:清华大学出版社 出版日期:2015_2_1 ISBN:9 ...

最新文章

  1. 直接通过OptionalAttribute, DefaultParameterValueAttribute定义缺省参数
  2. 7 款可替代 top 命令的工具!(二)
  3. python目录操作shutil
  4. ubuntu16.04 安装jdk
  5. (十五)算法设计思想之“回溯算法”
  6. 【英语学习】【Daily English】U07 Restaurant L02 I don't think this is what I ordered?
  7. 【BZOJ】1969: [Ahoi2005]LANE 航线规划
  8. 使用mysqldump备份数据库
  9. Lingoes 灵格斯词霸
  10. RNA 3. SCI 文章中基于TCGA 差异表达基因之 DESeq2
  11. Tilera--100核cpu
  12. 时钟显示(C语言实现)
  13. AI对项目管理的影响
  14. 银河麒麟安装达梦数据库
  15. 为什么程序员难找对象?
  16. JVM-13. 垃圾回收器
  17. Tita OKRs-E 使OKR成为您企业DNA一部分
  18. python【PyQt5】的环境搭建和使用(全网最全)其一
  19. 解决TeamViewer无法按给定网络地址联系伙伴
  20. Jetpack Compose详尽介绍

热门文章

  1. 路由器,你身边的“地雷”?
  2. ubuntu16.04 护眼神器Redshift
  3. mac字体能用在linux吗,mac的字体和win通用吗
  4. android studio signingconfigs 打正式包,Android-SigningConfigs打包配置
  5. 冠军联赛:当火焰变成焰火 海水变成泪水
  6. Python爬虫实战(2)之爬取NBA球队各个球员头像图片
  7. 【5月比赛合集】80场可报名的数据挖掘大奖赛,任君挑选!
  8. Led台灯对眼睛好吗?2022双十一不伤眼的护眼灯推荐
  9. 【转】抽象基类与接口,共性与个性的选择!
  10. Excel学习笔记:P10-图表制作(下)