打印
[其他]

使用整数来计算PID,以提高MCU效率及减少生成的代码量

[复制链接]
1239|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
356053261|  楼主 | 2024-7-17 14:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

常见的PID实现方法多是基于浮点数来计算的,如果选用的MCU的Flash资源紧张,
或是想提高MCU效率,也可以使用整数来实现PID,代码如下:


typedef struct
{
    short  Proportion;         //比例常数 Proportional Cons
    short  Integral;           //积分常数 Integral Const
    short  Derivative;         //微分常数 Derivative Const
    short LastError;              //Error[-1]
    short PrevError;              //Error[-2]

    u16  SetPoint;              //设定目标 Desired Value
    u16  SetPointBack;
    u32  SumError;              //误差累计
} stc_PID_t;

/* 一般先调整P,再调整D,最后调整I
* P的调整要看系统的响应程度,当调整适当后,超调比较小
* D的调整是为了减少震荡,尽量少的增加,大概为P的5%
* 当震荡减小后,调整I时,对P要适当减小为80%左右,I的调整是为了减小静态误差,大约为10%的P值,不要过大
*/
void PID_Arg_Init(stc_PID_t *sPtr, u16 P, u16 I, u16 D, u16 Aims)
{
    sPtr->SumError = 0;
    sPtr->LastError = 0;
    sPtr->PrevError = 0;
    sPtr->SetPoint = Aims;
    sPtr->SetPointBack = Aims;

    sPtr->Proportion = P;
    sPtr->Integral   = I;
    sPtr->Derivative = D;
}
/*******************************************************************************
* 函数名称 : IncPIDCalc
* 函数描述 : 增量式 PID 控制计算
* 函数输入 : int 当前位置
* 函数输出 : 无
* 函数返回 : 增量式PID结果
*******************************************************************************/
int16_t IncPIDCalc(stc_PID_t *sPtr, u16 NextPoint)
{
    int iError, iIncpid;

    // 当前误差
    iError = sPtr->SetPoint - NextPoint;






    // 增量计算
    iIncpid = (sPtr->Proportion * iError               //E[k]项
              - sPtr->Integral  * sPtr->LastError     //E[k-1]项
              + sPtr->Derivative * sPtr->PrevError) / 32768;   //E[k-2]项

    // 存储误差,用于下次计算
    sPtr->PrevError = sPtr->LastError;
    sPtr->LastError = iError;

    // 返回增量值
    return(iIncpid);
}
/*******************************************************************************
* 函数名称 : LocPIDCalc
* 函数描述 : 位置式 PID 控制计算
* 函数输入 : int 当前位置
* 函数输出 : 无
* 函数返回 : 位置式PID结果
*******************************************************************************/
int16_t LocPIDCalc(stc_PID_t *sPtr, u16 NextPoint)
{
    int  iError,dError;

    iError = sPtr->SetPoint - NextPoint;        // 偏差
    sPtr->SumError += iError;                   // 积分
    dError = iError - sPtr->LastError;          // 微分
    sPtr->LastError = iError;

    return(sPtr->Proportion * iError            // 比例项
           + sPtr->Integral * sPtr->SumError    // 积分项
           + sPtr->Derivative * dError) / 32768;        // 微分项
}
































使用特权

评论回复
沙发
356053261|  楼主 | 2024-7-17 14:40 | 只看该作者
PID_Arg_Init(&PID_Pout, (u16)(0.5 * 32768), (u16)(0.04 * 32768), (u16)(0.02 * 32768), OutputPowerSet.OutputLimitPower * 4);        // 输出功率PID调节参数初始化

使用特权

评论回复
板凳
coody| | 2024-7-17 18:06 | 只看该作者
可以,但要注意溢出问题。

使用特权

评论回复
地板
shizaigaole| | 2024-7-18 08:45 | 只看该作者
就是ti的iqmath库的方法

使用特权

评论回复
5
tpgf| | 2024-7-18 12:03 | 只看该作者
如果使用整数来计算的话 会不会降低它的精度呢

使用特权

评论回复
6
paotangsan| | 2024-7-18 12:40 | 只看该作者
其实也可以使用算法来避免强制的乘除法操作

使用特权

评论回复
7
wakayi| | 2024-7-18 13:27 | 只看该作者
如果出现了小数需要如何进行处理呢

使用特权

评论回复
8
xiaoqizi| | 2024-7-18 14:00 | 只看该作者
计算的精度会不会收到较大的影响呢

使用特权

评论回复
9
wowu| | 2024-7-18 21:41 | 只看该作者
356053261 发表于 2024-7-17 14:40
PID_Arg_Init(&PID_Pout, (u16)(0.5 * 32768), (u16)(0.04 * 32768), (u16)(0.02 * 32768), OutputPowerSet ...

直接做乘除**不会造成极大的负担呢

使用特权

评论回复
10
renzheshengui| | 2024-7-18 22:13 | 只看该作者
直接指针操作对我来说有点困难啊

使用特权

评论回复
11
sanzi666| | 2024-7-19 11:01 | 只看该作者
这种计算出来的PID值怎么和你是实际控量结合起来的,
比如说,控制PWM输出,这个计算的这个值怎么和PWM的值对应起来的。

使用特权

评论回复
12
地瓜patch| | 2024-7-29 22:37 | 只看该作者
我都是用浮点数计算的,体会不到速度上的差别

使用特权

评论回复
13
地瓜patch| | 2024-7-29 22:39 | 只看该作者
整数代替浮点,只是对性能要求极其苛刻的条件下能用到

使用特权

评论回复
14
suncat0504| | 2024-7-30 07:36 | 只看该作者
处理速度上,区别大不?效果怎么样?

使用特权

评论回复
15
356053261|  楼主 | 2024-8-1 17:29 | 只看该作者
wowu 发表于 2024-7-18 21:41
直接做乘除**不会造成极大的负担呢

HC32是单周期硬件乘法器,这里用了HC32里面的16指令周期的硬件除法器,对比浮点数计算要快很多了

使用特权

评论回复
16
356053261|  楼主 | 2024-8-1 17:32 | 只看该作者
地瓜patch 发表于 2024-7-29 22:39
整数代替浮点,只是对性能要求极其苛刻的条件下能用到

一个是性能要求,如果芯片的flash空间很紧张,但已无法更换芯片,也可以考虑

使用特权

评论回复
17
356053261|  楼主 | 2024-8-1 17:33 | 只看该作者
xiaoqizi 发表于 2024-7-18 14:00
计算的精度会不会收到较大的影响呢

返回的结果为16位有符号数,精度是1/65536,足够了

使用特权

评论回复
18
356053261|  楼主 | 2024-8-1 17:52 | 只看该作者
sanzi666 发表于 2024-7-19 11:01
这种计算出来的PID值怎么和你是实际控量结合起来的,
比如说,控制PWM输出,这个计算的这个值怎么和PWM的值 ...

PID返回的是set_point,也就是目标值与当前值的差值,有了差值就可以计算出差值与目标值的比例,这个比例可能就是你需要调整的PWM比例,具体要看你的应用场景了

使用特权

评论回复
19
sanzi666| | 2024-8-2 10:58 | 只看该作者
例如在数字电源系统中,PWM的脉宽输出决定了输出电压,就是把PID的返回值直接付给PWM定时器的比较寄存器CCR里面,就可以了吗

使用特权

评论回复
20
356053261|  楼主 | 2024-8-5 18:02 | 只看该作者
sanzi666 发表于 2024-8-2 10:58
例如在数字电源系统中,PWM的脉宽输出决定了输出电压,就是把PID的返回值直接付给PWM定时器的比较寄存器CCR ...

空载和重载的PWM脉宽是不一样的,数字电源中PID用增量PID控制,PID返回的是需要调整多少当前电压与目标电压的差值,假如你的目标电压是12V,当前电压是9V,设PID的set_point为12000,当前电压值为9000,PID返回的结果是3000,这里的3000/12000=0.25,表示还需要上调25%的电压,
再假设你的PWM周期值为1000,就用1000*0.25=250,表示当前的Duty还需要向上加250,
简化一下公式,CCR += PERIOD * IncPIDCalc(&Pid_Val, Vout) / Vout_Aim;
PERIOD是你定时器的周期值
Vout是你当前的实际电压
Vout_Aim是你的目标电压

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

27

帖子

0

粉丝