串级PID控制四轴飞行状态-分析

2023-05-16

参考网页: http://blog.csdn.net/nemol1990/article/details/45131603

一、概念

  • 单极PID:当你知道系统当前状态和期望状态后,如何将系统从当前状态调整到期望状态是个问题,在此我们可以用PID进行调整,PID分为位置式和增量式,位置式适合舵机等系统,在此使用的是增量式。
    公式:PID=P*e(n)+I*[(e(n)+e(n-1)+…+e(0)]+D*[e(n)-e(n-1)]
    D后面的当前误差减前次误差也可以直接使用陀螺仪的数据代替,原理一样。

  • 单极PID适合线性系统,当输出量和被控制量呈线性关系时单极PID能获得较好的效果,但是四轴不是线性系统,现代学者认为,四轴通常可以简化为一个二阶阻尼系统。为什么四轴不是线性系统呢?首先,输出的电压和电机转速不是呈正比的,其次,螺旋桨转速和升力是平方倍关系,故单极PID在四轴上很难取得很好效果,能飞,但是不好飞。

  • 串级PID就是两个PID串在一起,分为内环和外环PID。在此,我们使用内环PID控制,外环PI控制。单极PID输入的是期望角度,反馈的是角度数据,串级PID中外环输入反馈的也是角度数据,内环输入反馈的便是角速度数据。通俗来讲,内环就是你希望将四轴以多少度每秒的速度运动,然后他给你纠正过来,外环就是根据角度偏差告诉内环你该以多少度一秒运动。这样,即使外环数据剧烈变化,四轴的效果也不会显得很僵硬。

  • 在内环中,PID三个数据作用分别是:P(将四轴从偏差角速度纠正回期望角速度)D(抑制系统运动)I(消除角速度控制静差)

  • 外环PI中,两个数据的作用是:P(将四轴从偏差角度纠正回期望角度)I(消除角度控制静差)

二、单环PID

这里写图片描述
或许有些朋友看得懂框图,但是编程实现有一定困难,在这里笔者给出了伪代码:
这里写图片描述

三、串级PID

这里写图片描述
伪代码:
这里写图片描述

  • 外环PID

    当前角度误差 = 期望角度 - 当前角度
    外环PID_P项 = 外环Kp * 当前角度误差

    当前角度误差积分及其积分限幅
    外环PID_I项 = 外环Ki * 当前角度误差积分

    外环PID输出 = 外环PID_P项 + 外环PID_I项

  • 内环PID

    当前角速度误差 = 外环PID输出 - 当前角速度(直接用陀螺仪输出)
    内环PID_P项 = 内环Kp * 当前角速度误差

    当前角速度误差积分及其积分限幅
    内环PID_I项 = 内环Ki * 当前角速度误差积分

    当前角速度微分(本次角速度误差 - 上次角速度误差)
    内环PID_D项 = 内环Kd * 当前角速度的微分
    内环PID输出 = 内环PID_P项 + 内环PID_I项 + 内环PID_D项

四、以一个最简单的stc的四轴代码为例分析飞行控制

  1. 代码

        #include <STC15F2K60S2.h>   //STC15系列通用头文件
        #include <intrins.h>        //STC特殊命令符号声明
        #include <MPU6050.H>        //MPU6050数字陀螺仪
        #include <STC15W4KPWM.H>    //单片机所有IO口初始化-PWM口初始化
        //#include <EEPROM.h>       //STC-EEPROM内部存储
        #include <NRF24L01.h>       //NRF24L01 2.4G无线收发模块
        //#include <STC15W4K-ADC.h> //STC15W4K-ADC  硬件ADC模数转换
        #include <IMU.H>            //IMU飞行核心算法
        //==================================================//
        //  外围引脚定义
        //==================================================//
        sbit LEDH3=P5^4; //四轴航向灯 1=灭 0=亮
        sbit LEDH4=P1^0; //四轴航向灯 1=灭 0=亮
        sbit LEDH1=P3^2; //四轴航向灯 1=灭 0=亮
        sbit LEDH2=P3^3; //四轴航向灯 1=灭 0=亮
    
        unsigned int LED\_mun=0;  
        //==================================================//  
        //  飞行控制变量  
        //==================================================//  
        unsigned char JieSuo;   //断开/连接 解锁变量  
        unsigned int YouMen;    //油门变量    
        unsigned int HangXiang; //航向变量  
        unsigned int HengGun;   //横滚变量  
        unsigned int FuYang;    //俯仰变量  
        unsigned char  FYHG;    //俯仰横滚变量  
        unsigned char  GaoDu;   //高度变量  
        unsigned char  DianYa;  //电压变量  
        unsigned int ADC1,ADC2; //adc模数转换 10位 结果变量  
        unsigned char SSLL,lastR0,ZT;     //通讯状态 变量  
        //==================================================//  
        //                  全局函数定义  
        //==================================================//  
        unsigned char TxBuf[12]; //设置发送长度,最高为32字节      
        unsigned char RxBuf[12]; //设置接收长度,最高为32字节  
        //==================================================//  
        //  PID算法变量  
        //==================================================//  
        //连至上位机检查电机轴是否发生弯曲,发现问题电机及时更换  
        unsigned int YM=0;                      //油门变化速度控制,  
        int speed0=0,speed1=0,speed2=0,speed3=0;//电机速度参数  
        int PWM0=0,PWM1=0,PWM2=0,PWM3=0;        //加载至PWM模块的参数  
        int g_x=0,g_y=0,g_z=0;                  //陀螺仪矫正参数  
        char a_x=0,a_y=0,a_z=0;                 //角度矫正参数  
        //*****************角度参数*************************************************  
        float Last_Angle_gx=0;                    //外环PI输出量  上一次陀螺仪数据  
        float Last_Angle_gy=0;                        //外环PI输出量  上一次陀螺仪数据   
        double Gyro_y=0,Gyro_x=0,Gyro_z=0;        //Y轴陀螺仪数据暂存   
        double Accel_x=0,Accel_y=0,Accel_z=0;     //X轴加速度值暂存   
        double Angle_ax=0,Angle_ay=0,Angle_az=0;  //由加速度计算的加速度(弧度制)   
        double Angle_gy=0,Angle_gx=0,Angle_gz=0;  //由角速度计算的角速率(角度制)   
        double Anglezlate=0;                      //Z轴相关   
        int data AngleX=0,AngleY=0;               //四元数解算出的欧拉角   
        double Ax=0,Ay=0;Az=0;                    //加入遥控器控制量后的角度   
        //****************姿态处理和PID*********************************************   
        float FR1=0,FR2=0,FR3=0;        //方向控制数据变量   
        float xdata gx=0,gy=0;              //加入遥控器控制量后的角度   
    
        float  data PID_Output;             //PID最终输出量   
    
        float xdata ERRORX_In=0;            //内环P 内环I 内环D 内环误差积分 
        float xdata ERRORX_Out=0;           //外环P 外环I       外环误差积分
    
        float xdata ERRORY_In=0;
        float xdata ERRORY_Out=0;
    
        float xdata ERRORZ_Out=0;
    
        float xdata Last_Ax=0;
        float xdata Last_Ay=0;
    
        float xdata Last_gx=0;
        float xdata Last_gy=0;
    
    
        //==================================================//
        //   PID 手动微调参数值
        //==================================================//
        // D值只要不超过10都可以,D值在3以上10以下!!! D值不合适飞机就会荡
        #define Out_XP  15.0f   //外环P
        #define Out_XI  0.01f   //外环I
        #define Out_XD  5.0f    //外环D
    
        #define In_XP   0.55f   //内环P 720
        #define In_XI   0.01f   //内环I
        #define In_XD   3.0f    //内环D 720
    
        #define In_YP   In_XP
        #define In_YI   In_XI
        #define In_YD   In_XD
    
        #define Out_YP  Out_XP
        #define Out_YI  Out_XI
        #define Out_YD  Out_XD
    
        //float ZP=5.0,ZD=4.0;  //自旋控制的P D
        #define ZP  3.0f
        #define ZD  1.0f     //自旋控制的P D
    
        #define ERR_MAX 800  //
    
        //--------------------------------------------------//
        //  PID算法飞控自平衡 函数
        //--------------------------------------------------//
        void Flight(void)interrupt 1 
        {   
            //陀螺仪水平校准-后期更新
        //  Gyro_x = GetData(GYRO_XOUT_H)-g_x;//陀螺仪 值
        //  Gyro_y = GetData(GYRO_YOUT_H)-g_y;//减掉 校正值
        //  Gyro_z = GetData(GYRO_ZOUT_H)-g_z;  
        //  读取MCU6050 寄存器数据
            Gyro_x = GetData(GYRO_XOUT_H);//读出 X轴陀螺仪数据
            Gyro_y = GetData(GYRO_YOUT_H);//读出 Y轴陀螺仪数据
            Gyro_z = GetData(GYRO_ZOUT_H);//读出 Z轴陀螺仪数据  
            Accel_y= GetData(ACCEL_YOUT_H);//读出 X轴加速度数据
            Accel_x= GetData(ACCEL_XOUT_H);//读出 Y轴陀螺仪数据        
            Accel_z= GetData(ACCEL_ZOUT_H);//读出 Z轴加速度数据      
            //姿态数据算法 (借鉴STC官方算法)
            Last_Angle_gx=Angle_gx;   //储存上一次角速度数据
            Last_Angle_gy=Angle_gy;   //储存上一次角速度数据
            Angle_ax=(Accel_x)/8192;  //加速度处理
            Angle_az=(Accel_z)/8192;  //加速度量程 +-4g/S
            Angle_ay=(Accel_y)/8192;  //转换关系   8192LSB/g
            Angle_gx=(Gyro_x)/65.5;   //陀螺仪处理
            Angle_gy=(Gyro_y)/65.5;   //陀螺仪量程 +-500度/S
            Angle_gz=(Gyro_z)/65.5;   //转换关系65.5LSB/度
        //***********************************四元数解算***********************************
            IMUupdate(Angle_gx*0.0174533,Angle_gy*0.0174533,Angle_gz*0.0174533,Angle_ax,Angle_ay,Angle_az);//0.174533为PI/180 目的是将角度转弧度
        //****以下是飞行控制算法***********************************************************************
            YM=YouMen;   //输入油门量0-1000
    
            if(SSLL == lastR0)  //如果RxBuf[0]的数据没有收到 即失联
            {
                if(++ZT >= 100)
                {
                    ZT = 101;   //状态标识大于128即1秒没有收到数据,失控保护
    
                    RxBuf[4] = 128;
    
                    RxBuf[5] = 128; //触发失控保护 ,缓慢下降,俯仰横滚方向舵归中
    
                    RxBuf[6] = 128;
    
                    if(YouMen != 0)    //油门
                    {
                        YouMen--;   //油门在原值逐渐减小
                        YM=YouMen;
                        TxBuf[2]=YM/0xff;//发送 油门参数 高2位 
                        TxBuf[3]=YM%0xff;//发送 油门参数 低8位
                    }
                }
            }
            else{   ZT = 0;         }
            lastR0 = SSLL;
        //****以下是飞行控制算法***********************************************************************
    
        //************** MPU6050 X轴指向 ***********************************************************
        //  FR1=0;//关闭横滚
            FR1=((float)HengGun-128)/6; //得到 横滚数据变量
            Ax=-FR1+a_x-AngleX;   //角度控制量加载至角度
    
    
            if(YM > 30) ERRORX_Out += Ax;   //外环积分(油门小于某个值时不积分)
            else        ERRORX_Out = 0;     //油门小于定值时清除积分值
    
                 if(ERRORX_Out >  ERR_MAX)  ERRORX_Out =  ERR_MAX;  //积分限幅
            else if(ERRORX_Out < -ERR_MAX)  ERRORX_Out = -ERR_MAX;  //积分限幅
    
            PID_Output = Ax*Out_XP + ERRORX_Out*Out_XI+(Ax-Last_Ax)*Out_XD; //外环PID
            Last_Ax=Ax;
        //  if(YM > 20) ERRORX_In += (Angle_gy - PID_Output);   //内环积分(油门小于某个值时不积分)
            gy=PID_Output - Angle_gy;
    
    
            if(YM > 30) ERRORX_In += gy;    //内环积分(油门小于某个值时不积分)
            else        ERRORX_In  = 0; //油门小于定值时清除积分值
    
                 if(ERRORX_In >  ERR_MAX)   ERRORX_In =  ERR_MAX;
            else if(ERRORX_In < -ERR_MAX)   ERRORX_In = -ERR_MAX; //积分限幅
    
        //  PID_Output = (Angle_gy + PID_Output)*In_XP + ERRORX_In*In_XI + (Angle_gy - Last_Angle_gy)*In_XD;    //内环PID
            PID_Output = gy*In_XP + ERRORX_In*In_XI + (gy-Last_gy)*In_XD;
            Last_gy=gy;
            if(PID_Output >  1000)  PID_Output =  1000;  //输出量限幅
            if(PID_Output < -1000)  PID_Output = -1000;
    
            speed0 = 0 + PID_Output;    speed1 = 0 - PID_Output;
            speed3 = 0 + PID_Output;    speed2 = 0 - PID_Output;
        //**************MPU6050 Y轴指向**************************************************
            FR2=((float)FuYang-128)/6; //得到 俯仰数据变量
            Ay=-FR2-a_y-AngleY;      //角度控制量加载至角度
    
            if(YM > 30)     ERRORY_Out += Ay;               //外环积分(油门小于某个值时不积分)
            else            ERRORY_Out = 0;                 //油门小于定值时清除积分值
            if(ERRORY_Out >  ERR_MAX)   ERRORY_Out =  ERR_MAX;
            else if(ERRORY_Out < -ERR_MAX)  ERRORY_Out = -ERR_MAX;          //积分限幅
    
            PID_Output = Ay*Out_YP + ERRORY_Out*Out_YI+(Ay-Last_Ay)*Out_YD; //外环PID
            Last_Ay=Ay;
            gx=PID_Output - Angle_gx;
            if(YM > 30)ERRORY_In +=gx;                              //内环积分(油门小于某个值时不积分)
            else            ERRORY_In = 0;                          //油门小于定值时清除积分值
                 if(ERRORY_In >  ERR_MAX)   ERRORY_In =  ERR_MAX;
            else if(ERRORY_In < -ERR_MAX)   ERRORY_In = -ERR_MAX;   //积分限幅
    
        //  PID_Output = (Angle_gx + PID_Output)*In_YP + ERRORY_In*In_YI + (Angle_gx - Last_Angle_gx)*In_YD;    //内环PID
            PID_Output = gx*In_YP + ERRORY_In*In_YI + (gx - Last_gx)*In_YD;
            Last_gx=gx;
            if(PID_Output >  1000)  PID_Output =  1000;  //输出量限幅
            if(PID_Output < -1000)  PID_Output = -1000;
    
            speed0 = speed0 + PID_Output;   speed1 = speed1 + PID_Output;//加载到速度参数
            speed3 = speed3 - PID_Output;   speed2 = speed2 - PID_Output;
    
        //************** MPU6050 Z轴指向 ***************************** 
            FR3=((float)HangXiang-128)*1.5;//得到 航向数据变量
            Az=FR3+a_z-Angle_gz;
            if(YM > 30)     ERRORZ_Out += Az;
            else            ERRORZ_Out  = 0; 
            if(ERRORZ_Out >  800)   ERRORZ_Out =  800;
            else if(ERRORZ_Out < -800)  ERRORZ_Out = -800;  //积分限幅
            PID_Output = Az*ZP + ERRORZ_Out * 0.2f + (Az - Anglezlate) * ZD;
    
            Anglezlate = Az;
            speed0 = speed0 + PID_Output;   speed1 = speed1 - PID_Output;
            speed3 = speed3 - PID_Output;   speed2 = speed2 + PID_Output;
    
        //*****************PID控制数值--X机型控制***************************************************
        //**************将速度参数加载至PWM模块*************************************************  
            //速度参数控制,防止超过PWM参数范围0-1000(X型有效)
            PWM0=(YM+speed1);if(PWM0>1000)PWM0=1000;else if(PWM0<0)PWM0=0;      // 5
            PWM1=(YM+speed2);if(PWM1>1000)PWM1=1000;else if(PWM1<0)PWM1=0;      // 3
            PWM2=(YM+speed0);if(PWM2>1000)PWM2=1000;else if(PWM2<0)PWM2=0;      // 4
            PWM3=(YM+speed3);if(PWM3>1000)PWM3=1000;else if(PWM3<0)PWM3=0;      // 2
    
            //满足条件:(解锁=1;2.4G=5;油门大于30)才能控制电机
            if(JieSuo==1&&YM>=30)
              {PWM(1000-PWM0,1000-PWM1,1000-PWM2,1000-PWM3);} //启动PWM         2 3 4 5
            else      
              {PWM(1000,1000,1000,1000);}                     //关闭PWM
        } 
        //--------------------------------------------------//
        //  时间延时 函数
        //--------------------------------------------------//
        void Delay(unsigned int x)
        {unsigned int i,j;
          for(i=0;i<x;i++)
          for(j=0;j<250;j++);
        }
        //--------------------------------------------------//
        //  定时器0 初始化函数 V2.0
        //--------------------------------------------------//
        void Timer0Init(void)   //10微秒@27.000MHz
        {
            TR0 = 0;
            AUXR &= 0x7F;   //定时器时钟12T模式
            TMOD &= 0xF0;   //设置定时器模式
            IE  = 0x82;
            TL0 = 0x1C;     //设置定时初值
            TH0 = 0xA8;     //设置定时初值
            TF0 = 0;        //清除TF0标志
            TR0 = 1;        //定时器0开始计时
    
        }
    
        //--------------------------------------------------//
        //  程序 主函数
        //--------------------------------------------------//
        void main(void)
        {
    
        //  unsigned char DCDY;//电池电压 变量
            unsigned char SHU_mun;
            /*----华丽的分割线----华丽的分割线----华丽的分割线----华丽的分割线----*/
            PWMGO();        //初始化PWM
            Delay(10);    // 延时 100
        //  InitADC();      //ADC模数转换 初始化
            Delay(10);    // 延时 100
            InitMPU6050();  //初始化MPU-6050
            Delay(10);    // 延时 100
            init_NRF24L01() ;  //NRF24L01 初始化
            nRF24L01_RX_Mode(RxBuf);//数据接收
            Delay(10);    // 延时 100
            Timer0Init();   //初始化定时器
            /*----华丽的分割线----华丽的分割线---  ---*/
            Delay(100); //延时一会 1S
            YouMen =0;      //初始化油门变量 
            HangXiang=128;  //初始化航向变量 
            HengGun =128;   //初始化横滚变量 
            FuYang  =128;   //初始化俯仰变量
            /*----华丽的分割线----华丽的分割线----*/
    
            EA = 1;  //开总中断
            PWM(1000,1000,1000,1000);
            while(1)
            {               
                nRF24L01_TX_Mode(TxBuf);//数据发送
                Delay(1);
                nRF24L01_RX_Mode(RxBuf);//数据接收
                Delay(1);
    
                SSLL     =RxBuf[0];  //接收 失联变量
    
    
                JieSuo   =RxBuf[1];  //接收 命令值
    
                if(SSLL != lastR0)  //如果RxBuf[0]的数据没有收到 即失联
                    YouMen   =RxBuf[2]*0xff+RxBuf[3];  //接收 油门变量
    
                HangXiang=RxBuf[4];  //接收 航向变量 
                HengGun  =RxBuf[5];  //接收 横滚变量 
                FuYang   =RxBuf[6];  //接收 俯仰变量
            //  XXGG     =RxBuf[7];  //接收 设置参数变量
                a_x      =RxBuf[8]-128;  //接收
                a_y      =RxBuf[9]-128;  //接收
                a_z      =RxBuf[10]-128;     //接收
    
            /*----华丽的分割线---*/               
                LED_mun++;
    
                if(JieSuo==1)                //解锁飞机   SHU_mun
                {   
                    if(LED_mun>600) LED_mun=0;      
                    if(LED_mun>150)
                    {
                        LEDH1=0;LEDH2=0;LEDH3=0;LEDH4=0;  //点亮航向灯
                    }
                    else
                    {
                        LEDH1=1;LEDH2=1;LEDH3=1;LEDH4=1;  //关航向灯
                    }
                    if(SHU_mun==1)
                    {
                       PWM(600,600,600,600);
                       Delay(1000);    // 延时 
                       SHU_mun=0;
                    }
                }
                else                       //锁上飞机
                {
    
                    if(LED_mun<50)
                    {
                        SHU_mun=1;
                        LEDH1=1;LEDH2=1;LEDH3=0;LEDH4=1;  //点亮1航向灯
                    }
                    else if(LED_mun<100)
                    {
                        LEDH1=1;LEDH2=1;LEDH3=1;LEDH4=0;  //点亮2航向灯
                    }
                    else if(LED_mun<150)
                    {
                        LEDH1=1;LEDH2=0;LEDH3=1;LEDH4=1;  //点亮3航向灯
                    }else if(LED_mun<200)
                    {
                        LEDH1=0;LEDH2=1;LEDH3=1;LEDH4=1;  //点亮4航向灯
                    }else{ LED_mun=0;   }
                }
            /*----华丽的分割线----*/
            }
        }
    
  2. 解析

    HangXiang=RxBuf[4];  //接收 航向变量 
    HengGun  =RxBuf[5];  //接收 横滚变量 
    FuYang   =RxBuf[6];  //接收 俯仰变量
    a_x      =RxBuf[8]-128;  //角度矫正参数
    a_y      =RxBuf[9]-128;  //角度矫正参数
    a_z      =RxBuf[10]-128;     //角度矫正参数
    Gyro\_x = GetData(GYRO_XOUT_H);//读出 X轴陀螺仪数据    
    Gyro\_y = GetData(GYRO_YOUT_H);//读出 Y轴陀螺仪数据    
    Gyro\_z = GetData(GYRO_ZOUT_H);//读出 Z轴陀螺仪数据     
    Accel\_x= GetData(ACCEL_XOUT_H);//读出 X轴加速度数据  
    Accel\_y= GetData(ACCEL_YOUT_H);//读出 Y轴加速度数据           
    Accel\_z= GetData(ACCEL_ZOUT_H);//读出 Z轴加速度数据     
    
    YM=YouMen;   //输入油门量0-1000
    

四元数解算

        void IMUupdate(float gx, float gy, float gz, float ax, float ay, float az)
        {
            float idata norm;
            float idata vx, vy, vz;
            float idata ex, ey, ez;

            norm = sqrt(ax*ax + ay*ay + az*az); //把加速度计的三维向量转成单维向量   
            ax = ax / norm;
            ay = ay / norm;
            az = az / norm;

                //  下面是把四元数换算成《方向余弦矩阵》中的第三列的三个元素。 
                //  根据余弦矩阵和欧拉角的定义,地理坐标系的重力向量,转到机体坐标系,正好是这三个元素
                //  所以这里的vx vy vz,其实就是当前的欧拉角(即四元数)的机体坐标参照系上,换算出来的
                //  重力单位向量。
            vx = 2*(q1*q3 - q0*q2);
            vy = 2*(q0*q1 + q2*q3);
            vz = q0*q0 - q1*q1 - q2*q2 + q3*q3 ;

            ex = (ay*vz - az*vy) ;
            ey = (az*vx - ax*vz) ;
            ez = (ax*vy - ay*vx) ;

            exInt = exInt + ex * Ki;
            eyInt = eyInt + ey * Ki;
            ezInt = ezInt + ez * Ki;

            gx = gx + Kp*ex + exInt;
            gy = gy + Kp*ey + eyInt;
            gz = gz + Kp*ez + ezInt;

            q0 = q0 + (-q1*gx - q2*gy - q3*gz) * halfT;
            q1 = q1 + ( q0*gx + q2*gz - q3*gy) * halfT;
            q2 = q2 + ( q0*gy - q1*gz + q3*gx) * halfT;
            q3 = q3 + ( q0*gz + q1*gy - q2*gx) * halfT;

            norm = sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
            q0 = q0 / norm;
            q1 = q1 / norm;
            q2 = q2 / norm;
            q3 = q3 / norm;

            AngleX  = asin(2*(q0*q2 - q1*q3 )) * 57.2957795f; // 俯仰   换算成度
            AngleY = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll 
        //  AngleY = asin(2*(q0*q1 + q2*q3 )) * 57.2957795f; // 横滚
        }

无人机失联-关闭油门

    if(YouMen != 0)    //油门  
    {  
        YouMen--;   //油门在原值逐渐减小  
    }  

横滚

    FR1=((float)HengGun-128)/6; //得到 横滚数据变量  
    Ax=-FR1+a_x-AngleX;   //角度控制量加载至角度

    if(YM > 30) ERRORX_Out += Ax;   //外环积分(油门小于某个值时不积分)
    else        ERRORX_Out = 0;     //油门小于定值时清除积分值
    if(ERRORX_Out >  ERR_MAX)   ERRORX_Out =  ERR_MAX;  //积分限幅不超过800
    else if(ERRORX_Out < -ERR_MAX)  ERRORX_Out = -ERR_MAX;  //积分限幅不小于-800  


      PID_Output = Ax*Out_XP + ERRORX_Out*Out_XI+(Ax-Last_Ax)*Out_XD;   //外环PID
      Last_Ax=Ax;

    gy=PID_Output - Angle_gy;
    if(YM > 30) ERRORX_In += gy;    //内环积分(油门小于某个值时不积分)
    else        ERRORX_In  = 0; //油门小于定值时清除积分值
     if(ERRORX_In >  ERR_MAX)   ERRORX_In =  ERR_MAX;
    else if(ERRORX_In < -ERR_MAX)   ERRORX_In = -ERR_MAX; //积分限幅

    PID_Output = gy*In_XP + ERRORX_In*In_XI + (gy-Last_gy)*In_XD;  //内环PID
    Last_gy=gy;
    if(PID_Output >  1000)  PID_Output =  1000;  //输出量限幅
    if(PID_Output < -1000)  PID_Output = -1000;

    speed0 = 0 + PID_Output;    speed1 = 0 - PID_Output;
    speed3 = 0 + PID_Output;    speed2 = 0 - PID_Output;

俯仰

    FR2=((float)FuYang-128)/6; //得到 俯仰数据变量
    Ay=-FR2-a_y-AngleY;      //角度控制量加载至角度

    if(YM > 30)     ERRORY_Out += Ay;               //外环积分(油门小于某个值时不积分)
    else            ERRORY_Out = 0;                 //油门小于定值时清除积分值
    if(ERRORY_Out >  ERR_MAX)   ERRORY_Out =  ERR_MAX;
    else if(ERRORY_Out < -ERR_MAX)  ERRORY_Out = -ERR_MAX;          //积分限幅

    PID_Output = Ay*Out_YP + ERRORY_Out*Out_YI+(Ay-Last_Ay)*Out_YD; //外环PID
    Last_Ay=Ay;
    gx=PID_Output - Angle_gx;
    if(YM > 30)ERRORY_In +=gx;                              //内环积分(油门小于某个值时不积分)
    else            ERRORY_In = 0;                          //油门小于定值时清除积分值
         if(ERRORY_In >  ERR_MAX)   ERRORY_In =  ERR_MAX;
    else if(ERRORY_In < -ERR_MAX)   ERRORY_In = -ERR_MAX;   //积分限幅

    PID_Output = gx*In_YP + ERRORY_In*In_YI + (gx - Last_gx)*In_YD;
    Last_gx=gx;
    if(PID_Output >  1000)  PID_Output =  1000;  //输出量限幅
    if(PID_Output < -1000)  PID_Output = -1000;

    speed0 = speed0 + PID_Output;   speed1 = speed1 + PID_Output;//加载到速度参数
    speed3 = speed3 - PID_Output;   speed2 = speed2 - PID_Output;

航向

    FR3=((float)HangXiang-128)*1.5;//得到 航向数据变量
    Az=FR3+a_z-Angle_gz;
    if(YM > 30)     ERRORZ_Out += Az;
    else            ERRORZ_Out  = 0; 
    if(ERRORZ_Out >  800)   ERRORZ_Out =  800;
    else if(ERRORZ_Out < -800)  ERRORZ_Out = -800;  //积分限幅
    PID_Output = Az*ZP + ERRORZ_Out * 0.2f + (Az - Anglezlate) * ZD;

    Anglezlate = Az;
    speed0 = speed0 + PID_Output;   speed1 = speed1 - PID_Output;
    speed3 = speed3 - PID_Output;   speed2 = speed2 + PID_Output;

将速度参数加载至PWM模块

    PWM0=(YM+speed1);if(PWM0>1000)PWM0=1000;else if(PWM0<0)PWM0=0;      // 5
    PWM1=(YM+speed2);if(PWM1>1000)PWM1=1000;else if(PWM1<0)PWM1=0;      // 3
    PWM2=(YM+speed0);if(PWM2>1000)PWM2=1000;else if(PWM2<0)PWM2=0;      // 4
    PWM3=(YM+speed3);if(PWM3>1000)PWM3=1000;else if(PWM3<0)PWM3=0;      // 2

    //满足条件:(解锁=1;2.4G=5;油门大于30)才能控制电机
    if(JieSuo==1&&YM>=30)
      {PWM(1000-PWM0,1000-PWM1,1000-PWM2,1000-PWM3);} //启动PWM         2 3 4 5
    else      
      {PWM(1000,1000,1000,1000);}                     //关闭PWM
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

串级PID控制四轴飞行状态-分析 的相关文章

  • PID算法,计算的是差值,是差值

    typedef struct float Kp 比例系数Proportional float Ki 积分系数Integral float Kd 微分系数Derivative float Ek 当前误差 float Ek1 前一次误差 e k
  • PID简介

    一 基本定义 Sv 用户设定值 给定信号 Pv 控制对象当前状态值 反馈信号 E 偏差值 偏差信号 所以 E Sv Pv 二 PID各个控制基本分析 1 P控制 比例控制 Pout Kp Ek 假定从早上开机上电 我们每隔一秒钟就通过传感器
  • setns对当前进程无效问题的排查(getpid获取值不变)

    1 复现流程及lxc的处理 demo1程序与执行结果如下 此时在容器内部看不到执行的程序 int main int ret fd pid printf father pid old d n getpid fd open dev ns O R
  • PID控制器开发笔记之九:基于前馈补偿的PID控制器的实现

    对于一般的时滞系统来说 设定值的变动会产生较大的滞后才能反映在被控变量上 从而产生合理的调节 而前馈控制系统是根据扰动或给定值的变化按补偿原理来工作的控制系统 其特点是当扰动产生后 被控变量还未变化以前 根据扰动作用的大小进行控制 以补偿扰
  • 端口被占用怎么办?关闭占用端口的进程

    当你发现某个端口被占用时 但不知道是哪一个进程占用了端口 需要关闭占用该端口的进程 1 启动系统命令行 windows系统 win r 2 输入命令 netstat ano 可查看所有端口的使用情况 netstat aon findstr
  • 基于MATLAB的模糊pi控制器的设计

    基于MATLAB的模糊pi控制器的设计 模糊规则隶属函数的建立 a newfis fuzzypid 添加第一个输入变量e a addvar a input e 1 1 a addmf a input 1 N zmf 1 1 3 a addm
  • [经验] 轻松解读PID控制算法的三种参数的自整定方法

    轻松解读PID控制算法的三种参数的自整定方法 机器人论坛 电子技术论坛 广受欢迎的专业电子论坛 elecfans com
  • PID控制算法(PID控制原理与程序流程)

    PID控制算法 PID控制原理与程序流程 暗影玄极 博客园 cnblogs com
  • docker和主机之间的PID映射

    docker 命名空间与主机命名空间有何不同以及 pid 如何在这两者之间映射 谁能给我一个想法 有助于使用源代码在主机和 docker 之间映射 pid 的简单方法 您可以在中找到映射 proc PID status文件 它包含这样一行
  • 如何获取生成的 java 进程的 PID

    我正在编写几个 java 程序 在完成我想做的任何事情后 需要在单独的 JVM 中杀死 清理 为此 我需要获取我正在创建的 java 进程的 PID jps l可在 Windows 和 Unix 上运行 您可以使用 java 程序调用此命令
  • 在Qt中fork后获取进程的PID

    我正在创建一个成功分叉的 Qt C 控制台应用程序 当我在 fork 之前调用 QCoreApplication applicationPid 然后在 fork 之后 在子进程中 调用 QCoreApplication applicatio
  • 将手柄传递到管道中

    说我有 node foo js node bar js 有没有办法将 foo 的标准输入句柄传递给 bar js 我有一个罕见的情况 我想在管道中进行向后通信 至少我知道我可以发送node bar js的pidnode foo js 鉴于
  • 以编程方式获取另一个进程的父进程pid?

    我尝试谷歌 但发现getppid 它获取的父pidcurrent过程 我需要类似的东西getppid some other pid 有这样的事吗 基本上获取某个进程的 pid 并返回父进程的 pid 我认为最简单的事情是打开 proc 并解
  • 识别 DNS​​ 请求的 PID 源 (Windows XP)

    我希望确定发出 DNS 请求的进程 查看查询给了我一个线索 但并不能帮助我确定确切的过程 我可以在 Wireshark 中看到本地端口号 但请求太短暂 无法被 TCPView 接收 有没有可以捕获 DNS 请求和 PID 的日志记录工具 过
  • 打印 pid_t 的正确 printf 说明符是什么

    我目前正在使用显式转换为 long 并使用 ld用于印刷pid t 是否有一个说明符 例如 z for size t for pid t 如果不是最好的打印方式是什么pid t 没有这样的说明符 我认为你在做什么 铸造pid t to lo
  • 确定监听某个端口的进程pid

    正如标题所示 我正在运行多个游戏服务器 并且每个服务器都有相同的name但不同PID和port数字 我想匹配PID正在监听某个端口的服务器 然后我想终止这个进程 我需要它来完成我的 bash 脚本 这可能吗 因为在网上还没有找到解决方案 您
  • 如何查看linux中特定进程每5秒的内存消耗情况

    我只是想知道如何找到特定进程在特定时间 比如5秒 的内存消耗 我是linux新手 因此 详细的步骤将不胜感激 Use top p PID其中 PID 是进程 ID 应显示有关进程的信息 包括使用的系统内存百分比 类型d以及一个以秒为单位的整
  • PID和TID的区别

    PID和TID有什么区别 标准答案是 PID 用于进程 而 TID 用于线程 然而 我发现有些命令可以互换使用它们 例如 htop有一个 PID 列 其中显示同一进程的线程的 PID 具有不同的值 那么PID什么时候代表线程或进程呢 这很复
  • 在 python 中,是否有跨平台的方法来确定哪个进程正在侦听给定端口?

    在linux下 我可以使用lsof i如以下函数所示 def FindProcessUsingPort portnum import os fp os popen lsof i s portnum lines fp readlines fp
  • Mysql 连接到服务器:用户 root@localhost 的访问被拒绝

    edit9 是否有可能我只是缺少文件夹的一些权限 我真的非常非常感谢更多的建议 edit3 由于这篇文章没有得到足够的回复 而且这绝对是至关重要的 我尽快完成这件事 我重建了我的帖子以显示我认为到目前为止我已经扣除的内容 注意 通过许多不同

随机推荐