Skip to content

Commit

Permalink
introduced a 30Hz lowpass filter for the gyroscope data
Browse files Browse the repository at this point in the history
to make the numerical derivative term usable and hence the quad more stable
  • Loading branch information
MaEtUgR committed Apr 18, 2016
1 parent 9cd3d7f commit 12a4e1a
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 29 deletions.
44 changes: 44 additions & 0 deletions Filter/LowPassFilter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "LowPassFilter.h"

void LowPassFilter::set_cutoff_frequency(float sample_freq, float cutoff_freq)
{
_cutoff_freq = cutoff_freq;
if (_cutoff_freq <= 0.0f) {
// no filtering
return;
}
float fr = sample_freq/_cutoff_freq;
float ohm = tan(M_PI_F/fr);
float c = 1.0f+2.0f*cos(M_PI_F/4.0f)*ohm + ohm*ohm;
_b0 = ohm*ohm/c;
_b1 = 2.0f*_b0;
_b2 = _b0;
_a1 = 2.0f*(ohm*ohm-1.0f)/c;
_a2 = (1.0f-2.0f*cos(M_PI_F/4.0f)*ohm+ohm*ohm)/c;
}

float LowPassFilter::apply(float sample)
{
if (_cutoff_freq <= 0.0f) {
// no filtering
return sample;
}
// do the filtering
float delay_element_0 = sample - _delay_element_1 * _a1 - _delay_element_2 * _a2;
if (isnan(delay_element_0) || isinf(delay_element_0)) {
// don't allow bad values to propagate via the filter
delay_element_0 = sample;
}
float output = delay_element_0 * _b0 + _delay_element_1 * _b1 + _delay_element_2 * _b2;

_delay_element_2 = _delay_element_1;
_delay_element_1 = delay_element_0;

// return the value. Should be no need to check limits
return output;
}

float LowPassFilter::reset(float sample) {
_delay_element_1 = _delay_element_2 = sample;
return apply(sample);
}
90 changes: 90 additions & 0 deletions Filter/LowPassFilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/****************************************************************************
*
* Copyright (C) 2012 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/

/// @file LowPassFilter.h
/// @brief A class to implement a second order low pass filter
/// Author: Leonard Hall <[email protected]>
/// Adapted for PX4 by Andrew Tridgell

#ifndef LOWPASSFILTER_H_
#define LOWPASSFILTER_H_

#include "mbed.h"
#define M_PI_F 3.14159265358979323846f

class LowPassFilter
{
public:
// constructor
LowPassFilter(float sample_freq, float cutoff_freq) {
// set initial parameters
set_cutoff_frequency(sample_freq, cutoff_freq);
_delay_element_1 = _delay_element_2 = 0;
}

/**
* Change filter parameters
*/
void set_cutoff_frequency(float sample_freq, float cutoff_freq);

/**
* Add a new raw value to the filter
*
* @return retrieve the filtered result
*/
float apply(float sample);

/**
* Return the cutoff frequency
*/
float get_cutoff_freq(void) const {
return _cutoff_freq;
}

/**
* Reset the filter state to this value
*/
float reset(float sample);

private:
float _cutoff_freq;
float _a1;
float _a2;
float _b0;
float _b1;
float _b2;
float _delay_element_1; // buffered sample -1
float _delay_element_2; // buffered sample -2
};

#endif
14 changes: 11 additions & 3 deletions IMU/MPU9250/MPU9250.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ MPU9250::MPU9250(PinName MOSI, PinName MISO, PinName SCLK, PinName CS) : spi(MOS
spi.format(8,0); // setup the spi for standard 8 bit data and SPI-Mode 0
spi.frequency(5e6); // with a 5MHz clock rate

writeRegister8(MPU9250_PWR_MGMT_1, 0x80);
wait_ms(100);
writeRegister8(MPU9250_SMPLRT_DIV, 0x00);
writeRegister8(MPU9250_PWR_MGMT_1, 0x03);
wait_ms(15);
writeRegister8(MPU9250_CONFIG, 0x00);
writeRegister8(MPU9250_GYRO_CONFIG, 3 << 3);

/*
last 3 Bits of|Accelerometer(Fs=1kHz) |Gyroscope
MPU9250_CONFIG|Bandwidth(Hz)|Delay(ms)|Bandwidth(Hz)|Delay(ms)|Fs(kHz)
Expand All @@ -17,9 +25,9 @@ MPU9250::MPU9250(PinName MOSI, PinName MISO, PinName SCLK, PinName CS) : spi(MOS
5 |10 |13.8 |10 |13.4 |1
6 |5 |19.0 |5 |18.6 |1
*/
writeRegister8(MPU9250_CONFIG, 0x00);
/*writeRegister8(MPU9250_CONFIG, 0x00);
writeRegister8(MPU9250_GYRO_CONFIG, 0x18); // scales gyros range to +-2000dps
writeRegister8(MPU9250_ACCEL_CONFIG, 0x08); // scales accelerometers range to +-4g
writeRegister8(MPU9250_ACCEL_CONFIG, 0x08);*/ // scales accelerometers range to +-4g
}

uint8_t MPU9250::getWhoami() {
Expand Down Expand Up @@ -110,4 +118,4 @@ void MPU9250::writeRegister(uint8_t reg, uint8_t *buffer, int length) {
}

void MPU9250::select() { cs = 0; } // set Cable Select pin low to start SPI transaction
void MPU9250::deselect() { cs = 1; } // set Cable Select pin high to stop SPI transaction
void MPU9250::deselect() { cs = 1; } // set Cable Select pin high to stop SPI transaction
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

GCC_BIN =
PROJECT = FlyBed
OBJECTS = ./main.o ./PC/PC.o ./RemoteControl/RemoteControl.o ./RemoteControl/RC_Channel.o ./IMU/IMU_10DOF.o ./IMU/IMU_Filter/IMU_Filter.o ./IMU/MPU9250/MPU9250.o ./IMU/MPU6050/MPU6050.o ./IMU/MPU6050/I2C_Sensor.o ./LED/LED.o ./PID/PID.o ./Servo/Servo.o ./ParameterSystem/ParameterSystem.o
OBJECTS = ./main.o ./PC/PC.o ./RemoteControl/RemoteControl.o ./RemoteControl/RC_Channel.o ./IMU/IMU_10DOF.o ./IMU/IMU_Filter/IMU_Filter.o ./IMU/MPU9250/MPU9250.o ./IMU/MPU6050/MPU6050.o ./IMU/MPU6050/I2C_Sensor.o ./LED/LED.o ./PID/PID.o ./Servo/Servo.o ./ParameterSystem/ParameterSystem.o ./Filter/LowPassFilter.o
SYS_OBJECTS = ./mbed/TARGET_LPC1768/TOOLCHAIN_GCC_ARM/board.o ./mbed/TARGET_LPC1768/TOOLCHAIN_GCC_ARM/cmsis_nvic.o ./mbed/TARGET_LPC1768/TOOLCHAIN_GCC_ARM/retarget.o ./mbed/TARGET_LPC1768/TOOLCHAIN_GCC_ARM/startup_LPC17xx.o ./mbed/TARGET_LPC1768/TOOLCHAIN_GCC_ARM/system_LPC17xx.o
INCLUDE_PATHS = -I. -I./PC -I./RemoteControl -I./IMU -I./IMU/IMU_Filter -I./IMU/MPU9250 -I./IMU/MPU6050 -I./LED -I./PID -I./Servo -I./ParameterSystem -I./mbed -I./mbed/TARGET_LPC1768 -I./mbed/TARGET_LPC1768/TARGET_NXP -I./mbed/TARGET_LPC1768/TARGET_NXP/TARGET_LPC176X -I./mbed/TARGET_LPC1768/TARGET_NXP/TARGET_LPC176X/TARGET_MBED_LPC1768 -I./mbed/TARGET_LPC1768/TOOLCHAIN_GCC_ARM
INCLUDE_PATHS = -I. -I./PC -I./RemoteControl -I./IMU -I./IMU/IMU_Filter -I./IMU/MPU9250 -I./IMU/MPU6050 -I./LED -I./PID -I./Servo -I./ParameterSystem -I./Filter -I./mbed -I./mbed/TARGET_LPC1768 -I./mbed/TARGET_LPC1768/TARGET_NXP -I./mbed/TARGET_LPC1768/TARGET_NXP/TARGET_LPC176X -I./mbed/TARGET_LPC1768/TARGET_NXP/TARGET_LPC176X/TARGET_MBED_LPC1768 -I./mbed/TARGET_LPC1768/TOOLCHAIN_GCC_ARM
LIBRARY_PATHS = -L./mbed/TARGET_LPC1768/TOOLCHAIN_GCC_ARM
LIBRARIES = -lmbed
LINKER_SCRIPT = ./mbed/TARGET_LPC1768/TOOLCHAIN_GCC_ARM/LPC1768.ld
Expand Down
4 changes: 2 additions & 2 deletions PID/PID.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#include "mbed.h"

#define BUFFERSIZE 5
#define BUFFERSIZE 10

class PID {
public:
Expand All @@ -31,4 +31,4 @@ class PID {
int RollBufferIndex;
};

#endif
#endif
82 changes: 60 additions & 22 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/ \ V |
m1 m0 \
ROLL PITCH */

#include "mbed.h"
#include "LED.h" // LEDs framework for blinking ;)
#include "PC.h" // Serial Port via USB by Roland Elmiger for debugging with Terminal (driver needed: https://mbed.org/media/downloads/drivers/mbedWinSerial_16466.exe)
Expand All @@ -13,6 +14,8 @@
#include "PID.h" // PID Library (slim, self written)
#include "Servo.h" // Motor PPM using any DigitalOut Pin

#include "LowPassFilter.h"

#define PPM_FREQU 495 // Hz Frequency of PPM Signal for ESCs (maximum <500Hz)
#define INTEGRAL_MAX 300 // maximal output offset that can result from integrating errors
#define ROLL 0 // Axes
Expand All @@ -23,8 +26,8 @@

bool debug = true; // shows if we want output for the computer
bool level = false; // switches between self leveling and acro mode
float P_R = 2.6, I_R = 0.3, D_R = 0; // PID values for the rate controller
float P_A = 1.9, I_A = 0.2, D_A = 0; // PID values for the angle controller P_A = 1.865, I_A = 1.765, D_A = 0
float P_R = 6.0, I_R = 0.3, D_R = 0.1; // PID values for the rate controller
float P_A = 1.7, I_A = 0.2, D_A = 0.07; // PID values for the angle controller P_A = 1.9, I_A = 0.2, D_A = 0
float PY = 2.3, IY = 0, DY = 0; // PID values for Yaw
float Motor_speed[4] = {0,0,0,0}; // Mixed Motorspeeds, ready to send
bool toRCCalibrate = false;
Expand All @@ -36,14 +39,19 @@ int counter = 0;
int divider = 20;

LED LEDs;
PC pc(USBTX, USBRX, 115200); // USB
//PC pc(p9, p10, 115200); // Bluetooth PIN: 1234
//PC pc(USBTX, USBRX, 115200); // USB
PC pc(p9, p10, 115200); // Bluetooth PIN: 1234
IMU_10DOF IMU(p5, p6, p7, p19, p28, p27);
RemoteControl RC; // no p19/p20 !
PID Controller_Rate[] = {PID(P_R, I_R, D_R, INTEGRAL_MAX), PID(P_R, I_R, D_R, INTEGRAL_MAX), PID(PY, IY, DY, INTEGRAL_MAX)}; // 0:X:Roll 1:Y:Pitch 2:Z:Yaw
PID Controller_Angle[] = {PID(P_A, I_A, D_A, INTEGRAL_MAX), PID(P_A, I_A, D_A, INTEGRAL_MAX), PID(0, 0, 0, INTEGRAL_MAX)};
Servo ESC[] = {Servo(p21,PPM_FREQU), Servo(p22,PPM_FREQU), Servo(p23,PPM_FREQU), Servo(p24,PPM_FREQU)}; // use any DigitalOit Pin

float gyro_filter_frequ = 30;
LowPassFilter gyro_filter[] = {LowPassFilter(control_frequency, gyro_filter_frequ), LowPassFilter(control_frequency, gyro_filter_frequ), LowPassFilter(control_frequency, gyro_filter_frequ)};
float RC_filter_frequ = 5;
LowPassFilter RC_filter[] = {LowPassFilter(control_frequency, RC_filter_frequ), LowPassFilter(control_frequency, RC_filter_frequ), LowPassFilter(control_frequency, RC_filter_frequ)};

extern "C" void mbed_reset();

void loop() { // TODO: Times!!
Expand All @@ -69,13 +77,13 @@ void loop() { // TODO: Times!!
if (counter % 16 == 0)
Controller_Angle[i].compute(RC._angle[i], IMU.angle[i]); // give the controller the actual angles and get his advice to correct
Controller_Rate[i].setIntegrate(RC.armed()); // only integrate in controller when armed, so the value is not totally odd from not flying
Controller_Rate[i].compute(-Controller_Angle[i].Value, /*IMU.mpu2.data_gyro[i]*/IMU.mpu.Gyro[i]); // give the controller the actual gyro values and get his advice to correct
Controller_Rate[i].compute(-Controller_Angle[i].Value, /*IMU.mpu2.data_gyro[i]*/gyro_filter[i].apply(IMU.mpu.Gyro[i])); // give the controller the actual gyro values and get his advice to correct
//Controller_Rate[i].compute(-Controller_Angle[i].Value, (IMU.mpu2.data_gyro[i] + IMU.mpu.Gyro[i])/2 );
}
} else {
for(int i=0;i<2;i++) { // ACRO
Controller_Rate[i].setIntegrate(RC.armed()); // only integrate in controller when armed, so the value is not totally odd from not flying
Controller_Rate[i].compute((RC[i]-500.0)*100.0/500.0, /*IMU.mpu2.data_gyro[i]*/IMU.mpu.Gyro[i]); // give the controller the actual gyro values and get his advice to correct
Controller_Rate[i].compute(RC_filter[i].apply((RC[i]-500.0)*100.0/500.0), /*IMU.mpu2.data_gyro[i]*/gyro_filter[i].apply(IMU.mpu.Gyro[i])); // give the controller the actual gyro values and get his advice to correct
//Controller_Rate[i].compute((RC[i].read()-500.0)*100.0/500.0, (IMU.mpu2.data_gyro[i] + IMU.mpu.Gyro[i])/2 );
}
}
Expand All @@ -86,7 +94,7 @@ void loop() { // TODO: Times!!
else
Controller_Rate[2].compute(0, IMU.mpu.Gyro[2]); // give the controller the actual gyro values and get his advice to correct

float throttle = 100 + (RC[THROTTLE] * 500 / 1000); // power limitation to 60% TODO: better throttle control, so we don't need this
float throttle = 100 + (RC[THROTTLE] * 700 / 1000); // power limitation to 60% TODO: better throttle control, so we don't need this
Times[4] = LoopTimer.read(); // 53us

// Mixing
Expand All @@ -96,20 +104,21 @@ void loop() { // TODO: Times!!
Motor_speed[3] = throttle +SQRT2*Controller_Rate[ROLL].Value +SQRT2*Controller_Rate[PITCH].Value +Controller_Rate[YAW].Value;
Times[5] = LoopTimer.read(); // 17us

if(0) { // for SECURITY!
if(RC.armed()) { // for SECURITY!
debug = false;
// PITCH
//ESC[0] = (int)Motor_speed[0]>50 ? (int)Motor_speed[0] : 50;
//ESC[2] = (int)Motor_speed[2]>50 ? (int)Motor_speed[2] : 50;
// ROLL
//ESC[1] = (int)Motor_speed[1]>50 ? (int)Motor_speed[1] : 50;
//ESC[3] = (int)Motor_speed[3]>50 ? (int)Motor_speed[3] : 50;
#if 1
for(int i=0;i<4;i++) // Set new motorspeeds
ESC[i] = (int)Motor_speed[i]>100 ? (int)Motor_speed[i] : 100;
#else
//ESC[0] = (int)Motor_speed[0]>50 ? (int)Motor_speed[0] : 50; // right diagonal
//ESC[2] = (int)Motor_speed[2]>50 ? (int)Motor_speed[2] : 50;
ESC[1] = (int)Motor_speed[1]>50 ? (int)Motor_speed[1] : 50; // left diagonal
ESC[3] = (int)Motor_speed[3]>50 ? (int)Motor_speed[3] : 50;
#endif
} else {
for(int i=0;i<4;i++) // for security reason, set every motor to zero speed
ESC[i] = 0;
debug = true;
//debug = true;
}
Times[6] = LoopTimer.read(); // 6us

Expand All @@ -118,7 +127,7 @@ void loop() { // TODO: Times!!
counter++;

Times[7] = LoopTimer.read(); // 7us TOTAL 297us
while(LoopTimer.read() < 1/control_frequency); // Kill the rest of the time TODO: make a better solution so we can do misc things with these cycles
//while(LoopTimer.read() < 1/control_frequency); // Kill the rest of the time TODO: make a better solution so we can do misc things with these cycles
Times[8] = LoopTimer.read();
LoopTimer.stop();
LoopTimer.reset();
Expand All @@ -138,14 +147,30 @@ void loop() { // TODO: Times!!
//pc.printf("$ACC,%.3f,%.3f,%.3f\r\n", IMU.mpu.Acc[ROLL], IMU.mpu.Acc[PITCH], IMU.mpu.Acc[YAW]);
//pc.printf("$ANG,%.3f,%.3f,%.3f\r\n", IMU.angle[ROLL], IMU.angle[PITCH], IMU.angle[YAW]);
pc.printf("$RCANG,%.3f,%.3f,%.3f\r\n", RC._angle[ROLL], RC._angle[PITCH], RC._angle[YAW]);
//pc.printf("$CONTR,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f\r\n", Controller_Rate[ROLL].Value, Controller_Rate[PITCH].Value, Controller_Rate[YAW].Value, P_R, I_R, D_R, PY);
//pc.printf("$CONTA,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f\r\n", Controller_Angle[ROLL].Value, Controller_Angle[PITCH].Value, Controller_Angle[YAW].Value, P_A, I_A, D_A);
pc.printf("$CONTR,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f\r\n", Controller_Rate[ROLL].Value, Controller_Rate[PITCH].Value, Controller_Rate[YAW].Value, P_R, I_R, D_R, PY);
//pc.printf("$FILTER,%.3f,%.3f,%.3f\r\n", gyro_filter[ROLL].get_cutoff_freq(), gyro_filter[PITCH].get_cutoff_freq(), gyro_filter[YAW].get_cutoff_freq());
pc.printf("$CONTA,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f\r\n", Controller_Angle[ROLL].Value, Controller_Angle[PITCH].Value, Controller_Angle[YAW].Value, P_A, I_A, D_A);
//pc.printf("$MOT,%d,%d,%d,%d\r\n", (int)Motor_speed[0], (int)Motor_speed[1], (int)Motor_speed[2], (int)Motor_speed[3]);
/*pc.printf("$TIMES");
for(int i = 1; i < 10; i++)
pc.printf(",%.3f", (Times[i]-Times[i-1])*1e6);
pc.printf("\r\n");*/
wait(0.1);
} else {
/*int toplot = (int)(IMU.mpu.Gyro[1])*10;
for(int i = 0; i < toplot+200; i++)
pc.putc('=');
pc.putc('\r');
pc.putc('\n');*/
/*pc.putc('#');
pc.putc('!');
pc.putc('#');
float tosend = IMU.mpu.Gyro[1];
//float tosend = gyro_filter.apply(IMU.mpu.Gyro[1]);
//float tosend = gyro_filter.apply((RC[ELEVATOR]-500.0)*100.0/500.0);
for(unsigned int i = 0; i < sizeof(tosend); i++)
pc.putc(((char*)&tosend)[i]);*/
}
}

Expand All @@ -162,18 +187,31 @@ void executer() {
case 's': P_R -= 0.1; break;
case 'e': I_R += 0.1; break;
case 'd': I_R -= 0.1; break;
case 'x': D_R += 0.001; break;
case 'c': D_R -= 0.001; break;
case 'x': D_R += 0.01; break;
case 'c': D_R -= 0.01; break;
case 'r': P_A += 0.1; break;
case 'f': P_A -= 0.1; break;
case 't': I_A += 0.1; break;
case 'g': I_A -= 0.1; break;
case 'z': PY += 0.1; break;
case 'h': PY -= 0.1; break;
case 'z': D_A += 0.01; break;
case 'h': D_A -= 0.01; break;
case 'u': PY += 0.1; break;
case 'j': PY -= 0.1; break;
case 'o': control_frequency += 100; break;
case 'l': control_frequency -= 100; break;
case '?': toRCCalibrate = true; break;
case '\'': RC.enableStickCentering(); break;

/*case 'b': {
gyro_filter_frequ += 5;
for(int i=0;i<3;i++)
gyro_filter[i].set_cutoff_frequency(control_frequency, gyro_filter_frequ);
} break;
case 'v': {
gyro_filter_frequ -= 5;
for(int i=0;i<3;i++)
gyro_filter[i].set_cutoff_frequency(control_frequency, gyro_filter_frequ);
} break;*/
}
pc.putc(command);
LEDs.tilt(2);
Expand Down

0 comments on commit 12a4e1a

Please sign in to comment.