AP_TempCalibration.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. This program is free software: you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation, either version 3 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. /*
  14. temperature calibration library
  15. */
  16. #include "AP_TempCalibration.h"
  17. #include <stdio.h>
  18. #include <AP_Baro/AP_Baro.h>
  19. extern const AP_HAL::HAL& hal;
  20. #define TCAL_DEBUG 0
  21. #if TCAL_DEBUG
  22. # define debug(fmt, args ...) do {printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); } while(0)
  23. #else
  24. # define debug(fmt, args ...)
  25. #endif
  26. // table of user settable and learned parameters
  27. const AP_Param::GroupInfo AP_TempCalibration::var_info[] = {
  28. // @Param: _ENABLED
  29. // @DisplayName: Temperature calibration enable
  30. // @Description: Enable temperature calibration. Set to 0 to disable. Set to 1 to use learned values. Set to 2 to learn new values and use the values
  31. // @Values: 0:Disabled,1:Enabled,2:EnableAndLearn
  32. // @User: Advanced
  33. AP_GROUPINFO_FLAGS("_ENABLED", 1, AP_TempCalibration, enabled, TC_DISABLED, AP_PARAM_FLAG_ENABLE),
  34. // @Param: _TEMP_MIN
  35. // @DisplayName: Temperature calibration min learned temperature
  36. // @Description: Minimum learned temperature. This is automatically set by the learning process
  37. // @Units: degC
  38. // @ReadOnly: True
  39. // @Volatile: True
  40. // @User: Advanced
  41. AP_GROUPINFO("_TEMP_MIN", 2, AP_TempCalibration, temp_min, 0),
  42. // 3 was used by a duplicated temp_min entry (do not use in the future!)
  43. // @Param: _TEMP_MAX
  44. // @DisplayName: Temperature calibration max learned temperature
  45. // @Description: Maximum learned temperature. This is automatically set by the learning process
  46. // @Units: degC
  47. // @ReadOnly: True
  48. // @Volatile: True
  49. // @User: Advanced
  50. AP_GROUPINFO("_TEMP_MAX", 4, AP_TempCalibration, temp_max, 0),
  51. // @Param: _BARO_EXP
  52. // @DisplayName: Temperature Calibration barometer exponent
  53. // @Description: Learned exponent for barometer temperature correction
  54. // @ReadOnly: True
  55. // @Volatile: True
  56. // @User: Advanced
  57. AP_GROUPINFO("_BARO_EXP", 5, AP_TempCalibration, baro_exponent, 0),
  58. AP_GROUPEND
  59. };
  60. /*
  61. calculate the correction given an exponent and a temperature
  62. This one parameter correction is deliberately chosen to be very
  63. robust for extrapolation. It fits the characteristics of the
  64. ICM-20789 barometer nicely.
  65. */
  66. float AP_TempCalibration::calculate_correction(float temp, float exponent) const
  67. {
  68. return powf(MAX(temp - Tzero, 0), exponent);
  69. }
  70. /*
  71. setup for learning
  72. */
  73. void AP_TempCalibration::setup_learning(void)
  74. {
  75. learn_temp_start = AP::baro().get_temperature();
  76. learn_temp_step = 0.25;
  77. learn_count = 200;
  78. learn_i = 0;
  79. if (learn_values != nullptr) {
  80. delete [] learn_values;
  81. }
  82. learn_values = new float[learn_count];
  83. if (learn_values == nullptr) {
  84. return;
  85. }
  86. }
  87. /*
  88. calculate the sum of squares range of pressure values we get with
  89. the current data. This is the function we try to minimise in the
  90. calibration
  91. */
  92. float AP_TempCalibration::calculate_p_range(float baro_factor) const
  93. {
  94. float sum = 0;
  95. float P0 = learn_values[0] + calculate_correction(learn_temp_start, baro_factor);
  96. for (uint16_t i=0; i<learn_i; i++) {
  97. if (is_zero(learn_values[i])) {
  98. // gap in the data
  99. continue;
  100. }
  101. float temp = learn_temp_start + learn_temp_step*i;
  102. float correction = calculate_correction(temp, baro_factor);
  103. float P = learn_values[i] + correction;
  104. sum += sq(P - P0);
  105. }
  106. return sum / learn_i;
  107. }
  108. /*
  109. calculate a calibration value
  110. This fits a simple single value power function to the baro data to
  111. find the calibration exponent.
  112. */
  113. void AP_TempCalibration::calculate_calibration(void)
  114. {
  115. float current_err = calculate_p_range(baro_exponent);
  116. float test_exponent = baro_exponent + learn_delta;
  117. float test_err = calculate_p_range(test_exponent);
  118. if (test_err >= current_err) {
  119. test_exponent = baro_exponent - learn_delta;
  120. test_err = calculate_p_range(test_exponent);
  121. }
  122. if (test_exponent <= exp_limit_max &&
  123. test_exponent >= exp_limit_min &&
  124. test_err < current_err) {
  125. // move to new value
  126. debug("CAL: %.2f\n", test_exponent);
  127. if (!is_equal(test_exponent, baro_exponent.get())) {
  128. baro_exponent.set_and_save(test_exponent);
  129. }
  130. temp_min.set_and_save_ifchanged(learn_temp_start);
  131. temp_max.set_and_save_ifchanged(learn_temp_start + learn_i*learn_temp_step);
  132. }
  133. }
  134. /*
  135. update calibration learning
  136. */
  137. void AP_TempCalibration::learn_calibration(void)
  138. {
  139. // just for first baro now
  140. const AP_Baro &baro = AP::baro();
  141. if (!baro.healthy(0) ||
  142. hal.util->get_soft_armed() ||
  143. baro.get_temperature(0) < Tzero) {
  144. return;
  145. }
  146. // if we have any movement then we reset learning
  147. if (learn_values == nullptr ||
  148. !AP::ins().is_still()) {
  149. debug("learn reset\n");
  150. setup_learning();
  151. if (learn_values == nullptr) {
  152. return;
  153. }
  154. }
  155. float temp = baro.get_temperature(0);
  156. float P = baro.get_pressure(0);
  157. uint16_t idx = (temp - learn_temp_start) / learn_temp_step;
  158. if (idx >= learn_count) {
  159. // could change learn_temp_step here
  160. return;
  161. }
  162. if (is_zero(learn_values[idx])) {
  163. learn_values[idx] = P;
  164. debug("learning %u %.2f at %.2f\n", idx, learn_values[idx], temp);
  165. } else {
  166. // filter in new value
  167. learn_values[idx] = 0.9 * learn_values[idx] + 0.1 * P;
  168. }
  169. learn_i = MAX(learn_i, idx);
  170. uint32_t now = AP_HAL::millis();
  171. if (now - last_learn_ms > 100 &&
  172. idx*learn_temp_step > min_learn_temp_range &&
  173. temp - learn_temp_start > temp_max - temp_min) {
  174. last_learn_ms = now;
  175. // run estimation and update parameters
  176. calculate_calibration();
  177. }
  178. }
  179. /*
  180. apply learned calibration for current temperature
  181. */
  182. void AP_TempCalibration::apply_calibration(void)
  183. {
  184. AP_Baro &baro = AP::baro();
  185. // just for first baro now
  186. if (!baro.healthy(0)) {
  187. return;
  188. }
  189. float temp = baro.get_temperature(0);
  190. float correction = calculate_correction(temp, baro_exponent);
  191. baro.set_pressure_correction(0, correction);
  192. }
  193. /*
  194. called at 10Hz from the main thread. This is called both when armed
  195. and disarmed. It only does learning while disarmed, but needs to
  196. supply the corrections to the sensor libraries at all times
  197. */
  198. void AP_TempCalibration::update(void)
  199. {
  200. switch (enabled.get()) {
  201. case TC_DISABLED:
  202. break;
  203. case TC_ENABLE_LEARN:
  204. learn_calibration();
  205. FALLTHROUGH;
  206. case TC_ENABLE_USE:
  207. apply_calibration();
  208. break;
  209. }
  210. }