Robot Control Library
rc_altitude.c
/**
* @file rc_altitude.c
* @example rc_altitude
*
* This serves as an example of how to read the barometer and IMU together to
* estimate altitude
*
* @author James Strawson
* @date 3/14/2018
*/
#include <stdio.h>
#include <signal.h>
#include <math.h> // for M_PI
#include <rc/math/kalman.h>
#include <rc/math/filter.h>
#include <rc/time.h>
#include <rc/bmp.h>
#include <rc/mpu.h>
#define Nx 3
#define Ny 1
#define Nu 1
#define SAMPLE_RATE 200 // hz
#define DT (1.0/SAMPLE_RATE)
#define ACCEL_LP_TC 20*DT // fast LP filter for accel
#define PRINT_HZ 10
#define BMP_RATE_DIV 10 // optionally sample bmp less frequently than mpu
static int running = 0;
static rc_mpu_data_t mpu_data;
static rc_bmp_data_t bmp_data;
// interrupt handler to catch ctrl-c
static void __signal_handler(__attribute__ ((unused)) int dummy)
{
running=0;
return;
}
static void __dmp_handler(void)
{
int i;
double accel_vec[3];
static int bmp_sample_counter = 0;
// make copy of acceleration reading before rotating
for(i=0;i<3;i++) accel_vec[i]=mpu_data.accel[i];
// rotate accel vector
// do first-run filter setup
if(kf.step==0){
kf.x_est.d[0] = bmp_data.alt_m;
rc_filter_prefill_inputs(&acc_lp, accel_vec[2]-9.80665);
rc_filter_prefill_outputs(&acc_lp, accel_vec[2]-9.80665);
}
// calculate acceleration and smooth it just a tad
rc_filter_march(&acc_lp, accel_vec[2]-9.80665);
u.d[0] = acc_lp.newest_output;
// don't bother filtering Barometer, kalman will deal with that
y.d[0] = bmp_data.alt_m;
if(rc_kalman_update_lin(&kf, u, y)) running=0;
// now check if we need to sample BMP this loop
bmp_sample_counter++;
if(bmp_sample_counter>=BMP_RATE_DIV){
// perform the i2c reads to the sensor, on bad read just try later
if(rc_bmp_read(&bmp_data)) return;
bmp_sample_counter=0;
}
return;
}
int main(void)
{
rc_mpu_config_t mpu_conf;
// allocate appropirate memory for system
rc_matrix_zeros(&F, Nx, Nx);
rc_matrix_zeros(&G, Nx, Nu);
rc_matrix_zeros(&H, Ny, Nx);
rc_matrix_zeros(&Q, Nx, Nx);
rc_matrix_zeros(&R, Ny, Ny);
rc_matrix_zeros(&Pi, Nx, Nx);
rc_vector_zeros(&u, Nu);
rc_vector_zeros(&y, Ny);
// define system -DT; // accel bias
F.d[0][0] = 1.0;
F.d[0][1] = DT;
F.d[0][2] = 0.0;
F.d[1][0] = 0.0;
F.d[1][1] = 1.0;
F.d[1][2] = -DT; // subtract accel bias
F.d[2][0] = 0.0;
F.d[2][1] = 0.0;
F.d[2][2] = 1.0; // accel bias state
G.d[0][0] = 0.5*DT*DT;
G.d[0][1] = DT;
G.d[0][2] = 0.0;
H.d[0][0] = 1.0;
H.d[0][1] = 0.0;
H.d[0][2] = 0.0;
// covariance matrices
Q.d[0][0] = 0.000000001;
Q.d[1][1] = 0.000000001;
Q.d[2][2] = 0.0001; // don't want bias to change too quickly
R.d[0][0] = 1000000.0;
// initial P, cloned from converged P while running
Pi.d[0][0] = 1258.69;
Pi.d[0][1] = 158.6114;
Pi.d[0][2] = -9.9937;
Pi.d[1][0] = 158.6114;
Pi.d[1][1] = 29.9870;
Pi.d[1][2] = -2.5191;
Pi.d[2][0] = -9.9937;
Pi.d[2][1] = -2.5191;
Pi.d[2][2] = 0.3174;
// initialize the kalman filter
if(rc_kalman_alloc_lin(&kf,F,G,H,Q,R,Pi)==-1) return -1;
// initialize the little LP filter to take out accel noise
if(rc_filter_first_order_lowpass(&acc_lp, DT, ACCEL_LP_TC)) return -1;
// set signal handler so the loop can exit cleanly
signal(SIGINT, __signal_handler);
running = 1;
// init barometer and read in first data
printf("initializing barometer\n");
if(rc_bmp_read(&bmp_data)) return -1;
// init DMP
printf("initializing DMP\n");
mpu_conf = rc_mpu_default_config();
mpu_conf.dmp_sample_rate = SAMPLE_RATE;
mpu_conf.dmp_fetch_accel_gyro = 1;
if(rc_mpu_initialize_dmp(&mpu_data, mpu_conf)) return -1;
// wait for dmp to settle then start filter callback
printf("waiting for sensors to settle");
fflush(stdout);
rc_usleep(3000000);
rc_mpu_set_dmp_callback(__dmp_handler);
// print a header
printf("\r\n");
printf(" altitude |");
printf(" velocity |");
printf(" accel_bias |");
printf(" alt (bmp) |");
printf(" vert_accel |");
printf("\n");
//now just wait, print_data will run
while(running){
rc_usleep(1000000/PRINT_HZ);
printf("\r");
printf("%8.2fm |", kf.x_est.d[0]);
printf("%7.2fm/s |", kf.x_est.d[1]);
printf("%7.2fm/s^2|", kf.x_est.d[2]);
printf("%9.2fm |", bmp_data.alt_m);
printf("%7.2fm/s^2|", acc_lp.newest_output);
fflush(stdout);
}
printf("\n");
return 0;
}
int rc_bmp_read(rc_bmp_data_t *data)
Reads the newest temperature and pressure measurments from the barometer over the I2C bus.
int rc_bmp_power_off(void)
Puts the barometer into a low power state, should be called at the end of your program before close.
int rc_bmp_init(rc_bmp_oversample_t oversample, rc_bmp_filter_t filter)
powers on the barometer and initializes it with the given oversample and filter settings.
@ BMP_OVERSAMPLE_16
update rate 28 HZ
Definition: bmp.h:35
@ BMP_FILTER_16
Definition: bmp.h:49
int rc_mpu_set_dmp_callback(void(*func)(void))
Sets the callback function that will be triggered when new DMP data is ready.
int rc_mpu_initialize_dmp(rc_mpu_data_t *data, rc_mpu_config_t conf)
Initializes the MPU in DMP mode, see rc_test_dmp example.
int rc_mpu_power_off(void)
Powers off the MPU.
rc_mpu_config_t rc_mpu_default_config(void)
Returns an rc_mpu_config_t struct with default settings.
#define RC_KALMAN_INITIALIZER
Definition: kalman.h:111
int rc_kalman_alloc_lin(rc_kalman_t *kf, rc_matrix_t F, rc_matrix_t G, rc_matrix_t H, rc_matrix_t Q, rc_matrix_t R, rc_matrix_t Pi)
Allocates memory for a Kalman filter of given dimensions.
int rc_kalman_update_lin(rc_kalman_t *kf, rc_vector_t u, rc_vector_t y)
Kalman Filter state prediction step based on physical model.
int rc_matrix_zeros(rc_matrix_t *A, int rows, int cols)
Resizes matrix A and allocates memory for a matrix with specified rows & columns. The new memory is p...
#define RC_MATRIX_INITIALIZER
Definition: matrix.h:39
int rc_quaternion_rotate_vector_array(double v[3], double q[4])
Rotate a 3D vector v in-place about the origin by quaternion q by converting v to a quaternion and pe...
int rc_filter_prefill_outputs(rc_filter_t *f, double out)
Fills all previous outputs of the filter as if they had been equal to 'out'.
double rc_filter_march(rc_filter_t *f, double new_input)
March a filter forward one step with new input provided as an argument.
int rc_filter_prefill_inputs(rc_filter_t *f, double in)
Fills all previous inputs to the filter as if they had been equal to 'in'.
int rc_filter_first_order_lowpass(rc_filter_t *f, double dt, double tc)
Creates a first order low pass filter.
#define RC_FILTER_INITIALIZER
Definition: filter.h:82
int rc_vector_zeros(rc_vector_t *v, int length)
Resizes vector v and fills with zeros.
#define RC_VECTOR_INITIALIZER
Definition: vector.h:48
void rc_usleep(unsigned int us)
Sleep in microseconds.
Definition: bmp.h:56
double alt_m
altitude in meters
Definition: bmp.h:58
Struct containing configuration and state of a SISO filter.
Definition: filter.h:43
double newest_output
shortcut for the most recent output
Definition: filter.h:76
Definition: kalman.h:82
uint64_t step
counts times rc_kalman_measurement_update has been called
Definition: kalman.h:107
rc_vector_t x_est
Estimated state x[k|k] = x[k|k-1],y[k])
Definition: kalman.h:100
Struct containing the state of a matrix and a pointer to dynamically allocated memory to hold its con...
Definition: matrix.h:32
double ** d
pointer to allocated 2d array
Definition: matrix.h:35
configuration of the mpu sensor
Definition: mpu.h:155
int dmp_fetch_accel_gyro
set to 1 to optionally raw accel/gyro when reading DMP quaternion, default: 0 (off)
Definition: mpu.h:177
int dmp_sample_rate
sample rate in hertz, 200,100,50,40,25,20,10,8,5,4
Definition: mpu.h:176
data struct populated with new sensor data
Definition: mpu.h:199
double dmp_quat[4]
normalized quaternion from DMP based on ONLY Accel/Gyro
Definition: mpu.h:218
double accel[3]
accelerometer (XYZ) in units of m/s^2
Definition: mpu.h:202
Struct containing the state of a vector and a pointer to dynamically allocated memory to hold its con...
Definition: vector.h:41
double * d
pointer to dynamically allocated data
Definition: vector.h:43