|
|
|
@ -57,6 +57,7 @@
@@ -57,6 +57,7 @@
|
|
|
|
|
* http://en.wikipedia.org/wiki/Levenberg%E2%80%93Marquardt_algorithm
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#include "AP_Compass.h" |
|
|
|
|
#include "CompassCalibrator.h" |
|
|
|
|
#include <AP_HAL/AP_HAL.h> |
|
|
|
|
#include <AP_Math/AP_GeodesicGrid.h> |
|
|
|
@ -75,131 +76,111 @@ extern const AP_HAL::HAL& hal;
@@ -75,131 +76,111 @@ extern const AP_HAL::HAL& hal;
|
|
|
|
|
|
|
|
|
|
CompassCalibrator::CompassCalibrator() |
|
|
|
|
{ |
|
|
|
|
stop(); |
|
|
|
|
set_status(Status::NOT_STARTED); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Request to cancel calibration
|
|
|
|
|
void CompassCalibrator::stop() |
|
|
|
|
{ |
|
|
|
|
set_status(Status::NOT_STARTED); |
|
|
|
|
WITH_SEMAPHORE(state_sem); |
|
|
|
|
_requested_status = Status::NOT_STARTED; |
|
|
|
|
_status_set_requested = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CompassCalibrator::set_orientation(enum Rotation orientation, bool is_external, bool fix_orientation) |
|
|
|
|
{ |
|
|
|
|
_check_orientation = true; |
|
|
|
|
_orientation = orientation; |
|
|
|
|
_orig_orientation = orientation; |
|
|
|
|
_is_external = is_external; |
|
|
|
|
_fix_orientation = fix_orientation; |
|
|
|
|
WITH_SEMAPHORE(state_sem); |
|
|
|
|
cal_settings.check_orientation = true; |
|
|
|
|
cal_settings.orientation = orientation; |
|
|
|
|
cal_settings.orig_orientation = orientation; |
|
|
|
|
cal_settings.is_external = is_external; |
|
|
|
|
cal_settings.fix_orientation = fix_orientation; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CompassCalibrator::start(bool retry, float delay, uint16_t offset_max, uint8_t compass_idx) |
|
|
|
|
void CompassCalibrator::start(bool retry, float delay, uint16_t offset_max, uint8_t compass_idx, float tolerance) |
|
|
|
|
{ |
|
|
|
|
if (running()) { |
|
|
|
|
if (compass_idx > COMPASS_MAX_INSTANCES) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
_offset_max = offset_max; |
|
|
|
|
_attempt = 1; |
|
|
|
|
_retry = retry; |
|
|
|
|
_delay_start_sec = delay; |
|
|
|
|
_start_time_ms = AP_HAL::millis(); |
|
|
|
|
_compass_idx = compass_idx; |
|
|
|
|
set_status(Status::WAITING_TO_START); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CompassCalibrator::get_calibration(Vector3f &offsets, Vector3f &diagonals, Vector3f &offdiagonals, float &scale_factor) |
|
|
|
|
{ |
|
|
|
|
if (_status != Status::SUCCESS) { |
|
|
|
|
WITH_SEMAPHORE(state_sem); |
|
|
|
|
// Don't do this while we are already started
|
|
|
|
|
if (_running()) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
offsets = _params.offset; |
|
|
|
|
diagonals = _params.diag; |
|
|
|
|
offdiagonals = _params.offdiag; |
|
|
|
|
scale_factor = _params.scale_factor; |
|
|
|
|
cal_settings.offset_max = offset_max; |
|
|
|
|
cal_settings.attempt = 1; |
|
|
|
|
cal_settings.retry = retry; |
|
|
|
|
cal_settings.delay_start_sec = delay; |
|
|
|
|
cal_settings.start_time_ms = AP_HAL::millis(); |
|
|
|
|
cal_settings.compass_idx = compass_idx; |
|
|
|
|
cal_settings.tolerance = tolerance; |
|
|
|
|
|
|
|
|
|
// Request status change to Waiting to start
|
|
|
|
|
_requested_status = Status::WAITING_TO_START; |
|
|
|
|
_status_set_requested = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float CompassCalibrator::get_completion_percent() const |
|
|
|
|
// Record point mag sample and associated attitude sample to intermediate struct
|
|
|
|
|
void CompassCalibrator::new_sample(const Vector3f& sample) |
|
|
|
|
{ |
|
|
|
|
// first sampling step is 1/3rd of the progress bar
|
|
|
|
|
// never return more than 99% unless _status is Status::SUCCESS
|
|
|
|
|
switch (_status) { |
|
|
|
|
case Status::NOT_STARTED: |
|
|
|
|
case Status::WAITING_TO_START: |
|
|
|
|
return 0.0f; |
|
|
|
|
case Status::RUNNING_STEP_ONE: |
|
|
|
|
return 33.3f * _samples_collected/COMPASS_CAL_NUM_SAMPLES; |
|
|
|
|
case Status::RUNNING_STEP_TWO: |
|
|
|
|
return 33.3f + 65.7f*((float)(_samples_collected-_samples_thinned)/(COMPASS_CAL_NUM_SAMPLES-_samples_thinned)); |
|
|
|
|
case Status::SUCCESS: |
|
|
|
|
return 100.0f; |
|
|
|
|
case Status::FAILED: |
|
|
|
|
case Status::BAD_ORIENTATION: |
|
|
|
|
case Status::BAD_RADIUS: |
|
|
|
|
return 0.0f; |
|
|
|
|
}; |
|
|
|
|
// will not get here if the compiler is doing its job (no default clause)
|
|
|
|
|
return 0.0f; |
|
|
|
|
WITH_SEMAPHORE(sample_sem); |
|
|
|
|
_last_sample.set(sample); |
|
|
|
|
_last_sample.att.set_from_ahrs(); |
|
|
|
|
_new_sample = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// update completion mask based on latest sample
|
|
|
|
|
// used to ensure we have collected samples in all directions
|
|
|
|
|
void CompassCalibrator::update_completion_mask(const Vector3f& v) |
|
|
|
|
{ |
|
|
|
|
Matrix3f softiron { |
|
|
|
|
_params.diag.x, _params.offdiag.x, _params.offdiag.y, |
|
|
|
|
_params.offdiag.x, _params.diag.y, _params.offdiag.z, |
|
|
|
|
_params.offdiag.y, _params.offdiag.z, _params.diag.z |
|
|
|
|
}; |
|
|
|
|
Vector3f corrected = softiron * (v + _params.offset); |
|
|
|
|
int section = AP_GeodesicGrid::section(corrected, true); |
|
|
|
|
if (section < 0) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
_completion_mask[section / 8] |= 1 << (section % 8); |
|
|
|
|
bool CompassCalibrator::failed() { |
|
|
|
|
WITH_SEMAPHORE(state_sem); |
|
|
|
|
return (cal_state.status == Status::FAILED || |
|
|
|
|
cal_state.status == Status::BAD_ORIENTATION ||
|
|
|
|
|
cal_state.status == Status::BAD_RADIUS); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// reset and update the completion mask using all samples in the sample buffer
|
|
|
|
|
void CompassCalibrator::update_completion_mask() |
|
|
|
|
{ |
|
|
|
|
memset(_completion_mask, 0, sizeof(_completion_mask)); |
|
|
|
|
for (int i = 0; i < _samples_collected; i++) { |
|
|
|
|
update_completion_mask(_sample_buffer[i].get()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool CompassCalibrator::running() { |
|
|
|
|
WITH_SEMAPHORE(state_sem);
|
|
|
|
|
return (cal_state.status == Status::RUNNING_STEP_ONE || cal_state.status == Status::RUNNING_STEP_TWO); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool CompassCalibrator::check_for_timeout() |
|
|
|
|
{ |
|
|
|
|
uint32_t tnow = AP_HAL::millis(); |
|
|
|
|
if (running() && tnow - _last_sample_ms > 1000) { |
|
|
|
|
_retry = false; |
|
|
|
|
set_status(Status::FAILED); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
const CompassCalibrator::Report CompassCalibrator::get_report() { |
|
|
|
|
WITH_SEMAPHORE(state_sem); |
|
|
|
|
return cal_report; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CompassCalibrator::new_sample(const Vector3f& sample) |
|
|
|
|
const CompassCalibrator::State CompassCalibrator::get_state() { |
|
|
|
|
WITH_SEMAPHORE(state_sem); |
|
|
|
|
return cal_state; |
|
|
|
|
} |
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
|
////////////////////// PRIVATE METHODS //////////////////////
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void CompassCalibrator::update() |
|
|
|
|
{ |
|
|
|
|
_last_sample_ms = AP_HAL::millis(); |
|
|
|
|
|
|
|
|
|
if (_status == Status::WAITING_TO_START) { |
|
|
|
|
set_status(Status::RUNNING_STEP_ONE); |
|
|
|
|
} |
|
|
|
|
//pickup samples from intermediate struct
|
|
|
|
|
pull_sample(); |
|
|
|
|
|
|
|
|
|
if (running() && _samples_collected < COMPASS_CAL_NUM_SAMPLES && accept_sample(sample)) { |
|
|
|
|
update_completion_mask(sample); |
|
|
|
|
_sample_buffer[_samples_collected].set(sample); |
|
|
|
|
_sample_buffer[_samples_collected].att.set_from_ahrs(); |
|
|
|
|
_samples_collected++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
WITH_SEMAPHORE(state_sem); |
|
|
|
|
//update_settings
|
|
|
|
|
if (!running()) { |
|
|
|
|
update_cal_settings(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CompassCalibrator::update(bool &failure) |
|
|
|
|
{ |
|
|
|
|
failure = false; |
|
|
|
|
//update requested state
|
|
|
|
|
if (_status_set_requested) { |
|
|
|
|
_status_set_requested = false; |
|
|
|
|
set_status(_requested_status); |
|
|
|
|
} |
|
|
|
|
//update report and status
|
|
|
|
|
update_cal_status(); |
|
|
|
|
update_cal_report(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// collect the minimum number of samples
|
|
|
|
|
if (!fitting()) { |
|
|
|
|
if (!_fitting()) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -207,7 +188,6 @@ void CompassCalibrator::update(bool &failure)
@@ -207,7 +188,6 @@ void CompassCalibrator::update(bool &failure)
|
|
|
|
|
if (_fit_step >= 10) { |
|
|
|
|
if (is_equal(_fitness, _initial_fitness) || isnan(_fitness)) { // if true, means that fitness is diverging instead of converging
|
|
|
|
|
set_status(Status::FAILED); |
|
|
|
|
failure = true; |
|
|
|
|
} else { |
|
|
|
|
set_status(Status::RUNNING_STEP_TWO); |
|
|
|
|
} |
|
|
|
@ -224,7 +204,6 @@ void CompassCalibrator::update(bool &failure)
@@ -224,7 +204,6 @@ void CompassCalibrator::update(bool &failure)
|
|
|
|
|
set_status(Status::SUCCESS); |
|
|
|
|
} else { |
|
|
|
|
set_status(Status::FAILED); |
|
|
|
|
failure = true; |
|
|
|
|
} |
|
|
|
|
} else if (_fit_step < 15) { |
|
|
|
|
run_sphere_fit(); |
|
|
|
@ -236,17 +215,124 @@ void CompassCalibrator::update(bool &failure)
@@ -236,17 +215,124 @@ void CompassCalibrator::update(bool &failure)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
|
////////////////////// PRIVATE METHODS //////////////////////
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
|
bool CompassCalibrator::running() const |
|
|
|
|
void CompassCalibrator::pull_sample() |
|
|
|
|
{ |
|
|
|
|
CompassSample mag_sample; |
|
|
|
|
{ |
|
|
|
|
WITH_SEMAPHORE(sample_sem); |
|
|
|
|
if (!_new_sample) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
if (_status == Status::WAITING_TO_START) { |
|
|
|
|
set_status(Status::RUNNING_STEP_ONE); |
|
|
|
|
} |
|
|
|
|
_new_sample = false; |
|
|
|
|
mag_sample = _last_sample; |
|
|
|
|
} |
|
|
|
|
if (_running() && _samples_collected < COMPASS_CAL_NUM_SAMPLES && accept_sample(mag_sample.get())) { |
|
|
|
|
update_completion_mask(mag_sample.get()); |
|
|
|
|
_sample_buffer[_samples_collected] = mag_sample; |
|
|
|
|
_samples_collected++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CompassCalibrator::update_cal_settings() |
|
|
|
|
{ |
|
|
|
|
_tolerance = cal_settings.tolerance; |
|
|
|
|
_check_orientation = cal_settings.check_orientation; |
|
|
|
|
_orientation = cal_settings.orientation; |
|
|
|
|
_orig_orientation = cal_settings.orig_orientation; |
|
|
|
|
_is_external = cal_settings.is_external; |
|
|
|
|
_fix_orientation = cal_settings.fix_orientation; |
|
|
|
|
_offset_max = cal_settings.offset_max; |
|
|
|
|
_attempt = cal_settings.attempt; |
|
|
|
|
_retry = cal_settings.retry; |
|
|
|
|
_delay_start_sec = cal_settings.delay_start_sec; |
|
|
|
|
_start_time_ms = cal_settings.start_time_ms; |
|
|
|
|
_compass_idx = cal_settings.compass_idx; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// update completion mask based on latest sample
|
|
|
|
|
// used to ensure we have collected samples in all directions
|
|
|
|
|
void CompassCalibrator::update_completion_mask(const Vector3f& v) |
|
|
|
|
{ |
|
|
|
|
Matrix3f softiron { |
|
|
|
|
_params.diag.x, _params.offdiag.x, _params.offdiag.y, |
|
|
|
|
_params.offdiag.x, _params.diag.y, _params.offdiag.z, |
|
|
|
|
_params.offdiag.y, _params.offdiag.z, _params.diag.z |
|
|
|
|
}; |
|
|
|
|
Vector3f corrected = softiron * (v + _params.offset); |
|
|
|
|
int section = AP_GeodesicGrid::section(corrected, true); |
|
|
|
|
if (section < 0) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
_completion_mask[section / 8] |= 1 << (section % 8); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// reset and update the completion mask using all samples in the sample buffer
|
|
|
|
|
void CompassCalibrator::update_completion_mask() |
|
|
|
|
{ |
|
|
|
|
memset(_completion_mask, 0, sizeof(_completion_mask)); |
|
|
|
|
for (int i = 0; i < _samples_collected; i++) { |
|
|
|
|
update_completion_mask(_sample_buffer[i].get()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CompassCalibrator::update_cal_status() |
|
|
|
|
{ |
|
|
|
|
cal_state.status = _status; |
|
|
|
|
cal_state.attempt = _attempt; |
|
|
|
|
memcpy(cal_state.completion_mask, _completion_mask, sizeof(completion_mask_t)); |
|
|
|
|
cal_state.completion_pct = 0.0f; |
|
|
|
|
// first sampling step is 1/3rd of the progress bar
|
|
|
|
|
// never return more than 99% unless _status is Status::SUCCESS
|
|
|
|
|
switch (_status) { |
|
|
|
|
case Status::NOT_STARTED: |
|
|
|
|
case Status::WAITING_TO_START: |
|
|
|
|
cal_state.completion_pct = 0.0f; |
|
|
|
|
break; |
|
|
|
|
case Status::RUNNING_STEP_ONE: |
|
|
|
|
cal_state.completion_pct = 33.3f * _samples_collected/COMPASS_CAL_NUM_SAMPLES; |
|
|
|
|
break; |
|
|
|
|
case Status::RUNNING_STEP_TWO: |
|
|
|
|
cal_state.completion_pct = 33.3f + 65.7f*((float)(_samples_collected-_samples_thinned)/(COMPASS_CAL_NUM_SAMPLES-_samples_thinned)); |
|
|
|
|
break; |
|
|
|
|
case Status::SUCCESS: |
|
|
|
|
cal_state.completion_pct = 100.0f; |
|
|
|
|
break; |
|
|
|
|
case Status::FAILED: |
|
|
|
|
case Status::BAD_ORIENTATION: |
|
|
|
|
case Status::BAD_RADIUS: |
|
|
|
|
cal_state.completion_pct = 0.0f; |
|
|
|
|
break; |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CompassCalibrator::update_cal_report() |
|
|
|
|
{ |
|
|
|
|
cal_report.status = _status; |
|
|
|
|
cal_report.fitness = sqrtf(_fitness); |
|
|
|
|
cal_report.ofs = _params.offset; |
|
|
|
|
cal_report.diag = _params.diag; |
|
|
|
|
cal_report.offdiag = _params.offdiag; |
|
|
|
|
cal_report.scale_factor = _params.scale_factor; |
|
|
|
|
cal_report.orientation_confidence = _orientation_confidence; |
|
|
|
|
cal_report.original_orientation = _orig_orientation; |
|
|
|
|
cal_report.orientation = _orientation_solution; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// running method for use in thread
|
|
|
|
|
bool CompassCalibrator::_running() const |
|
|
|
|
{ |
|
|
|
|
return _status == Status::RUNNING_STEP_ONE || _status == Status::RUNNING_STEP_TWO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool CompassCalibrator::fitting() const |
|
|
|
|
// fitting method for use in thread
|
|
|
|
|
bool CompassCalibrator::_fitting() const |
|
|
|
|
{ |
|
|
|
|
return running() && (_samples_collected == COMPASS_CAL_NUM_SAMPLES); |
|
|
|
|
return _running() && (_samples_collected == COMPASS_CAL_NUM_SAMPLES); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// initialize fitness before starting a fit
|
|
|
|
@ -825,6 +911,8 @@ bool CompassCalibrator::calculate_orientation(void)
@@ -825,6 +911,8 @@ bool CompassCalibrator::calculate_orientation(void)
|
|
|
|
|
|
|
|
|
|
float variance[ROTATION_MAX_AUTO_ROTATION+1] {}; |
|
|
|
|
|
|
|
|
|
_orientation_solution = _orientation; |
|
|
|
|
|
|
|
|
|
for (enum Rotation r = ROTATION_NONE; r <= ROTATION_MAX_AUTO_ROTATION; r = (enum Rotation)(r+1)) { |
|
|
|
|
// calculate the average implied earth field across all samples
|
|
|
|
|
Vector3f total_ef {}; |
|
|
|
@ -904,6 +992,7 @@ bool CompassCalibrator::calculate_orientation(void)
@@ -904,6 +992,7 @@ bool CompassCalibrator::calculate_orientation(void)
|
|
|
|
|
// we won't change the orientation, but we set _orientation
|
|
|
|
|
// for reporting purposes
|
|
|
|
|
_orientation = besti; |
|
|
|
|
_orientation_solution = besti; |
|
|
|
|
set_status(Status::BAD_ORIENTATION); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
@ -923,6 +1012,7 @@ bool CompassCalibrator::calculate_orientation(void)
@@ -923,6 +1012,7 @@ bool CompassCalibrator::calculate_orientation(void)
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_orientation = besti; |
|
|
|
|
_orientation_solution = besti; |
|
|
|
|
|
|
|
|
|
// re-run the fit to get the diagonals and off-diagonals for the
|
|
|
|
|
// new orientation
|
|
|
|
|