2012-11-05 00:27:03 -04:00
/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2012-10-11 21:27:19 -03:00
# include <AP_Progmem.h>
2012-11-05 00:27:03 -04:00
# include "AP_InertialSensor.h"
2012-11-07 02:20:22 -04:00
# include <AP_Common.h>
2012-10-11 21:27:19 -03:00
# include <AP_HAL.h>
2013-08-29 00:13:28 -03:00
# include <AP_Notify.h>
2012-10-11 21:27:19 -03:00
extern const AP_HAL : : HAL & hal ;
2012-11-05 00:27:03 -04:00
# define SAMPLE_UNIT 1
// Class level parameters
const AP_Param : : GroupInfo AP_InertialSensor : : var_info [ ] PROGMEM = {
2012-11-29 16:15:12 -04:00
// @Param: PRODUCT_ID
// @DisplayName: IMU Product ID
2013-11-12 18:55:58 -04:00
// @Description: Which type of IMU is installed (read-only).
2013-11-26 09:18:28 -04:00
// @User: Advanced
2014-01-17 17:44:42 -04:00
// @Values: 0:Unknown,1:APM1-1280,2:APM1-2560,88:APM2,3:SITL,4:PX4v1,5:PX4v2,256:Flymaple,257:Linux
2012-11-05 00:27:03 -04:00
AP_GROUPINFO ( " PRODUCT_ID " , 0 , AP_InertialSensor , _product_id , 0 ) ,
2012-11-29 16:15:12 -04:00
2013-01-02 03:31:58 -04:00
// @Param: ACCSCAL_X
// @DisplayName: Accelerometer scaling of X axis
// @Description: Accelerometer scaling of X axis. Calculated during acceleration calibration routine
2013-05-21 04:00:22 -03:00
// @Range: 0.8 1.2
2013-01-02 03:31:58 -04:00
// @User: Advanced
// @Param: ACCSCAL_Y
// @DisplayName: Accelerometer scaling of Y axis
// @Description: Accelerometer scaling of Y axis Calculated during acceleration calibration routine
2013-05-21 04:00:22 -03:00
// @Range: 0.8 1.2
2013-01-02 03:31:58 -04:00
// @User: Advanced
// @Param: ACCSCAL_Z
// @DisplayName: Accelerometer scaling of Z axis
// @Description: Accelerometer scaling of Z axis Calculated during acceleration calibration routine
2013-05-21 04:00:22 -03:00
// @Range: 0.8 1.2
2012-11-29 16:15:12 -04:00
// @User: Advanced
2013-12-08 18:50:12 -04:00
AP_GROUPINFO ( " ACCSCAL " , 1 , AP_InertialSensor , _accel_scale [ 0 ] , 0 ) ,
2012-11-29 16:15:12 -04:00
2013-01-02 03:31:58 -04:00
// @Param: ACCOFFS_X
// @DisplayName: Accelerometer offsets of X axis
// @Description: Accelerometer offsets of X axis. This is setup using the acceleration calibration or level operations
// @Units: m/s/s
// @Range: -300 300
// @User: Advanced
// @Param: ACCOFFS_Y
// @DisplayName: Accelerometer offsets of Y axis
// @Description: Accelerometer offsets of Y axis. This is setup using the acceleration calibration or level operations
// @Units: m/s/s
// @Range: -300 300
// @User: Advanced
// @Param: ACCOFFS_Z
// @DisplayName: Accelerometer offsets of Z axis
// @Description: Accelerometer offsets of Z axis. This is setup using the acceleration calibration or level operations
2012-11-29 16:15:12 -04:00
// @Units: m/s/s
2013-01-02 03:31:58 -04:00
// @Range: -300 300
2012-11-29 16:15:12 -04:00
// @User: Advanced
2013-12-08 18:50:12 -04:00
AP_GROUPINFO ( " ACCOFFS " , 2 , AP_InertialSensor , _accel_offset [ 0 ] , 0 ) ,
2012-11-29 16:15:12 -04:00
2013-01-02 03:31:58 -04:00
// @Param: GYROFFS_X
// @DisplayName: Gyro offsets of X axis
// @Description: Gyro sensor offsets of X axis. This is setup on each boot during gyro calibrations
// @Units: rad/s
// @User: Advanced
// @Param: GYROFFS_Y
// @DisplayName: Gyro offsets of Y axis
// @Description: Gyro sensor offsets of Y axis. This is setup on each boot during gyro calibrations
// @Units: rad/s
// @User: Advanced
// @Param: GYROFFS_Z
// @DisplayName: Gyro offsets of Z axis
// @Description: Gyro sensor offsets of Z axis. This is setup on each boot during gyro calibrations
2012-11-29 16:15:12 -04:00
// @Units: rad/s
// @User: Advanced
2013-12-08 18:50:12 -04:00
AP_GROUPINFO ( " GYROFFS " , 3 , AP_InertialSensor , _gyro_offset [ 0 ] , 0 ) ,
2012-11-29 16:15:12 -04:00
// @Param: MPU6K_FILTER
// @DisplayName: MPU6000 filter frequency
2013-04-09 12:14:53 -03:00
// @Description: Filter frequency to ask the MPU6000 to apply to samples. This can be set to a lower value to try to cope with very high vibration levels in aircraft. The default value on ArduPlane, APMrover2 and ArduCopter is 20Hz. This option takes effect on the next reboot or gyro initialisation
2012-11-29 16:15:12 -04:00
// @Units: Hz
// @Values: 0:Default,5:5Hz,10:10Hz,20:20Hz,42:42Hz,98:98Hz
// @User: Advanced
AP_GROUPINFO ( " MPU6K_FILTER " , 4 , AP_InertialSensor , _mpu6000_filter , 0 ) ,
2013-12-08 18:50:12 -04:00
# if INS_MAX_INSTANCES > 1
AP_GROUPINFO ( " ACC2SCAL " , 5 , AP_InertialSensor , _accel_scale [ 1 ] , 0 ) ,
AP_GROUPINFO ( " ACC2OFFS " , 6 , AP_InertialSensor , _accel_offset [ 1 ] , 0 ) ,
AP_GROUPINFO ( " GYR2OFFS " , 7 , AP_InertialSensor , _gyro_offset [ 1 ] , 0 ) ,
# endif
2014-06-26 01:04:33 -03:00
# if INS_MAX_INSTANCES > 2
AP_GROUPINFO ( " ACC3SCAL " , 8 , AP_InertialSensor , _accel_scale [ 2 ] , 0 ) ,
AP_GROUPINFO ( " ACC3OFFS " , 9 , AP_InertialSensor , _accel_offset [ 2 ] , 0 ) ,
AP_GROUPINFO ( " GYR3OFFS " , 10 , AP_InertialSensor , _gyro_offset [ 2 ] , 0 ) ,
# endif
2012-11-05 00:27:03 -04:00
AP_GROUPEND
} ;
2013-04-12 01:30:35 -03:00
AP_InertialSensor : : AP_InertialSensor ( ) :
_accel ( ) ,
2014-07-15 00:43:32 -03:00
_gyro ( ) ,
_board_orientation ( ROTATION_NONE )
2013-04-12 01:30:35 -03:00
{
2012-12-19 00:55:38 -04:00
AP_Param : : setup_object_defaults ( this , var_info ) ;
}
2012-11-05 00:27:03 -04:00
void
AP_InertialSensor : : init ( Start_style style ,
2013-09-19 05:32:19 -03:00
Sample_rate sample_rate )
2012-11-05 00:27:03 -04:00
{
2012-10-11 21:27:19 -03:00
_product_id = _init_sensor ( sample_rate ) ;
2012-11-05 00:27:03 -04:00
// check scaling
2013-12-08 18:50:12 -04:00
for ( uint8_t i = 0 ; i < get_accel_count ( ) ; i + + ) {
if ( _accel_scale [ i ] . get ( ) . is_zero ( ) ) {
_accel_scale [ i ] . set ( Vector3f ( 1 , 1 , 1 ) ) ;
}
2012-11-05 00:27:03 -04:00
}
2012-11-07 02:20:22 -04:00
if ( WARM_START ! = style ) {
// do cold-start calibration for gyro only
2013-09-19 05:32:19 -03:00
_init_gyro ( ) ;
2012-11-05 00:27:03 -04:00
}
}
// save parameters to eeprom
2012-11-07 02:20:22 -04:00
void AP_InertialSensor : : _save_parameters ( )
2012-11-05 00:27:03 -04:00
{
_product_id . save ( ) ;
2013-12-08 18:50:12 -04:00
for ( uint8_t i = 0 ; i < INS_MAX_INSTANCES ; i + + ) {
_accel_scale [ i ] . save ( ) ;
_accel_offset [ i ] . save ( ) ;
_gyro_offset [ i ] . save ( ) ;
}
2012-11-05 00:27:03 -04:00
}
void
2013-09-19 05:32:19 -03:00
AP_InertialSensor : : init_gyro ( )
2012-11-05 00:27:03 -04:00
{
2013-09-19 05:32:19 -03:00
_init_gyro ( ) ;
2012-11-05 00:27:03 -04:00
// save calibration
2012-11-07 02:20:22 -04:00
_save_parameters ( ) ;
2012-11-05 00:27:03 -04:00
}
void
2013-09-19 05:32:19 -03:00
AP_InertialSensor : : _init_gyro ( )
2012-11-05 00:27:03 -04:00
{
2013-12-08 18:50:12 -04:00
uint8_t num_gyros = min ( get_gyro_count ( ) , INS_MAX_INSTANCES ) ;
2014-02-08 04:18:47 -04:00
Vector3f last_average [ INS_MAX_INSTANCES ] , best_avg [ INS_MAX_INSTANCES ] ;
float best_diff [ INS_MAX_INSTANCES ] ;
bool converged [ INS_MAX_INSTANCES ] ;
2012-11-05 00:27:03 -04:00
// cold start
2013-12-11 02:21:25 -04:00
hal . console - > print_P ( PSTR ( " Init Gyro " ) ) ;
2012-11-05 00:27:03 -04:00
2013-08-13 23:52:19 -03:00
// flash leds to tell user to keep the IMU still
AP_Notify : : flags . initialising = true ;
2012-11-05 00:27:03 -04:00
// remove existing gyro offsets
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_gyros ; k + + ) {
_gyro_offset [ k ] = Vector3f ( 0 , 0 , 0 ) ;
best_diff [ k ] = 0 ;
2014-01-15 09:45:11 -04:00
last_average [ k ] . zero ( ) ;
2013-12-08 18:50:12 -04:00
converged [ k ] = false ;
}
2012-11-05 00:27:03 -04:00
2014-01-15 09:45:11 -04:00
for ( int8_t c = 0 ; c < 5 ; c + + ) {
hal . scheduler - > delay ( 5 ) ;
2012-11-05 00:27:03 -04:00
update ( ) ;
}
2014-01-16 23:45:56 -04:00
// the strategy is to average 50 points over 0.5 seconds, then do it
2012-11-05 00:27:03 -04:00
// again and see if the 2nd average is within a small margin of
// the first
2013-12-08 18:50:12 -04:00
uint8_t num_converged = 0 ;
2012-11-05 00:27:03 -04:00
// we try to get a good calibration estimate for up to 10 seconds
2014-01-16 23:45:56 -04:00
// if the gyros are stable, we should get it in 1 second
for ( int16_t j = 0 ; j < = 20 & & num_converged < num_gyros ; j + + ) {
2014-02-08 04:18:47 -04:00
Vector3f gyro_sum [ INS_MAX_INSTANCES ] , gyro_avg [ INS_MAX_INSTANCES ] , gyro_diff [ INS_MAX_INSTANCES ] ;
float diff_norm [ INS_MAX_INSTANCES ] ;
2012-11-05 00:27:03 -04:00
uint8_t i ;
2014-03-23 22:52:16 -03:00
memset ( diff_norm , 0 , sizeof ( diff_norm ) ) ;
2013-12-11 02:21:25 -04:00
hal . console - > print_P ( PSTR ( " * " ) ) ;
2012-11-05 00:27:03 -04:00
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_gyros ; k + + ) {
gyro_sum [ k ] . zero ( ) ;
}
2014-01-15 09:45:11 -04:00
for ( i = 0 ; i < 50 ; i + + ) {
2012-11-05 00:27:03 -04:00
update ( ) ;
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_gyros ; k + + ) {
gyro_sum [ k ] + = get_gyro ( k ) ;
}
2012-10-11 21:27:19 -03:00
hal . scheduler - > delay ( 5 ) ;
2012-11-05 00:27:03 -04:00
}
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_gyros ; k + + ) {
gyro_avg [ k ] = gyro_sum [ k ] / i ;
gyro_diff [ k ] = last_average [ k ] - gyro_avg [ k ] ;
diff_norm [ k ] = gyro_diff [ k ] . length ( ) ;
}
2014-01-15 09:45:11 -04:00
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_gyros ; k + + ) {
if ( converged [ k ] ) continue ;
if ( j = = 0 ) {
best_diff [ k ] = diff_norm [ k ] ;
best_avg [ k ] = gyro_avg [ k ] ;
2014-01-15 09:45:11 -04:00
} else if ( gyro_diff [ k ] . length ( ) < ToRad ( 0.1f ) ) {
2013-12-08 18:50:12 -04:00
// we want the average to be within 0.1 bit, which is 0.04 degrees/s
last_average [ k ] = ( gyro_avg [ k ] * 0.5f ) + ( last_average [ k ] * 0.5f ) ;
_gyro_offset [ k ] = last_average [ k ] ;
converged [ k ] = true ;
num_converged + + ;
} else if ( diff_norm [ k ] < best_diff [ k ] ) {
best_diff [ k ] = diff_norm [ k ] ;
best_avg [ k ] = ( gyro_avg [ k ] * 0.5f ) + ( last_average [ k ] * 0.5f ) ;
}
last_average [ k ] = gyro_avg [ k ] ;
2012-11-05 00:27:03 -04:00
}
}
2013-08-13 23:52:19 -03:00
// stop flashing leds
AP_Notify : : flags . initialising = false ;
2013-12-08 18:50:12 -04:00
if ( num_converged = = num_gyros ) {
// all OK
return ;
}
2012-11-05 00:27:03 -04:00
// we've kept the user waiting long enough - use the best pair we
// found so far
2013-12-08 18:50:12 -04:00
hal . console - > println ( ) ;
for ( uint8_t k = 0 ; k < num_gyros ; k + + ) {
if ( ! converged [ k ] ) {
hal . console - > printf_P ( PSTR ( " gyro[%u] did not converge: diff=%f dps \n " ) ,
( unsigned ) k , ToDeg ( best_diff [ k ] ) ) ;
_gyro_offset [ k ] = best_avg [ k ] ;
}
}
2012-11-05 00:27:03 -04:00
}
void
2013-09-19 05:32:19 -03:00
AP_InertialSensor : : init_accel ( )
2012-11-05 00:27:03 -04:00
{
2013-09-19 05:32:19 -03:00
_init_accel ( ) ;
2012-11-05 00:27:03 -04:00
// save calibration
2012-11-07 02:20:22 -04:00
_save_parameters ( ) ;
2012-11-05 00:27:03 -04:00
}
void
2013-09-19 05:32:19 -03:00
AP_InertialSensor : : _init_accel ( )
2012-11-05 00:27:03 -04:00
{
2013-12-08 18:50:12 -04:00
uint8_t num_accels = min ( get_accel_count ( ) , INS_MAX_INSTANCES ) ;
uint8_t flashcount = 0 ;
2014-02-08 04:18:47 -04:00
Vector3f prev [ INS_MAX_INSTANCES ] ;
Vector3f accel_offset [ INS_MAX_INSTANCES ] ;
float total_change [ INS_MAX_INSTANCES ] ;
float max_offset [ INS_MAX_INSTANCES ] ;
2012-11-05 00:27:03 -04:00
2014-03-23 22:52:16 -03:00
memset ( max_offset , 0 , sizeof ( max_offset ) ) ;
memset ( total_change , 0 , sizeof ( total_change ) ) ;
2012-11-05 00:27:03 -04:00
// cold start
2012-10-11 21:27:19 -03:00
hal . scheduler - > delay ( 100 ) ;
2012-11-05 00:27:03 -04:00
2013-12-11 02:21:25 -04:00
hal . console - > print_P ( PSTR ( " Init Accel " ) ) ;
2012-11-05 00:27:03 -04:00
2013-08-13 23:52:19 -03:00
// flash leds to tell user to keep the IMU still
AP_Notify : : flags . initialising = true ;
2012-11-05 00:27:03 -04:00
// clear accelerometer offsets and scaling
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
_accel_offset [ k ] = Vector3f ( 0 , 0 , 0 ) ;
_accel_scale [ k ] = Vector3f ( 1 , 1 , 1 ) ;
2012-11-05 00:27:03 -04:00
2013-12-08 18:50:12 -04:00
// initialise accel offsets to a large value the first time
// this will force us to calibrate accels at least twice
accel_offset [ k ] = Vector3f ( 500 , 500 , 500 ) ;
}
2012-11-05 00:27:03 -04:00
// loop until we calculate acceptable offsets
2013-12-08 18:50:12 -04:00
while ( true ) {
2012-11-05 00:27:03 -04:00
// get latest accelerometer values
update ( ) ;
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
// store old offsets
prev [ k ] = accel_offset [ k ] ;
2012-11-05 00:27:03 -04:00
2013-12-08 18:50:12 -04:00
// get new offsets
accel_offset [ k ] = get_accel ( k ) ;
}
2012-11-05 00:27:03 -04:00
// We take some readings...
2012-11-07 02:20:22 -04:00
for ( int8_t i = 0 ; i < 50 ; i + + ) {
2012-11-05 00:27:03 -04:00
2012-10-11 21:27:19 -03:00
hal . scheduler - > delay ( 20 ) ;
2012-11-05 00:27:03 -04:00
update ( ) ;
// low pass filter the offsets
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
accel_offset [ k ] = accel_offset [ k ] * 0.9f + get_accel ( k ) * 0.1f ;
}
2012-11-05 00:27:03 -04:00
// display some output to the user
if ( flashcount > = 10 ) {
2013-12-11 02:21:25 -04:00
hal . console - > print_P ( PSTR ( " * " ) ) ;
2012-11-05 00:27:03 -04:00
flashcount = 0 ;
}
flashcount + + ;
}
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
// null gravity from the Z accel
accel_offset [ k ] . z + = GRAVITY_MSS ;
2012-11-05 00:27:03 -04:00
2013-12-08 18:50:12 -04:00
total_change [ k ] =
fabsf ( prev [ k ] . x - accel_offset [ k ] . x ) +
fabsf ( prev [ k ] . y - accel_offset [ k ] . y ) +
fabsf ( prev [ k ] . z - accel_offset [ k ] . z ) ;
max_offset [ k ] = ( accel_offset [ k ] . x > accel_offset [ k ] . y ) ? accel_offset [ k ] . x : accel_offset [ k ] . y ;
max_offset [ k ] = ( max_offset [ k ] > accel_offset [ k ] . z ) ? max_offset [ k ] : accel_offset [ k ] . z ;
}
uint8_t num_converged = 0 ;
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
if ( total_change [ k ] < = AP_INERTIAL_SENSOR_ACCEL_TOT_MAX_OFFSET_CHANGE & &
max_offset [ k ] < = AP_INERTIAL_SENSOR_ACCEL_MAX_OFFSET ) {
num_converged + + ;
}
}
if ( num_converged = = num_accels ) break ;
2012-11-05 00:27:03 -04:00
2012-10-11 21:27:19 -03:00
hal . scheduler - > delay ( 500 ) ;
2013-12-08 18:50:12 -04:00
}
2012-11-05 00:27:03 -04:00
// set the global accel offsets
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
_accel_offset [ k ] = accel_offset [ k ] ;
}
2012-11-05 00:27:03 -04:00
2013-08-13 23:52:19 -03:00
// stop flashing the leds
AP_Notify : : flags . initialising = false ;
2013-12-11 02:21:25 -04:00
hal . console - > print_P ( PSTR ( " " ) ) ;
2012-11-05 00:27:03 -04:00
}
2012-11-20 03:41:04 -04:00
# if !defined( __AVR_ATmega1280__ )
2012-10-11 21:27:19 -03:00
// calibrate_accel - perform accelerometer calibration including providing user
// instructions and feedback Gauss-Newton accel calibration routines borrowed
// from Rolfe Schmidt blog post describing the method:
// http://chionophilous.wordpress.com/2011/10/24/accelerometer-calibration-iv-1-implementing-gauss-newton-on-an-atmega/
// original sketch available at
// http://rolfeschmidt.com/mathtools/skimetrics/adxl_gn_calibration.pde
2013-09-19 05:32:19 -03:00
bool AP_InertialSensor : : calibrate_accel ( AP_InertialSensor_UserInteract * interact ,
float & trim_roll ,
float & trim_pitch )
2012-11-05 00:27:03 -04:00
{
2013-12-08 18:50:12 -04:00
uint8_t num_accels = min ( get_accel_count ( ) , INS_MAX_INSTANCES ) ;
2014-02-08 04:18:47 -04:00
Vector3f samples [ INS_MAX_INSTANCES ] [ 6 ] ;
Vector3f new_offsets [ INS_MAX_INSTANCES ] ;
Vector3f new_scaling [ INS_MAX_INSTANCES ] ;
Vector3f orig_offset [ INS_MAX_INSTANCES ] ;
Vector3f orig_scale [ INS_MAX_INSTANCES ] ;
2013-12-08 18:50:12 -04:00
uint8_t num_ok = 0 ;
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
// backup original offsets and scaling
orig_offset [ k ] = _accel_offset [ k ] . get ( ) ;
orig_scale [ k ] = _accel_scale [ k ] . get ( ) ;
// clear accelerometer offsets and scaling
_accel_offset [ k ] = Vector3f ( 0 , 0 , 0 ) ;
_accel_scale [ k ] = Vector3f ( 1 , 1 , 1 ) ;
}
2012-11-05 00:27:03 -04:00
// capture data from 6 positions
2013-12-08 18:50:12 -04:00
for ( uint8_t i = 0 ; i < 6 ; i + + ) {
2012-11-07 02:20:22 -04:00
const prog_char_t * msg ;
2012-11-05 00:27:03 -04:00
// display message to user
2012-11-07 02:20:22 -04:00
switch ( i ) {
2012-11-05 00:27:03 -04:00
case 0 :
2012-11-07 02:20:22 -04:00
msg = PSTR ( " level " ) ;
2012-11-05 00:27:03 -04:00
break ;
case 1 :
2013-02-06 20:34:33 -04:00
msg = PSTR ( " on its LEFT side " ) ;
2012-11-05 00:27:03 -04:00
break ;
case 2 :
2013-02-06 20:34:33 -04:00
msg = PSTR ( " on its RIGHT side " ) ;
2012-11-05 00:27:03 -04:00
break ;
case 3 :
2013-02-06 20:34:33 -04:00
msg = PSTR ( " nose DOWN " ) ;
2012-11-05 00:27:03 -04:00
break ;
case 4 :
2013-02-06 20:34:33 -04:00
msg = PSTR ( " nose UP " ) ;
2012-11-05 00:27:03 -04:00
break ;
2012-12-16 03:23:17 -04:00
default : // default added to avoid compiler warning
2012-11-05 00:27:03 -04:00
case 5 :
2013-02-06 20:34:33 -04:00
msg = PSTR ( " on its BACK " ) ;
2012-11-05 00:27:03 -04:00
break ;
}
2012-12-19 18:26:19 -04:00
interact - > printf_P (
2013-12-27 21:41:49 -04:00
PSTR ( " Place vehicle %S and press any key. \n " ) , msg ) ;
2012-11-05 00:27:03 -04:00
2012-10-11 21:27:19 -03:00
// wait for user input
2014-08-07 00:09:17 -03:00
if ( ! interact - > blocking_read ( ) ) {
2014-04-17 12:10:11 -03:00
//No need to use interact->printf_P for an error, blocking_read does this when it fails
goto failed ;
}
2012-11-05 00:27:03 -04:00
// clear out any existing samples from ins
update ( ) ;
2013-09-26 21:33:08 -03:00
// average 32 samples
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
samples [ k ] [ i ] = Vector3f ( ) ;
}
2013-09-26 21:33:08 -03:00
uint8_t num_samples = 0 ;
while ( num_samples < 32 ) {
2013-10-08 03:28:39 -03:00
if ( ! wait_for_sample ( 1000 ) ) {
interact - > printf_P ( PSTR ( " Failed to get INS sample \n " ) ) ;
2013-12-08 18:50:12 -04:00
goto failed ;
2013-09-26 21:33:08 -03:00
}
2013-10-08 03:28:39 -03:00
// read samples from ins
update ( ) ;
// capture sample
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
samples [ k ] [ i ] + = get_accel ( k ) ;
}
2013-10-08 03:28:39 -03:00
hal . scheduler - > delay ( 10 ) ;
num_samples + + ;
2012-11-05 00:27:03 -04:00
}
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
samples [ k ] [ i ] / = num_samples ;
}
2012-11-05 00:27:03 -04:00
}
// run the calibration routine
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
bool success = _calibrate_accel ( samples [ k ] , new_offsets [ k ] , new_scaling [ k ] ) ;
interact - > printf_P ( PSTR ( " Offsets[%u]: %.2f %.2f %.2f \n " ) ,
( unsigned ) k ,
new_offsets [ k ] . x , new_offsets [ k ] . y , new_offsets [ k ] . z ) ;
interact - > printf_P ( PSTR ( " Scaling[%u]: %.2f %.2f %.2f \n " ) ,
( unsigned ) k ,
new_scaling [ k ] . x , new_scaling [ k ] . y , new_scaling [ k ] . z ) ;
if ( success ) num_ok + + ;
}
2013-05-08 03:19:06 -03:00
2013-12-08 18:50:12 -04:00
if ( num_ok = = num_accels ) {
2012-12-19 18:26:19 -04:00
interact - > printf_P ( PSTR ( " Calibration successful \n " ) ) ;
2012-11-07 02:20:22 -04:00
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
// set and save calibration
_accel_offset [ k ] . set ( new_offsets [ k ] ) ;
_accel_scale [ k ] . set ( new_scaling [ k ] ) ;
}
2012-11-07 02:20:22 -04:00
_save_parameters ( ) ;
2013-01-30 07:47:57 -04:00
2013-12-08 18:50:12 -04:00
// calculate the trims as well from primary accels and pass back to caller
_calculate_trim ( samples [ 0 ] [ 0 ] , trim_roll , trim_pitch ) ;
2013-01-30 07:47:57 -04:00
2012-11-20 03:26:51 -04:00
return true ;
}
2012-11-07 02:20:22 -04:00
2013-12-08 18:50:12 -04:00
failed :
2013-05-08 03:19:06 -03:00
interact - > printf_P ( PSTR ( " Calibration FAILED \n " ) ) ;
2012-11-20 03:26:51 -04:00
// restore original scaling and offsets
2013-12-08 18:50:12 -04:00
for ( uint8_t k = 0 ; k < num_accels ; k + + ) {
_accel_offset [ k ] . set ( orig_offset [ k ] ) ;
_accel_scale [ k ] . set ( orig_scale [ k ] ) ;
}
2012-11-20 03:26:51 -04:00
return false ;
2012-11-05 00:27:03 -04:00
}
2013-04-22 11:55:53 -03:00
/// calibrated - returns true if the accelerometers have been calibrated
/// @note this should not be called while flying because it reads from the eeprom which can be slow
bool AP_InertialSensor : : calibrated ( )
{
2014-07-13 10:05:21 -03:00
// check each accelerometer has offsets saved
for ( uint8_t i = 0 ; i < get_accel_count ( ) ; i + + ) {
if ( ! _accel_offset [ i ] . load ( ) ) {
return false ;
}
}
// if we got this far the accelerometers must have been calibrated
return true ;
2013-04-22 11:55:53 -03:00
}
2012-11-05 00:27:03 -04:00
// _calibrate_model - perform low level accel calibration
// accel_sample are accelerometer samples collected in 6 different positions
// accel_offsets are output from the calibration routine
// accel_scale are output from the calibration routine
// returns true if successful
2012-10-11 21:27:19 -03:00
bool AP_InertialSensor : : _calibrate_accel ( Vector3f accel_sample [ 6 ] ,
2013-12-08 18:50:12 -04:00
Vector3f & accel_offsets , Vector3f & accel_scale )
2012-11-05 00:27:03 -04:00
{
int16_t i ;
int16_t num_iterations = 0 ;
float eps = 0.000000001 ;
float change = 100.0 ;
float data [ 3 ] ;
float beta [ 6 ] ;
float delta [ 6 ] ;
float ds [ 6 ] ;
float JS [ 6 ] [ 6 ] ;
2012-11-07 02:20:22 -04:00
bool success = true ;
2012-11-05 00:27:03 -04:00
// reset
beta [ 0 ] = beta [ 1 ] = beta [ 2 ] = 0 ;
2013-04-05 10:57:46 -03:00
beta [ 3 ] = beta [ 4 ] = beta [ 5 ] = 1.0f / GRAVITY_MSS ;
2012-11-05 00:27:03 -04:00
while ( num_iterations < 20 & & change > eps ) {
num_iterations + + ;
_calibrate_reset_matrices ( ds , JS ) ;
for ( i = 0 ; i < 6 ; i + + ) {
data [ 0 ] = accel_sample [ i ] . x ;
data [ 1 ] = accel_sample [ i ] . y ;
data [ 2 ] = accel_sample [ i ] . z ;
_calibrate_update_matrices ( ds , JS , beta , data ) ;
}
_calibrate_find_delta ( ds , JS , delta ) ;
change = delta [ 0 ] * delta [ 0 ] +
delta [ 0 ] * delta [ 0 ] +
delta [ 1 ] * delta [ 1 ] +
delta [ 2 ] * delta [ 2 ] +
delta [ 3 ] * delta [ 3 ] / ( beta [ 3 ] * beta [ 3 ] ) +
delta [ 4 ] * delta [ 4 ] / ( beta [ 4 ] * beta [ 4 ] ) +
delta [ 5 ] * delta [ 5 ] / ( beta [ 5 ] * beta [ 5 ] ) ;
for ( i = 0 ; i < 6 ; i + + ) {
beta [ i ] - = delta [ i ] ;
}
}
// copy results out
2013-04-05 10:57:46 -03:00
accel_scale . x = beta [ 3 ] * GRAVITY_MSS ;
accel_scale . y = beta [ 4 ] * GRAVITY_MSS ;
accel_scale . z = beta [ 5 ] * GRAVITY_MSS ;
2012-11-05 00:27:03 -04:00
accel_offsets . x = beta [ 0 ] * accel_scale . x ;
accel_offsets . y = beta [ 1 ] * accel_scale . y ;
accel_offsets . z = beta [ 2 ] * accel_scale . z ;
2012-11-07 02:20:22 -04:00
// sanity check scale
2013-01-10 14:42:24 -04:00
if ( accel_scale . is_nan ( ) | | fabsf ( accel_scale . x - 1.0f ) > 0.1f | | fabsf ( accel_scale . y - 1.0f ) > 0.1f | | fabsf ( accel_scale . z - 1.0f ) > 0.1f ) {
2012-11-07 02:20:22 -04:00
success = false ;
}
2013-07-12 22:43:07 -03:00
// sanity check offsets (3.5 is roughly 3/10th of a G, 5.0 is roughly half a G)
if ( accel_offsets . is_nan ( ) | | fabsf ( accel_offsets . x ) > 3.5f | | fabsf ( accel_offsets . y ) > 3.5f | | fabsf ( accel_offsets . z ) > 3.5f ) {
2012-11-07 02:20:22 -04:00
success = false ;
}
// return success or failure
return success ;
2012-11-05 00:27:03 -04:00
}
2012-10-11 21:27:19 -03:00
void AP_InertialSensor : : _calibrate_update_matrices ( float dS [ 6 ] , float JS [ 6 ] [ 6 ] ,
float beta [ 6 ] , float data [ 3 ] )
2012-11-05 00:27:03 -04:00
{
int16_t j , k ;
float dx , b ;
float residual = 1.0 ;
float jacobian [ 6 ] ;
for ( j = 0 ; j < 3 ; j + + ) {
b = beta [ 3 + j ] ;
dx = ( float ) data [ j ] - beta [ j ] ;
residual - = b * b * dx * dx ;
2013-01-10 14:42:24 -04:00
jacobian [ j ] = 2.0f * b * b * dx ;
jacobian [ 3 + j ] = - 2.0f * b * dx * dx ;
2012-11-05 00:27:03 -04:00
}
for ( j = 0 ; j < 6 ; j + + ) {
dS [ j ] + = jacobian [ j ] * residual ;
for ( k = 0 ; k < 6 ; k + + ) {
JS [ j ] [ k ] + = jacobian [ j ] * jacobian [ k ] ;
}
}
}
// _calibrate_reset_matrices - clears matrices
void AP_InertialSensor : : _calibrate_reset_matrices ( float dS [ 6 ] , float JS [ 6 ] [ 6 ] )
{
int16_t j , k ;
for ( j = 0 ; j < 6 ; j + + ) {
2013-01-10 14:42:24 -04:00
dS [ j ] = 0.0f ;
2012-11-05 00:27:03 -04:00
for ( k = 0 ; k < 6 ; k + + ) {
2013-01-10 14:42:24 -04:00
JS [ j ] [ k ] = 0.0f ;
2012-11-05 00:27:03 -04:00
}
}
}
void AP_InertialSensor : : _calibrate_find_delta ( float dS [ 6 ] , float JS [ 6 ] [ 6 ] , float delta [ 6 ] )
{
//Solve 6-d matrix equation JS*x = dS
//first put in upper triangular form
int16_t i , j , k ;
float mu ;
//make upper triangular
for ( i = 0 ; i < 6 ; i + + ) {
//eliminate all nonzero entries below JS[i][i]
for ( j = i + 1 ; j < 6 ; j + + ) {
mu = JS [ i ] [ j ] / JS [ i ] [ i ] ;
2013-01-10 14:42:24 -04:00
if ( mu ! = 0.0f ) {
2012-11-05 00:27:03 -04:00
dS [ j ] - = mu * dS [ i ] ;
for ( k = j ; k < 6 ; k + + ) {
JS [ k ] [ j ] - = mu * JS [ k ] [ i ] ;
}
}
}
}
//back-substitute
for ( i = 5 ; i > = 0 ; i - - ) {
dS [ i ] / = JS [ i ] [ i ] ;
2013-01-10 14:42:24 -04:00
JS [ i ] [ i ] = 1.0f ;
2012-11-05 00:27:03 -04:00
for ( j = 0 ; j < i ; j + + ) {
mu = JS [ i ] [ j ] ;
dS [ j ] - = mu * dS [ i ] ;
2013-01-10 14:42:24 -04:00
JS [ i ] [ j ] = 0.0f ;
2012-11-05 00:27:03 -04:00
}
}
for ( i = 0 ; i < 6 ; i + + ) {
delta [ i ] = dS [ i ] ;
}
2012-11-07 02:20:22 -04:00
}
2012-11-20 03:41:04 -04:00
2013-01-30 07:47:57 -04:00
// _calculate_trim - calculates the x and y trim angles (in radians) given a raw accel sample (i.e. no scaling or offsets applied) taken when the vehicle was level
void AP_InertialSensor : : _calculate_trim ( Vector3f accel_sample , float & trim_roll , float & trim_pitch )
{
// scale sample and apply offsets
2013-12-08 18:50:12 -04:00
Vector3f accel_scale = _accel_scale [ 0 ] . get ( ) ;
Vector3f accel_offsets = _accel_offset [ 0 ] . get ( ) ;
2013-01-30 07:47:57 -04:00
Vector3f scaled_accels_x ( accel_sample . x * accel_scale . x - accel_offsets . x ,
0 ,
accel_sample . z * accel_scale . z - accel_offsets . z ) ;
Vector3f scaled_accels_y ( 0 ,
accel_sample . y * accel_scale . y - accel_offsets . y ,
accel_sample . z * accel_scale . z - accel_offsets . z ) ;
// calculate x and y axis angle (i.e. roll and pitch angles)
Vector3f vertical = Vector3f ( 0 , 0 , - 1 ) ;
trim_roll = scaled_accels_y . angle ( vertical ) ;
trim_pitch = scaled_accels_x . angle ( vertical ) ;
// angle call doesn't return the sign so take care of it here
if ( scaled_accels_y . y > 0 ) {
trim_roll = - trim_roll ;
}
if ( scaled_accels_x . x < 0 ) {
trim_pitch = - trim_pitch ;
}
}
2012-11-20 03:41:04 -04:00
# endif // __AVR_ATmega1280__