PX4 Firmware
PX4 Autopilot Software http://px4.io
temperature_compensation.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2016 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file temperature_compensation.cpp
36  *
37  * Sensors temperature compensation methods
38  *
39  * @author Paul Riseborough <gncsolns@gmail.com>
40  */
41 
43 #include <parameters/param.h>
44 #include <px4_platform_common/defines.h>
45 #include <px4_platform_common/log.h>
46 
47 namespace sensors
48 {
49 
51 {
52  char nbuf[16];
53  int ret = PX4_ERROR;
54 
55  /* rate gyro calibration parameters */
56  parameter_handles.gyro_tc_enable = param_find("TC_G_ENABLE");
57  int32_t gyro_tc_enabled = 0;
58  ret = param_get(parameter_handles.gyro_tc_enable, &gyro_tc_enabled);
59 
60  if (ret == PX4_OK && gyro_tc_enabled) {
61  for (unsigned j = 0; j < GYRO_COUNT_MAX; j++) {
62  sprintf(nbuf, "TC_G%d_ID", j);
63  parameter_handles.gyro_cal_handles[j].ID = param_find(nbuf);
64 
65  for (unsigned i = 0; i < 3; i++) {
66  sprintf(nbuf, "TC_G%d_X3_%d", j, i);
67  parameter_handles.gyro_cal_handles[j].x3[i] = param_find(nbuf);
68  sprintf(nbuf, "TC_G%d_X2_%d", j, i);
69  parameter_handles.gyro_cal_handles[j].x2[i] = param_find(nbuf);
70  sprintf(nbuf, "TC_G%d_X1_%d", j, i);
71  parameter_handles.gyro_cal_handles[j].x1[i] = param_find(nbuf);
72  sprintf(nbuf, "TC_G%d_X0_%d", j, i);
73  parameter_handles.gyro_cal_handles[j].x0[i] = param_find(nbuf);
74  sprintf(nbuf, "TC_G%d_SCL_%d", j, i);
75  parameter_handles.gyro_cal_handles[j].scale[i] = param_find(nbuf);
76  }
77 
78  sprintf(nbuf, "TC_G%d_TREF", j);
79  parameter_handles.gyro_cal_handles[j].ref_temp = param_find(nbuf);
80  sprintf(nbuf, "TC_G%d_TMIN", j);
81  parameter_handles.gyro_cal_handles[j].min_temp = param_find(nbuf);
82  sprintf(nbuf, "TC_G%d_TMAX", j);
83  parameter_handles.gyro_cal_handles[j].max_temp = param_find(nbuf);
84  }
85  }
86 
87  /* accelerometer calibration parameters */
88  parameter_handles.accel_tc_enable = param_find("TC_A_ENABLE");
89  int32_t accel_tc_enabled = 0;
90  ret = param_get(parameter_handles.accel_tc_enable, &accel_tc_enabled);
91 
92  if (ret == PX4_OK && accel_tc_enabled) {
93  for (unsigned j = 0; j < ACCEL_COUNT_MAX; j++) {
94  sprintf(nbuf, "TC_A%d_ID", j);
95  parameter_handles.accel_cal_handles[j].ID = param_find(nbuf);
96 
97  for (unsigned i = 0; i < 3; i++) {
98  sprintf(nbuf, "TC_A%d_X3_%d", j, i);
99  parameter_handles.accel_cal_handles[j].x3[i] = param_find(nbuf);
100  sprintf(nbuf, "TC_A%d_X2_%d", j, i);
101  parameter_handles.accel_cal_handles[j].x2[i] = param_find(nbuf);
102  sprintf(nbuf, "TC_A%d_X1_%d", j, i);
103  parameter_handles.accel_cal_handles[j].x1[i] = param_find(nbuf);
104  sprintf(nbuf, "TC_A%d_X0_%d", j, i);
105  parameter_handles.accel_cal_handles[j].x0[i] = param_find(nbuf);
106  sprintf(nbuf, "TC_A%d_SCL_%d", j, i);
107  parameter_handles.accel_cal_handles[j].scale[i] = param_find(nbuf);
108  }
109 
110  sprintf(nbuf, "TC_A%d_TREF", j);
111  parameter_handles.accel_cal_handles[j].ref_temp = param_find(nbuf);
112  sprintf(nbuf, "TC_A%d_TMIN", j);
113  parameter_handles.accel_cal_handles[j].min_temp = param_find(nbuf);
114  sprintf(nbuf, "TC_A%d_TMAX", j);
115  parameter_handles.accel_cal_handles[j].max_temp = param_find(nbuf);
116  }
117  }
118 
119  /* barometer calibration parameters */
120  parameter_handles.baro_tc_enable = param_find("TC_B_ENABLE");
121  int32_t baro_tc_enabled = 0;
122  ret = param_get(parameter_handles.baro_tc_enable, &baro_tc_enabled);
123 
124  if (ret == PX4_OK && baro_tc_enabled) {
125  for (unsigned j = 0; j < BARO_COUNT_MAX; j++) {
126  sprintf(nbuf, "TC_B%d_ID", j);
127  parameter_handles.baro_cal_handles[j].ID = param_find(nbuf);
128  sprintf(nbuf, "TC_B%d_X5", j);
129  parameter_handles.baro_cal_handles[j].x5 = param_find(nbuf);
130  sprintf(nbuf, "TC_B%d_X4", j);
131  parameter_handles.baro_cal_handles[j].x4 = param_find(nbuf);
132  sprintf(nbuf, "TC_B%d_X3", j);
133  parameter_handles.baro_cal_handles[j].x3 = param_find(nbuf);
134  sprintf(nbuf, "TC_B%d_X2", j);
135  parameter_handles.baro_cal_handles[j].x2 = param_find(nbuf);
136  sprintf(nbuf, "TC_B%d_X1", j);
137  parameter_handles.baro_cal_handles[j].x1 = param_find(nbuf);
138  sprintf(nbuf, "TC_B%d_X0", j);
139  parameter_handles.baro_cal_handles[j].x0 = param_find(nbuf);
140  sprintf(nbuf, "TC_B%d_SCL", j);
141  parameter_handles.baro_cal_handles[j].scale = param_find(nbuf);
142  sprintf(nbuf, "TC_B%d_TREF", j);
143  parameter_handles.baro_cal_handles[j].ref_temp = param_find(nbuf);
144  sprintf(nbuf, "TC_B%d_TMIN", j);
145  parameter_handles.baro_cal_handles[j].min_temp = param_find(nbuf);
146  sprintf(nbuf, "TC_B%d_TMAX", j);
147  parameter_handles.baro_cal_handles[j].max_temp = param_find(nbuf);
148  }
149  }
150 
151  return PX4_OK;
152 }
153 
155 {
156  int ret = 0;
157 
158  ParameterHandles parameter_handles;
159  ret = initialize_parameter_handles(parameter_handles);
160 
161  if (ret != 0) {
162  return ret;
163  }
164 
165  /* rate gyro calibration parameters */
166  if (!hil_enabled) {
167  param_get(parameter_handles.gyro_tc_enable, &_parameters.gyro_tc_enable);
168 
169  } else {
171  }
172 
173  if (_parameters.gyro_tc_enable == 1) {
174  for (unsigned j = 0; j < GYRO_COUNT_MAX; j++) {
175  if (param_get(parameter_handles.gyro_cal_handles[j].ID, &(_parameters.gyro_cal_data[j].ID)) == PX4_OK) {
179 
180  for (unsigned int i = 0; i < 3; i++) {
181  param_get(parameter_handles.gyro_cal_handles[j].x3[i], &(_parameters.gyro_cal_data[j].x3[i]));
182  param_get(parameter_handles.gyro_cal_handles[j].x2[i], &(_parameters.gyro_cal_data[j].x2[i]));
183  param_get(parameter_handles.gyro_cal_handles[j].x1[i], &(_parameters.gyro_cal_data[j].x1[i]));
184  param_get(parameter_handles.gyro_cal_handles[j].x0[i], &(_parameters.gyro_cal_data[j].x0[i]));
185  param_get(parameter_handles.gyro_cal_handles[j].scale[i], &(_parameters.gyro_cal_data[j].scale[i]));
186  }
187 
188  } else {
189  // Set all cal values to zero and scale factor to unity
190  memset(&_parameters.gyro_cal_data[j], 0, sizeof(_parameters.gyro_cal_data[j]));
191 
192  // Set the scale factor to unity
193  for (unsigned int i = 0; i < 3; i++) {
194  _parameters.gyro_cal_data[j].scale[i] = 1.0f;
195  }
196 
197  PX4_WARN("FAIL GYRO %d CAL PARAM LOAD - USING DEFAULTS", j);
198  ret = PX4_ERROR;
199  }
200  }
201  }
202 
203  /* accelerometer calibration parameters */
204  if (!hil_enabled) {
206 
207  } else {
209  }
210 
211  if (_parameters.accel_tc_enable == 1) {
212  for (unsigned j = 0; j < ACCEL_COUNT_MAX; j++) {
213  if (param_get(parameter_handles.accel_cal_handles[j].ID, &(_parameters.accel_cal_data[j].ID)) == PX4_OK) {
217 
218  for (unsigned int i = 0; i < 3; i++) {
219  param_get(parameter_handles.accel_cal_handles[j].x3[i], &(_parameters.accel_cal_data[j].x3[i]));
220  param_get(parameter_handles.accel_cal_handles[j].x2[i], &(_parameters.accel_cal_data[j].x2[i]));
221  param_get(parameter_handles.accel_cal_handles[j].x1[i], &(_parameters.accel_cal_data[j].x1[i]));
222  param_get(parameter_handles.accel_cal_handles[j].x0[i], &(_parameters.accel_cal_data[j].x0[i]));
223  param_get(parameter_handles.accel_cal_handles[j].scale[i], &(_parameters.accel_cal_data[j].scale[i]));
224  }
225 
226  } else {
227  // Set all cal values to zero and scale factor to unity
228  memset(&_parameters.accel_cal_data[j], 0, sizeof(_parameters.accel_cal_data[j]));
229 
230  // Set the scale factor to unity
231  for (unsigned int i = 0; i < 3; i++) {
232  _parameters.accel_cal_data[j].scale[i] = 1.0f;
233  }
234 
235  PX4_WARN("FAIL ACCEL %d CAL PARAM LOAD - USING DEFAULTS", j);
236  ret = PX4_ERROR;
237  }
238  }
239  }
240 
241  /* barometer calibration parameters */
242  if (!hil_enabled) {
243  param_get(parameter_handles.baro_tc_enable, &_parameters.baro_tc_enable);
244 
245  } else {
247  }
248 
249  if (_parameters.baro_tc_enable == 1) {
250  for (unsigned j = 0; j < BARO_COUNT_MAX; j++) {
251  if (param_get(parameter_handles.baro_cal_handles[j].ID, &(_parameters.baro_cal_data[j].ID)) == PX4_OK) {
255  param_get(parameter_handles.baro_cal_handles[j].x5, &(_parameters.baro_cal_data[j].x5));
256  param_get(parameter_handles.baro_cal_handles[j].x4, &(_parameters.baro_cal_data[j].x4));
257  param_get(parameter_handles.baro_cal_handles[j].x3, &(_parameters.baro_cal_data[j].x3));
258  param_get(parameter_handles.baro_cal_handles[j].x2, &(_parameters.baro_cal_data[j].x2));
259  param_get(parameter_handles.baro_cal_handles[j].x1, &(_parameters.baro_cal_data[j].x1));
260  param_get(parameter_handles.baro_cal_handles[j].x0, &(_parameters.baro_cal_data[j].x0));
261  param_get(parameter_handles.baro_cal_handles[j].scale, &(_parameters.baro_cal_data[j].scale));
262 
263  } else {
264  // Set all cal values to zero and scale factor to unity
265  memset(&_parameters.baro_cal_data[j], 0, sizeof(_parameters.baro_cal_data[j]));
266 
267  // Set the scale factor to unity
268  _parameters.baro_cal_data[j].scale = 1.0f;
269 
270  PX4_WARN("FAIL BARO %d CAL PARAM LOAD - USING DEFAULTS", j);
271  ret = PX4_ERROR;
272  }
273  }
274  }
275 
276  /* the offsets & scales might have changed, so make sure to report that change later when applying the
277  * next corrections
278  */
282 
283  return ret;
284 }
285 
286 bool TemperatureCompensation::calc_thermal_offsets_1D(SensorCalData1D &coef, float measured_temp, float &offset)
287 {
288  bool ret = true;
289 
290  // clip the measured temperature to remain within the calibration range
291  float delta_temp;
292 
293  if (measured_temp > coef.max_temp) {
294  delta_temp = coef.max_temp - coef.ref_temp;
295  ret = false;
296 
297  } else if (measured_temp < coef.min_temp) {
298  delta_temp = coef.min_temp - coef.ref_temp;
299  ret = false;
300 
301  } else {
302  delta_temp = measured_temp - coef.ref_temp;
303 
304  }
305 
306  // calulate the offset
307  float temp_var = delta_temp;
308  offset = coef.x0 + coef.x1 * temp_var;
309  temp_var *= delta_temp;
310  offset += coef.x2 * temp_var;
311  temp_var *= delta_temp;
312  offset += coef.x3 * temp_var;
313  temp_var *= delta_temp;
314  offset += coef.x4 * temp_var;
315  temp_var *= delta_temp;
316  offset += coef.x5 * temp_var;
317 
318  return ret;
319 
320 }
321 
322 bool TemperatureCompensation::calc_thermal_offsets_3D(const SensorCalData3D &coef, float measured_temp, float offset[])
323 {
324  bool ret = true;
325 
326  // clip the measured temperature to remain within the calibration range
327  float delta_temp;
328 
329  if (measured_temp > coef.max_temp) {
330  delta_temp = coef.max_temp - coef.ref_temp;
331  ret = false;
332 
333  } else if (measured_temp < coef.min_temp) {
334  delta_temp = coef.min_temp - coef.ref_temp;
335  ret = false;
336 
337  } else {
338  delta_temp = measured_temp - coef.ref_temp;
339 
340  }
341 
342  // calulate the offsets
343  float delta_temp_2 = delta_temp * delta_temp;
344  float delta_temp_3 = delta_temp_2 * delta_temp;
345 
346  for (uint8_t i = 0; i < 3; i++) {
347  offset[i] = coef.x0[i] + coef.x1[i] * delta_temp + coef.x2[i] * delta_temp_2 + coef.x3[i] * delta_temp_3;
348  }
349 
350  return ret;
351 
352 }
353 
355 {
356  if (_parameters.gyro_tc_enable != 1) {
357  return 0;
358  }
359 
360  return set_sensor_id(device_id, topic_instance, _gyro_data, _parameters.gyro_cal_data, GYRO_COUNT_MAX);
361 }
362 
364 {
365  if (_parameters.accel_tc_enable != 1) {
366  return 0;
367  }
368 
369  return set_sensor_id(device_id, topic_instance, _accel_data, _parameters.accel_cal_data, ACCEL_COUNT_MAX);
370 }
371 
373 {
374  if (_parameters.baro_tc_enable != 1) {
375  return 0;
376  }
377 
378  return set_sensor_id(device_id, topic_instance, _baro_data, _parameters.baro_cal_data, BARO_COUNT_MAX);
379 }
380 
381 template<typename T>
382 int TemperatureCompensation::set_sensor_id(uint32_t device_id, int topic_instance, PerSensorData &sensor_data,
383  const T *sensor_cal_data, uint8_t sensor_count_max)
384 {
385  for (int i = 0; i < sensor_count_max; ++i) {
386  if (device_id == (uint32_t)sensor_cal_data[i].ID) {
387  sensor_data.device_mapping[topic_instance] = i;
388  return i;
389  }
390  }
391 
392  return -1;
393 }
394 
396  float temperature, float *offsets, float *scales)
397 {
398  if (_parameters.gyro_tc_enable != 1) {
399  return 0;
400  }
401 
402  uint8_t mapping = _gyro_data.device_mapping[topic_instance];
403 
404  if (mapping == 255) {
405  return -1;
406  }
407 
408  calc_thermal_offsets_3D(_parameters.gyro_cal_data[mapping], temperature, offsets);
409 
410  // get the sensor scale factors and correct the data
411  for (unsigned axis_index = 0; axis_index < 3; axis_index++) {
412  scales[axis_index] = _parameters.gyro_cal_data[mapping].scale[axis_index];
413  sensor_data(axis_index) = (sensor_data(axis_index) - offsets[axis_index]) * scales[axis_index];
414  }
415 
416  if (fabsf(temperature - _gyro_data.last_temperature[topic_instance]) > 1.0f) {
417  _gyro_data.last_temperature[topic_instance] = temperature;
418  return 2;
419  }
420 
421  return 1;
422 }
423 
425  float temperature, float *offsets, float *scales)
426 {
427  if (_parameters.accel_tc_enable != 1) {
428  return 0;
429  }
430 
431  uint8_t mapping = _accel_data.device_mapping[topic_instance];
432 
433  if (mapping == 255) {
434  return -1;
435  }
436 
437  calc_thermal_offsets_3D(_parameters.accel_cal_data[mapping], temperature, offsets);
438 
439  // get the sensor scale factors and correct the data
440  for (unsigned axis_index = 0; axis_index < 3; axis_index++) {
441  scales[axis_index] = _parameters.accel_cal_data[mapping].scale[axis_index];
442  sensor_data(axis_index) = (sensor_data(axis_index) - offsets[axis_index]) * scales[axis_index];
443  }
444 
445  if (fabsf(temperature - _accel_data.last_temperature[topic_instance]) > 1.0f) {
446  _accel_data.last_temperature[topic_instance] = temperature;
447  return 2;
448  }
449 
450  return 1;
451 }
452 
453 int TemperatureCompensation::apply_corrections_baro(int topic_instance, float &sensor_data, float temperature,
454  float *offsets, float *scales)
455 {
456  if (_parameters.baro_tc_enable != 1) {
457  return 0;
458  }
459 
460  uint8_t mapping = _baro_data.device_mapping[topic_instance];
461 
462  if (mapping == 255) {
463  return -1;
464  }
465 
466  calc_thermal_offsets_1D(_parameters.baro_cal_data[mapping], temperature, *offsets);
467 
468  // get the sensor scale factors and correct the data
469  *scales = _parameters.baro_cal_data[mapping].scale;
470  sensor_data = (sensor_data - *offsets) * *scales;
471 
472  if (fabsf(temperature - _baro_data.last_temperature[topic_instance]) > 1.0f) {
473  _baro_data.last_temperature[topic_instance] = temperature;
474  return 2;
475  }
476 
477  return 1;
478 }
479 
481 {
482  PX4_INFO("Temperature Compensation:");
483  PX4_INFO(" gyro: enabled: %i", _parameters.gyro_tc_enable);
484 
485  if (_parameters.gyro_tc_enable == 1) {
486  for (int i = 0; i < GYRO_COUNT_MAX; ++i) {
487  uint8_t mapping = _gyro_data.device_mapping[i];
488 
489  if (_gyro_data.device_mapping[i] != 255) {
490  PX4_INFO(" using device ID %i for topic instance %i", _parameters.gyro_cal_data[mapping].ID, i);
491  }
492  }
493  }
494 
495  PX4_INFO(" accel: enabled: %i", _parameters.accel_tc_enable);
496 
497  if (_parameters.accel_tc_enable == 1) {
498  for (int i = 0; i < ACCEL_COUNT_MAX; ++i) {
499  uint8_t mapping = _accel_data.device_mapping[i];
500 
501  if (_accel_data.device_mapping[i] != 255) {
502  PX4_INFO(" using device ID %i for topic instance %i", _parameters.accel_cal_data[mapping].ID, i);
503  }
504  }
505  }
506 
507  PX4_INFO(" baro: enabled: %i", _parameters.baro_tc_enable);
508 
509  if (_parameters.baro_tc_enable == 1) {
510  for (int i = 0; i < BARO_COUNT_MAX; ++i) {
511  uint8_t mapping = _baro_data.device_mapping[i];
512 
513  if (_baro_data.device_mapping[i] != 255) {
514  PX4_INFO(" using device ID %i for topic instance %i", _parameters.baro_cal_data[mapping].ID, i);
515  }
516  }
517  }
518 }
519 
520 }
float ref_temp
reference temperature used by the curve-fit
constexpr uint8_t GYRO_COUNT_MAX
Definition: common.h:47
int set_sensor_id_accel(uint32_t device_id, int topic_instance)
__EXPORT int param_get(param_t param, void *val)
Copy the value of a parameter.
Definition: parameters.cpp:589
static int set_sensor_id(uint32_t device_id, int topic_instance, PerSensorData &sensor_data, const T *sensor_cal_data, uint8_t sensor_count_max)
static int32_t device_id[max_accel_sens]
bool calc_thermal_offsets_1D(SensorCalData1D &coef, float measured_temp, float &offset)
Calculate the offset required to compensate the sensor for temperature effects using a 5th order meth...
Global flash based parameter store.
constexpr uint8_t ACCEL_COUNT_MAX
Definition: common.h:48
int parameters_update(bool hil_enabled=false)
(re)load the parameters.
bool calc_thermal_offsets_3D(const SensorCalData3D &coef, float measured_temp, float offset[])
Calculate the offsets required to compensate the sensor for temperature effects If the measured tempe...
Sensor correction methods.
__EXPORT param_t param_find(const char *name)
Look up a parameter by name.
Definition: parameters.cpp:370
Definition: common.h:43
int set_sensor_id_gyro(uint32_t device_id, int topic_instance)
supply information which device_id matches a specific uORB topic_instance (needed if a system has mul...
float min_temp
minimum temperature with valid compensation data
void print_status()
output current configuration status to console
int apply_corrections_baro(int topic_instance, float &sensor_data, float temperature, float *offsets, float *scales)
int apply_corrections_accel(int topic_instance, matrix::Vector3f &sensor_data, float temperature, float *offsets, float *scales)
float max_temp
maximum temperature with valid compensation data
int apply_corrections_gyro(int topic_instance, matrix::Vector3f &sensor_data, float temperature, float *offsets, float *scales)
Apply Thermal corrections to gyro (& other) sensor data.
float last_temperature[SENSOR_COUNT_MAX]
map a topic instance to the parameters index
static int initialize_parameter_handles(ParameterHandles &parameter_handles)
initialize ParameterHandles struct
int set_sensor_id_baro(uint32_t device_id, int topic_instance)
constexpr uint8_t BARO_COUNT_MAX
Definition: common.h:49