MiniFly微型四轴学习开发日志(二)——四轴PID算法

文章目录

限幅位置式PID介绍

角度环PID

角速度环PID

Z轴高度环PID

姿态控制量与实际油门值整合得到四轴电机PWM


有关MiniFly微型四轴基本框架介绍以及三个PID算法的大致框架,已经在日志(一)当中介绍,以下是链接。本文是基于日志(一)对PID进行进一步的介绍

https://blog.csdn.net/watchingdog/article/details/116089883?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161924912016780269877037%2522%252C%2522scm%2522%253A%252220140713.130102334.wap%255Fall.%2522%257D&request_id=161924912016780269877037&biz_id=0&utm_medium=distribute.wap_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-17-116089883.wap_first_rank_v2_rank_v29&utm_term=minifly四轴&spm=1018.2118.3001.4187

限幅增量式PID介绍

首先,再介绍三个PID开始之前,先介绍限幅的位置式PID,以下介绍转引自以下博文:位置式PID与增量式PID区别浅析_增量式和位置式pid的优缺点-CSDN博客

位置式PID是对系统当前的实际位置与你想达到的语气位置的偏差进行校正的PID控制。其公式为

由公式看出,其输出与过去所有状态有关,积分项为误差的累加值,输出u(k)因此对应执行器的实际位置,一旦输出大幅度变化,系统将会剧变。

另一方面,积分项饱和时,误差仍在积分作用下累计,一旦误差开始反向变化,系统需要时间从饱和区退出。因而输出到达最小最大时,应停止积分作用,进行积分限幅和输出限幅。以下为算法基本例程:

 
  1. typedef struct PID

  2. {

  3. float P,I,D,limit;

  4. }PID;

  5. typedef struct Error

  6. {

  7. float Current_Error;//当前误差

  8. float Last_Error;//上一次误差

  9. float Previous_Error;//上上次误差

  10. }Error;

  11. /*

  12. * @brief 位置式PID

  13. * @since v1.0

  14. * *sptr :误差参数

  15. * *pid: PID参数

  16. * NowPlace:当前位置

  17. * Point: 预期位置

  18. */

  19. // 位置式PID控制

  20. float PID_Realize(Error *sptr,PID *pid, int32 NowPlace, float Point)

  21. {

  22. int32 iError, // 当前误差

  23. Realize; //实际输出

  24. iError = Point - NowPlace; // 计算当前误差

  25. sptr->Current_Error += pid->I * iError; // 误差积分

  26. sptr->Current_Error = sptr->Current_Error > pid->limit?pid->limit:sptr->Current_Error;//积分限幅,成立取前一项,不成立取后一项

  27. sptr->Current_Error = sptr->Current_Error <-pid->limit?-pid->limit:sptr->Current_Error;

  28. Realize = pid->P * iError //比例P

  29. + sptr->Current_Error //积分I

  30. + pid->D * (iError - sptr->Last_Error); //微分D

  31. sptr->Last_Error = iError; // 更新上次误差

  32. return Realize; // 返回实际值

  33. }

而在MiniFly当中,其限幅位置式PID如下:

 
  1. float pidUpdate(PidObject* pid, const float error)

  2. {

  3. float output;

  4. pid->error = error; //当前误差

  5. pid->integ += pid->error * pid->dt;//误差积分

  6. if (pid->integ > pid->iLimit)//误差限幅

  7. {

  8. pid->integ = pid->iLimit;

  9. }

  10. else if (pid->integ < pid->iLimitLow)

  11. {

  12. pid->integ = pid->iLimitLow;

  13. }

  14. pid->deriv = (pid->error - pid->prevError) / pid->dt;//误差微分

  15. pid->outP = pid->kp * pid->error;

  16. pid->outI = pid->ki * pid->integ;

  17. pid->outD = pid->kd * pid->deriv;

  18. output = pid->outP + pid->outI + pid->outD; //计算pid输出

  19. pid->prevError = pid->error;//更新上次误差

  20. return output;//返回校正后的PID输出值

  21. }

第一个入口参数为将被更新结构体对象的指针,第二个入口参数为当前偏差。

角度环PID

角度环PID代码如下:

 
  1. void attitudeAnglePID(attitude_t *actualAngle, attitude_t *desiredAngle, attitude_t *outDesiredRate)

  2. {/* 角度环 PID */

  3. outDesiredRate->roll = pidUpdate(&pidAngleRoll, desiredAngle->roll - actualAngle->roll);

  4. outDesiredRate->pitch = pidUpdate(&pidAnglePitch, desiredAngle->pitch - actualAngle->pitch);

  5. float yawError = desiredAngle->yaw - actualAngle->yaw ;

  6. if (yawError > 180.0f)

  7. yawError -= 360.0f;

  8. else if (yawError < -180.0)

  9. yawError += 360.0f;

  10. outDesiredRate->yaw = pidUpdate(&pidAngleYaw, yawError);

  11. }

入口参数为三个attitude_t结构体指针,attitude_t即姿态数据结构类型。

第一个结构体为数据融合之后的输出值,也就是日志一当中说的将处理过的加速计数据与陀螺仪数据结合之后得到的姿态数据,即测量的当前角度。

第二个结构体为期望的角度值,来自WIFI/遥控RC。

第三个结构体对象即PID后的输出。三者都含有姿态数据的roll/pitch/yaw三个参数,前两个参数只要调用限幅位置式的pid校正即可,yaw由于代表的偏航角度,要限制其范围在-180°-180°之后再进行限幅位置式pid校正。

outDesiredRate为角度环PID输出的结构体对象,将在角速度环PID当中调用。

角速度环PID

角速度环PID代码如下:

 
  1. void attitudeRatePID(Axis3f *actualRate, attitude_t *desiredRate, control_t *output)

  2. {/* 角速度环 PID */

  3. output->roll = pidOutLimit(pidUpdate(&pidRateRoll, desiredRate->roll - actualRate->x));

  4. output->pitch = pidOutLimit(pidUpdate(&pidRatePitch, desiredRate->pitch - actualRate->y));

  5. output->yaw = pidOutLimit(pidUpdate(&pidRateYaw, desiredRate->yaw - actualRate->z));

  6. }

第一个入口参数是一个3轴数据结构体指针,也就是日志一当中处理过的陀螺仪数据——3轴陀螺仪结构体变量sensors→gyro,即当前角速度测量值。

第二个入口参数是一个姿态数据结构体指针,指向角度环PID输出outDesiredRate,即角速度期望值。

第三个入口参数是一个控制数据结构体指针,指向control结构体变量,此结构体包含角速度PID输出数据——姿态控制量数据,即用于控制电机的三个参数roll/pitch/yaw。

函数内部则是简单的进行限幅位置式PID之后,用pidOutlimit函数限制姿态控制量,调整范围再(-32768~32768),防止调整量过大难以控制。

Z轴高度环PID

Z轴高度换PID代码如下:

 
  1. void altholdPID(float* thrust, const state_t *state, const setpoint_t *setpoint)

  2. {

  3. float newThrust = 0.0;

  4. newThrust =THRUST_SCALE * runPidZ(&posPid.pidVZ, state->position.z, setpoint, POS_UPDATE_DT);

  5. if(getCommanderKeyFlight()) /* 定高飞模式检测重量*/

  6. detecWeight(*thrust, newThrust, state->velocity.z);

  7. *thrust = newThrust + posPid.thrustBase;

  8. if (*thrust > 60000)

  9. {

  10. *thrust = 60000;

  11. }

  12. }

首先,实际油门值=定高油门基准值posPid.thrustBase+newThrust。定高油门基准值即定高模式下,脱控时的油门,它对悬停作用大,影响定高效果。定稿油门基准值由detecWeight函数调整,它的功能是检测四轴的原理,原理为:每次起飞之后,实时监测Z轴方向速度。当连续一段时间Z轴速度接近0,即该油门值可悬停,稍作处理后即新的油门基准值。

另一方面,newThrust来源于Z轴PID*油门放大系数,其代码如下:

 
  1. static float runPidZ(pidAxis_t *axis, float input, const setpoint_t *setpoint, float dt)

  2. {

  3. float out = 0.f;

  4. if (axis->preMode == false && setpoint->isAltHold == true)

  5. {

  6. flag = false;

  7. positionResetAllPID();

  8. axis->setpoint = input + START_HIRHT;

  9. posPid.thrustBase = limitThrustBase(configParam.thrustBase);

  10. }

  11. axis->preMode = setpoint->isAltHold;

  12. if(setpoint->isAltHold == true)

  13. {

  14. axis->setpoint += setpoint->velocity.z * dt;

  15. out = pidUpdate(&axis->pid, axis->setpoint - input);

  16. }

  17. return out;

  18. }

这俩函数的第三个参数都是一个setpoint结构体指针,调用时是同一个结构体指针。

bool型参数成员变量isAltHold标志当前控制模式,如果是定高模式即为true,手动模式为false。

preMode为上次控制模式的记录。

因此,当发生手动模式切换到定稿模式,即前者true后者false时,先是用positionResetALLPID函数清楚之前的pid参数,计算当前高度期望值,并对定高油门基准值积分。

定高模式下,先是让高度期望值对Z轴油门速度进行积分;另一方面,测量高度值来自将姿态数据的去除重力之后的Z轴加速度与气压计BMP280经过双重滤波之后的数据进行融合得到。二者相减之后,得到偏差高度,输入到限幅位置式pid之中校正得到输出out,再乘上油门放大系数即newThrust。

之后将newThrust与油门基准值相加即实际的油门值。

姿态控制量与实际油门值整合得到四轴电机PWM

代码如下:

 
  1. void powerControl(control_t *control) /*功率输出控制*/

  2. {

  3. s16 r = control->roll / 2.0f;

  4. s16 p = control->pitch / 2.0f;

  5. motorPWM.m1 = limitThrust(control->thrust - r - p - control->yaw);

  6. motorPWM.m2 = limitThrust(control->thrust - r + p + control->yaw);

  7. motorPWM.m3 = limitThrust(control->thrust + r + p - control->yaw);

  8. motorPWM.m4 = limitThrust(control->thrust + r - p + control->yaw);

  9. if (motorSetEnable)

  10. {

  11. motorPWM=motorPWMSet;

  12. }

  13. motorsSetRatio(MOTOR_M1, motorPWM.m1); /*控制电机输出百分比*/

  14. motorsSetRatio(MOTOR_M2, motorPWM.m2);

  15. motorsSetRatio(MOTOR_M3, motorPWM.m3);

  16. motorsSetRatio(MOTOR_M4, motorPWM.m4);

  17. }

  18. [点击并拖拽以移动]

x轴模式下的四轴如图:

箭头方向为正方向,即机头方向。正常情况下,M4、M2逆时针运行,M3、M1顺时针运行。

先对控制结构体control当中的roll/pitch/yaw/Thrust进行说明:输出的油门值,即电机PWM应该等于Thrust加上校正之后需要调整的r、p、yaw,而后三者的符号决定方式如下:

(1)roll代表着四轴围绕X轴左右倾斜的横滚角,以右为正方向。因此当向左偏移时,应该产生一个向右的力,即M3、M4同侧出力,二者的r为“+”;而M1、M2为反侧出力,即r符号位“-”;

(2)pitch代表着四轴围绕Y轴前后倾斜的俯仰角,以前为正方向。因此当向后偏移时,应该产生一个向前的力,即M3、M2同侧出力,二者的p为“+”;而M1、M4为反侧出力,即p符号位“-”;

(3)yaw代表着四轴围绕Z轴作旋转的旋转角,以逆时针为正方向。因此当顺时针偏移时,应该产生一个使其逆时针的力,即M2、M4同侧出力,二者的yaw为“+”;而M1、M3为反侧出力,即yaw符号位“-”;

之后通过motorSetRatio函数,即可赋予各个电机对应的PWM值。

这就是大致的四轴PID算法。

MiniFly微型四轴学习开发日志(二)——四轴PID算法_四轴高度pid控制-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值