2017-01-20 05:08:34 -04:00
#! /usr/bin/env python
"""
2017-01-24 19:42:15 -04:00
Reads in IMU data from a static thermal calibration test and performs
a curve fit of gyro , accel and baro bias vs temperature
2017-01-20 05:08:34 -04:00
Data can be gathered using the following sequence :
2017-01-24 19:42:15 -04:00
1 ) Set the TC_A_ENABLE , TC_B_ENABLE and TC_G_ENABLE parameters to 0 to
thermal compensation and reboot
2017-01-20 05:08:34 -04:00
2 ) Perform a gyro and accel cal
2 ) Set the SYS_LOGGER parameter to 1 to use the new system logger
2017-01-24 19:42:15 -04:00
3 ) Set the SDLOG_MODE parameter to 3 to enable logging of sensor data
for calibration and power off
2017-01-20 05:08:34 -04:00
4 ) Cold soak the board for 30 minutes
5 ) Move to a warm dry environment .
6 ) Apply power for 45 minutes , keeping the board still .
7 ) Remove power and extract the . ulog file
8 ) Open a terminal window in the script file directory
2017-01-24 19:42:15 -04:00
9 ) Run the script file ' python process_sensor_caldata.py
< full path name to . ulog file >
2017-01-20 05:08:34 -04:00
2017-01-24 19:42:15 -04:00
Outputs thermal compensation parameters in a file named
< inputfilename > . params which can be loaded onto the
board using QGroundControl
2017-01-20 05:08:34 -04:00
Outputs summary plots in a pdf file named < inputfilename > . pdf
"""
2017-01-24 19:42:15 -04:00
from __future__ import print_function
2017-01-20 05:08:34 -04:00
2017-01-24 19:42:15 -04:00
import argparse
import os
import matplotlib . pyplot as plt
import numpy as np
2017-01-20 05:08:34 -04:00
2017-01-24 19:42:15 -04:00
import pyulog
class Param ( dict ) :
def __init__ ( self , name , val ) :
"""
Initialize a param dict
"""
self . name = name
self . val = val
def temp_calibration ( data , topic , fields , units , label ) :
"""
Performe a temperature calibration on a sensor .
"""
# pylint: disable=no-member
params = { }
int_params = [ ' ID ' ]
float_params = [
' TMIN ' , ' TMAX ' , ' TREF ' ,
' X0_0 ' , ' X1_0 ' , ' X2_0 ' , ' X3_0 ' ,
' X0_1 ' , ' X1_1 ' , ' X2_1 ' , ' X3_1 ' ,
' X0_2 ' , ' X1_2 ' , ' X2_2 ' , ' X3_2 ' ,
' SCL_0 ' , ' SCL_1 ' , ' SCL_2 '
]
# define data dictionary of thermal correction parameters
for field in int_params :
params [ field ] = {
' val ' : 0 ,
' type ' : ' INT ' ,
}
for field in float_params : params [ field ] = {
' val ' : 0 ,
' type ' : ' FLOAT ' ,
}
2017-01-26 17:42:36 -04:00
# curve fit the data for corrections - note corrections have same sign as sensor bias and will need to be subtracted from the raw reading to remove the bias
2017-01-24 19:42:15 -04:00
try :
params [ ' ID ' ] [ ' val ' ] = int ( np . median ( data [ ' device_id ' ] ) )
except :
print ( ' no device id ' )
pass
# find the min, max and reference temperature
params [ ' TMIN ' ] [ ' val ' ] = float ( np . amin ( data [ ' temperature ' ] ) )
params [ ' TMAX ' ] [ ' val ' ] = float ( np . amax ( data [ ' temperature ' ] ) )
params [ ' TREF ' ] [ ' val ' ] = float ( 0.5 * ( params [ ' TMIN ' ] [ ' val ' ] + params [ ' TMAX ' ] [ ' val ' ] ) )
temp_rel = data [ ' temperature ' ] - params [ ' TREF ' ] [ ' val ' ]
temp_rel_resample = np . linspace (
float ( params [ ' TMIN ' ] [ ' val ' ] - params [ ' TREF ' ] [ ' val ' ] ) ,
float ( params [ ' TMAX ' ] [ ' val ' ] - params [ ' TREF ' ] [ ' val ' ] ) , 100 )
temp_resample = temp_rel_resample + params [ ' TREF ' ] [ ' val ' ]
for i , field in enumerate ( fields ) :
2017-01-26 17:42:36 -04:00
coef = np . polyfit ( temp_rel , data [ field ] , 3 )
2017-01-24 19:42:15 -04:00
for j in range ( 3 ) :
params [ ' X {:d} _ {:d} ' . format ( 3 - j , i ) ] [ ' val ' ] = float ( coef [ j ] )
fit_coef = np . poly1d ( coef )
resample = fit_coef ( temp_rel_resample )
# draw plots
plt . subplot ( len ( fields ) , 1 , i + 1 )
plt . plot ( data [ ' temperature ' ] , data [ field ] , ' b ' )
2017-01-26 17:42:36 -04:00
plt . plot ( temp_resample , resample , ' r ' )
2017-01-24 19:42:15 -04:00
plt . title ( ' {:s} Bias vs Temperature ' . format ( topic ) )
plt . ylabel ( ' {:s} bias {:s} ' . format ( field , units ) )
plt . xlabel ( ' temperature (degC) ' )
plt . grid ( )
return params
def process_file ( log_path , out_path , template_path ) :
"""
Command line interface to temperature calibration .
"""
log = pyulog . ULog ( log_path , ' sensor_gyro, sensor_accel, sensor_baro ' )
data = { }
for d in log . data_list :
data [ ' {:s} _ {:d} ' . format ( d . name , d . multi_id ) ] = d . data
params = { }
# open file to save plots to PDF
# from matplotlib.backends.backend_pdf import PdfPages
# output_plot_filename = ulog_file_name + ".pdf"
# pp = PdfPages(output_plot_filename)
configs = [
{
' msg ' : ' sensor_gyro ' ,
' fields ' : [ ' x ' , ' y ' , ' z ' ] ,
' units ' : ' rad/s ' ,
' label ' : ' TC_G '
} ,
{
' msg ' : ' sensor_accel ' ,
' fields ' : [ ' x ' , ' y ' , ' z ' ] ,
' units ' : ' m/s^2 ' ,
' label ' : ' TC_A '
} ,
{
' msg ' : ' sensor_baro ' ,
' fields ' : [ ' pressure ' ] ,
' units ' : ' m ' ,
' label ' : ' TC_B '
} ,
]
for config in configs :
for d in log . data_list :
if d . name == config [ ' msg ' ] :
plt . figure ( figsize = ( 20 , 13 ) )
topic = ' {:s} _ {:d} ' . format ( d . name , d . multi_id )
print ( ' found {:s} data ' . format ( topic ) )
label = ' {:s} {:d} ' . format (
config [ ' label ' ] , d . multi_id )
params [ topic ] = {
' params ' : temp_calibration (
data = d . data , topic = topic ,
fields = config [ ' fields ' ] ,
units = config [ ' units ' ] ,
label = label ) ,
' label ' : label
}
plt . savefig ( ' {:s} _cal.pdf ' . format ( topic ) )
# JSON file generation
# import json
# print(json.dumps(params, indent=2))
body = ' '
for sensor in sorted ( params . keys ( ) ) :
for param in sorted ( params [ sensor ] [ ' params ' ] . keys ( ) ) :
label = params [ sensor ] [ ' label ' ]
pdict = params [ sensor ] [ ' params ' ]
if pdict [ param ] [ ' type ' ] == ' INT ' :
type_id = 6
elif pdict [ param ] [ ' type ' ] == ' FLOAT ' :
type_id = 9
val = pdict [ param ] [ ' val ' ]
name = ' {:s} _ {:s} ' . format ( label , param )
body + = " 1 \t 1 \t {name:20s} \t {val:15g} \t {type_id:5d} \n " . format ( * * locals ( ) )
# simple template file output
text = """ # Sensor thermal compensation parameters
#
# Vehicle-Id Component-Id Name Value Type
{ body : s }
""" .format(body=body)
with open ( out_path , ' w ' ) as f :
f . write ( text )
if __name__ == " __main__ " :
parser = argparse . ArgumentParser (
description = ' Analyse the sensor_gyro message data ' )
parser . add_argument ( ' filename ' , metavar = ' file.ulg ' , help = ' ULog input file ' )
args = parser . parse_args ( )
ulog_file_name = args . filename
template_path = os . path . join ( os . path . dirname (
os . path . realpath ( __file__ ) ) , ' templates ' )
process_file ( log_path = args . filename , out_path = ulog_file_name . replace ( ' ulg ' , ' params ' ) ,
template_path = template_path )
# vim: set et fenc=utf-8 ff=unix sts=0 sw=4 ts=4 :