/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- #include #include "AP_Compass.h" // don't allow any axis of the offset to go above 2000 #define COMPASS_OFS_LIMIT 2000 /* * this offset learning algorithm is inspired by this paper from Bill Premerlani * * http://gentlenav.googlecode.com/files/MagnetometerOffsetNullingRevisited.pdf * * The base algorithm works well, but is quite sensitive to * noise. After long discussions with Bill, the following changes were * made: * * 1) we keep a history buffer that effectively divides the mag * vectors into a set of N streams. The algorithm is run on the * streams separately * * 2) within each stream we only calculate a change when the mag * vector has changed by a significant amount. * * This gives us the property that we learn quickly if there is no * noise, but still learn correctly (and slowly) in the face of lots of * noise. */ void Compass::learn_offsets(void) { if (_learn == 0) { // auto-calibration is disabled return; } // this gain is set so we converge on the offsets in about 5 // minutes with a 10Hz compass const float gain = 0.01f; const float max_change = 10.0f; const float min_diff = 50.0f; if (!_null_init_done) { // first time through _null_init_done = true; for (uint8_t k=0; k max_change) { diff *= max_change / length; } Vector3f new_offsets = _state[k].offset.get() - diff; if (new_offsets.is_nan()) { // don't apply bad offsets continue; } // constrain offsets new_offsets.x = constrain_float(new_offsets.x, -COMPASS_OFS_LIMIT, COMPASS_OFS_LIMIT); new_offsets.y = constrain_float(new_offsets.y, -COMPASS_OFS_LIMIT, COMPASS_OFS_LIMIT); new_offsets.z = constrain_float(new_offsets.z, -COMPASS_OFS_LIMIT, COMPASS_OFS_LIMIT); // set the new offsets _state[k].offset.set(new_offsets); } }