What is the recommended method for adjusting the bandwidth of the output signals on the accelerometer and gyro breakout boards? Without being able to modify Cout on the breakout boards, I assume the best approach is an RC filter on the output? I am sampling accelerometer and gyro outputs at 50-Hz so need to set the output BW to approx 25-Hz. It seems the breakout boards have Cout of 2.2nF which sets BW to approx 350-Hz.
I’m a newbie, but I’ve always filtered my accelerometer breakout board signal using a resistor and a capacitor. Seems to work fine. Try reviewing the datasheet for the specific accelerometer you have. It’s very helpful. It will tell you the best method to adjust your bandwidth.
As for gyro, I never filter it because I want it to be as sensitive as possible because it’s immune to vibrations (unlike accelerometers). I recommend browsing the “Project” section regarding accelerometers and gyro. I’ve seen so many people post stuff there regarding those subjects.
The project I have been working on for some time is a 4 rotor platform. I started out with a 2 axis accelerometer and quickly realized this wouldn’t work very well. I added gyros, and fused the accelerometer and gyro data in a Kalman filter. I used the gyros I had on hand at the time, one Sparkfun ADXRS150 breakout board and one Analog Devices ADXRS150 evaluation board. The platform is very stable around the balance point on the axis using the Analog Devices evaluation board and significantly less stable around the axis using the Sparkfun breakout board. Everything else (HW & SW) is identical for each axis. The difference seems to be that the Analog Devices evaluation board sets the BW to 10-Hz where as the Sparkfun breakout board sets the BW to ~350Hz. Even though the gyros are to some extent mechanically isolated, there is still considerable vibration. Also, Nyquist theory dictates that the signal BW should be half the sampling frequency. In my case, sampling frequency is 50-Hz and I don’t need anything above 25-Hz rate for dynamically balancing the platform.
In my experience the gyros don’t appear to be immune to vibration. Any noise in the sample will introduce error into the filter calculations.
I guess it makes more sense how Sparkfun is configuring their breakout boards, as we can use external filtering to set the BW for a particular application.
I am getting ready to move from my first prototype HW to next version which I hope will actually fly. I will use the Sparkfun breakout boards as the form factor is easier to use than the Analog Devices boards. I will probably use a 2 or 3 pole active low pass Butterworth filter with a corner frequency of 25-Hz on each of the gyros.
Wow. The kalman filter actually worked well? I haven’t tried kalman filter yet. I thought that gyro is not affected by acceleration. Anyway, I was just thinking that a segway IMU takes 100 sample per second just to balance on one axis. I would think that the lighter the platform the more sampling you would need. Just a thought. I am curious about your project. My goal is to really build an autonomous helicopter. Could you post a picture of your project? Thanks.
I am in Tokyo but when I get home at the end of next week I will put a video on YouTube so you can see the platform balancing on its test stand.
Each axis (pitch and roll) has a Kalman filter that updates at a 50-Hz rate. It took a while to get it working, but it was well worth it. I would definitely recommend this approach.
In the next version I will add a 3rd Kalman filter to fuse compass heading and gyro for the yaw axis or head holding.
I would eventually like to move to a helicopter platform. I thought it would be easier to develop the HW and SW on a four rotor platform initially. I drive the two motors on each axis differentially to maintain balance, this could be changed to drive pitch and roll servos on a helicopter in future.
Waw… a 3rd Kalman filter thats fantastic
I am impatient to see it.
Thanks
Yup. Exciting indeed! How’s Tokyo? Lots of girls? =)
Anyway, my first uC is a Basic Stamp II but it’s not capable to handle simple negative numbers. As a result you have to manipulate it byte-wise and have to add more codes to “trick” it into simulating it to calculate negative numbers. That’s when I changed to ZBasic (thanks Steve!). -But anyway, I’ve always been wondering if you need matrix operations to use Kalman filters. I know that it’s possible to simulate matrix calculations in ZBasic but it would require additional codes.
I am using the tilt.c code for the Kalman filter, check out the following link.
http://www.rotomotion.com/downloads/tilt.c
The code has been highly optimized, expanding and simplifying the matrix calculations so there is no matrix math required, per se. However, it is all floating-point math.
I am using a TI MSP430F148 with 8-MHz clock. This variant has a Hardware Multiplier which I am sure is helping. All the code is written in C.
The sampling period for the Kalman filters must be precisely maintained to to ensure accurate integration of the gyro rate data. I do this with a State Machine that runs inside the WDT Interval Timer interrupt service routine. The Interval Timer ISR runs every 4.1ms and there are 5 states. The X-axis and Y-axis balance routines, including the tilt.c code, each run in their own state, so every 20.5ms. I will add the yaw axis to an unused state in the next version. That leaves 2 states for doing other things such as updating throttle, pitch, roll and yaw settings from the R/C transmitter. A separate interrupt routine is measuring the width of the pulses from the R/C transmitter. Monitoring 2 channels in the current version and plan to go to 6 channels in the next version.
the tilt.c from the link that you posted say :
* 1 dimensional tilt sensor using a dual axis accelerometer
* and single axis angular rate gyro.
So, how can hadle a 3 Axis ACC and a 3 Axis Gyro :?: [/quote]
You are correct. Input to tilt.c is rate data from a single gyro and angle data from a single axis of an accelerometer. tilt.c implements a Kalman filter to fuse the data, providing a better angle estimate. This angle estimate is used with gyro rate data in a PD routine to balance around a single axis.
I currently use two instances of tilt.c, one to balance the x-axis and one to balance the y-axis (y_tilt.c). In the next version, I will add a third instance of tilt.c to balance about the z-axis (z_tilt.c).
Great!
Can you , then Share the whole titlc.c for all the 3 Axis, please ?
Thanks in Advance
Here is the tilt.c routine I use on the X-axis:
/*
* Please refer to tilt.c by Trammell Hudson <hudson@rotomotion.com>. Original code
* with extensive comments is available at http://www.rotomotion.com/downloads/tilt.c
*
* The State Update Routine is called every dt seconds with a new gyro rate measurement
* in degrees/second. Gyro reading and scaling is accomplished in a separete routine
* prior to calling state_update.
*
* The Kalman Update Routine is also called every dt seconds with a new angle measurement
* in degrees. The angle measurement is the difference between the desired balance angle and the
* angle derived from the accelerometer reading. Conversion and scaling of the accelerometer acceleration
* data to an angle measurement is accomplished in a separate routine prior to calling kalman_update.
*
* The accelerometer alone is not fast enough to balance the platform. The gyro is fast enough
* and the rate data can be integrated to calculate angle, however, bias or drift of the integrated
* gyro rate data over time makes them unusable on their own. The Kalman Filter fuses the
* accelerometer data with the gyro data to effectively unbias the integrated gyro rate data
* providing a better angle estimate than either the accelerometer or gyro on their own.
*/
#include <math.h>
static const float dt = 0.0205; //time in seconds between gyro rate measurements
#define R_angle_PARAM 0.3 //Adjusts Kalman filter convergence speed
#define Q_angle_PARAM 0.001 //Used in Q matix to set level of trust in accel
#define Q_gyro_PARAM 0.006 //data relative to gyro; originally 0.001 & 0.003
static float P[2][2] = { //Covariance matrix
{ 1, 0 },
{ 0, 1 } };
static float Pdot[4];
float angle = 0; //angle state
float q_bias = 0; //gyro bias state
float rate = 0; //unbiased angular rate
static const float R_angle = R_angle_PARAM;
static const float Q_angle = Q_angle_PARAM; //Q matrix
static const float Q_gyro = Q_gyro_PARAM;
//********************State Update Routine********************
//Called every dt seconds with a biased gyro measurement, it updates the
//current angle and rate estimate.
void state_update(float q_m) //gyro measurement
{
const float q = q_m - q_bias; //unbias gyro measurement
Pdot[0] = Q_angle - P[0][1] - P[1][0]; //Compute the derivative of the covariance matrix
Pdot[1] = - P[1][1];
Pdot[2] = - P[1][1];
Pdot[3] = Q_gyro;
rate = q; //Save unbiased gyro estimate
angle += q * dt; //Update angle estimate
P[0][0] += Pdot[0] * dt; //Update covariance matirx
P[0][1] += Pdot[1] * dt;
P[1][0] += Pdot[2] * dt;
P[1][1] += Pdot[3] * dt;
}
//********************Kalman Update Routine********************
//Called every time a new accelometer measurement is available,
//in this implementation every dt seconds
float angle_err;
float C_0;
void kalman_update(float angle_m)
{
const float C_0 = 1;
/* const float C_1 = 0; */ //not used
const float PCt_0 = C_0 * P[0][0]; //+ C_1 * P[0][1] = 0
const float PCt_1 = C_0 * P[1][0]; //+ C_1 * P[1][1] = 0
const float E = R_angle + C_0 * PCt_0; //Compute error estimate
/* + C_1 * PCt_1 = 0 */ //not used
const float K_0 = PCt_0 / E; //Compute Kalman filter gains
const float K_1 = PCt_1 / E;
const float t_0 = PCt_0; //Compute measured angle
const float t_1 = C_0 * P[0][1]; //and error in the estimate
P[0][0] -= K_0 * t_0;
P[0][1] -= K_0 * t_1;
P[1][0] -= K_1 * t_0;
P[1][1] -= K_1 * t_1;
angle_err = angle_m - angle;
angle += K_0 * angle_err; //Update state estimate
q_bias += K_1 * angle_err;
}
Here is the y_tilt.c routine I use on the Y-axis.
/*
* Please refer to tilt.c by Trammell Hudson <hudson@rotomotion.com>. Original code
* with extensive comments is available at http://www.rotomotion.com/downloads/tilt.c
*
* The State Update Routine is called every dt seconds with a new gyro rate measurement
* in degrees/second. Gyro reading and scaling is accomplished in a separete routine
* prior to calling state_update.
*
* The Kalman Update Routine is also called every dt seconds with a new angle measurement
* in degrees. The angle measurement is the difference between the desired balance angle and the
* angle derived from the accelerometer reading. Conversion and scaling of the accelerometer acceleration
* data to an angle measurement is accomplished in a separate routine prior to calling kalman_update.
*
* The accelerometer alone is not fast enough to balance the platform. The gyro is fast enough
* and the rate data can be integrated to calculate angle, however, bias or drift of the integrated
* gyro rate data over time makes them unusable on their own. The Kalman Filter fuses the
* accelerometer data with the gyro data to effectively unbias the integrated gyro rate data
* providing a better angle estimate than either the accelerometer or gyro on their own.
*/
#include <math.h>
static const float y_dt = 0.0205; //time in seconds between gyro rate measurements
#define y_R_angle_PARAM 0.3 //Adjusts Kalman filter convergence speed
#define y_Q_angle_PARAM 0.001 //Used in Q matix to set level of trust in accel
#define y_Q_gyro_PARAM 0.006 //data relative to gyro; originally 0.001 & 0.003
static float y_P[2][2] = { //Covariance matrix
{ 1, 0 },
{ 0, 1 } };
static float y_Pdot[4];
float y_angle = 0; //angle state
float y_q_bias = 0; //gyro bias state
float y_rate = 0; //unbiased angular rate
static const float y_R_angle = y_R_angle_PARAM;
static const float y_Q_angle = y_Q_angle_PARAM; //Q matrix
static const float y_Q_gyro = y_Q_gyro_PARAM;
//********************State Update Routine********************
/*Called every y_dt seconds with a biased gyro measurement,
* it updates the current angle and rate estimate.*/
void y_state_update(float y_q_m) //gyro measurement
{
const float y_q = y_q_m - y_q_bias; //Unbias gyro measurement
y_Pdot[0] = y_Q_angle - y_P[0][1] - y_P[1][0]; //Compute the derivative of the covariance matrix
y_Pdot[1] = - y_P[1][1];
y_Pdot[2] = - y_P[1][1];
y_Pdot[3] = y_Q_gyro;
y_rate = y_q; //Save unbiased gyro estimate
y_angle += y_q * y_dt; //Update angle estimate
y_P[0][0] += y_Pdot[0] * y_dt; //Update covariance matirx
y_P[0][1] += y_Pdot[1] * y_dt;
y_P[1][0] += y_Pdot[2] * y_dt;
y_P[1][1] += y_Pdot[3] * y_dt;
}
//********************Kalman Update Routine********************
/*Called every time a new accelometer measurement is available,
* in this implementation every y_dt seconds*/
float y_angle_err;
float y_C_0;
void y_kalman_update(float y_angle_m)
{
const float y_C_0 = 1;
/* const float C_1 = 0; */ //not used
const float y_PCt_0 = y_C_0 * y_P[0][0]; // + C_1 * P[0][1] = 0
const float y_PCt_1 = y_C_0 * y_P[1][0]; // + C_1 * P[1][1] = 0
const float y_E = y_R_angle + y_C_0 * y_PCt_0; //Compute error estimate
/* + C_1 * PCt_1 = 0 */ //not used
const float y_K_0 = y_PCt_0 / y_E; //Compute Kalman filter gains
const float y_K_1 = y_PCt_1 / y_E;
const float y_t_0 = y_PCt_0; //Compute measured angle
const float y_t_1 = y_C_0 * y_P[0][1]; //and error in the estimate
y_P[0][0] -= y_K_0 * y_t_0;
y_P[0][1] -= y_K_0 * y_t_1;
y_P[1][0] -= y_K_1 * y_t_0;
y_P[1][1] -= y_K_1 * y_t_1;
y_angle_err = y_angle_m - y_angle;
y_angle += y_K_0 * y_angle_err; //Update state estimate
y_q_bias += y_K_1 * y_angle_err;
}
Nothing fancy going on here, I just append everything with “y_”
When it comes time to add the Z-axis, I will create a file z_tilt.c and append everything with “z_”
Pretty Cool
waiting for ur new Video
Thanks
Hey AutoSeqStart
Any news for ur new video ?
Cheers
Here is the link for the video…
great
I put an RC low pass filter with a corner frequency of 10-Hz on the rate output of the Sparkfun ADXRS150 gyro breakout board. The balancing performance on this axis of the platform is now similar to the performance of the axis with the Analog Devices ADXRS150 evaluation board which comes Cout set for 10-Hz BW.
For this kind of application, the Sparkfun gyro breakout boards definitely need a low pass filter. 10-Hz seems to work well.
Does anyone have experience with higher order low pass filters on these gyros in applications similar to this? I have been looking at the MAX7420 switched-capacitor filter, corner frequency is set by an external clock (100xFc) which can be easily generated.
excellent post and replies! Thanks for sharing this knowledge with us
AutoSeqStart:
Does anyone have experience with higher order low pass filters on these gyros in applications similar to this? I have been looking at the MAX7420 switched-capacitor filter, corner frequency is set by an external clock (100xFc) which can be easily generated.
I don’t know, but do you have a good reason why you don’t use a digital filter? For MEMS sensors I just set the hardware filter to cutoff at a very high frequency, then digitally filter the signal to reduce the bandwidth, shape the response, and lower the noise.
You can evaluate 10 filters in a few minutes, no soldering, buying parts. Do the least amount in HW makes for higher reliablity.
bungalow_steve thanks for the post, I think that’s a great discussion to have. I agree that less HW is generally better.
Honestly, I don’t have experience with digital filters beyond simple averaging or smoothing of samples. I have read application notes on FIR filters and some special implementations of IIR filters. I assume you are implementing FIR filters?
What are your MEMS sensor applications and what are the characteristics of digital filters you are using?
Given 2 accelerometers and 3 gyros, how feasible do you think it is to filter these 5 data streams for a 10-Hz cutoff frequency at a sample rate of 50-Hz, say 5th order Butterworth response?