Compare commits

...

20 Commits

Author SHA1 Message Date
bresch bc9df1a2d4 slew_rate: pass vector by const ref 2024-03-13 10:35:40 +01:00
bresch d32680f87f AutoTrim: allow auto-trimming when no airspeed is available 2024-03-13 10:35:40 +01:00
bresch 8eadfa8bf6 HACK: temporarily deactivate modules for flash space 2024-03-13 10:35:40 +01:00
bresch badfbd7bac FwRateControl: remove redundant if
Same as outer if
2024-03-13 10:35:40 +01:00
bresch 8cc190e327 remove print 2024-03-13 10:35:40 +01:00
bresch bbfc772f1f AutoTrimStatus: add comments to message fields 2024-03-13 10:35:40 +01:00
bresch 8bf3c2960c FwTrim: print trim update 2024-03-13 10:35:40 +01:00
bresch 797bf6e775 correctly reset auto-trim after save 2024-03-13 10:35:40 +01:00
bresch a86b9fc6c9 FwAutoTrim: only use auto-trim values if enabled 2024-03-13 10:35:40 +01:00
bresch 6fc91f9a76 FwAutoTrim: save estimated trim back to params 2024-03-13 10:35:40 +01:00
bresch afc9d6dfab FwTrim class 2024-03-13 10:35:40 +01:00
bresch 5f992423c1 remove kconfig 2024-03-13 10:35:40 +01:00
bresch d526cb7d6c use auto_trim on top of current trim 2024-03-13 10:35:40 +01:00
bresch d6c141dd29 add slew-rate 2024-03-13 10:35:40 +01:00
bresch 215081b6e2 migrate fw_auto_trim module to fw_rate_control 2024-03-13 10:35:40 +01:00
bresch 96517c05a0 TEMP: verbose auto-trim logging 2024-03-13 10:35:40 +01:00
bresch c661a6dfa7 address review comments 2024-03-13 10:35:40 +01:00
bresch 42a8e1b1c0 add fw_auto_trim on v5 and v6 boards 2024-03-13 10:35:40 +01:00
bresch 6b42fb0ff7 Add new auto-trim module for fixed-wings
Estimates the torque trim values while flying
2024-03-13 10:35:40 +01:00
bresch f671e036e8 WelfordMeanVector: include matrix lib 2024-03-13 10:35:40 +01:00
16 changed files with 716 additions and 78 deletions

View File

@ -53,7 +53,7 @@ CONFIG_MODULES_CAMERA_FEEDBACK=y
CONFIG_MODULES_COMMANDER=y
CONFIG_MODULES_CONTROL_ALLOCATOR=y
CONFIG_MODULES_DATAMAN=y
CONFIG_MODULES_DIFFERENTIAL_DRIVE=y
CONFIG_MODULES_DIFFERENTIAL_DRIVE=n
CONFIG_MODULES_EKF2=y
CONFIG_MODULES_ESC_BATTERY=y
CONFIG_MODULES_EVENTS=y

View File

@ -13,7 +13,7 @@ CONFIG_DRIVERS_BAROMETER_BMP388=y
CONFIG_DRIVERS_BAROMETER_INVENSENSE_ICP201XX=y
CONFIG_DRIVERS_BAROMETER_MS5611=y
CONFIG_DRIVERS_CAMERA_CAPTURE=y
CONFIG_DRIVERS_CAMERA_TRIGGER=y
CONFIG_DRIVERS_CAMERA_TRIGGER=n
CONFIG_COMMON_DIFFERENTIAL_PRESSURE=y
CONFIG_COMMON_DISTANCE_SENSOR=y
CONFIG_DRIVERS_DSHOT=y

17
msg/AutoTrimStatus.msg Normal file
View File

@ -0,0 +1,17 @@
uint64 timestamp # Time since system start (microseconds)
uint64 timestamp_sample
float32[3] trim_estimate # Roll/pitch/yaw trim value obtained from torque setpoints averaged over several seconds
float32[3] trim_estimate_var
float32[3] trim_test # Same as trim_estimate but done on a shorter period. Used to validate the trim estimate.
float32[3] trim_test_var
float32[3] trim_validated # Final roll/pitch/yaw trim estimate, verified by trim_test
uint8 STATE_IDLE = 0
uint8 STATE_SAMPLING = 1
uint8 STATE_SAMPLING_TEST = 2
uint8 STATE_VERIFICATION = 3
uint8 STATE_COMPLETE = 4
uint8 STATE_FAIL = 5
uint8 state

View File

@ -52,6 +52,7 @@ set(msg_files
ArmingCheckReply.msg
ArmingCheckRequest.msg
AutotuneAttitudeControlStatus.msg
AutoTrimStatus.msg
BatteryStatus.msg
Buffer128.msg
ButtonEvent.msg

View File

@ -39,6 +39,8 @@
#pragma once
#include <lib/mathlib/mathlib.h>
namespace math
{

View File

@ -56,13 +56,13 @@ public:
* Set maximum rate of change for the value
* @param slew_rate maximum rate of change
*/
void setSlewRate(const Type slew_rate) { _slew_rate = slew_rate; }
void setSlewRate(const Type &slew_rate) { _slew_rate = slew_rate; }
/**
* Set value ignoring slew rate for initialization purpose
* @param value new applied value
*/
void setForcedValue(const Type value) { _value = value; }
void setForcedValue(const Type &value) { _value = value; }
/**
* Get value from last update of the slew rate
@ -76,7 +76,7 @@ public:
* @param deltatime time in seconds since last update
* @return actual value that complies with the slew rate
*/
Type update(const Type new_value, const float deltatime)
Type update(const Type &new_value, const float deltatime)
{
// Limit the rate of change of the value
const Type dvalue_desired = new_value - _value;
@ -90,3 +90,14 @@ protected:
Type _slew_rate{}; ///< maximum rate of change for the value
Type _value{}; ///< state to keep last value of the slew rate
};
template<>
inline matrix::Vector3f SlewRate<matrix::Vector3f>::update(const matrix::Vector3f &new_value, const float deltatime)
{
// Limit the rate of change of the value
const matrix::Vector3f dvalue_desired = new_value - _value;
const matrix::Vector3f dvalue_max = _slew_rate * deltatime;
const matrix::Vector3f dvalue = matrix::constrain(dvalue_desired, -dvalue_max, dvalue_max);
_value += dvalue;
return _value;
}

View File

@ -30,6 +30,7 @@
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
add_subdirectory(fw_trim)
px4_add_module(
MODULE modules__fw_rate_control
@ -38,6 +39,7 @@ px4_add_module(
FixedwingRateControl.cpp
FixedwingRateControl.hpp
DEPENDS
FwTrim
px4_work_queue
RateControl
SlewRate

View File

@ -37,7 +37,6 @@ using namespace time_literals;
using namespace matrix;
using math::constrain;
using math::interpolate;
using math::radians;
FixedwingRateControl::FixedwingRateControl(bool vtol) :
@ -54,6 +53,7 @@ FixedwingRateControl::FixedwingRateControl(bool vtol) :
parameters_update();
_rate_ctrl_status_pub.advertise();
_trim_slew.setSlewRate(Vector3f(0.01f, 0.01f, 0.01f));
}
FixedwingRateControl::~FixedwingRateControl()
@ -96,6 +96,12 @@ FixedwingRateControl::parameters_update()
return PX4_OK;
}
void
FixedwingRateControl::save_params()
{
_trim.saveParams();
}
void
FixedwingRateControl::vehicle_manual_poll()
{
@ -127,13 +133,14 @@ FixedwingRateControl::vehicle_manual_poll()
} else {
// Manual/direct control, filled in FW-frame. Note that setpoints will get transformed to body frame prior publishing.
const Vector3f trim = _trim_slew.getState();
_vehicle_torque_setpoint.xyz[0] = math::constrain(_manual_control_setpoint.roll * _param_fw_man_r_sc.get() +
_param_trim_roll.get(), -1.f, 1.f);
trim(0), -1.f, 1.f);
_vehicle_torque_setpoint.xyz[1] = math::constrain(-_manual_control_setpoint.pitch * _param_fw_man_p_sc.get() +
_param_trim_pitch.get(), -1.f, 1.f);
trim(1), -1.f, 1.f);
_vehicle_torque_setpoint.xyz[2] = math::constrain(_manual_control_setpoint.yaw * _param_fw_man_y_sc.get() +
_param_trim_yaw.get(), -1.f, 1.f);
trim(2), -1.f, 1.f);
_vehicle_thrust_setpoint.xyz[0] = math::constrain((_manual_control_setpoint.throttle + 1.f) * .5f, 0.f, 1.f);
}
@ -256,7 +263,14 @@ void FixedwingRateControl::Run()
const bool is_fixed_wing = _vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_FIXED_WING;
_in_fw_or_transition_wo_tailsitter_transition = is_fixed_wing || is_in_transition_except_tailsitter;
_vehicle_control_mode_sub.update(&_vcontrol_mode);
{
const bool armed_prev = _vcontrol_mode.flag_armed;
_vehicle_control_mode_sub.update(&_vcontrol_mode);
if (!_vcontrol_mode.flag_armed && armed_prev) {
save_params();
}
}
vehicle_land_detected_poll();
@ -321,78 +335,56 @@ void FixedwingRateControl::Run()
}
}
/* bi-linear interpolation over airspeed for actuator trim scheduling */
Vector3f trim(_param_trim_roll.get(), _param_trim_pitch.get(), _param_trim_yaw.get());
_trim.setAirspeed(airspeed);
_trim_slew.update(_trim.getTrim(), dt);
if (airspeed < _param_fw_airspd_trim.get()) {
trim(0) += interpolate(airspeed, _param_fw_airspd_min.get(), _param_fw_airspd_trim.get(),
_param_fw_dtrim_r_vmin.get(),
0.0f);
trim(1) += interpolate(airspeed, _param_fw_airspd_min.get(), _param_fw_airspd_trim.get(),
_param_fw_dtrim_p_vmin.get(),
0.0f);
trim(2) += interpolate(airspeed, _param_fw_airspd_min.get(), _param_fw_airspd_trim.get(),
_param_fw_dtrim_y_vmin.get(),
0.0f);
_rates_sp_sub.update(&_rates_sp);
} else {
trim(0) += interpolate(airspeed, _param_fw_airspd_trim.get(), _param_fw_airspd_max.get(), 0.0f,
_param_fw_dtrim_r_vmax.get());
trim(1) += interpolate(airspeed, _param_fw_airspd_trim.get(), _param_fw_airspd_max.get(), 0.0f,
_param_fw_dtrim_p_vmax.get());
trim(2) += interpolate(airspeed, _param_fw_airspd_trim.get(), _param_fw_airspd_max.get(), 0.0f,
_param_fw_dtrim_y_vmax.get());
Vector3f body_rates_setpoint = Vector3f(_rates_sp.roll, _rates_sp.pitch, _rates_sp.yaw);
// Tailsitter: rotate setpoint from hover to fixed-wing frame (controller is in fixed-wing frame, interface in hover)
if (_vehicle_status.is_vtol_tailsitter) {
body_rates_setpoint = Vector3f(-_rates_sp.yaw, _rates_sp.pitch, _rates_sp.roll);
}
if (_vcontrol_mode.flag_control_rates_enabled) {
_rates_sp_sub.update(&_rates_sp);
// Run attitude RATE controllers which need the desired attitudes from above, add trim.
const Vector3f angular_acceleration_setpoint = _rate_control.update(rates, body_rates_setpoint, angular_accel, dt,
_landed);
Vector3f body_rates_setpoint = Vector3f(_rates_sp.roll, _rates_sp.pitch, _rates_sp.yaw);
const Vector3f gain_ff(_param_fw_rr_ff.get(), _param_fw_pr_ff.get(), _param_fw_yr_ff.get());
const Vector3f feedforward = gain_ff.emult(body_rates_setpoint) * _airspeed_scaling;
// Tailsitter: rotate setpoint from hover to fixed-wing frame (controller is in fixed-wing frame, interface in hover)
if (_vehicle_status.is_vtol_tailsitter) {
body_rates_setpoint = Vector3f(-_rates_sp.yaw, _rates_sp.pitch, _rates_sp.roll);
}
Vector3f control_u = angular_acceleration_setpoint * _airspeed_scaling * _airspeed_scaling + feedforward;
// Run attitude RATE controllers which need the desired attitudes from above, add trim.
const Vector3f angular_acceleration_setpoint = _rate_control.update(rates, body_rates_setpoint, angular_accel, dt,
_landed);
// Special case yaw in Acro: if the parameter FW_ACRO_YAW_CTL is not set then don't control yaw
if (!_vcontrol_mode.flag_control_attitude_enabled && !_param_fw_acro_yaw_en.get()) {
control_u(2) = _manual_control_setpoint.yaw * _param_fw_man_y_sc.get();
_rate_control.resetIntegral(2);
}
const Vector3f gain_ff(_param_fw_rr_ff.get(), _param_fw_pr_ff.get(), _param_fw_yr_ff.get());
const Vector3f feedforward = gain_ff.emult(body_rates_setpoint) * _airspeed_scaling;
if (control_u.isAllFinite()) {
matrix::constrain(control_u + _trim_slew.getState(), -1.f, 1.f).copyTo(_vehicle_torque_setpoint.xyz);
Vector3f control_u = angular_acceleration_setpoint * _airspeed_scaling * _airspeed_scaling + feedforward;
} else {
_rate_control.resetIntegral();
_trim_slew.getState().copyTo(_vehicle_torque_setpoint.xyz);
}
// Special case yaw in Acro: if the parameter FW_ACRO_YAW_CTL is not set then don't control yaw
if (!_vcontrol_mode.flag_control_attitude_enabled && !_param_fw_acro_yaw_en.get()) {
control_u(2) = _manual_control_setpoint.yaw * _param_fw_man_y_sc.get();
_rate_control.resetIntegral(2);
}
/* throttle passed through if it is finite */
_vehicle_thrust_setpoint.xyz[0] = PX4_ISFINITE(_rates_sp.thrust_body[0]) ? _rates_sp.thrust_body[0] : 0.0f;
if (control_u.isAllFinite()) {
matrix::constrain(control_u + trim, -1.f, 1.f).copyTo(_vehicle_torque_setpoint.xyz);
/* scale effort by battery status */
if (_param_fw_bat_scale_en.get() && _vehicle_thrust_setpoint.xyz[0] > 0.1f) {
} else {
_rate_control.resetIntegral();
trim.copyTo(_vehicle_torque_setpoint.xyz);
}
if (_battery_status_sub.updated()) {
battery_status_s battery_status{};
/* throttle passed through if it is finite */
_vehicle_thrust_setpoint.xyz[0] = PX4_ISFINITE(_rates_sp.thrust_body[0]) ? _rates_sp.thrust_body[0] : 0.0f;
/* scale effort by battery status */
if (_param_fw_bat_scale_en.get() && _vehicle_thrust_setpoint.xyz[0] > 0.1f) {
if (_battery_status_sub.updated()) {
battery_status_s battery_status{};
if (_battery_status_sub.copy(&battery_status) && battery_status.connected && battery_status.scale > 0.f) {
_battery_scale = battery_status.scale;
}
if (_battery_status_sub.copy(&battery_status) && battery_status.connected && battery_status.scale > 0.f) {
_battery_scale = battery_status.scale;
}
_vehicle_thrust_setpoint.xyz[0] *= _battery_scale;
}
_vehicle_thrust_setpoint.xyz[0] *= _battery_scale;
}
// publish rate controller status
@ -405,6 +397,7 @@ void FixedwingRateControl::Run()
} else {
// full manual
_rate_control.resetIntegral();
_trim.reset();
}
// Add feed-forward from roll control output to yaw control output
@ -432,6 +425,8 @@ void FixedwingRateControl::Run()
_vehicle_torque_setpoint.timestamp_sample = angular_velocity.timestamp_sample;
_vehicle_torque_setpoint_pub.publish(_vehicle_torque_setpoint);
}
_trim.updateAutoTrim(Vector3f(_vehicle_torque_setpoint.xyz), dt);
}
updateActuatorControlsStatus(dt);

View File

@ -69,6 +69,8 @@
#include <uORB/topics/vehicle_thrust_setpoint.h>
#include <uORB/topics/vehicle_torque_setpoint.h>
#include "fw_trim/FwTrim.hpp"
using matrix::Eulerf;
using matrix::Quatf;
@ -170,13 +172,6 @@ private:
(ParamBool<px4::params::FW_BAT_SCALE_EN>) _param_fw_bat_scale_en,
(ParamFloat<px4::params::FW_DTRIM_P_VMAX>) _param_fw_dtrim_p_vmax,
(ParamFloat<px4::params::FW_DTRIM_P_VMIN>) _param_fw_dtrim_p_vmin,
(ParamFloat<px4::params::FW_DTRIM_R_VMAX>) _param_fw_dtrim_r_vmax,
(ParamFloat<px4::params::FW_DTRIM_R_VMIN>) _param_fw_dtrim_r_vmin,
(ParamFloat<px4::params::FW_DTRIM_Y_VMAX>) _param_fw_dtrim_y_vmax,
(ParamFloat<px4::params::FW_DTRIM_Y_VMIN>) _param_fw_dtrim_y_vmin,
(ParamFloat<px4::params::FW_MAN_P_SC>) _param_fw_man_p_sc,
(ParamFloat<px4::params::FW_MAN_R_SC>) _param_fw_man_r_sc,
(ParamFloat<px4::params::FW_MAN_Y_SC>) _param_fw_man_y_sc,
@ -200,15 +195,14 @@ private:
(ParamFloat<px4::params::FW_YR_P>) _param_fw_yr_p,
(ParamFloat<px4::params::FW_YR_D>) _param_fw_yr_d,
(ParamFloat<px4::params::TRIM_PITCH>) _param_trim_pitch,
(ParamFloat<px4::params::TRIM_ROLL>) _param_trim_roll,
(ParamFloat<px4::params::TRIM_YAW>) _param_trim_yaw,
(ParamInt<px4::params::FW_SPOILERS_MAN>) _param_fw_spoilers_man
)
RateControl _rate_control; ///< class for rate control calculations
FwTrim _trim{this};
SlewRate<matrix::Vector3f> _trim_slew{}; ///< prevents large trim changes
void updateActuatorControlsStatus(float dt);
/**
@ -216,6 +210,8 @@ private:
*/
int parameters_update();
void save_params();
void vehicle_manual_poll();
void vehicle_land_detected_poll();

View File

@ -295,6 +295,21 @@ PARAM_DEFINE_INT32(FW_BAT_SCALE_EN, 0);
*/
PARAM_DEFINE_INT32(FW_ARSP_SCALE_EN, 1);
/**
* Auto-Trim mode
*
* In calibration mode, the estimated trim is used to set the TRIM_ROLL/PITCH/YAW
* parameters after landing. The parameter is then changed to continuous mode.
* In continuous mode, part of the auto-trim estimated
* during flight is used to update the exitsting trim.
*
* @group FW Rate Control
* @value 0 Disabled
* @value 1 Calibration
* @value 2 Continuous
*/
PARAM_DEFINE_INT32(FW_ATRIM_MODE, 1);
/**
* Roll trim increment at minimum airspeed
*

View File

@ -0,0 +1,39 @@
############################################################################
#
# Copyright (c) 2024 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(FwTrim
FwTrim.cpp
FwAutoTrim.cpp
)
# target_link_libraries(FwTrim)
target_include_directories(FwTrim PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -0,0 +1,203 @@
/****************************************************************************
*
* Copyright (c) 2024 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 "FwAutoTrim.hpp"
#include <mathlib/mathlib.h>
using namespace time_literals;
using matrix::Vector3f;
FwAutoTrim::FwAutoTrim(ModuleParams *parent) :
ModuleParams(parent)
{
_auto_trim_status_pub.advertise();
updateParams();
}
FwAutoTrim::~FwAutoTrim()
{
perf_free(_cycle_perf);
}
void FwAutoTrim::updateParams()
{
ModuleParams::updateParams();
}
void FwAutoTrim::reset()
{
_state = state::idle;
_trim_estimate.reset();
_trim_test.reset();
_trim_validated.zero();
}
void FwAutoTrim::update(const Vector3f &torque_sp, const float dt)
{
if (_vehicle_land_detected_sub.updated()) {
vehicle_land_detected_s vehicle_land_detected;
if (_vehicle_land_detected_sub.copy(&vehicle_land_detected)) {
_landed = vehicle_land_detected.landed;
}
}
perf_begin(_cycle_perf);
if (_vehicle_status_sub.updated()) {
vehicle_status_s vehicle_status;
if (_vehicle_status_sub.copy(&vehicle_status)) {
_armed = (vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED);
_fixed_wing = (vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_FIXED_WING);
_tailsitter = vehicle_status.is_vtol_tailsitter;
}
}
if (_airspeed_validated_sub.updated()) {
airspeed_validated_s airspeed_validated;
if (_airspeed_validated_sub.copy(&airspeed_validated)) {
if (PX4_ISFINITE(airspeed_validated.calibrated_airspeed_m_s)) {
_calibrated_airspeed_m_s = airspeed_validated.calibrated_airspeed_m_s;
} else {
_calibrated_airspeed_m_s = airspeed_validated.calibrated_ground_minus_wind_m_s;
}
}
}
if (_vehicle_attitude_sub.updated()) {
vehicle_attitude_s vehicle_attitude;
if (_vehicle_attitude_sub.copy(&vehicle_attitude)) {
const Vector3f earth_z_in_body_frame = matrix::Quatf(vehicle_attitude.q).rotateVectorInverse(Vector3f(0.f, 0.f, 1.f));
_cos_tilt = _tailsitter ? earth_z_in_body_frame(0) : earth_z_in_body_frame(2);
}
}
const hrt_abstime now = hrt_absolute_time();
const bool run_auto_trim = _fixed_wing
&& _armed
&& !_landed
&& (dt > 0.001f) && (dt < 0.1f)
&& torque_sp.isAllFinite()
&& ((_calibrated_airspeed_m_s >= _param_fw_airspd_min.get() && _calibrated_airspeed_m_s <= _param_fw_airspd_max.get())
|| (!PX4_ISFINITE(_calibrated_airspeed_m_s) && (_param_sys_has_num_aspd.get() == 0)))
&& _cos_tilt > cosf(math::radians(_kTiltMaxDeg));
if (run_auto_trim) {
switch (_state) {
default:
// fallthrough
case state::idle:
_trim_estimate.reset();
_trim_test.reset();
_state = state::sampling;
_state_start_time = now;
break;
case state::sampling:
// Average the torque setpoint over a long period of time
_trim_estimate.update(torque_sp);
if ((now - _state_start_time) > 5_s) {
_state = state::sampling_test;
_state_start_time = now;
}
break;
case state::sampling_test:
// Average a smaller amount of data to validate the trim estimate
_trim_test.update(torque_sp);
if ((now - _state_start_time) > 2_s) {
_state = state::verification;
_state_start_time = now;
}
break;
case state::verification: {
// Perform a statistical test between the estimated and test data
const float gate = 5.f;
const Vector3f innovation = _trim_test.mean() - _trim_estimate.mean();
const Vector3f innovation_variance = _trim_test.variance() + _trim_estimate.variance();
const float test_ratio = Vector3f(innovation.edivide(innovation_variance * gate * gate)) * innovation;
if ((test_ratio <= 1.f) && !innovation.longerThan(0.05f)) {
_trim_validated = _trim_estimate.mean();
_state = state::complete;
_state_start_time = now;
} else {
_state = state::fail;
}
} break;
case state::complete:
//TODO: depending on param, stay or restart
_state = state::idle;
break;
}
} else {
_state = state::fail;
}
publishStatus(now);
perf_end(_cycle_perf);
}
void FwAutoTrim::publishStatus(const hrt_abstime now)
{
auto_trim_status_s status_msg{};
_trim_validated.copyTo(status_msg.trim_validated);
_trim_estimate.mean().copyTo(status_msg.trim_estimate);
_trim_estimate.variance().copyTo(status_msg.trim_estimate_var);
_trim_test.mean().copyTo(status_msg.trim_test);
_trim_test.variance().copyTo(status_msg.trim_test_var);
status_msg.state = static_cast<int>(_state);
status_msg.timestamp = now;
status_msg.timestamp_sample = now;
_auto_trim_status_pub.publish(status_msg);
}

View File

@ -0,0 +1,111 @@
/****************************************************************************
*
* Copyright (c) 2024 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 <drivers/drv_hrt.h>
#include <lib/hysteresis/hysteresis.h>
#include <lib/perf/perf_counter.h>
#include <lib/mathlib/math/WelfordMeanVector.hpp>
#include <px4_platform_common/defines.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/posix.h>
#include <uORB/Publication.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/topics/airspeed_validated.h>
#include <uORB/topics/auto_trim_status.h>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/vehicle_land_detected.h>
#include <uORB/topics/vehicle_status.h>
using namespace time_literals;
class FwAutoTrim : public ModuleParams
{
public:
FwAutoTrim(ModuleParams *parent);
~FwAutoTrim();
void reset();
void update(const matrix::Vector3f &torque_sp, float dt);
const matrix::Vector3f &getTrim() const { return _trim_validated; }
protected:
void updateParams() override;
private:
void publishStatus(const hrt_abstime now);
uORB::Publication<auto_trim_status_s> _auto_trim_status_pub{ORB_ID(auto_trim_status)};
uORB::Subscription _vehicle_land_detected_sub{ORB_ID(vehicle_land_detected)};
uORB::Subscription _airspeed_validated_sub{ORB_ID(airspeed_validated)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude)};
enum class state {
idle = auto_trim_status_s::STATE_IDLE,
sampling = auto_trim_status_s::STATE_SAMPLING,
sampling_test = auto_trim_status_s::STATE_SAMPLING_TEST,
verification = auto_trim_status_s::STATE_VERIFICATION,
complete = auto_trim_status_s::STATE_COMPLETE,
fail = auto_trim_status_s::STATE_FAIL,
} _state{state::idle};
hrt_abstime _state_start_time{0};
bool _armed{false};
bool _landed{false};
bool _fixed_wing{false};
bool _tailsitter{false};
float _calibrated_airspeed_m_s{0.f};
float _cos_tilt{0.f};
math::WelfordMeanVector<float, 3> _trim_estimate{};
math::WelfordMeanVector<float, 3> _trim_test{};
matrix::Vector3f _trim_validated{};
perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, "[AutoTrim] cycle time")};
DEFINE_PARAMETERS(
(ParamInt<px4::params::SYS_HAS_NUM_ASPD>) _param_sys_has_num_aspd,
(ParamFloat<px4::params::FW_AIRSPD_MIN>) _param_fw_airspd_min,
(ParamFloat<px4::params::FW_AIRSPD_MAX>) _param_fw_airspd_max
)
static constexpr float _kTiltMaxDeg = 10.f;
};

View File

@ -0,0 +1,150 @@
/****************************************************************************
*
* Copyright (c) 2024 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 "FwTrim.hpp"
#include <mathlib/mathlib.h>
using namespace time_literals;
using matrix::Vector3f;
using math::interpolate;
FwTrim::FwTrim(ModuleParams *parent) :
ModuleParams(parent)
{
updateParams();
_airspeed = _param_fw_airspd_trim.get();
}
void FwTrim::updateParams()
{
ModuleParams::updateParams();
updateParameterizedTrim();
}
void FwTrim::saveParams()
{
const Vector3f autotrim = _auto_trim.getTrim();
bool updated = false;
const Vector3f trim_prev(_param_trim_roll.get(), _param_trim_pitch.get(), _param_trim_yaw.get());
if (_param_fw_atrim_mode.get() == static_cast<int32_t>(AutoTrimMode::kCalibration)) {
// Replace the current trim with the one identified during auto-trim
updated |= _param_trim_roll.commit_no_notification(_param_trim_roll.get() + autotrim(0));
updated |= _param_trim_pitch.commit_no_notification(_param_trim_pitch.get() + autotrim(1));
updated |= _param_trim_yaw.commit_no_notification(_param_trim_yaw.get() + autotrim(2));
if (updated) {
_param_fw_atrim_mode.set(static_cast<int32_t>(AutoTrimMode::kContinuous));
_param_fw_atrim_mode.commit();
}
} else if (_param_fw_atrim_mode.get() == static_cast<int32_t>(AutoTrimMode::kContinuous)) {
// In continuous trim mode, limit the amount of trim that can be applied back to the parameter
const Vector3f constrained_autotrim = matrix::constrain(autotrim, -0.05f, 0.05f);
updated |= _param_trim_roll.commit_no_notification(_param_trim_roll.get() + constrained_autotrim(0));
updated |= _param_trim_pitch.commit_no_notification(_param_trim_pitch.get() + constrained_autotrim(1));
updated |= _param_trim_yaw.commit_no_notification(_param_trim_yaw.get() + constrained_autotrim(2));
if (updated) {
_param_trim_yaw.commit();
}
} else {
// nothing to do
}
if (updated) {
const Vector3f trim_new(_param_trim_roll.get(), _param_trim_pitch.get(), _param_trim_yaw.get());
PX4_INFO("Trim offset committed: [%.3f %.3f %.3f]->[%.3f %.3f %.3f]",
(double)trim_prev(0), (double)trim_prev(1), (double)trim_prev(2),
(double)trim_new(0), (double)trim_new(1), (double)trim_new(2));
}
_auto_trim.reset();
}
void FwTrim::reset()
{
_auto_trim.reset();
}
void FwTrim::updateAutoTrim(const Vector3f &torque_sp, const float dt)
{
_auto_trim.update(torque_sp - _parameterized_trim, dt);
}
Vector3f FwTrim::getTrim() const
{
Vector3f trim = _parameterized_trim;
if (_param_fw_atrim_mode.get() > 0) {
trim += _auto_trim.getTrim();
}
return trim;
}
void FwTrim::setAirspeed(const float airspeed)
{
_airspeed = airspeed;
updateParameterizedTrim();
}
void FwTrim::updateParameterizedTrim()
{
/* bi-linear interpolation over airspeed for actuator trim scheduling */
Vector3f trim(_param_trim_roll.get(), _param_trim_pitch.get(), _param_trim_yaw.get());
if (_airspeed < _param_fw_airspd_trim.get()) {
trim(0) += interpolate(_airspeed, _param_fw_airspd_min.get(), _param_fw_airspd_trim.get(),
_param_fw_dtrim_r_vmin.get(),
0.0f);
trim(1) += interpolate(_airspeed, _param_fw_airspd_min.get(), _param_fw_airspd_trim.get(),
_param_fw_dtrim_p_vmin.get(),
0.0f);
trim(2) += interpolate(_airspeed, _param_fw_airspd_min.get(), _param_fw_airspd_trim.get(),
_param_fw_dtrim_y_vmin.get(),
0.0f);
} else {
trim(0) += interpolate(_airspeed, _param_fw_airspd_trim.get(), _param_fw_airspd_max.get(), 0.0f,
_param_fw_dtrim_r_vmax.get());
trim(1) += interpolate(_airspeed, _param_fw_airspd_trim.get(), _param_fw_airspd_max.get(), 0.0f,
_param_fw_dtrim_p_vmax.get());
trim(2) += interpolate(_airspeed, _param_fw_airspd_trim.get(), _param_fw_airspd_max.get(), 0.0f,
_param_fw_dtrim_y_vmax.get());
}
_parameterized_trim = trim;
}

View File

@ -0,0 +1,95 @@
/****************************************************************************
*
* Copyright (c) 2024 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 <drivers/drv_hrt.h>
#include <px4_platform_common/defines.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/posix.h>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/vehicle_control_mode.h>
#include "FwAutoTrim.hpp"
using namespace time_literals;
class FwTrim : public ModuleParams
{
public:
FwTrim(ModuleParams *parent);
~FwTrim() = default;
void saveParams();
void reset();
void setAirspeed(float airspeed);
void updateAutoTrim(const matrix::Vector3f &torque_sp, float dt);
matrix::Vector3f getTrim() const;
protected:
void updateParams() override;
private:
enum class AutoTrimMode {
kDisabled = 0,
kCalibration,
kContinuous
};
void updateParameterizedTrim();
FwAutoTrim _auto_trim{this};
matrix::Vector3f _parameterized_trim{};
float _airspeed{0.f};
DEFINE_PARAMETERS(
(ParamInt<px4::params::FW_ATRIM_MODE>) _param_fw_atrim_mode,
(ParamFloat<px4::params::TRIM_PITCH>) _param_trim_pitch,
(ParamFloat<px4::params::TRIM_ROLL>) _param_trim_roll,
(ParamFloat<px4::params::TRIM_YAW>) _param_trim_yaw,
(ParamFloat<px4::params::FW_DTRIM_P_VMAX>) _param_fw_dtrim_p_vmax,
(ParamFloat<px4::params::FW_DTRIM_P_VMIN>) _param_fw_dtrim_p_vmin,
(ParamFloat<px4::params::FW_DTRIM_R_VMAX>) _param_fw_dtrim_r_vmax,
(ParamFloat<px4::params::FW_DTRIM_R_VMIN>) _param_fw_dtrim_r_vmin,
(ParamFloat<px4::params::FW_DTRIM_Y_VMAX>) _param_fw_dtrim_y_vmax,
(ParamFloat<px4::params::FW_DTRIM_Y_VMIN>) _param_fw_dtrim_y_vmin,
(ParamFloat<px4::params::FW_AIRSPD_TRIM>) _param_fw_airspd_trim,
(ParamFloat<px4::params::FW_AIRSPD_MIN>) _param_fw_airspd_min,
(ParamFloat<px4::params::FW_AIRSPD_MAX>) _param_fw_airspd_max
)
};

View File

@ -51,6 +51,7 @@ void LoggedTopics::add_default_topics()
add_topic("airspeed", 1000);
add_optional_topic("airspeed_validated", 200);
add_optional_topic("autotune_attitude_control_status", 100);
add_optional_topic("auto_trim_status");
add_optional_topic("camera_capture");
add_optional_topic("camera_trigger");
add_topic("cellular_status", 200);