【转】位置式、增量式PID算法C语言实现
2024-08-26 13:17:39
位置式、增量式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
模式,在调用时两种方式可以使用同一个接口,这样对于用户来说就屏蔽了内部细节。
参考
【来源】
最新文章
- target和currentTarget的区别
- selenium webdriver学习(一)
- Lingo语法
- [saiku] 访问saiku首页的时候前后台处理流程
- uva 11029
- Android初级教程启动定时器详解
- 深度学习与计算机视觉:基于Python的神经网络的实现
- WindowsService调用API
- 在datasnap 中使用unidac 访问数据(客户端)
- python测试开发django-55.xadmin使用markdown文档编辑器(django-mdeditor)
- QLineSeries QChartView 生成折线
- PHP二叉树
- Quartz配置
- zstack使用笔记之端口转发
- oracle 视图带参数
- poj 3368 rmq ***
- java.lang.NoClassDefFoundError: Could not initialize class com.demo.jdbc.utils.MyJdbcUtils
- 再议GCC编译时的静态库依赖次顺问题
- 使用单体模式设计原生js插件
- 工具 | Axure基础操作 No.5
热门文章
- WPF loading遮罩层 LoadingMask
- MySQL调优基础, 与hikari数据库连接池配合
- [Spark][Python]RDD flatMap 操作例子
- ABPZero中的Name和SurName处理,以及EmailAddress解决方案(完美)。
- 基于DDD的.NET开发框架ABP实例,多租户 (Saas)应用程序,采用.NET MVC, Angularjs, EntityFramework-介绍
- Webpack 2 视频教程 004 - Webpack 初体验
- Linux下rsyslog日志收集服务环境部署记录
- 快速排序 O(nlogn)
- vs2013——单元测试&;&; 性能图
- jeecg中vaildfrom的复杂的表单校验