我正在尝试使用Tiva C系列微控制器在四轴飞行器上实现PID控制,但在使PID稳定系统方面遇到困难。

在测试PID时,我注意到PID控制器的响应缓慢或微弱(四边形在小角度下无响应)。换句话说,似乎四边形的角度范围必须相对较大(15度以上)才能显示任何响应。即使这样,无论我为系统选择的D,D增益如何,响应始终会过头。在低P时,我可以防止过冲,但是过冲会变得太弱。

我不确定PID算法是否存在问题,或者它是否存在某种不良的硬件配置(IMU采样率低或PWM配置错误),但是我对我的PID代码有强烈的怀疑注意更改某些增益并不能改善系统响应。

如果有人可以指出我发布的音调分量的PID片段中是否做错了什么,我将不胜感激。我也有一个滚动PID,但是它与我发布的代码相似,因此我将其遗漏。

void pitchPID(int16_t pitch_conversion)
{
  float  current_pitch = pitch_conversion;
  //d_temp_pitch is global variable
  //i_temp_pitch is global variable
  float  pid_pitch=0; //pitch pid controller
  float  P_term, I_term, D_term;
  float  error_pitch = desired_pitch - current_pitch;

  //if statement checks for error pitch in negative or positive direction
  if ((error_pitch>error_max)||(error_pitch<error_min))
    {
      if (error_pitch > error_max) //negative pitch- rotor3&4 speed up
        {
          P_term = pitch_kp*error_pitch; //proportional
          i_temp_pitch += error_pitch;//accumulate error
          if (i_temp_pitch > iMax)
            {
            i_temp_pitch = iMax;
            }
          I_term = pitch_ki*i_temp_pitch;
          if(I_term < 0)
            {
            I_term=-1*I_term;
            }
          D_term = pitch_kd*(d_temp_pitch-error_pitch);
          if(D_term>0)
            {
            D_term=-1*D_term;
            }
          d_temp_pitch = error_pitch; //store current error for next iteration
          pid_pitch = P_term+I_term+D_term;
          if(pid_pitch<0)
            {
            pid_pitch=(-1)*pid_pitch;
            }
          //change rotor3&4
          pitchPID_adjustment (pid_pitch, 'n'); //n for negative pitch
        }
      else if (error_pitch < error_min) // positive pitch- rotor 1&2 speed up
        {
        P_term = pitch_kp*error_pitch; //proportional
        i_temp_pitch += error_pitch;
        if (i_temp_pitch < iMin)
          {
          i_temp_pitch = iMin;
          }
        I_term = pitch_ki*i_temp_pitch;
        if(I_term > 0)
          {
          I_term=-1*I_term;
          }
        D_term = pitch_kd*(d_temp_pitch - error_pitch);
        if(D_term < 0)
          {
          D_term=-1*D_term;
          }
        d_temp_pitch = error_pitch;
        pid_pitch = P_term+I_term+D_term;
        if(pid_pitch<0)
          {
          pid_pitch=(-1)*pid_pitch;
          }
        print(pid_pitch);//pitch
        printString("\r\n");
        //change rotor1&2
        pitchPID_adjustment(pid_pitch,'p'); //p for positive pitch
      }
    }
  }




void pitchPID_adjustment(float pitchPIDcontrol, unsigned char pitch_attitude)
  {
  if (pitchPIDcontrol>(maximum_dutyCycle-set_dutyCycle))
    {
    pitchPIDcontrol=maximum_dutyCycle-set_dutyCycle;
    }
  switch (pitch_attitude){
  //change rotor1&2
  case 'p': //positive status
    PWM0_2_CMPA_R += (pitchPIDcontrol);//(RED)//motor1
    PWM0_0_CMPA_R += (pitchPIDcontrol);//(Yellow)//motor2
    break;
  //change rotor 3&4
  case 'n': //negative status
    PWM0_1_CMPA_R += pitchPIDcontrol;//(ORANGE)//motor3
    PWM1_1_CMPA_R += pitchPIDcontrol;//(green)//motor4
    break;
  }


另外,有人可以告诉我这种电动机混合的工作原理吗?:

Front =Throttle + PitchPID 
Back =Throttle - PitchPID 
Left =Throttle + RollPID 
Right =Throttle - RollPID


与我在功能中所做的比较:

void pitchPID_adjustment(float pitchPIDcontrol, unsigned char pitch_attitude)


评论

您的态度估计有多准确?您如何产生态度?

我将MPU 6050用作传感器。我使用数据表中的寄存器编写了IMU来回驱动程序。角度测量值不错,但速度很慢。在水平表面上,滚动的读数为-2度,俯仰读数为-3度。这些值有时会持续很长时间。当我将四边形置于90度时,实际读数约为78-82度。当电动机加速时,角度开始明显波动(与怠速状态成4-5度左右)。另外,我应该提到,我只是简单地猜测了10ms的采样时间,用于整合陀螺仪速度。

我正在使用免费的滤镜来融合加速度计角度和陀螺仪角度。

使用非线性卡尔曼滤波器。免费过滤器不应被信任。他们被想要进入无人机的计算机人员使用。

@ holmeski-我将进一步研究统计信号处理,但是现在我需要确保im正确实现我的PID。非线性卡尔曼滤波器可能更适合我的四轴飞行器,因为我的系统会产生很多振动。希望它也可以使我的角度改变不那么迟钝。如果没有,那么我可能不得不看一下mpu-6050的配置。

#1 楼

我目前看到的明显问题是,您在I和D项上强加了极性。通常,您会使用很多符号检查,符号分配和条件编程。

都不属于PID控制器。整个控制器应如下所示:

pError = Input - Output;
iError = iError + pError*dt;
dError = (pError - previousError);
previousError = pError;

ControlSignal = kp*pError + ki*iError + kd*dError;


就这样。无需检查,分配等。有关拆分控制信号而不是分别控制每对电机的信息,请参阅我的相关答案(这是我认为您的目标是使用if elseif代码)。

想象一个PID控制器,当三个人看着您的车辆时,所有人都试图根据他们的“经验”(模式)给出建议。每个“人”都会给您以下三个“陈述”之一:您有:




先生。比例-此人查看输出的位置,并将其与您要求的内容进行比较。此人给出的声明是:


您想要的东西与您拥有的东西有很大的不同-采取大行动。
您想要的东西和所拥有的东西之间存在细微的差别-采取一些小动作。
您所拥有的就是您所要的-不采取任何行动。



先生。积分-这个人看的是与Mr. Proportional先生相同的错误值,但是将其与这种错误的发生时间进行了比较。此人给出的陈述是:


您有慢性/急性错误(长时间的小错误或短时间的大错误)-采取大行动。
您有轻微的错误(短时间的小错误)-采取小动作。
您的错误历史记录是中性的(时间*正误差等于时间*负误差)-不采取任何措施。



先生。导数-此人看与Mr. Proportional一样的错误值,但将其与错误值的变化进行比较。此人给出的声明是:


您的错误越来越大-采取更大的行动。
您的错误越来越小-采取负面行动。
您的错误没有改变-不采取任何措施。



重要的是要注意那里的导数先生的陈述2-假设您在已经停车的人后面驾驶汽车。随着您与他们的距离越来越近(误差越来越小),您不仅要放任自流,还想刹车!微分作用是使您“刹车”的原因-在超出设定点之前,没有其他术语(P或I)会给您带来负面作用。导数是唯一一个告诉您放慢速度的术语,因为您越来越近。

帮助理解这些术语的另一种方法是从物理上理解它们的含义。说您的参考是速度。这意味着:


比例误差会将您的速度与目标速度进行比较。想要60岁而您要55岁?加速。要65岁吗?慢一点。这很简单。
积分误差将目标速度的积分与实际速度的积分进行比较。这意味着它将目标位置与您的实际位置进行比较。你应该已经在城市里了吗?然后,您需要加快速度。您是否应该在城市,但仍在家里?加快整个道路。
导数误差比较目标速度和实际速度之间的差的导数。您前面的人在拉开吗?加速!您面前的人在踩刹车吗?慢一点!如前所述,如果您的目标是紧接在您前面的人之后,那么比例和积分都将“告诉”您以加快速度。如果您不开始刹车,则导数是唯一告诉您要尾随它们的“人”。

那么,当您在错误条件上强加符号时会发生什么?

假设您在开车中,试图跟朋友一起去餐馆。在这里,朋友的速度代表速度参考,而您的速度代表您的速度反馈。道路上的速度限制为35英里/小时(55公里/小时)。这是发生的情况:


您的朋友开始移动。
您仍然静止不动,因此会发生以下错误:


比例误差为正(您想转为35,而实际上为0)。
积分误差有点正(您的朋友离您更远)。
微分误差很大且正(您的朋友正迅速地远离您)。
您强制标志:强制积分误差为正(已经是)而微分误差为负。
这意味着微分误差正在“告诉”您汽车距离您越来越远,但是您将其反转,并假设派生误差意味着您与朋友越来越近。这是错误的。

操作:按比例-适量地按压气体。积分-稍微踩一下油门踏板。导数-应该踩很多油门踏板,但是将其倒转,所以要踩很多踩踏板。


最终,您的朋友变得足够远,以至于比例和积分误差变得足够大,以至于它覆盖了您的(不正确颠倒的)导数项。此时:


比例误差很大(仍然为零,并且希望为35)。
积分误差很大(您的朋友在您面前非常非常远)。
微分项仍然很大(朋友仍在与您逐渐远离),但您仍在强迫它为负数。
动作:成比例-大量踩气。一体式-踩油门踏板。导数-应该踩油门踏板,但将其倒转,所以要踩刹车。


一段时间后,时速达到34.999 mph。比例误差仍然(略)为正值,因为您想移至35,而实际上是34.999,因此比例误差为0.001


比例误差勉强为正(仍然慢于35mph )
积分误差最大(您到目前为止离您的朋友最远,因为您的朋友一直都在35岁)
微分误差大约为零(您的速度几乎与您的朋友相同,所以现在比例误差趋于稳定)
您强制执行以下操作:强制积分误差为正(已经是),微分误差为负(几乎为零,因此变化可以忽略不计)。
动作:成比例-没有动作,因为您时速接近35英里。积分-您现在真的离您的朋友很远,因此您不禁烦恼。微分-无作用,因为比例误差几乎稳定。


现在,因为您踩油门了,所以您通过了每小时35英里的时速并达到了每小时35.01英里的时速。现在,您的比例误差变成了负数(想要35,即将达到35.01,因此误差为-0.01)。


比例误差几乎为零(正好超过速度极限)
积分误差非常大,而且仍然很正,因为您实际上远远落后于朋友。
微分误差几乎为零(因为比例误差仍几乎为零)。
您强制标志:强制导数误差为正-几乎没有变化,因为它几乎为零。当您强制积分误差为负数时,问题就来了-它很大而且非常正!现在,您将其强制为负数。这意味着Integral先生告诉您您远远落后于您的朋友,但是您将其倒置并假设存在积分错误,意味着您在朋友面前遥遥领先。
动作:成比例-没有动作,因为您的时速为35英里/小时。整体-您远远落后于您的朋友,应该踩油门踏板,但是您倒转了一下,现在认为您远远领先于您的朋友,所以您踩了刹车踏板!导数-由于比例误差非常稳定,因此不采取任何措施。



此时,您遇到了一个循环-当您的速度刚刚超过35mph时,您会猛踩刹车,因为您会逆转积分误差;而当您的速度低于35mph时,您会踩到油门,因为您未取消积分误差。这应该(猛烈地!)使汽车(飞机)晃动,并防止您消除任何稳态错误。

更糟糕的是,我不确定一旦到达设定位置后它会如何运行,但我认为持续不断的符号翻转可能会阻止您在想要的位置附近停下来(如果它稳定了的话)完全没有)。

评论


$ \ begingroup $
建议我使用以下方式更改电动机速度:电动机速度1 =电动机速度1 + PitchPID电动机速度2 =电动机速度2-PitchPID电动机速度3 =电动机速度3+ RollPID电动机速度4 =电动机速度5-RollPID,而不是像我在第二个功能中发布的那样,使用速度调节来配对或电动机?
$ \ endgroup $
– Saeed Molaie
16-4-4在4:02



$ \ begingroup $
另外,我要指出的是,我没有花任何采样时间进行PID增益的计算,因为我不确定它是如何完成的,或者为什么它很重要。是否可以仅使用示波器/ LA准确记录采样时间?另外,我是否只需要测量完成一项PID功能所需的时间长度?
$ \ endgroup $
– Saeed Molaie
16-4-4在4:14



$ \ begingroup $
如果我使用定时器中断,可能会更好,因为完成一个循环所花费的时间将视情况而定。
$ \ endgroup $
– Saeed Molaie
16年4月4日在5:26

$ \ begingroup $
@SaeedMolaie-是的,使用中断是最佳途径,因为事实上您知道间隔是多少,您无需花费时间对其进行测量,并且该间隔稳定且不受逻辑分支或数据传输的影响。您的基本代码。
$ \ endgroup $
–卡盘
16年4月4日在18:31

$ \ begingroup $
关于您的其他评论,您可以将它们全部添加在一起。 PID输出将为您提供一些值(推力),用于修改电动机速度。因此motor1 = heightSpeed / 4 + pitchPID / 4 -rollPID / 4 + yawPID / 4。翻转适用于所有电动机的标志。所有四个马达的总和应为heightSpeed-应该有两个正螺距PID / 4和两个负螺距PID / 4,依此类推,以便两个前向电动机组合为pitchPID / 2和两个后接make -pitchPID / 2,并且然后将它们组合为(+ x)​​ pitchPID / 2 +(-x)*(-pitchPID / 2)。
$ \ endgroup $
–卡盘
16年4月4日在18:35