์ด๋ฒ์๋ ์ ๊ตญ๊ณผํ์ ๋ํ์ ์ง์ถ ํ ๋ค์ ๊ฐ์ฅ ๋ง์ ์๊ฐ์ ๋ค์ฌ์ ๊ณต๋ถํ๋ PID์ ๋ํด์ ๊ธ์ ์จ๋ณด๊ณ ์ ํ๋ค.
๋จผ์ , ๋๋ ์ผ๋ฐ์ ์ผ๋ก ๊ทธ๋ฐ ์๊ณ ๋ฆฌ์ฆ์ ์ ํ ๋ชจ๋ฅด๋ ์ํฉ์ด์๋ค. ๊ทธ๋ฐ๋ฐ ์ค์ค์ด๋ก ์ธํด์ ์๋์ ์ด๋ฅผ ํด์ผ ํ๋ ์ํฉ์ด ์ค๊ฒ ๋์๊ณ , ๊ฒฐ๊ตญ pid๋ฅผ ๊ณต๋ถํ๊ธฐ ์์ํ๋ค.
1. ์ ์
PID์ ์ด๋ ๋น๋ก-์ ๋ถ-๋ฏธ๋ถ ์ ์ด๊ธฐ(Proportional-Integral-Differential controller)๋ก ์ค์ ์์ฉ๋ถ์ผ์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ๋ํ์ ์ธ ํํ์ ์ ์ด๊ธฐ๋ฒ์ด๋ค.
๋ค์๊ณผ ๊ฐ์ ๊ทธ๋ฆผ์ ๋ชจ์ต์ผ๋ก ์ด๋ฃจ์ด์ง๋ ์ ์ด๊ธฐ์ด๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก feedback ์ ์ด๊ธฐ์ ํํ๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, ์ ์ดํ๊ณ ์ ํ๋ ๋์์ output์ ์ธก์ ํ์ฌ ์ํ๊ณ ์ ํ๋ Set point๊ณผ ๋น๊ตํด์ error๊ฐ์ ๊ณ์ฐํ๊ณ ์ด ์ค์ฐจ๊ฐ์ ์ด์ฉํด์ ์ ์ด์ ํ์ํ ์ ์ด๊ฐ์ ๊ณ์ฐํ๋ ๊ตฌ์กฐ๋ก ๋์ด์๋ค.
์ด๋ ์ ์ด ํ๋ผ๋ฏธํฐ $K_p, K_i, K_d$๋ฅผ gain์ด๋ผ๊ณ ๋ถ๋ฅด๋ฉฐ์ด ๊ฐ์ ์ ์ ํ๊ฒ ๊ณ์ฐํ๋ ๊ณผ์ ์ tuning์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ์์ ํตํด์ ๋ชฉํ๊ฐ๊น์ง ์ ์ด๋ฅผ ์งํํ๋ค.
PID์ ์ด์ ๋ํ ์ธ๋ถ์ ์ธ ์ค๋ช
P(๋น๋ก) ์ ์ด๋ ์ ์ด๋๊ณผ ๋ชฉํ๊ฐ์ ์ฐจ์ด(ํธ์ฐจ)์ ๋น๋กํ์ฌ ์ ์ดํ๋ ๋ฐฉ๋ฒ์ด๋ค.
ํธ์ฐจ๊ฐ ํฌ๋ฉด ์กฐ์๋์ ํฌ๊ฒ, ํธ์ฐจ๊ฐ ์์ผ๋ฉด ์กฐ์๋์ ์๊ฒ ํ์ฌ ์ ์ด๋์ ๋ชฉํฏ๊ฐ์ ์ ์ฐํ๊ฒ ๋๋ฌ์ํจ๋ค.
$K_p$๊ฐ์ ํฌ๊ฒ ํ๋ฉด ํธ์ฐจ์ ๋ฐ๋ฅธ ์กฐ์๋์ด ํฌ๋ฏ๋ก, ์์น์๊ฐ์ด ์ค์ด ๋น ๋ฅด๊ฒ ๋ชฉํฏ๊ฐ์ ๋๋ฌํ๋ ๋์ , ์ค๋ฒ์ํธ ๊ฐ์ด ํฌ๊ณ ์์คํ ์ ๋ฌด๋ฆฌ๋ฅผ ์ค ์ํ์ด ์๋ค.
๋ฐ๋๋ก $K_p$์ ์๊ฒ ํ๋ฉด ์์น์๊ฐ์ ๊ธธ์ด์ ธ ๋ชฉํ๊ฐ์ ๋๋ฆฌ๊ฒ ๋๋ฌํ๋ค.
P์ ์ด์ ๋ฌธ์ ์ ์ ๋ชฉํ์น์ ๊ฐ๊น์์ง ์๋ก ํธ์ฐจ๊ฐ ์์์ ธ ์ธ๋ฐํ ์ ์ด๊ฐ ํ๋ค๊ฒ ๋๋ค๋ ์ ์ด๋ค.
I(์ ๋ถ) ์ ์ด๋ ํธ์ฐจ๋ฅผ ์๊ฐ์ ๋ํด ๋์ (์ ๋ถ)ํ๊ณ , ์ด ๋์ ๊ฐ์ด ํน์ ๊ฐ์ด ๋๋ฉด ์กฐ์๋์ ์ฆ๊ฐ์์ผ ํธ์ฐจ๋ฅผ ์์ฐ์ผ๋ก์จ ๋ชฉํฏ๊ฐ์ ๋์ฑ ์ ๋ฐํ๊ฒ ์ ๊ทผํ๋๋ก ํ๋ค.
$K_i$๊ฐ์ด ํฌ๋ฉด ์ค๋ฒ์ํธ๊ฐ ์ปค์ง๊ณ ์์น์๊ฐ์ด ์ฝ๊ฐ ๊ฐ์ํ๋ค.
$K_i$๊ฐ์ด ์์ผ๋ฉด ๊ทธ ๋ฐ๋๊ฐ ๋๋ค. I ์ ์ด์ ๋ฌธ์ ์ ์ ๋ฐ์์๋(์๋ต์๊ฐ)๊ฐ ๋๋ ค์ง๋ ๊ฒ์ด๋ค.
D(๋ฏธ๋ถ) ์ ์ด๋ ๋ชฉํ๋๊ณผ ์ ์ด๋์ ํธ์ฐจ๋ฅผ ๋น๊ตํด ์ด์ ๋ฐ๋๋๋ ์ชฝ(๊ธฐ์ธ๊ธฐ)์ผ๋ก ์กฐ์ํ๋ ๋ฐฉ์์ด๋ค.
๊ธ๊ฒฉํ ์ด๋ ํ ๋ฐฉํฅ์ผ๋ก ํ์ด ์ ๋ฆฌ๋ฉด ๋ฐ๋๋ฐฉํฅ์ผ๋ก ํ์ ์ฃผ์ด ๊ทธ ํ์ ์์ํ๋ ค๋ ์ ์ด์ด๋ค.
์ธ๋๊ณผ ๋ชฉํ๊ฐ์ ํธ์ฐจ, ํน์ ์ด๋ฒ ํธ์ฐจ์ ์ง์ ํธ์ฐจ๋ฅผ ๋น๊ตํด ์ด ํฌ๊ธฐ์ ๋ฐ๋ผ ์กฐ์๋์ ๊ฒฐ์ ํ๋ฉฐ, D ์ ์ด๋ฅผ ํตํด ์ธ๋์ ๋ํ ์๋ต์ฑ์ ๊ฐ์ ํ๊ณ ์์ ์ฑ์ ๋์ผ ์ ์๋ค.
$K_d$๊ฐ์ด ํด์๋ก ์์ ํ์ ๊ฑธ๋ฆฌ๋ ์๊ฐ์ด ์ค์ด๋ค์ด ์ ์ฐฉ์๊ฐ์ด ๊ฐ์๋๋ค.
2. ์ค์ค์ด์ PID์ ๊ด๊ณ
์ค์ค์ด๋ ๋ชจํฐ๋ฅผ ์์ง์ฌ์ ์๋ค๋ก ์ด๋ํ๋ ๋ฅ๋ ฅ์ ํ ๋๋ก ์ผ์ ๊ฐ๋๋ฅผ ๋ง์ถฐ์ผ ํ๋ค.
์ด๋ฌํ ๋ถ๋ถ์์ ์ํ๋ ๊ฐ๋๋ฅผ set ํ๊ณ , imu๋ฅผ ์ด์ฉํด์ ํ์ฌ ๊ฐ๋๋ฅผ ๊ตฌํ๋ค๋ฉด error ๊ฐ์ ๊ตฌํ ์ ์๊ฒ ๋๋ค.
์ดํ ์ด e ๊ฐ์ ์ต์ํํ๋ ๋ฐฉํฅ์ผ๋ก ๋ง๋ค์ด ๊ฐ๋๋ฅผ ์กฐ์ ํ๊ธฐ ์ํด์ PID ์ ์ด๋ฅผ ์ด์ฉํ๋ฉด ์ํ๋ ๊ฐ๋๋ฅผ ๋ง๋ค ์ ์๋ค๋ ๊ฐ์ค์ ์ธ์ฐ๊ฒ ๋์๋ค.
์ด ๋ถ๋ถ์์ encoder ๋ชจํฐ๋ฅผ ์ด์ฉํ๋ฉด ๋์ง ์์๊น? ํ๋ ์๊ฐ๋ ํด๋ณด์๋ค. ํ์ง๋ง, encoder๋ชจํฐ์ ์ฅ์ ์ ์ ํํ ์ด๋ ์ ๋๋ฅผ ์์ง์๋์ง ์๊ณ , ์ด๋ฅผ ํตํด์ ๋ณด์ ์ด ๊ฐ๋ฅํ๋ค๋ ์ ์ด๋ผ๊ณ ์๊ฐํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ์ ๋ชฉํ์ธ ๊ฐ๋ ์กฐ์ ์ ์ํด์๋ ์ค์๊ฐ ํผ๋๋ฐฑ์ด ํ์ํ์ผ๋ฏ๋ก ๊ฒฐ๊ตญ PID์ ์ด๊ฐ ํ์ํ๋ค๋ ๊ฒฐ๋ก ์ ๋๋ฌํ์๋ค.
๊ฐ์ฅ ๋จผ์ ์ฌ์ฉํ PID์ ๋ฐฉ์์ ๋ค์ ๊ธ์์ ์ด๋ฏธ ํ๋ฒ ์์ฑํ์๋ค.
PID๋ฅผ ๋ธ๋ก๊ทธ์์ ์ฌ์ฉํ, ๊ทธ๋ฆฌ๊ณ ์ ํ๋ธ์์ ์ฌ์ฉํ ์ด๊ฒ์ ๊ฒ์ ๋ผ์์ ์ฌ์ฉํ์๋ค. ํ์ง๋ง ์ด๋ ๋ด๊ฐ PID๋ฅผ ์ดํดํ์ง ์๊ณ ๊ทธ๋ฅ ์ฌ์ฉํ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํด์ ์ ๋๋ก PID๋ฅผ ์ฝ๋๋ก ์ธ์ ๋ ๊ตฌํํ๊ณ ์ถ๋ค๋ ์์ฌ์ด ์๊ฒผ๋ค. ๋ฐ๋ผ์ ๋ง์ ์์นญ์ ํตํด์ ์ฌ๋ฌ ์ฝ๋๋ฅผ ๋ฏ์ด๋ณด๊ฒ ๋์๋ค.
BlancingWii๋ผ๋ ์ฝ๋๋ฅผ ์ฐพ์๊ณ , ๊ทธ ์์ ํ์ผ์ ํ๋ํ๋ ๋ฏ์ด๋ณด๊ฒ ๋์๋ค.
๊ทธ๋ฐ๋ฐ ๋ฌธ์ ๊ฐ ์๊ฒผ์๋ค.
์ฝ๋๊ฐ 1000์ค ์ด์์ธ ๊ฒ๋ค์ด ๋ง์ ๊ฒ์ ๋ฌผ๋ก ์ด๊ณ ๋ ์ผ์ด์๋ค.
๊ทธ๋ ๊ฒ ์ ๋ 5์์ ๋๋ถํฐ ์์นจ 7์๊น์ง ์ฝ๋๋ง ํด์ํ๊ณ ์ด๋ฅผ ๊ฒฐ๊ตญ ํด์์ ํ ๊ฒฐ๊ณผ๋ ๊ต์ฅํ ํ๋ฌดํ์๋ค.
์ ๋ง ์ด๋ก ์ ์ธ ๋ด์ฉ ๊ทธ์์ฒด๊ฐ ์ฝ๋์๋ ๊ฒ์ด๋ค.
#include "Arduino.h"
#include "config.h"
#include "def.h"
#include "types.h"
#include "BalancingWii.h"
#include "IMU.h"
#include "Sensors.h"
void getEstimatedAttitude();
void computeIMU () {
uint8_t axis;
static int16_t gyroADCprevious[3] = {0,0,0};
int16_t gyroADCp[3];
int16_t gyroADCinter[3];
static uint32_t timeInterleave = 0;
//we separate the 2 situations because reading gyro values with a gyro only setup can be acchieved at a higher rate
//gyro+nunchuk: we must wait for a quite high delay betwwen 2 reads to get both WM+ and Nunchuk data. It works with 3ms
//gyro only: the delay to read 2 consecutive values can be reduced to only 0.65ms
#if defined(NUNCHUCK)
annexCode();
while((uint16_t)(micros()-timeInterleave)<INTERLEAVING_DELAY) ; //interleaving delay between 2 consecutive reads
timeInterleave=micros();
ACC_getADC();
getEstimatedAttitude(); // computation time must last less than one interleaving delay
while((uint16_t)(micros()-timeInterleave)<INTERLEAVING_DELAY) ; //interleaving delay between 2 consecutive reads
timeInterleave=micros();
f.NUNCHUKDATA = 1;
while(f.NUNCHUKDATA) ACC_getADC(); // For this interleaving reading, we must have a gyro update at this point (less delay)
for (axis = 0; axis < 3; axis++) {
// empirical, we take a weighted value of the current and the previous values
// /4 is to average 4 values, note: overflow is not possible for WMP gyro here
imu.gyroData[axis] = (imu.gyroADC[axis]*3+gyroADCprevious[axis])>>2;
gyroADCprevious[axis] = imu.gyroADC[axis];
}
#else
#if ACC
ACC_getADC();
getEstimatedAttitude();
#endif
#if GYRO
Gyro_getADC();
#endif
for (axis = 0; axis < 3; axis++)
gyroADCp[axis] = imu.gyroADC[axis];
timeInterleave=micros();
annexCode();
uint8_t t=0;
while((uint16_t)(micros()-timeInterleave)<650) t=1; //empirical, interleaving delay between 2 consecutive reads
if (!t) annex650_overrun_count++;
#if GYRO
Gyro_getADC();
#endif
for (axis = 0; axis < 3; axis++) {
gyroADCinter[axis] = imu.gyroADC[axis]+gyroADCp[axis];
// empirical, we take a weighted value of the current and the previous values
imu.gyroData[axis] = (gyroADCinter[axis]+gyroADCprevious[axis])/3;
gyroADCprevious[axis] = gyroADCinter[axis]>>1;
if (!ACC) imu.accADC[axis]=0;
}
#endif
#if defined(GYRO_SMOOTHING)
static int16_t gyroSmooth[3] = {0,0,0};
for (axis = 0; axis < 3; axis++) {
imu.gyroData[axis] = (int16_t) ( ( (int32_t)((int32_t)gyroSmooth[axis] * (conf.Smoothing[axis]-1) )+imu.gyroData[axis]+1 ) / conf.Smoothing[axis]);
gyroSmooth[axis] = imu.gyroData[axis];
}
#elif defined(TRI)
static int16_t gyroYawSmooth = 0;
imu.gyroData[YAW] = (gyroYawSmooth*2+imu.gyroData[YAW])/3;
gyroYawSmooth = imu.gyroData[YAW];
#endif
}
// **************************************************
// Simplified IMU based on "Complementary Filter"
// Inspired by http://starlino.com/imu_guide.html
//
// adapted by ziss_dm : http://www.multiwii.com/forum/viewtopic.php?f=8&t=198
//
// The following ideas was used in this project:
// 1) Rotation matrix: http://en.wikipedia.org/wiki/Rotation_matrix
// 2) Small-angle approximation: http://en.wikipedia.org/wiki/Small-angle_approximation
// 3) C. Hastings approximation for atan2()
// 4) Optimization tricks: http://www.hackersdelight.org/
//
// Currently Magnetometer uses separate CF which is used only
// for heading approximation.
//
// **************************************************
//****** advanced users settings *******************
/* Set the Low Pass Filter factor for ACC
Increasing this value would reduce ACC noise (visible in GUI), but would increase ACC lag time. */
#ifndef ACC_LPF_FACTOR
#define ACC_LPF_FACTOR 120.0f
#endif
/* Set the Gyro Weight for Gyro/Acc complementary filter
Increasing this value would reduce and delay Acc influence on the output of the filter*/
#ifndef GYR_CMPF_FACTOR
#define GYR_CMPF_FACTOR 700
#endif
/* Set the Gyro Weight for Gyro/Magnetometer complementary filter
Increasing this value would reduce and delay Magnetometer influence on the output of the filter*/
#define GYR_CMPFM_FACTOR 250
//****** end of advanced users settings *************
#define INV_GYR_CMPF_FACTOR (1.0f / (GYR_CMPF_FACTOR + 1.0f))
#define INV_GYR_CMPFM_FACTOR (1.0f / (GYR_CMPFM_FACTOR + 1.0f))
typedef struct fp_vector {
float X,Y,Z;
} t_fp_vector_def;
typedef union {
float A[3];
t_fp_vector_def V;
} t_fp_vector;
typedef struct int32_t_vector {
int32_t X,Y,Z;
} t_int32_t_vector_def;
typedef union {
int32_t A[3];
t_int32_t_vector_def V;
} t_int32_t_vector;
int16_t _atan2(int32_t y, int32_t x){
float z = (float)y / x;
int16_t a;
if ( abs(y) < abs(x) ){
a = 573 * z / (1.0f + 0.28f * z * z);
if (x<0) {
if (y<0) a -= 1800;
else a += 1800;
}
} else {
a = 900 - 573 * z / (z * z + 0.28f);
if (y<0) a -= 1800;
}
return a;
}
float InvSqrt (float x){
union{
int32_t i;
float f;
} conv;
conv.f = x;
conv.i = 0x5f3759df - (conv.i >> 1);
return 0.5f * conv.f * (3.0f - x * conv.f * conv.f);
}
// Rotate Estimated vector(s) with small angle approximation, according to the gyro data
void rotateV(struct fp_vector *v,float* delta) {
fp_vector v_tmp = *v;
v->Z -= delta[ROLL] * v_tmp.X + delta[PITCH] * v_tmp.Y;
v->X += delta[ROLL] * v_tmp.Z - delta[YAW] * v_tmp.Y;
v->Y += delta[PITCH] * v_tmp.Z + delta[YAW] * v_tmp.X;
}
static float accLPF[3] = {0, 0, ACC_1G};
static float invG; // 1/|G|
static t_fp_vector EstG;
static t_int32_t_vector EstG32;
#if MAG
static t_int32_t_vector EstM32;
static t_fp_vector EstM;
#endif
void getEstimatedAttitude(){
uint8_t axis;
int32_t accMag = 0;
float scale, deltaGyroAngle[3];
uint8_t validAcc;
static uint16_t previousT;
uint16_t currentT = micros();
scale = (currentT - previousT) * GYRO_SCALE; // GYRO_SCALE unit: radian/microsecond
previousT = currentT;
// Initialization
for (axis = 0; axis < 3; axis++) {
deltaGyroAngle[axis] = imu.gyroADC[axis] * scale; // radian
accLPF[axis] = accLPF[axis] * (1.0f - (1.0f/ACC_LPF_FACTOR)) + imu.accADC[axis] * (1.0f/ACC_LPF_FACTOR);
imu.accSmooth[axis] = accLPF[axis];
accMag += (int32_t)imu.accSmooth[axis]*imu.accSmooth[axis] ;
}
rotateV(&EstG.V,deltaGyroAngle);
#if MAG
rotateV(&EstM.V,deltaGyroAngle);
#endif
accMag = accMag*100/((int32_t)ACC_1G*ACC_1G);
validAcc = 72 < (uint16_t)accMag && (uint16_t)accMag < 133;
// Apply complimentary filter (Gyro drift correction)
// If accel magnitude >1.15G or <0.85G and ACC vector outside of the limit range => we neutralize the effect of accelerometers in the angle estimation.
// To do that, we just skip filter, as EstV already rotated by Gyro
for (axis = 0; axis < 3; axis++) {
if ( validAcc )
EstG.A[axis] = (EstG.A[axis] * GYR_CMPF_FACTOR + imu.accSmooth[axis]) * INV_GYR_CMPF_FACTOR;
EstG32.A[axis] = EstG.A[axis]; //int32_t cross calculation is a little bit faster than float
#if MAG
EstM.A[axis] = (EstM.A[axis] * GYR_CMPFM_FACTOR + imu.magADC[axis]) * INV_GYR_CMPFM_FACTOR;
EstM32.A[axis] = EstM.A[axis];
#endif
}
if ((int16_t)EstG32.A[2] > ACCZ_25deg)
f.SMALL_ANGLES_25 = 1;
else
f.SMALL_ANGLES_25 = 0;
// Attitude of the estimated vector
int32_t sqGX_sqGZ = sq(EstG32.V.X) + sq(EstG32.V.Z);
invG = InvSqrt(sqGX_sqGZ + sq(EstG32.V.Y));
att.angle[ROLL] = _atan2(EstG32.V.X , EstG32.V.Z);
att.angle[PITCH] = _atan2(EstG32.V.Y , InvSqrt(sqGX_sqGZ)*sqGX_sqGZ);
#if MAG
att.heading = _atan2(
EstM32.V.Z * EstG32.V.X - EstM32.V.X * EstG32.V.Z,
(EstM.V.Y * sqGX_sqGZ - (EstM32.V.X * EstG32.V.X + EstM32.V.Z * EstG32.V.Z) * EstG.V.Y)*invG );
att.heading += conf.mag_declination; // Set from GUI
att.heading /= 10;
#endif
#if defined(THROTTLE_ANGLE_CORRECTION)
cosZ = EstG.V.Z / ACC_1G * 100.0f; // cos(angleZ) * 100
throttleAngleCorrection = THROTTLE_ANGLE_CORRECTION * constrain(100 - cosZ, 0, 100) >>3; // 16 bit ok: 200*150 = 30000
#endif
}
#define UPDATE_INTERVAL 25000 // 40hz update rate (20hz LPF on acc)
#define BARO_TAB_SIZE 21
#define ACC_Z_DEADBAND (ACC_1G>>5) // was 40 instead of 32 now
#define applyDeadband(value, deadband) \
if(abs(value) < deadband) { \
value = 0; \
} else if(value > 0){ \
value -= deadband; \
} else if(value < 0){ \
value += deadband; \
}
#if BARO
uint8_t getEstimatedAltitude(){
int32_t BaroAlt;
static float baroGroundTemperatureScale,logBaroGroundPressureSum;
static float vel = 0.0f;
static uint16_t previousT;
uint16_t currentT = micros();
uint16_t dTime;
dTime = currentT - previousT;
if (dTime < UPDATE_INTERVAL) return 0;
previousT = currentT;
if(calibratingB > 0) {
logBaroGroundPressureSum = log(baroPressureSum);
baroGroundTemperatureScale = (baroTemperature + 27315) * 29.271267f;
calibratingB--;
}
// baroGroundPressureSum is not supposed to be 0 here
// see: https://code.google.com/p/ardupilot-mega/source/browse/libraries/AP_Baro/AP_Baro.cpp
BaroAlt = ( logBaroGroundPressureSum - log(baroPressureSum) ) * baroGroundTemperatureScale;
alt.EstAlt = (alt.EstAlt * 6 + BaroAlt * 2) >> 3; // additional LPF to reduce baro noise (faster by 30 µs)
#if (defined(VARIOMETER) && (VARIOMETER != 2)) || !defined(SUPPRESS_BARO_ALTHOLD)
//P
int16_t error16 = constrain(AltHold - alt.EstAlt, -300, 300);
applyDeadband(error16, 10); //remove small P parametr to reduce noise near zero position
BaroPID = constrain((conf.pid[PIDALT].P8 * error16 >>7), -150, +150);
//I
errorAltitudeI += conf.pid[PIDALT].I8 * error16 >>6;
errorAltitudeI = constrain(errorAltitudeI,-30000,30000);
BaroPID += errorAltitudeI>>9; //I in range +/-60
// projection of ACC vector to global Z, with 1G subtructed
// Math: accZ = A * G / |G| - 1G
int16_t accZ = (imu.accSmooth[ROLL] * EstG32.V.X + imu.accSmooth[PITCH] * EstG32.V.Y + imu.accSmooth[YAW] * EstG32.V.Z) * invG;
static int16_t accZoffset = 0;
if (!f.ARMED) {
accZoffset -= accZoffset>>3;
accZoffset += accZ;
}
accZ -= accZoffset>>3;
applyDeadband(accZ, ACC_Z_DEADBAND);
static int32_t lastBaroAlt;
//int16_t baroVel = (alt.EstAlt - lastBaroAlt) * 1000000.0f / dTime;
int16_t baroVel = (alt.EstAlt - lastBaroAlt) * (1000000 / UPDATE_INTERVAL);
lastBaroAlt = alt.EstAlt;
baroVel = constrain(baroVel, -300, 300); // constrain baro velocity +/- 300cm/s
applyDeadband(baroVel, 10); // to reduce noise near zero
// Integrator - velocity, cm/sec
vel += accZ * ACC_VelScale * dTime;
// apply Complimentary Filter to keep the calculated velocity based on baro velocity (i.e. near real velocity).
// By using CF it's possible to correct the drift of integrated accZ (velocity) without loosing the phase, i.e without delay
vel = vel * 0.985f + baroVel * 0.015f;
//D
alt.vario = vel;
applyDeadband(alt.vario, 5);
BaroPID -= constrain(conf.pid[PIDALT].D8 * alt.vario >>4, -150, 150);
#endif
return 1;
}
#endif //BARO
์ด ์ฝ๋๋ก ๊ธฐ์ต์ ํ๊ณ ์์ผ๋ฉฐ ๋๋ฌด ์ง๊ด์ ์ด์๊ธฐ์ ํ๋ฌดํ๋ ๊ธฐ์ต์ ๊ฐ์ง๊ณ ์๋ค.