accel & gyro calibration helpers

This commit is contained in:
Daniel Agar
2020-08-21 10:12:13 -04:00
parent bae2589fed
commit e3e8c55e82
35 changed files with 1245 additions and 945 deletions
+1 -1
View File
@@ -33,7 +33,6 @@
add_subdirectory(data_validator)
add_subdirectory(sensor_calibration) # used by vehicle_{acceleration, angular_velocity, imu}
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_subdirectory(vehicle_acceleration)
add_subdirectory(vehicle_angular_velocity)
@@ -54,6 +53,7 @@ px4_add_module(
drivers__device
git_ecl
mathlib
sensor_calibration
vehicle_acceleration
vehicle_angular_velocity
vehicle_air_data
@@ -1,39 +0,0 @@
############################################################################
#
# Copyright (c) 2020 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
px4_add_library(sensor_calibration
SensorCalibration.cpp
SensorCalibration.hpp
)
target_compile_options(sensor_calibration PRIVATE ${MAX_CUSTOM_OPT_LEVEL})
target_include_directories(sensor_calibration PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..)
@@ -1,252 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#include "SensorCalibration.hpp"
#include <lib/parameters/param.h>
using namespace matrix;
using namespace time_literals;
using math::radians;
namespace sensors
{
void SensorCalibration::set_device_id(uint32_t device_id)
{
if (_device_id != device_id) {
_device_id = device_id;
SensorCorrectionsUpdate(true);
ParametersUpdate();
}
}
matrix::Vector3f SensorCalibration::Correct(const matrix::Vector3f &data)
{
SensorCorrectionsUpdate();
return _rotation * matrix::Vector3f{(data - _thermal_offset - _offset).emult(_scale)};
}
const char *SensorCalibration::SensorString() const
{
switch (_type) {
case SensorType::Accelerometer:
return "ACC";
case SensorType::Gyroscope:
return "GYRO";
}
return nullptr;
}
int SensorCalibration::FindCalibrationIndex(uint32_t device_id) const
{
if (device_id == 0) {
return -1;
}
for (unsigned i = 0; i < MAX_SENSOR_COUNT; ++i) {
char str[16] {};
sprintf(str, "CAL_%s%u_ID", SensorString(), i);
int32_t device_id_val = 0;
if (param_get(param_find(str), &device_id_val) != OK) {
PX4_ERR("Could not access param %s", str);
continue;
}
if ((uint32_t)device_id_val == device_id) {
return i;
}
}
return -1;
}
bool SensorCalibration::CalibrationEnabled(uint8_t calibration_index) const
{
char str[20] {};
sprintf(str, "CAL_%s%u_EN", SensorString(), calibration_index);
int32_t enabled_val = 1;
param_get(param_find(str), &enabled_val);
return (enabled_val != 0);
}
Vector3f SensorCalibration::CalibrationOffset(uint8_t calibration_index) const
{
// offsets (x, y, z)
Vector3f offset{0.f, 0.f, 0.f};
char str[20] {};
sprintf(str, "CAL_%s%u_XOFF", SensorString(), calibration_index);
param_get(param_find(str), &offset(0));
sprintf(str, "CAL_%s%u_YOFF", SensorString(), calibration_index);
param_get(param_find(str), &offset(1));
sprintf(str, "CAL_%s%u_ZOFF", SensorString(), calibration_index);
param_get(param_find(str), &offset(2));
return offset;
}
Vector3f SensorCalibration::CalibrationScale(uint8_t calibration_index) const
{
// scale factors (x, y, z)
Vector3f scale{1.f, 1.f, 1.f};
// gyroscope doesn't have a scale factor calibration
if (_type != SensorType::Gyroscope) {
char str[20] {};
sprintf(str, "CAL_%s%u_XSCALE", SensorString(), calibration_index);
param_get(param_find(str), &scale(0));
sprintf(str, "CAL_%s%u_YSCALE", SensorString(), calibration_index);
param_get(param_find(str), &scale(1));
sprintf(str, "CAL_%s%u_ZSCALE", SensorString(), calibration_index);
param_get(param_find(str), &scale(2));
}
return scale;
}
void SensorCalibration::SensorCorrectionsUpdate(bool force)
{
// check if the selected sensor has updated
if (_sensor_correction_sub.updated() || force) {
// valid device id required
if (_device_id == 0) {
return;
}
sensor_correction_s corrections;
if (_sensor_correction_sub.copy(&corrections)) {
// find sensor_corrections index
for (int i = 0; i < MAX_SENSOR_COUNT; i++) {
if ((_type == SensorType::Accelerometer) && (corrections.accel_device_ids[i] == _device_id)) {
switch (i) {
case 0:
_thermal_offset = Vector3f{corrections.accel_offset_0};
return;
case 1:
_thermal_offset = Vector3f{corrections.accel_offset_1};
return;
case 2:
_thermal_offset = Vector3f{corrections.accel_offset_2};
return;
}
} else if ((_type == SensorType::Gyroscope) && (corrections.gyro_device_ids[i] == _device_id)) {
switch (i) {
case 0:
_thermal_offset = Vector3f{corrections.gyro_offset_0};
return;
case 1:
_thermal_offset = Vector3f{corrections.gyro_offset_1};
return;
case 2:
_thermal_offset = Vector3f{corrections.gyro_offset_2};
return;
}
}
}
}
// zero thermal offset if not found
_thermal_offset.zero();
}
}
void SensorCalibration::ParametersUpdate()
{
if (!_external) {
// fine tune the rotation
float x_offset = 0.f;
float y_offset = 0.f;
float z_offset = 0.f;
param_get(param_find("SENS_BOARD_X_OFF"), &x_offset);
param_get(param_find("SENS_BOARD_Y_OFF"), &y_offset);
param_get(param_find("SENS_BOARD_Z_OFF"), &z_offset);
const Dcmf board_rotation_offset(Eulerf(radians(x_offset), radians(y_offset), radians(z_offset)));
// get transformation matrix from sensor/board to body frame
int32_t board_rot = 0;
param_get(param_find("SENS_BOARD_ROT"), &board_rot);
_rotation = board_rotation_offset * get_rot_matrix((enum Rotation)board_rot);
} else {
// TODO: per sensor external rotation
_rotation.setIdentity();
}
int calibration_index = FindCalibrationIndex(_device_id);
if (calibration_index >= 0) {
_enabled = CalibrationEnabled(calibration_index);
_offset = CalibrationOffset(calibration_index);
_scale = CalibrationScale(calibration_index);
} else {
_enabled = true;
_offset.zero();
_scale = Vector3f{1.f, 1.f, 1.f};
}
}
void SensorCalibration::PrintStatus()
{
if (_type != SensorType::Gyroscope) {
PX4_INFO("%s %d EN: %d, offset: [%.4f %.4f %.4f] scale: [%.4f %.4f %.4f]", SensorString(), _device_id, _enabled,
(double)_offset(0), (double)_offset(1), (double)_offset(2), (double)_scale(0), (double)_scale(1), (double)_scale(2));
} else {
PX4_INFO("%s %d EN: %d, offset: [%.4f %.4f %.4f]", SensorString(), _device_id, _enabled,
(double)_offset(0), (double)_offset(1), (double)_offset(2));
}
if (_thermal_offset.norm() > 0.f) {
PX4_INFO("%s %d temperature offset: [%.4f %.4f %.4f]", SensorString(), _device_id,
(double)_thermal_offset(0), (double)_thermal_offset(1), (double)_thermal_offset(2));
}
}
} // namespace sensors
@@ -1,106 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#pragma once
#include <lib/conversion/rotation.h>
#include <lib/matrix/matrix/math.hpp>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/log.h>
#include <uORB/Subscription.hpp>
#include <uORB/topics/sensor_correction.h>
namespace sensors
{
class SensorCalibration
{
public:
enum class SensorType : uint8_t {
Accelerometer,
Gyroscope,
};
SensorCalibration() = delete;
explicit SensorCalibration(SensorType type) : _type(type) {}
~SensorCalibration() = default;
void PrintStatus();
void set_device_id(uint32_t device_id);
void set_external(bool external = true) { _external = external; }
uint32_t device_id() const { return _device_id; }
bool enabled() const { return _enabled; }
bool external() const { return _external; }
// apply offsets and scale
// rotate corrected measurements from sensor to body frame
matrix::Vector3f Correct(const matrix::Vector3f &data);
void ParametersUpdate();
void SensorCorrectionsUpdate(bool force = false);
const matrix::Dcmf &getBoardRotation() const { return _rotation; }
private:
static constexpr int MAX_SENSOR_COUNT = 3;
int FindCalibrationIndex(uint32_t device_id) const;
bool CalibrationEnabled(uint8_t calibration_index) const;
matrix::Vector3f CalibrationOffset(uint8_t calibration_index) const;
matrix::Vector3f CalibrationScale(uint8_t calibration_index) const;
const char *SensorString() const;
uORB::Subscription _sensor_correction_sub{ORB_ID(sensor_correction)};
matrix::Dcmf _rotation;
matrix::Vector3f _offset{0.f, 0.f, 0.f};
matrix::Vector3f _scale{1.f, 1.f, 1.f};
matrix::Vector3f _thermal_offset{0.f, 0.f, 0.f};
uint32_t _device_id{0};
const SensorType _type;
bool _enabled{true};
bool _external{false};
};
} // namespace sensors
+6 -6
View File
@@ -47,12 +47,12 @@ PARAM_DEFINE_INT32(CAL_MAG_PRIME, 0);
* recommended.
*
* Bits:
* DETECT_ORIENTATION_TAIL_DOWN = 1
* DETECT_ORIENTATION_NOSE_DOWN = 2
* DETECT_ORIENTATION_LEFT = 4
* DETECT_ORIENTATION_RIGHT = 8
* DETECT_ORIENTATION_UPSIDE_DOWN = 16
* DETECT_ORIENTATION_RIGHTSIDE_UP = 32
* ORIENTATION_TAIL_DOWN = 1
* ORIENTATION_NOSE_DOWN = 2
* ORIENTATION_LEFT = 4
* ORIENTATION_RIGHT = 8
* ORIENTATION_UPSIDE_DOWN = 16
* ORIENTATION_RIGHTSIDE_UP = 32
*
* @min 34
* @max 63
@@ -41,6 +41,6 @@ target_compile_options(vehicle_acceleration PRIVATE ${MAX_CUSTOM_OPT_LEVEL})
target_link_libraries(vehicle_acceleration
PRIVATE
mathlib
sensor_calibration
px4_work_queue
sensor_calibration
)
@@ -33,8 +33,7 @@
#pragma once
#include <sensor_calibration/SensorCalibration.hpp>
#include <lib/sensor_calibration/Accelerometer.hpp>
#include <lib/mathlib/math/Limits.hpp>
#include <lib/matrix/matrix/math.hpp>
#include <lib/mathlib/math/filter/LowPassFilter2pVector3f.hpp>
@@ -88,7 +87,7 @@ private:
{this, ORB_ID(sensor_accel), 2}
};
SensorCalibration _calibration{SensorCalibration::SensorType::Accelerometer};
calibration::Accelerometer _calibration{};
matrix::Vector3f _bias{0.f, 0.f, 0.f};
@@ -41,6 +41,6 @@ target_compile_options(vehicle_angular_velocity PRIVATE ${MAX_CUSTOM_OPT_LEVEL})
target_link_libraries(vehicle_angular_velocity
PRIVATE
mathlib
sensor_calibration
px4_work_queue
sensor_calibration
)
@@ -33,8 +33,7 @@
#pragma once
#include <sensor_calibration/SensorCalibration.hpp>
#include <lib/sensor_calibration/Gyroscope.hpp>
#include <lib/mathlib/math/Limits.hpp>
#include <lib/matrix/matrix/math.hpp>
#include <lib/mathlib/math/filter/LowPassFilter2pVector3f.hpp>
@@ -91,7 +90,7 @@ private:
{this, ORB_ID(sensor_gyro), 2}
};
SensorCalibration _calibration{SensorCalibration::SensorType::Gyroscope};
calibration::Gyroscope _calibration{};
matrix::Vector3f _bias{0.f, 0.f, 0.f};
@@ -38,4 +38,4 @@ px4_add_library(vehicle_imu
VehicleIMU.hpp
)
target_compile_options(vehicle_imu PRIVATE ${MAX_CUSTOM_OPT_LEVEL})
target_link_libraries(vehicle_imu PRIVATE sensor_calibration px4_work_queue)
target_link_libraries(vehicle_imu PRIVATE px4_work_queue sensor_calibration)
+40 -32
View File
@@ -191,6 +191,11 @@ void VehicleIMU::Run()
ParametersUpdate();
if (!_accel_calibration.enabled() || !_gyro_calibration.enabled()) {
return;
}
bool sensor_data_gap = false;
bool update_integrator_config = false;
bool publish_status = false;
@@ -269,7 +274,7 @@ void VehicleIMU::Run()
if (accel.clip_counter[0] > 0 || accel.clip_counter[1] > 0 || accel.clip_counter[2] > 0) {
// rotate sensor clip counts into vehicle body frame
const Vector3f clipping{_accel_calibration.getBoardRotation() *
const Vector3f clipping{_accel_calibration.rotation() *
Vector3f{(float)accel.clip_counter[0], (float)accel.clip_counter[1], (float)accel.clip_counter[2]}};
// round to get reasonble clip counts per axis (after board rotation)
@@ -332,42 +337,45 @@ void VehicleIMU::Run()
if (_accel_integrator.reset(delta_velocity, accel_integral_dt)
&& _gyro_integrator.reset(delta_angle, gyro_integral_dt)) {
// delta angle: apply offsets, scale, and board rotation
_gyro_calibration.SensorCorrectionsUpdate();
const float gyro_dt_inv = 1.e6f / gyro_integral_dt;
const Vector3f delta_angle_corrected{_gyro_calibration.Correct(delta_angle * gyro_dt_inv) / gyro_dt_inv};
if (_accel_calibration.enabled() && _gyro_calibration.enabled()) {
// delta velocity: apply offsets, scale, and board rotation
_accel_calibration.SensorCorrectionsUpdate();
const float accel_dt_inv = 1.e6f / accel_integral_dt;
Vector3f delta_velocity_corrected{_accel_calibration.Correct(delta_velocity * accel_dt_inv) / accel_dt_inv};
// delta angle: apply offsets, scale, and board rotation
_gyro_calibration.SensorCorrectionsUpdate();
const float gyro_dt_inv = 1.e6f / gyro_integral_dt;
const Vector3f delta_angle_corrected{_gyro_calibration.Correct(delta_angle * gyro_dt_inv) / gyro_dt_inv};
UpdateAccelVibrationMetrics(delta_velocity_corrected);
UpdateGyroVibrationMetrics(delta_angle_corrected);
// delta velocity: apply offsets, scale, and board rotation
_accel_calibration.SensorCorrectionsUpdate();
const float accel_dt_inv = 1.e6f / accel_integral_dt;
Vector3f delta_velocity_corrected{_accel_calibration.Correct(delta_velocity * accel_dt_inv) / accel_dt_inv};
// vehicle_imu_status
// publish before vehicle_imu so that error counts are available synchronously if needed
if (publish_status || (hrt_elapsed_time(&_status.timestamp) >= 100_ms)) {
_status.accel_device_id = _accel_calibration.device_id();
_status.gyro_device_id = _gyro_calibration.device_id();
_status.timestamp = hrt_absolute_time();
_vehicle_imu_status_pub.publish(_status);
UpdateAccelVibrationMetrics(delta_velocity_corrected);
UpdateGyroVibrationMetrics(delta_angle_corrected);
// vehicle_imu_status
// publish before vehicle_imu so that error counts are available synchronously if needed
if (publish_status || (hrt_elapsed_time(&_status.timestamp) >= 100_ms)) {
_status.accel_device_id = _accel_calibration.device_id();
_status.gyro_device_id = _gyro_calibration.device_id();
_status.timestamp = hrt_absolute_time();
_vehicle_imu_status_pub.publish(_status);
}
// publish vehicle_imu
vehicle_imu_s imu;
imu.timestamp_sample = _last_timestamp_sample_gyro;
imu.accel_device_id = _accel_calibration.device_id();
imu.gyro_device_id = _gyro_calibration.device_id();
delta_angle_corrected.copyTo(imu.delta_angle);
delta_velocity_corrected.copyTo(imu.delta_velocity);
imu.delta_angle_dt = gyro_integral_dt;
imu.delta_velocity_dt = accel_integral_dt;
imu.delta_velocity_clipping = _delta_velocity_clipping;
imu.timestamp = hrt_absolute_time();
_vehicle_imu_pub.publish(imu);
}
// publish vehicle_imu
vehicle_imu_s imu;
imu.timestamp_sample = _last_timestamp_sample_gyro;
imu.accel_device_id = _accel_calibration.device_id();
imu.gyro_device_id = _gyro_calibration.device_id();
delta_angle_corrected.copyTo(imu.delta_angle);
delta_velocity_corrected.copyTo(imu.delta_velocity);
imu.delta_angle_dt = gyro_integral_dt;
imu.delta_velocity_dt = accel_integral_dt;
imu.delta_velocity_clipping = _delta_velocity_clipping;
imu.timestamp = hrt_absolute_time();
_vehicle_imu_pub.publish(imu);
// reset clip counts
_delta_velocity_clipping = 0;
@@ -35,11 +35,11 @@
#include "Integrator.hpp"
#include <sensor_calibration/SensorCalibration.hpp>
#include <lib/mathlib/math/Limits.hpp>
#include <lib/matrix/matrix/math.hpp>
#include <lib/perf/perf_counter.h>
#include <lib/sensor_calibration/Accelerometer.hpp>
#include <lib/sensor_calibration/Gyroscope.hpp>
#include <px4_platform_common/log.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/px4_config.h>
@@ -91,8 +91,8 @@ private:
uORB::SubscriptionCallbackWorkItem _sensor_accel_sub;
uORB::SubscriptionCallbackWorkItem _sensor_gyro_sub;
SensorCalibration _accel_calibration{SensorCalibration::SensorType::Accelerometer};
SensorCalibration _gyro_calibration{SensorCalibration::SensorType::Gyroscope};
calibration::Accelerometer _accel_calibration{};
calibration::Gyroscope _gyro_calibration{};
Integrator _accel_integrator{}; // 200 Hz default
Integrator _gyro_integrator{true}; // 200 Hz default, coning compensation enabled