微型四旋翼飛行器的設(shè)計(jì)與制作
PID控制:
以下是筆者的PID控制代碼:
- void
Quadrotor_Control(const float Exp_Pitch, const float Exp_Roll, const float Exp_Yaw) - {
s16 outputPWM_Pitch, outputPWM_Roll, outputPWM_Yaw; // --- 得到當(dāng)前系統(tǒng)的誤差-->利用期望角度減去當(dāng)前角度 g_Attitude_Error.g_Error_Pitch = Exp_Pitch - g_Pitch; g_Attitude_Error.g_Error_Roll = Exp_Roll - g_Roll; g_Attitude_Error.g_Error_Yaw = Exp_Yaw - g_Yaw; // --- 傾角太大,放棄控制 if (fabs(g_Attitude_Error.g_Error_Pitch) >= 55 || fabs(g_Attitude_Error.g_Error_Roll) >= 55) { PWM2_LED = 0; //藍(lán)燈亮起 PWM_Set(0, 0, 0, 0); //停機(jī) return ; } PWM2_LED = 1; //藍(lán)燈熄滅 // --- 穩(wěn)定指示燈,黃色.當(dāng)角度值小于3°時(shí),判定為基本穩(wěn)定,黃燈亮起 if (fabs(g_Attitude_Error.g_Error_Pitch) <= 3 && fabs(g_Attitude_Error.g_Error_Roll) <= 3) PWM4_LED = 0; else PWM4_LED = 1; // --- 積分運(yùn)算與積分誤差限幅 if (fabs(g_Attitude_Error.g_Error_Pitch) <= 20) //積分分離-->在姿態(tài)誤差角小于20°時(shí)引入積分 { //Pitch //累加誤差 g_Attitude_Error.g_ErrorI_Pitch += g_Attitude_Error.g_Error_Pitch; //積分限幅 if (g_Attitude_Error.g_ErrorI_Pitch >= PITCH_I_MAX) g_Attitude_Error.g_ErrorI_Pitch = PITCH_I_MAX; else if (g_Attitude_Error.g_ErrorI_Pitch <= -PITCH_I_MAX) g_Attitude_Error.g_ErrorI_Pitch = -PITCH_I_MAX; } if (fabs(g_Attitude_Error.g_Error_Roll) <= 20) { //Roll //累加誤差 g_Attitude_Error.g_ErrorI_Roll += g_Attitude_Error.g_Error_Roll; //積分限幅 if (g_Attitude_Error.g_ErrorI_Roll >= ROLL_I_MAX) g_Attitude_Error.g_ErrorI_Roll = ROLL_I_MAX; else if (g_Attitude_Error.g_ErrorI_Roll <= -ROLL_I_MAX) g_Attitude_Error.g_ErrorI_Roll = -ROLL_I_MAX; } // --- PID運(yùn)算-->這里的微分D運(yùn)算并非傳統(tǒng)意義上的利用前一次的誤差減去上一次的誤差得來 // --- 而是直接利用陀螺儀的值來替代微分項(xiàng),這樣的處理非常好,因?yàn)榍擅罾昧擞布O(shè)施,陀螺儀本身就是具有增量的效果 outputPWM_Pitch = (s16)(g_PID_Kp * g_Attitude_Error.g_Error_Pitch + - g_PID_Ki
* g_Attitude_Error.g_ErrorI_Pitch - g_PID_Kd * g_MPU6050Data_Filter.gyro_x_c); outputPWM_Roll = (s16)(g_PID_Kp * g_Attitude_Error.g_Error_Roll + - g_PID_Ki
* g_Attitude_Error.g_ErrorI_Roll - g_PID_Kd * g_MPU6050Data_Filter.gyro_y_c); outputPWM_Yaw = (s16)(g_PID_Yaw_Kp * g_Attitude_Error.g_Error_Yaw); // --- 給出PWM控制量到四個(gè)電機(jī)-->X模式控制 //特別注意,這里輸出反相了,因?yàn)檎`差是反的 g_motor1_PWM = g_BasePWM + outputPWM_Pitch + outputPWM_Roll + outputPWM_Yaw; g_motor2_PWM = g_BasePWM - outputPWM_Pitch + outputPWM_Roll - outputPWM_Yaw; g_motor3_PWM = g_BasePWM - outputPWM_Pitch - outputPWM_Roll + outputPWM_Yaw; g_motor4_PWM = g_BasePWM + outputPWM_Pitch - outputPWM_Roll - outputPWM_Yaw; // --- PWM反向清零,因?yàn)闆]有反轉(zhuǎn) if (g_motor1_PWM < 0) g_motor1_PWM = 0; if (g_motor2_PWM < 0) g_motor2_PWM = 0; if (g_motor3_PWM < 0) g_motor3_PWM = 0; if (g_motor4_PWM < 0) g_motor4_PWM = 0; // --- PWM限幅 if (g_motor1_PWM >= g_MaxPWM) g_motor1_PWM = g_MaxPWM; if (g_motor2_PWM >= g_MaxPWM) g_motor2_PWM = g_MaxPWM; if (g_motor3_PWM >= g_MaxPWM) g_motor3_PWM = g_MaxPWM; if (g_motor4_PWM >= g_MaxPWM) g_motor4_PWM = g_MaxPWM; if (g_Fly_Enable) //允許起飛,給出PWM PWM_Set(g_motor1_PWM, g_motor2_PWM, g_motor3_PWM, g_motor4_PWM); else PWM_Set(0, 0, 0, 0); //停機(jī) - }
加速度計(jì)濾波:
以下是筆者的8深度窗口滑動(dòng)濾波代碼,算法經(jīng)過優(yōu)化,減少了數(shù)組的移動(dòng)和求和運(yùn)算,利用了循環(huán)隊(duì)列的原理避免了求和運(yùn)算:
- void
IMU_Filter(void) - {
s32 resultx = 0; static s32 s_resulttmpx[ACC_FILTER_DELAY] = {0}; static u8 s_bufferCounterx = 0; static s32 s_totalx = 0; s32 resulty = 0; static s32 s_resulttmpy[ACC_FILTER_DELAY] = {0}; static u8 s_bufferCountery = 0; static s32 s_totaly = 0; s32 resultz = 0; static s32 s_resulttmpz[ACC_FILTER_DELAY] = {0}; static u8 s_bufferCounterz = 0; static s32 s_totalz = 0; //加速度計(jì)濾波 s_totalx -= s_resulttmpx[s_bufferCounterx]; //從總和中刪除頭部元素的值,履行頭部指針職責(zé) s_resulttmpx[s_bufferCounterx] = g_MPU6050Data.accel_x; //將采樣值放到尾部指針處,履行尾部指針職責(zé) s_totalx += g_MPU6050Data.accel_x; //更新總和 resultx = s_totalx / ACC_FILTER_DELAY; //計(jì)算平均值,并輸入到一個(gè)固定變量中 s_bufferCounterx++; //更新指針 if (s_bufferCounterx == ACC_FILTER_DELAY) //到達(dá)隊(duì)列邊界 s_bufferCounterx = 0; g_MPU6050Data_Filter.accel_x_f = resultx; s_totaly -= s_resulttmpy[s_bufferCountery]; s_resulttmpy[s_bufferCountery] = g_MPU6050Data.accel_y; s_totaly += g_MPU6050Data.accel_y; resulty = s_totaly / ACC_FILTER_DELAY; s_bufferCountery++; if (s_bufferCountery == ACC_FILTER_DELAY) s_bufferCountery = 0; g_MPU6050Data_Filter.accel_y_f = resulty; s_totalz -= s_resulttmpz[s_bufferCounterz]; s_resulttmpz[s_bufferCounterz] = g_MPU6050Data.accel_z; s_totalz += g_MPU6050Data.accel_z; resultz = s_totalz / ACC_FILTER_DELAY; s_bufferCounterz++; if (s_bufferCounterz == ACC_FILTER_DELAY) s_bufferCounterz = 0; g_MPU6050Data_Filter.accel_z_f = resultz; - }
基于NRF24L01的Bootloader:
以下為Bootloader中的APP函數(shù)跳轉(zhuǎn)關(guān)鍵代碼:
- void
IAP_Load_App(u32 appxaddr) - {
if(((*(vu32*)appxaddr) & 0x2FFE0000) == 0x20000000) //檢查棧頂?shù)刂肥欠窈戏?wbr /> { Jump_To_App = (IAP_FunEntrance)*(vu32*)(appxaddr + 4); //用戶代碼區(qū)第二個(gè)字為程序開始地址(復(fù)位地址)-->詳見startup.s Line61 //(vu32*)(appxaddr + 4) --> 將FLASH的首地址強(qiáng)制轉(zhuǎn)換為vu32的指針 //*(vu32*)(appxaddr + 4) --> 解引用該地址上存放的APP跳轉(zhuǎn)地址,即main函數(shù)入口 //(IAP_FunEntrance)*(vu32*)(appxaddr + 4) --> 將main函數(shù)入口地址強(qiáng)制轉(zhuǎn)換為指向函數(shù)的指針給Jump_To_App MSR_MSP(*(vu32*)appxaddr); //初始化APP堆棧指針(用戶代碼區(qū)的第一個(gè)字用于存放棧頂?shù)刂? Jump_To_App(); //跳轉(zhuǎn)到APP,執(zhí)行APP } - }
- typedef
void (*IAP_FunEntrance)(void); //定義一個(gè)指向函數(shù)的指針 - IAP_FunEntrance
Jump_To_App;
評(píng)論