位置式、增量式PID算法C语言实现

芯片:STM32F107VC

编译器:KEIL4

作者:SY

日期:2017-9-21 15:29:19

概述

PID 算法是一种工控领域常见的控制算法,用于闭环反馈控制。有以下两种分类:

  • 增量式

    每次周期性计算出的 PID 为增量值,是在上一次控制量的基础上进行的调整。

  • 位置式

    每次周期性计算出的 PID 为绝对的数值,是执行机构实际的位置。

我们使用高级语言的思想去实现两种 PID ,做到对于用户来说,调用相同的接口,内部实现不同的 PID 算法。

代码

pid.h

 enum PID_MODE {
PID_INC = , //增量式
PID_POS, //位置式
}; struct PID {
enum PID_MODE mode; float kp; //比例系数
float ki; //积分系数
float kd; //微分系数 double targetPoint; //目标点
double lastError; //Error[-1]
double prevError; //Error[-2] void (*init)(struct PID *this, double targetPoint); //PID初始化
double (*outputLimit)(struct PID *this, double output); //PID输出限制
void (*setParameter)(struct PID *this, \
float kp, float ki, float kd); //设置PID参数
double (*calculate)(struct PID *this, double samplePoint); //计算PID
}; /* 增量式PID */
struct PID_INC {
struct PID pid;
}; /* 位置式PID */
struct PID_POS {
struct PID pid;
double iSum; //积分和
};

其中 struct PID 就是我们提供给用户的接口,可以理解为 抽象类 ,增量式和位置式 PID 都去继承该抽象类,然后实现其中的抽象方法。

对于位置式 PID 拥有自己的成员 iSum 。

pid.c

 /*
*********************************************************************************************************
* PID
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* Function Name : PID_Init
* Description : PID初始化
* Input : None
* Output : None
* Return : None
*********************************************************************************************************
*/
static void PID_Init(struct PID *this, double targetPoint)
{
this->targetPoint = targetPoint;
this->lastError = ;
this->prevError = ;
} /*
*********************************************************************************************************
* Function Name : PID_OutputLimit
* Description : PID输出限制
* Input : None
* Output : None
* Return : None
*********************************************************************************************************
*/
static double PID_OutputLimit(struct PID *this, double output)
{
if (output < ) {
output = ;
} else if (output > DIGITAL_THROTTLE_VALVE_MAX_DEGREE) {
output = DIGITAL_THROTTLE_VALVE_MAX_DEGREE;
}
return output;
} /*
*********************************************************************************************************
* Function Name : PID_SetParameter
* Description : PID设置参数
* Input : None
* Output : None
* Return : None
*********************************************************************************************************
*/
static void PID_SetParameter(struct PID *this, float kp, float ki, float kd)
{
this->kp = kp;
this->ki = ki;
this->kd = kd;
} /*
*********************************************************************************************************
* Function Name : PID_SetTargetValue
* Description : PID设置目标值
* Input : None
* Output : None
* Return : None
*********************************************************************************************************
*/
void PID_SetTargetValue(struct PID *this, double targetPoint)
{
this->targetPoint = targetPoint;
} /*
*********************************************************************************************************
* Function Name : PID_GetTargetValue
* Description : PID获取目标值
* Input : None
* Output : None
* Return : None
*********************************************************************************************************
*/
double PID_GetTargetValue(struct PID *this)
{
return this->targetPoint;
} /*
*********************************************************************************************************
* 增量式PID
*********************************************************************************************************
*/
static double PID_IncCalculate(struct PID *this, double samplePoint); struct PID_INC g_PID_Inc = {
.pid = {
.mode = PID_INC,
.init = PID_Init,
.outputLimit = PID_OutputLimit,
.setParameter = PID_SetParameter,
.calculate = PID_IncCalculate,
},
}; /*
*********************************************************************************************************
* Function Name : PID_IncCalculate
* Description : 增量式PID计算
* Input : None
* Output : None
* Return : None
*********************************************************************************************************
*/
static double PID_IncCalculate(struct PID *this, double samplePoint)
{
double nowError = this->targetPoint - samplePoint;
double out = this->kp * nowError +\
this->ki * this->lastError +\
this->kd * this->prevError;
this->prevError = this->lastError;
this->lastError = nowError; if (this->outputLimit) {
out = this->outputLimit(this, out);
} return out;
} /*
*********************************************************************************************************
* 位置式PID
*********************************************************************************************************
*/
static double PID_PosCalculate(struct PID *this, double samplePoint);
static void PID_PosInit(struct PID *this, double targetPoint); struct PID_POS g_PID_Pos = {
.pid = {
.mode = PID_POS,
.init = PID_PosInit,
.outputLimit = PID_OutputLimit,
.setParameter = PID_SetParameter,
.calculate = PID_PosCalculate,
},
}; /*
*********************************************************************************************************
* Function Name : PID_PosInit
* Description : 位置式PID初始化
* Input : None
* Output : None
* Return : None
*********************************************************************************************************
*/
static void PID_PosInit(struct PID *this, double targetPoint)
{
PID_Init(this, targetPoint);
struct PID_POS *pid_Handle = (struct PID_POS *)this;
pid_Handle->iSum = ;
} /*
*********************************************************************************************************
* Function Name : PID_PosCalculate
* Description : 位置式PID计算
* Input : None
* Output : None
* Return : None
*********************************************************************************************************
*/
static double PID_PosCalculate(struct PID *this, double samplePoint)
{
struct PID_POS *pid_Handle = (struct PID_POS *)this; double nowError = this->targetPoint - samplePoint;
this->lastError = nowError;
//积分累计误差
pid_Handle->iSum += nowError;
double out = this->kp * nowError +\
this->ki * pid_Handle->iSum +\
this->kd * (nowError - this->prevError);
this->prevError = nowError; if (this->outputLimit) {
out = this->outputLimit(this, out);
} return out;
}

对于上述内容:最关键的是两个变量 struct PID_INC g_PID_Inc 和 struct PID_POS g_PID_Pos ,他们针对抽象类实现了各自的算法。

其中有一个很重要的小技巧,举例:

 static double PID_PosCalculate(struct PID *this, double samplePoint)
{
struct PID_POS *pid_Handle = (struct PID_POS *)this;
}

该函数的接口是 struct PID *this ,但是我们内部将他强制转换为 struct PID_POS *pid_Handle ,这是两种不同的数据类型,为什么可以进行转换呢?而且转换后的数据是正确的?

因为对于 C 语言来说,结构体内部的第一个成员的地址和该结构体变量的地址是一样的,所以可以相互转换,实现向下转型,这就是 C 语言的强大之处。

测试

app.c

 struct KernelCtrl {
struct DTV dtv;
struct PID *dtvPid;
}; static struct KernelCtrl g_kernelCtrl; /*
*********************************************************************************************************
* Function Name : Kernel_Ctrl_Init
* Description : 内核控制初始化
* Input : None
* Output : None
* Return : None
*********************************************************************************************************
*/
void Kernel_Ctrl_Init(void)
{
#if 1
/* 增量式 */
g_kernelCtrl.dtvPid = &g_PID_Inc.pid;
#else
/* 位置式 */
g_kernelCtrl.dtvPid = &g_PID_Pos.pid;
#endif
}

只要在初始化时,指定使用哪一种 PID 模式,在调用时两种方式可以使用同一个接口,这样对于用户来说就屏蔽了内部细节。

参考

Pid控制算法-变积分的pid算法的C++实现

【来源】

【转】位置式、增量式PID算法C语言实现的更多相关文章

  1. PID算法(C语言)

    /************ PID算法(C语言) ************/ #include <stdio.h> #include<math.h> struct _pid { ...

  2. PID控制算法的C语言实现四 增量型PID的C语言实现

    /*------------------------------------------- 2 位置型PID C实现(控制电机转速) --------------------------------- ...

  3. 023_STM32之PID算法原理及应用

    (O)关于程序BUG说明,看最后面的红色字体,视频和源代码中都没有说明 (一)PID控制算法(P:比例 I:积分 D:微分) (二)首先先说明原理,使用的是数字PID算法,模拟PID算法在计算机这样的 ...

  4. PID控制器开发笔记之一:PID算法原理及基本实现

    在自动控制中,PID及其衍生出来的算法是应用最广的算法之一.各个做自动控制的厂家基本都有会实现这一经典算法.我们在做项目的过程中,也时常会遇到类似的需求,所以就想实现这一算法以适用于更多的应用场景. ...

  5. PID算法的C语言实现

    1.根据我控制算法类文章中关于PID的理论的一些描述,同时也根据网络上一些其他的PID文章,以及自己最近一个项目的实践后,总结了几套基于C语言的PID算法,由于网络中很少有人进行分享完整的PID算法实 ...

  6. 位置式PID与增量式PID算法

    位置式PID与增量式PID算法  PID控制是一个二阶线性控制器     定义:通过调整比例.积分和微分三项参数,使得大多数的工业控制系统获得良好的闭环控制性能.     优点             ...

  7. T12焊台控制器制作教程 | T12烙铁 | PID增量式算法恒温控制 | 运算放大器-热电偶电压采集 | OLED屏幕显示-SPI通信 | 旋转编码器EC11用户操作

    前言 购买T12烙铁的相关配件已经1年多了,期间也尝试了一些开源的T12控制器,但都没有成功,要么是配套资料少,要么是英文的,其中51和arduino的居多,STM32的较少.求人不如求己,索性自己开 ...

  8. 位置式PID与增量式PID

    //位置式PID float Kp; float Ki; float Kd; float eSum,e0,e1; float pid_control(float now,float target) { ...

  9. 增量式pid和位置式PID参数整定过程对比

    //增量式PID float IncPIDCalc(PID_Typedef* PIDx,float SetValue,float MeaValue)//err»ý·Ö·ÖÀë³£Êý { PIDx-& ...

随机推荐

  1. HNOI2018简要题解

    HNOI2018简要题解 D1T1 寻宝游戏 题意 某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为 ...

  2. slurm用户快速入门手册

    1. 概述2. 架构3. 命令3.1 sacct3.2 sattach3.4 sbatch3.5 sbcast3.6 scancel3.7 scontrol3.8 sinfo3.9 smap3.10 ...

  3. FreeCAD源码初步了解

    FreeCAD简介 FreeCAD是基于OpenCASCADE的开源CAD/CAE软件,完全开源(GPL的LGPL许可证),官方源码地址,详情可参考维基百科,百度百科等等. 如果要编译FreeCAD, ...

  4. kvm虚拟化关闭虚拟网卡virbr0的方法

    我们知道:kvm虚拟化环境安装好后,ifconfig会发现多了一个虚拟网卡virbr0这是由于安装和启用了libvirt服务后生成的,libvirt在服务器(host)上生成一个 virtual ne ...

  5. 'config.h' file not found 解决过程

    最近将ReactNative业务集成进现有APP项目中,出现了几个具有代表性的问题,下面记录一下 问题1. [!] CocoaPods could not find compatible versio ...

  6. Linux内核分析— —扒开系统调用的三层皮(上)

    实验部分 根据系统调用表,选取一个系统调用.我选得是mkdir这个系统调用,其系统调用号为39,即0x27 由于mkdir函数的原型为int mkdir (const char *filename, ...

  7. Github学习心得体会

    https://github.com/Accredit/TEST 在本学期第一次接触Github,在系统的学习了Github的使用之后,自己对Github有了一个基本的使用了解.在这个过程中,自己也认 ...

  8. 安装python包时报错

    pip install numpy  时  报错: Traceback (most recent call last):  File "d:\学习\python\python-3.6.5\l ...

  9. mysql数据库忘记密码时如何修改

    工具/原料 mysql数据库 cmd命令行 打开mysql.exe和mysqld.exe所在的文件夹,复制路径地址 打开cmd命令提示符,进入上一步mysql.exe所在的文件夹

  10. Maven+SSM整合.doc

    Maven + SSM整合 1开发环境搭建 1.1Eclipse4.7(Oxygen) + Tomcat8 + Maven3.5.2 2Maven Web项目创建 2.1新建maven项目 2.2 选 ...