📜 ⬆️ ⬇️

Alt Hold for CC3D Flight Controller at Baro

I think I will not discover America. But when I needed to stabilize in height with a baro for CC3D, there was no solution. There were options to reflash it from OpenPilot to something else, and there are such things. But I decided to go not the most standard and easy way. My solution is a controller between the receiver and the CC3D (or any other), which measures altitude and makes adjustments to the gas channel, and then sends it to the flight controller. Thus keeps the height.



How is it all going. I used the BMP085 pressure sensor - not the most accurate, but it was available. Therefore, there are significant fluctuations in the video. However, in the range of 1 meter height is kept.

BMP085 is connected via I2C to the Arduino. Inputs A4, A5 and power: the receiver is hooked to inputs 2 and 3. The output to CC3D 9 and 10: 9 is the Aux1 channel, if it is not needed, then it can be not transmitted to the flight controller.
')
In my solution, this is a two-way switch to activate hold mode. Personally, I do not use this switch further in the copter.

Skeche Arduino:
/* Alt Holt with BMP095 * by Aleksandr Stroganov 16.01.2016 * * In from receiver * PD2 - AUX1 in * PD3 - Trl in * * Out to CC3D * PB1 Aux1 out * PB2 Trl out */ #ifndef F_CPU #define F_CPU 16000000UL #endif //I2C and BMP085 #define F_I2C 400000UL //  I2C #define TWBR_VALUE (((F_CPU)/(F_I2C)-16)/2) //     #define BMP085_ADDRESS 0x77 // I2C address of BMP085 const unsigned char OSS = 3; // Oversampling Setting // BMP085 Calibration values int ac1; int ac2; int ac3; unsigned int ac4; unsigned int ac5; unsigned int ac6; int b1; int b2; int mb; int mc; int md; // b5 is calculated in bmp085GetTemperature(...), this variable is also used in bmp085GetPressure(...) // so ...Temperature(...) must be called before ...Pressure(...). long b5; // Loop Flag #define TRUE 1 #define FALSE 0 // Flight Mode #define Manual_Mode 0 #define Alt_Hold 1 volatile unsigned int cnt_rising = 0; volatile unsigned int cnt_falling = 0; volatile unsigned int cnt_thro = 0; volatile unsigned int cnt_FMD = 0; volatile unsigned int t_scale = 0; //   40 // LPF #define SamplingTime 0.025 // Control Loop Period 40Hz float CutOffFrequency = 3.0; // Hz float LPF_error = 0.0; float LPF_ee = 0.0; float LPF_ee1 = 0.0; float LPF_ww = 0.0; // PID int Thro_Neutral = 3000; // Pulse width when throttle is neutral int Thro_Deadband = 30; // throttle zero when it's in between -30~30 float A_outer_Pgain = 0.5; float A_inner_Pgain = 1.5; float A_inner_Igain = 0.25; float A_inner_Dgain = 0.0; float Hov_Thro = 0.0; int THRO_CMD_MAX = 200; int ALT_RATE_ERR_MAX = 200; int ALT_RATE_I_MAX = 200; int ALT_PID_MAX = 400; byte T_flag = 0; byte FMD_flag = 0; //   short T; // long P; // float H_alt = 0.0; float H_temp = 0.0; float Altitude = 0.0; float Altitude_cm = 0.0; float Altitude_LPF = 0.0; float ClimbRate = 0.0; float pre_Altitude_LPF = 0.0; int Thro_cmd = 0; long Thro_in = 0; float ALT_ERR = 0.0; float ALT_RATE_ERR = 0.0; float ALT_RATE_P = 0.0; float ALT_RATE_I = 0.0; float ALT_RATE_D = 0.0; float pre_ALT_RATE_ERR = 0.0; float ALT_PID = 0.0; float ALT_cont = 0.0; void Port_init() { //DDRD |= 0x00; // INT 0,1    //PORTD = 0b00001100; // INT 0,1    DDRD = (0 << DDD2)|(0 << DDD3); // INT 0,1    PORTD = (1 << PORTD2)|(1 << PORTD3); // INT 0,1    EIMSK = 0b00000011; // INT 0,1     EICRA = 0b00001111; // INT 0,1 - rising edge   } void Timer1_init() { DDRB |= 0b00000110; // OC1A, OC1B     TCCR1A = 0b10100010; // Fast PWM    TCCR1B = 0b00011010; // Fast PWM 14 mode, 8 sclaer. ICR1 = 40000; // 20ms, 50Hz period } void Timer2_init() { TCCR2A = 0b00000011; // Fast PWM, No OC output,    TCCR2B = 0b00001100; // Fast PWM 7 mode, 64 sclaer. OCR2A = 250; // 1ms, 1000Hz period TIMSK2 = 0b00000001; } ISR(INT0_vect) // Flight Mode  { if(EICRA == 0b00001111){ cnt_rising = TCNT1; EICRA = 0b00001110; } else{ cnt_falling = TCNT1; cnt_FMD = (40001 - cnt_rising + cnt_falling) % 40001; EICRA = 0b00001111; } } ISR(INT1_vect) // Throttle  { if(EICRA == 0b00001111){ cnt_rising = TCNT1; EICRA = 0b00001011; } else{ cnt_falling = TCNT1; cnt_thro = (40001 - cnt_rising + cnt_falling) % 40001; EICRA = 0b00001111; } } ISR(TIMER2_OVF_vect) // 1000   40      { t_scale ++; if(t_scale == 25){ t_scale = 0; T_flag = TRUE; } } byte FMD_check(unsigned int FMD_in) //    { byte FMD_out; if(FMD_in > 2150 && FMD_in < 2900) { FMD_out = 1; } else if(FMD_in > 2900 && FMD_in < 3700) { FMD_out = 1; } else { FMD_out = 0; //  . } return FMD_out; } void Limit_cut(float *ff, int MIN_LIMIT, int MAX_LIMIT) { if(*ff < MIN_LIMIT) { *ff = MIN_LIMIT; } else if(*ff > MAX_LIMIT) { *ff = MAX_LIMIT; } } void Limit_cut_int(int *ff, int MIN_LIMIT, int MAX_LIMIT) { if(*ff < MIN_LIMIT) { *ff = MIN_LIMIT; } else if(*ff > MAX_LIMIT) { *ff = MAX_LIMIT; } } void Limit_cut_long(long *ff, long int MIN_LIMIT, long int MAX_LIMIT) { if(*ff < MIN_LIMIT) { *ff = MIN_LIMIT; } else if(*ff > MAX_LIMIT) { *ff = MAX_LIMIT; } } void Stick_Deadband(int *Stick, int MIN_RANGE, int MAX_RANGE) { if(*Stick > MIN_RANGE && *Stick < MAX_RANGE) { *Stick = 0; } } //  I2C void TWI_Init(void) { TWBR = TWBR_VALUE; TWSR = 0x00; } // Stores all of the bmp085's calibration values into global variables // Calibration values are required to calculate temp and pressure // This function should be called at the beginning of the program void bmp085Calibration() { ac1 = bmp085ReadInt(0xAA); ac2 = bmp085ReadInt(0xAC); ac3 = bmp085ReadInt(0xAE); ac4 = bmp085ReadInt(0xB0); ac5 = bmp085ReadInt(0xB2); ac6 = bmp085ReadInt(0xB4); b1 = bmp085ReadInt(0xB6); b2 = bmp085ReadInt(0xB8); mb = bmp085ReadInt(0xBA); mc = bmp085ReadInt(0xBC); md = bmp085ReadInt(0xBE); } // Read 2 bytes from the BMP085 // First byte will be from 'address' // Second byte will be from 'address'+1 int bmp085ReadInt(unsigned char address) { unsigned char msb, lsb; TWI_Start(); TWI_Start_SLA_W( BMP085_ADDRESS, address, 0 ); TWI_Stop(); TWI_Start(); TWI_Start_SLA_R( BMP085_ADDRESS ); msb = TWI_Read_Byte( 1 ); lsb = TWI_Read_Byte( 2 ); TWI_Stop(); return (int) msb<<8 | lsb; } void TWI_Start() { /*  */ TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); while(!(TWCR & (1<<TWINT))); } char TWI_Start_SLA_R( unsigned char address_i2c ) { /*   SLA-R*/ TWDR = (address_i2c<<1)|1; TWCR = (1<<TWINT)|(1<<TWEN); while(!(TWCR & (1<<TWINT))); } char TWI_Start_SLA_W( unsigned char address_i2c, unsigned char adr, unsigned char data ) { //   SLA-W TWDR = (address_i2c<<1)|0; TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); while(!(TWCR & (1<<TWINT))); //   TWDR = adr; TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); while(!(TWCR & (1<<TWINT))); if( data ) { TWDR = data; TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); while(!(TWCR & (1<<TWINT))); } } char TWI_Read_Byte(char n) { char data; /* */ if( n > 1 ) { TWCR = (1<<TWINT)|(1<<TWEN)|(0<<TWEA); } else { TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); } while(!(TWCR & (1<<TWINT))); data = TWDR; return data; } void TWI_Stop() { /*  */ TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN); } // Read the uncompensated temperature value unsigned int bmp085ReadUT() { unsigned int ut; // Write 0x2E into Register 0xF4 // This requests a temperature reading TWI_Start(); TWI_Start_SLA_W( BMP085_ADDRESS, 0xF4, 0x2E ); TWI_Stop(); // Wait at least 4.5ms delay(5); // Read two bytes from registers 0xF6 and 0xF7 ut = bmp085ReadInt(0xF6); return ut; } // Read the uncompensated pressure value unsigned long bmp085ReadUP() { unsigned char msb, lsb, xlsb; unsigned long up = 0; // Write 0x34+(OSS<<6) into register 0xF4 // Request a pressure reading w/ oversampling setting TWI_Start(); TWI_Start_SLA_W( BMP085_ADDRESS, 0xF4, 0x34 + (OSS<<6) ); TWI_Stop(); // Wait for conversion, delay time dependent on OSS delay(2 + (3<<OSS)); // Read register 0xF6 (MSB), 0xF7 (LSB), and 0xF8 (XLSB) TWI_Start(); TWI_Start_SLA_W( BMP085_ADDRESS, 0xF6, 0 ); TWI_Stop(); TWI_Start(); TWI_Start_SLA_R( BMP085_ADDRESS ); msb = TWI_Read_Byte( 1 ); lsb = TWI_Read_Byte( 1 ); xlsb = TWI_Read_Byte( 2 ); TWI_Stop(); up = (((unsigned long) msb << 16) | ((unsigned long) lsb << 8) | (unsigned long) xlsb) >> (8-OSS); return up; } // Calculate temperature given ut. // Value returned will be in units of 0.1 deg C short bmp085GetTemperature(unsigned int ut) { long x1, x2; x1 = (((long)ut - (long)ac6)*(long)ac5) >> 15; x2 = ((long)mc << 11)/(x1 + md); b5 = x1 + x2; return ((b5 + 8)>>4); // return ((b5 + 8)/pow(2,4)); } // Calculate pressure given up // calibration values must be known // b5 is also required so bmp085GetTemperature(...) must be called first. // Value returned will be pressure in units of Pa. long bmp085GetPressure(unsigned long up) { long x1, x2, x3, b3, b6, p; unsigned long b4, b7; b6 = b5 - 4000; // Calculate B3 x1 = (b2 * (b6 * b6)>>12)>>11; x2 = (ac2 * b6)>>11; x3 = x1 + x2; b3 = (((((long)ac1)*4 + x3)<<OSS) + 2)>>2; // Calculate B4 x1 = (ac3 * b6)>>13; x2 = (b1 * ((b6 * b6)>>12))>>16; x3 = ((x1 + x2) + 2)>>2; b4 = (ac4 * (unsigned long)(x3 + 32768))>>15; b7 = ((unsigned long)(up - b3) * (50000>>OSS)); if (b7 < 0x80000000) p = (b7<<1)/b4; else p = (b7/b4)<<1; x1 = (p>>8) * (p>>8); x1 = (x1 * 3038)>>16; x2 = (-7357 * p)>>16; p += (x1 + x2 + 3791)>>4; return p; } float LPF(float input, float CutOffFrequency) { float output; LPF_error = input - LPF_ww; LPF_ee = LPF_error * CutOffFrequency; LPF_ww = LPF_ww + (LPF_ee+LPF_ee1)*SamplingTime*0.5; LPF_ee1 = LPF_ee; output = LPF_ww; return output; } void setup() { TWI_Init(); bmp085Calibration(); SREG |= 0x80; Port_init(); Timer1_init(); Timer2_init(); } void loop() { if(T_flag == TRUE) //  40 { T = bmp085GetTemperature( bmp085ReadUT() ); P = bmp085GetPressure( bmp085ReadUP() ); H_temp = (P/100.0f)/1013.25; H_alt = (1-pow(H_temp,0.190284)) * 145366.45; Altitude = 0.3048*H_alt; Altitude_cm = (float)((long)(Altitude*100)); // 1cm Altitude_LPF = LPF(Altitude_cm, CutOffFrequency); // LPF // Altitude_feedback ClimbRate = (Altitude_LPF - pre_Altitude_LPF)/SamplingTime; // ClimbRate_feedback pre_Altitude_LPF = Altitude_LPF; FMD_flag = FMD_check(cnt_FMD); // FMD   OCR1A = cnt_FMD; //  CC3D   switch(FMD_flag){ case Manual_Mode: //    OCR1B = cnt_thro; //  CC3D   Hov_Thro = cnt_thro-2000; Limit_cut(&Hov_Thro, 800, 1200); Thro_in = Altitude_LPF; ALT_RATE_I = 0.0; break; case Alt_Hold: //     Thro_cmd = 0.25 * ((int)cnt_thro-Thro_Neutral); // -250~250 means -2.5~2.5 Stick_Deadband(&Thro_cmd, -Thro_Deadband, Thro_Deadband); Limit_cut_int(&Thro_cmd, -THRO_CMD_MAX, THRO_CMD_MAX); Thro_in = Thro_in + Thro_cmd*SamplingTime; Limit_cut_long(&Thro_in, -200000000, 200000000); ALT_ERR = ((float)Thro_in) - Altitude_LPF; ALT_RATE_ERR = ALT_ERR * A_outer_Pgain - ClimbRate; Limit_cut(&ALT_RATE_ERR, -ALT_RATE_ERR_MAX, ALT_RATE_ERR_MAX); ALT_RATE_P = ALT_RATE_ERR * A_inner_Pgain; ALT_RATE_I = ALT_RATE_I + (ALT_RATE_ERR * A_inner_Igain) * SamplingTime; Limit_cut(&ALT_RATE_I, -ALT_RATE_I_MAX, ALT_RATE_I_MAX); ALT_RATE_D = (ALT_RATE_ERR - pre_ALT_RATE_ERR)/SamplingTime * A_inner_Dgain; pre_ALT_RATE_ERR = ALT_RATE_ERR; ALT_PID = ALT_RATE_P + ALT_RATE_I + ALT_RATE_D; Limit_cut(&ALT_PID, -ALT_PID_MAX, ALT_PID_MAX); ALT_cont = ALT_PID + Hov_Thro; OCR1B = 2000 + (int)(ALT_cont); break; default: break; } T_flag = FALSE; } } 


Ready solution looks like this:









Test flight (used by Arduino Uno). Altitude hold mode enabled. I only adjust the position. I apologize to the public for the pilot of me not very much, so the copter pretty shakes from side to side.



Questions, suggestions and suggestions are happy to hear.

Source: https://habr.com/ru/post/367699/


All Articles