PID 控制器C++代码实例分析


前言


一、PID控制器是什么?

PID 控制器是一种反馈控制机制,用于将系统的实际输出与期望输出之间的误差最小化。它由三部分组成:比例(P)、积分(I)和微分(D)控制。


PID 控制器公式

PID 控制器的基本公式如下:

u ( t ) = K p e ( t ) + K i ∫ 0 t e ( τ )   d τ + K d d d t e ( t ) u(t) = K_p e(t) + K_i \int_0^t e(\tau) \, d\tau + K_d \frac{d}{dt} e(t) u(t)=Kpe(t)+Ki0te(τ)dτ+Kddtde(t)

其中:

  • ( u(t) ) 是控制输出
  • ( e(t) ) 是误差,即设定点与过程变量之间的差值
  • ( K_p ) 是比例增益
  • ( K_i ) 是积分增益
  • ( K_d ) 是微分增益

具体分量解释:

  • 比例项:( K_p e(t) )
  • 积分项:( K_i \int_0^t e(\tau) , d\tau )
  • 微分项:( K_d \frac{d}{dt} e(t) )

每个项的作用如下:

  • 比例项用于快速响应并减小误差。
  • 积分项用于消除稳态误差。
  • 微分项用于预测误差的变化趋势,从而提高系统的稳定性。

二、代码解释

1.代码

代码如下(示例):

class PidController
{
public:
    PidController() = default;

    explicit PidController(float _p, float _i, float _d, float _ramp, float _limit) :
        p(_p), i(_i), d(_d), outputRamp(_ramp), limit(_limit)
    {
        timeStamp = micros();
    }


    float operator()(float error);

    float p = 0;
    float i = 0;
    float d = 0;
    float outputRamp = 0;
    float limit = 0;


protected:
    float errorLast = 0;
    float outputLast = 0;
    float integralLast = 0;
    unsigned long timeStamp = 0;
};
float PidController::operator()(float error)
{
    auto time = micros();  // 获取当前时间(以微秒为单位)
    float dt = (float) (time - timeStamp) * 1e-6f;  // 计算时间间隔(秒)

    // Quick fix for strange cases (micros overflow)
    if (dt <= 0 || dt > 0.5f) dt = 1e-3f;  // 时间间隔的合理性检查

    // 比例项计算
    float pTerm = p * error;

    // 积分项计算(梯形积分)
    float iTerm = integralLast + i * dt * 0.5f * (error + errorLast);
    iTerm = CONSTRAINT(iTerm, -limit, limit);  // 限制积分项在范围内

    // 微分项计算
    float dTerm = d * (error - errorLast) / dt;

    // 总输出计算
    float output = pTerm + iTerm + dTerm;
    output = CONSTRAINT(output, -limit, limit);  // 限制输出在范围内

    // 如果定义了输出斜坡
    if (outputRamp > 0)
    {
        // 限制输出的加速度(斜坡限制)
        float outputRate = (output - outputLast) / dt;
        if (outputRate > outputRamp)
            output = outputLast + outputRamp * dt;
        else if (outputRate < -outputRamp)
            output = outputLast - outputRamp * dt;
    }

    // 更新状态
    integralLast = iTerm;
    outputLast = output;
    errorLast = error;
    timeStamp = time;

    return output;  // 返回控制器的输出值
}

这段代码实现了一个典型的 PID 控制器

2.详细解释

2.1.时间计算和合理性检查

auto time = micros();  // 获取当前时间
float dt = (float) (time - timeStamp) * 1e-6f;  // 计算时间间隔(微秒)
if (dt <= 0 || dt > 0.5f) dt = 1e-3f;  // 处理时间间隔异常
  • 使用 micros() 函数获取当前时间(单位是微秒)。
  • 计算当前时间和上次更新时间之间的时间间隔 dt。
  • 处理 dt 的异常情况,比如时间溢出或异常值,将其设为默认值 1e-3f(1毫秒)。

2.1.比例项

float pTerm = p * error;
  • 比例项是根据当前误差 error 计算的。比例增益 p 决定了比例控制的强度。
  • p: 这是比例增益(Proportional Gain),一个调整系数,用于决定比例控制的强度。比例增益是一个常数,通常由用户根据具体系统的需求进行调整。
  • error: 这是当前误差值,计算为系统的目标值(setpoint)与实际值(process variable)之间的差值。
  • 比例控制的目标是减少误差。比例增益 p 通过与误差 error 相乘,生成一个控制信号,这个信号与误差成正比,通过调整比例增益 p,可以控制系统的响应速度和稳定性。
  • 比例项是当前误差的线性函数。比例增益 p 决定了系统对误差的敏感度。较大的比例增益会使系统对误差更敏感,快速反应;较小的比例增益则会使系统反应较慢。

2.1.积分项

float iTerm = integralLast + i * dt * 0.5f * (error + errorLast);
iTerm = CONSTRAINT(iTerm, -limit, limit);  // 限制积分项
  • 使用梯形积分法来更新积分项 iTerm,梯形积分法使用当前误差和上一次误差的平均值来近似误差积分。

梯形积分法是一种数值积分方法,其基本原理如下:
将被积函数曲线下的面积近似为多个梯形的面积之和。
在这里插入图片描述

  • integralLast: 上一次计算的积分项值。这是前一次控制器状态保存的积分项,用于计算当前的积分项。

  • i: 积分增益(Integral Gain),一个常数,用于决定积分控制的强度。

  • dt: 当前时间间隔(秒),计算为当前时间与上次时间的差值。

  • error: 当前误差值,即目标值与实际值之间的差值。

  • errorLast: 上一次的误差值,用于计算积分的平均值。

  • CONSTRAINT 函数用于限制积分项在指定范围内,以防止积分饱和。

  • 积分项的主要作用是消除系统中的稳态误差,即使误差很小,积分项会逐渐累积以使系统达到期望值。

在 PID 控制器中,通过计算误差的平均值并乘以时间间隔 dt,可以得到在这个时间间隔内的积分增量。这样做是为了在离散时间控制系统中近似积分的计算,同时避免直接计算积分的复杂性。

2.1.微分项

float dTerm = d * (error - errorLast) / dt;
  • 微分项根据误差的变化率来计算。d 是微分增益,error - errorLast 是误差的变化量,dt 是时间间隔。误差的变化量除以时间间隔计算误差变化的速率,即误差的导数。
  • 微分项考虑误差的变化率,有助于预测未来误差趋势。
  • 通过对误差变化的响应,微分项可以帮助减少系统的振荡,提高系统稳定性。
  • 增强对误差变化的响应,提高系统的动态性能。

2.1.总输出计算

float output = pTerm + iTerm + dTerm;
output = CONSTRAINT(output, -limit, limit);  // 限制输出
#define CONSTRAINT(amt, low, high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
  • 计算比例、积分和微分项的和,得到控制器的总输出。
  • CONSTRAINT 函数用于限制输出在指定范围内,以防止超出控制范围。

2.1.输出斜坡(Ramp)

if (outputRamp > 0)
{
    float outputRate = (output - outputLast) / dt;
    if (outputRate > outputRamp)
        output = outputLast + outputRamp * dt;
    else if (outputRate < -outputRamp)
        output = outputLast - outputRamp * dt;
}

  • 如果定义了输出斜坡 outputRamp,则限制输出的变化速率,防止控制器输出变化过快。
  • outputRate 计算输出的变化速率,并根据 outputRamp 限制输出的加速度。
  • 正向限制: 如果当前输出变化率 outputRate 大于允许的最大斜坡 outputRamp,则将输出限制为上一个输出值加上最大斜坡限制乘以时间间隔
  • 反向限制: 如果当前输出变化率 outputRate 小于负的最大斜坡限制 -outputRamp,则将输出限制为上一个输出值减去最大斜坡限制乘以时间间隔
  • 这段代码的目的是为了防止输出信号的剧烈变化,从而减少控制系统的振荡和不稳定性。通过限制输出变化率,可以使控制信号在变动时更加平稳,避免突发性的大幅度调整。这种方法通常用于保护系统不受过大的瞬时变化影响,保持系统的稳定性。

2.1.更新状态

integralLast = iTerm;
outputLast = output;
errorLast = error;
timeStamp = time;
  • 更新状态变量以便于下一次调用时使用。

2.1.返回控制器的输出控制量

return output;

总结

这段代码实现了一个功能全面的 PID 控制器,包括比例、积分、微分控制,时间间隔处理,积分饱和限制,输出范围限制,以及输出斜坡限制。它处理了时间间隔异常,确保积分项和输出在合理范围内,并通过斜坡限制防止输出剧烈变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值