41 #include <px4_platform_common/defines.h> 42 #include <px4_platform_common/posix.h> 43 #include <px4_platform_common/time.h> 53 #include <mathlib/mathlib.h> 66 unsigned int size,
unsigned int max_iterations,
float delta,
float *sphere_x,
float *sphere_y,
float *sphere_z,
70 float x_sumplain = 0.0f;
72 float x_sumcube = 0.0f;
74 float y_sumplain = 0.0f;
76 float y_sumcube = 0.0f;
78 float z_sumplain = 0.0f;
80 float z_sumcube = 0.0f;
93 for (
unsigned int i = 0; i < size; i++) {
95 float x2 = x[i] * x[i];
96 float y2 = y[i] * y[i];
97 float z2 = z[i] * z[i];
101 x_sumcube += x2 * x[i];
105 y_sumcube += y2 * y[i];
109 z_sumcube += z2 * z[i];
111 xy_sum += x[i] * y[i];
112 xz_sum += x[i] * z[i];
113 yz_sum += y[i] * z[i];
115 x2y_sum += x2 * y[i];
116 x2z_sum += x2 * z[i];
118 y2x_sum += y2 * x[i];
119 y2z_sum += y2 * z[i];
121 z2x_sum += z2 * x[i];
122 z2y_sum += z2 * y[i];
143 float x_sum = x_sumplain / size;
144 float x_sum2 = x_sumsq / size;
145 float x_sum3 = x_sumcube / size;
146 float y_sum = y_sumplain / size;
147 float y_sum2 = y_sumsq / size;
148 float y_sum3 = y_sumcube / size;
149 float z_sum = z_sumplain / size;
150 float z_sum2 = z_sumsq / size;
151 float z_sum3 = z_sumcube / size;
153 float XY = xy_sum / size;
154 float XZ = xz_sum / size;
155 float YZ = yz_sum / size;
156 float X2Y = x2y_sum / size;
157 float X2Z = x2z_sum / size;
158 float Y2X = y2x_sum / size;
159 float Y2Z = y2z_sum / size;
160 float Z2X = z2x_sum / size;
161 float Z2Y = z2y_sum / size;
164 float F0 = x_sum2 + y_sum2 + z_sum2;
165 float F1 = 0.5f * F0;
166 float F2 = -8.0f * (x_sum3 + Y2X + Z2X);
167 float F3 = -8.0f * (X2Y + y_sum3 + Z2Y);
168 float F4 = -8.0f * (X2Z + Y2Z + z_sum3);
179 float QS = A2 + B2 + C2;
180 float QB = -2.0f * (A * x_sum + B * y_sum + C * z_sum);
183 float Rsq = F0 + QB + QS;
186 float Q0 = 0.5f * (QS - Rsq);
188 float Q2 = 8.0f * (QS - Rsq + QB + F0);
189 float aA, aB, aC, nA, nB, nC, dA, dB, dC;
194 while (n < max_iterations) {
198 aA = Q2 + 16.0f * (A2 - 2.0f * A * x_sum + x_sum2);
199 aB = Q2 + 16.0f * (B2 - 2.0f * B * y_sum + y_sum2);
200 aC = Q2 + 16.0f * (C2 - 2.0f * C * z_sum + z_sum2);
206 nA = A - ((F2 + 16.0f * (B * XY + C * XZ + x_sum * (-A2 - Q0) + A * (x_sum2 + Q1 - C * z_sum - B * y_sum))) / aA);
207 nB = B - ((F3 + 16.0f * (A * XY + C * YZ + y_sum * (-B2 - Q0) + B * (y_sum2 + Q1 - A * x_sum - C * z_sum))) / aB);
208 nC = C - ((F4 + 16.0f * (A * XZ + B * YZ + z_sum * (-C2 - Q0) + C * (z_sum2 + Q1 - A * x_sum - B * y_sum))) / aC);
215 if ((dA * dA + dB * dB + dC * dC) <= delta) {
break; }
225 QB = -2.0f * (A * x_sum + B * y_sum + C * z_sum);
227 Q0 = 0.5f * (QS - Rsq);
229 Q2 = 8.0f * (QS - Rsq + QB + F0);
235 *sphere_radius = sqrtf(Rsq);
241 unsigned int size,
int max_iterations,
float delta,
float *offset_x,
float *offset_y,
float *offset_z,
242 float *sphere_radius,
float *diag_x,
float *diag_y,
float *diag_z,
float *offdiag_x,
float *offdiag_y,
float *offdiag_z)
244 float _fitness = 1.0e30f, _sphere_lambda = 1.0f, _ellipsoid_lambda = 1.0f;
246 for (
int i = 0; i < max_iterations; i++) {
248 size, offset_x, offset_y, offset_z,
249 sphere_radius, diag_x, diag_y, diag_z, offdiag_x, offdiag_y, offdiag_z);
255 for (
int i = 0; i < max_iterations; i++) {
257 size, offset_x, offset_y, offset_z,
258 sphere_radius, diag_x, diag_y, diag_z, offdiag_x, offdiag_y, offdiag_z);
264 int run_lm_sphere_fit(
const float x[],
const float y[],
const float z[],
float &_fitness,
float &_sphere_lambda,
265 unsigned int size,
float *offset_x,
float *offset_y,
float *offset_z,
266 float *sphere_radius,
float *diag_x,
float *diag_y,
float *diag_z,
float *offdiag_x,
float *offdiag_y,
float *offdiag_z)
269 const float lma_damping = 10.0f;
270 float _samples_collected = size;
271 float fitness = _fitness;
272 float fit1 = 0.0f, fit2 = 0.0f;
277 float residual = 0.0f;
280 for (uint16_t k = 0; k < _samples_collected; k++) {
282 float sphere_jacob[4];
284 float A = (*diag_x * (x[k] - *offset_x)) + (*offdiag_x * (y[k] - *offset_y)) + (*offdiag_y * (z[k] - *offset_z));
285 float B = (*offdiag_x * (x[k] - *offset_x)) + (*diag_y * (y[k] - *offset_y)) + (*offdiag_z * (z[k] - *offset_z));
286 float C = (*offdiag_y * (x[k] - *offset_x)) + (*offdiag_z * (y[k] - *offset_y)) + (*diag_z * (z[k] - *offset_z));
287 float length = sqrtf(A * A + B * B + C * C);
290 sphere_jacob[0] = 1.0f;
292 sphere_jacob[1] = 1.0f * (((*diag_x * A) + (*offdiag_x * B) + (*offdiag_y * C)) / length);
293 sphere_jacob[2] = 1.0f * (((*offdiag_x * A) + (*diag_y * B) + (*offdiag_z * C)) / length);
294 sphere_jacob[3] = 1.0f * (((*offdiag_y * A) + (*offdiag_z * B) + (*diag_z * C)) / length);
295 residual = *sphere_radius - length;
297 for (uint8_t i = 0; i < 4; i++) {
299 for (uint8_t j = 0; j < 4; j++) {
300 JTJ(i, j) += sphere_jacob[i] * sphere_jacob[j];
301 JTJ2(i, j) += sphere_jacob[i] * sphere_jacob[j];
304 JTFI[i] += sphere_jacob[i] * residual;
311 float fit1_params[4] = {*sphere_radius, *offset_x, *offset_y, *offset_z};
312 float fit2_params[4];
313 memcpy(fit2_params, fit1_params,
sizeof(fit1_params));
315 for (uint8_t i = 0; i < 4; i++) {
316 JTJ(i, i) += _sphere_lambda;
317 JTJ2(i, i) += _sphere_lambda / lma_damping;
328 for (uint8_t row = 0; row < 4; row++) {
329 for (uint8_t col = 0; col < 4; col++) {
330 fit1_params[row] -= JTFI[col] * JTJ(row, col);
331 fit2_params[row] -= JTFI[col] * JTJ2(row, col);
336 for (uint16_t k = 0; k < _samples_collected; k++) {
337 float A = (*diag_x * (x[k] - fit1_params[1])) + (*offdiag_x * (y[k] - fit1_params[2])) + (*offdiag_y *
338 (z[k] + fit1_params[3]));
339 float B = (*offdiag_x * (x[k] - fit1_params[1])) + (*diag_y * (y[k] - fit1_params[2])) + (*offdiag_z *
340 (z[k] + fit1_params[3]));
341 float C = (*offdiag_y * (x[k] - fit1_params[1])) + (*offdiag_z * (y[k] - fit1_params[2])) + (*diag_z *
342 (z[k] - fit1_params[3]));
343 float length = sqrtf(A * A + B * B + C * C);
344 residual = fit1_params[0] - length;
345 fit1 += residual * residual;
347 A = (*diag_x * (x[k] - fit2_params[1])) + (*offdiag_x * (y[k] - fit2_params[2])) + (*offdiag_y *
348 (z[k] - fit2_params[3]));
349 B = (*offdiag_x * (x[k] - fit2_params[1])) + (*diag_y * (y[k] - fit2_params[2])) + (*offdiag_z *
350 (z[k] - fit2_params[3]));
351 C = (*offdiag_y * (x[k] - fit2_params[1])) + (*offdiag_z * (y[k] - fit2_params[2])) + (*diag_z *
352 (z[k] - fit2_params[3]));
353 length = sqrtf(A * A + B * B + C * C);
354 residual = fit2_params[0] - length;
355 fit2 += residual * residual;
358 fit1 = sqrtf(fit1) / _samples_collected;
359 fit2 = sqrtf(fit2) / _samples_collected;
361 if (fit1 > _fitness && fit2 > _fitness) {
362 _sphere_lambda *= lma_damping;
364 }
else if (fit2 < _fitness && fit2 < fit1) {
365 _sphere_lambda /= lma_damping;
366 memcpy(fit1_params, fit2_params,
sizeof(fit1_params));
369 }
else if (fit1 < _fitness) {
375 if (PX4_ISFINITE(fitness) && fitness < _fitness) {
377 *sphere_radius = fit1_params[0];
378 *offset_x = fit1_params[1];
379 *offset_y = fit1_params[2];
380 *offset_z = fit1_params[3];
388 int run_lm_ellipsoid_fit(
const float x[],
const float y[],
const float z[],
float &_fitness,
float &_sphere_lambda,
389 unsigned int size,
float *offset_x,
float *offset_y,
float *offset_z,
390 float *sphere_radius,
float *diag_x,
float *diag_y,
float *diag_z,
float *offdiag_x,
float *offdiag_y,
float *offdiag_z)
393 const float lma_damping = 10.0f;
394 float _samples_collected = size;
395 float fitness = _fitness;
402 float residual = 0.0f;
403 float ellipsoid_jacob[9];
406 for (uint16_t k = 0; k < _samples_collected; k++) {
409 float A = (*diag_x * (x[k] - *offset_x)) + (*offdiag_x * (y[k] - *offset_y)) + (*offdiag_y * (z[k] - *offset_z));
410 float B = (*offdiag_x * (x[k] - *offset_x)) + (*diag_y * (y[k] - *offset_y)) + (*offdiag_z * (z[k] - *offset_z));
411 float C = (*offdiag_y * (x[k] - *offset_x)) + (*offdiag_z * (y[k] - *offset_y)) + (*diag_z * (z[k] - *offset_z));
412 float length = sqrtf(A * A + B * B + C * C);
413 residual = *sphere_radius - length;
414 fit1 += residual * residual;
416 ellipsoid_jacob[0] = 1.0f * (((*diag_x * A) + (*offdiag_x * B) + (*offdiag_y * C)) / length);
417 ellipsoid_jacob[1] = 1.0f * (((*offdiag_x * A) + (*diag_y * B) + (*offdiag_z * C)) / length);
418 ellipsoid_jacob[2] = 1.0f * (((*offdiag_y * A) + (*offdiag_z * B) + (*diag_z * C)) / length);
420 ellipsoid_jacob[3] = -1.0f * ((x[k] - *offset_x) * A) / length;
421 ellipsoid_jacob[4] = -1.0f * ((y[k] - *offset_y) * B) / length;
422 ellipsoid_jacob[5] = -1.0f * ((z[k] - *offset_z) * C) / length;
424 ellipsoid_jacob[6] = -1.0f * (((y[k] - *offset_y) * A) + ((x[k] - *offset_x) * B)) / length;
425 ellipsoid_jacob[7] = -1.0f * (((z[k] - *offset_z) * A) + ((x[k] - *offset_x) * C)) / length;
426 ellipsoid_jacob[8] = -1.0f * (((z[k] - *offset_z) * B) + ((y[k] - *offset_y) * C)) / length;
428 for (uint8_t i = 0; i < 9; i++) {
430 for (uint8_t j = 0; j < 9; j++) {
431 JTJ[i * 9 + j] += ellipsoid_jacob[i] * ellipsoid_jacob[j];
432 JTJ2[i * 9 + j] += ellipsoid_jacob[i] * ellipsoid_jacob[j];
435 JTFI[i] += ellipsoid_jacob[i] * residual;
442 float fit1_params[9] = {*offset_x, *offset_y, *offset_z, *diag_x, *diag_y, *diag_z, *offdiag_x, *offdiag_y, *offdiag_z};
443 float fit2_params[9];
444 memcpy(fit2_params, fit1_params,
sizeof(fit1_params));
446 for (uint8_t i = 0; i < 9; i++) {
447 JTJ[i * 9 + i] += _sphere_lambda;
448 JTJ2[i * 9 + i] += _sphere_lambda / lma_damping;
460 for (uint8_t row = 0; row < 9; row++) {
461 for (uint8_t col = 0; col < 9; col++) {
462 fit1_params[row] -= JTFI[col] * JTJ[row * 9 + col];
463 fit2_params[row] -= JTFI[col] * JTJ2[row * 9 + col];
468 for (uint16_t k = 0; k < _samples_collected; k++) {
469 float A = (fit1_params[3] * (x[k] - fit1_params[0])) + (fit1_params[6] * (y[k] - fit1_params[1])) + (fit1_params[7] *
470 (z[k] - fit1_params[2]));
471 float B = (fit1_params[6] * (x[k] - fit1_params[0])) + (fit1_params[4] * (y[k] - fit1_params[1])) + (fit1_params[8] *
472 (z[k] - fit1_params[2]));
473 float C = (fit1_params[7] * (x[k] - fit1_params[0])) + (fit1_params[8] * (y[k] - fit1_params[1])) + (fit1_params[5] *
474 (z[k] - fit1_params[2]));
475 float length = sqrtf(A * A + B * B + C * C);
476 residual = *sphere_radius - length;
477 fit1 += residual * residual;
479 A = (fit2_params[3] * (x[k] - fit2_params[0])) + (fit2_params[6] * (y[k] - fit2_params[1])) + (fit2_params[7] *
480 (z[k] - fit2_params[2]));
481 B = (fit2_params[6] * (x[k] - fit2_params[0])) + (fit2_params[4] * (y[k] - fit2_params[1])) + (fit2_params[8] *
482 (z[k] - fit2_params[2]));
483 C = (fit2_params[7] * (x[k] - fit2_params[0])) + (fit2_params[8] * (y[k] - fit2_params[1])) + (fit2_params[5] *
484 (z[k] - fit2_params[2]));
485 length = sqrtf(A * A + B * B + C * C);
486 residual = *sphere_radius - length;
487 fit2 += residual * residual;
490 fit1 = sqrtf(fit1) / _samples_collected;
491 fit2 = sqrtf(fit2) / _samples_collected;
493 if (fit1 > _fitness && fit2 > _fitness) {
494 _sphere_lambda *= lma_damping;
496 }
else if (fit2 < _fitness && fit2 < fit1) {
497 _sphere_lambda /= lma_damping;
498 memcpy(fit1_params, fit2_params,
sizeof(fit1_params));
501 }
else if (fit1 < _fitness) {
506 if (PX4_ISFINITE(fitness) && fitness < _fitness) {
508 *offset_x = fit1_params[0];
509 *offset_y = fit1_params[1];
510 *offset_z = fit1_params[2];
511 *diag_x = fit1_params[3];
512 *diag_y = fit1_params[4];
513 *diag_z = fit1_params[5];
514 *offdiag_x = fit1_params[6];
515 *offdiag_y = fit1_params[7];
516 *offdiag_z = fit1_params[8];
525 bool lenient_still_position)
527 static constexpr
unsigned ndim = 3;
529 float accel_ema[ndim] = { 0.0f };
530 float accel_disp[3] = { 0.0f, 0.0f, 0.0f };
531 static constexpr
float ema_len = 0.5f;
532 static constexpr
float normal_still_thr = 0.25;
533 float still_thr2 = powf(lenient_still_position ? (normal_still_thr * 3) : normal_still_thr, 2);
534 static constexpr
float accel_err_thr = 5.0f;
535 const hrt_abstime still_time = lenient_still_position ? 500000 : 1300000;
537 px4_pollfd_struct_t fds[1];
538 fds[0].fd = accel_sub;
539 fds[0].events = POLLIN;
551 unsigned poll_errcount = 0;
555 int poll_ret =
px4_poll(fds, 1, 1000);
561 float dt = (t - t_prev) / 1000000.0
f;
563 float w = dt / ema_len;
565 for (
unsigned i = 0; i < ndim; i++) {
569 float d = di - accel_ema[i];
570 accel_ema[i] += d * w;
572 accel_disp[i] = accel_disp[i] * (1.0f - w);
574 if (d > still_thr2 * 8.0
f) {
575 d = still_thr2 * 8.0f;
578 if (d > accel_disp[i]) {
584 if (accel_disp[0] < still_thr2 &&
585 accel_disp[1] < still_thr2 &&
586 accel_disp[2] < still_thr2) {
592 t_timeout = t + timeout;
596 if (t > t_still + still_time) {
602 }
else if (accel_disp[0] > still_thr2 * 4.0
f ||
603 accel_disp[1] > still_thr2 * 4.0
f ||
604 accel_disp[2] > still_thr2 * 4.0
f) {
613 }
else if (poll_ret == 0) {
621 if (poll_errcount > 1000) {
628 fabsf(accel_ema[1]) < accel_err_thr &&
629 fabsf(accel_ema[2]) < accel_err_thr) {
634 fabsf(accel_ema[1]) < accel_err_thr &&
635 fabsf(accel_ema[2]) < accel_err_thr) {
639 if (fabsf(accel_ema[0]) < accel_err_thr &&
641 fabsf(accel_ema[2]) < accel_err_thr) {
645 if (fabsf(accel_ema[0]) < accel_err_thr &&
647 fabsf(accel_ema[2]) < accel_err_thr) {
651 if (fabsf(accel_ema[0]) < accel_err_thr &&
652 fabsf(accel_ema[1]) < accel_err_thr &&
657 if (fabsf(accel_ema[0]) < accel_err_thr &&
658 fabsf(accel_ema[1]) < accel_err_thr &&
670 static const char *rgOrientationStrs[] = {
680 return rgOrientationStrs[orientation];
688 bool lenient_still_position)
701 unsigned orientation_failures = 0;
710 if (orientation_failures > 4) {
716 unsigned int side_complete_count = 0;
720 if (side_data_collected[i]) {
721 side_complete_count++;
725 if (side_complete_count == detect_orientation_side_count) {
735 if (!side_data_collected[cur_orientation]) {
736 strncat(pendingStr,
" ",
sizeof(pendingStr) - 1);
746 lenient_still_position);
749 orientation_failures++;
756 if (side_data_collected[orient]) {
757 orientation_failures++;
768 orientation_failures = 0;
771 result = calibration_worker(orient, cancel_sub, worker_data);
783 side_data_collected[orient] =
true;
793 if (sub_accel >= 0) {
804 if (vehicle_command_sub >= 0) {
809 while (
orb_check(vehicle_command_sub, &update) == 0 && update) {
814 return vehicle_command_sub;
825 case vehicle_command_s::VEHICLE_CMD_RESULT_ACCEPTED:
829 case vehicle_command_s::VEHICLE_CMD_RESULT_DENIED:
841 px4_pollfd_struct_t fds[1];
842 fds[0].fd = cancel_sub;
843 fds[0].events = POLLIN;
852 if (cmd.
command == vehicle_command_s::VEHICLE_CMD_PREFLIGHT_CALIBRATION &&
#define CAL_ERROR_SENSOR_MSG
static orb_advert_t * mavlink_log_pub
#define mavlink_log_critical(_pub, _text,...)
Send a mavlink critical message and print to console.
int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
#define CAL_QGC_SIDE_DONE_MSG
Definition of geo / math functions to perform geodesic calculations.
Common calibration messages.
detect_orientation_return
int run_lm_sphere_fit(const float x[], const float y[], const float z[], float &_fitness, float &_sphere_lambda, unsigned int size, float *offset_x, float *offset_y, float *offset_z, float *sphere_radius, float *diag_x, float *diag_y, float *diag_z, float *offdiag_x, float *offdiag_y, float *offdiag_z)
#define CAL_QGC_ORIENTATION_DETECTED_MSG
calibrate_return calibrate_from_orientation(orb_advert_t *mavlink_log_pub, int cancel_sub, bool side_data_collected[detect_orientation_side_count], calibration_from_orientation_worker_t calibration_worker, void *worker_data, bool lenient_still_position)
Perform calibration sequence which require a rest orientation detection prior to calibration.
float accelerometer_m_s2[3]
void tune_positive(bool use_buzzer)
Blink green LED and play positive tune (if use_buzzer == true).
int ellipsoid_fit_least_squares(const float x[], const float y[], const float z[], unsigned int size, int max_iterations, float delta, float *offset_x, float *offset_y, float *offset_z, float *sphere_radius, float *diag_x, float *diag_y, float *diag_z, float *offdiag_x, float *offdiag_y, float *offdiag_z)
High-resolution timer with callouts and timekeeping.
static constexpr float CONSTANTS_ONE_G
calibrate_return(* calibration_from_orientation_worker_t)(detect_orientation_return orientation, int cancel_sub, void *worker_data)
Opaque worker data.
int sphere_fit_least_squares(const float x[], const float y[], const float z[], unsigned int size, unsigned int max_iterations, float delta, float *sphere_x, float *sphere_y, float *sphere_z, float *sphere_radius)
Least-squares fit of a sphere to a set of points.
void calibrate_cancel_unsubscribe(int cmd_sub)
Called to cancel the subscription to the cancel command.
int orb_subscribe(const struct orb_metadata *meta)
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
const char * detect_orientation_str(enum detect_orientation_return orientation)
Returns the human readable string representation of the orientation.
Commander helper functions definitions.
int run_lm_ellipsoid_fit(const float x[], const float y[], const float z[], float &_fitness, float &_sphere_lambda, unsigned int size, float *offset_x, float *offset_y, float *offset_z, float *sphere_radius, float *diag_x, float *diag_y, float *diag_z, float *offdiag_x, float *offdiag_y, float *offdiag_z)
#define CAL_QGC_CANCELLED_MSG
int orb_unsubscribe(int handle)
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
SquareMatrix< Type, M > I() const
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Driver for the PX4 audio alarm port, /dev/tone_alarm.
__BEGIN_DECLS typedef void * orb_advert_t
ORB topic advertiser handle.
bool mat_inverse(float *A, float *inv, uint8_t n)
void rgbled_set_color_and_mode(uint8_t color, uint8_t mode, uint8_t blinks, uint8_t prio)
bool calibrate_cancel_check(orb_advert_t *mavlink_log_pub, int cancel_sub)
Used to periodically check for a cancel command.
int orb_check(int handle, bool *updated)
static void calibrate_answer_command(orb_advert_t *mavlink_log_pub, struct vehicle_command_s &cmd, unsigned result)
#define calibration_log_critical(_pub, _text,...)
enum detect_orientation_return detect_orientation(orb_advert_t *mavlink_log_pub, int cancel_sub, int accel_sub, bool lenient_still_position)
Wait for vehicle to become still and detect it's orientation.
static const unsigned detect_orientation_side_count
#define CAL_QGC_FAILED_MSG
int calibrate_cancel_subscribe()
Called at the beginning of calibration in order to subscribe to the cancel command.
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
void tune_negative(bool use_buzzer)
Blink red LED and play negative tune (if use_buzzer == true).
#define calibration_log_info(_pub, _text,...)