mixer_module: create MixingOutput library and use in fmu

This should be a pure refactoring, no functional change.
This commit is contained in:
Beat Küng
2019-08-22 08:22:57 +02:00
committed by Daniel Agar
parent 0ec6e79a0a
commit d3fb610fde
9 changed files with 831 additions and 448 deletions
@@ -57,6 +57,15 @@ public:
virtual void Run() = 0;
/**
* Switch to a different WorkQueue.
* NOTE: Caller is responsible for synchronization.
*
* @param config The WorkQueue configuration (see WorkQueueManager.hpp).
* @return true if initialization was successful
*/
bool ChangeWorkQeue(const wq_config_t &config) { return Init(config); }
protected:
/**
+1
View File
@@ -40,5 +40,6 @@ px4_add_module(
arch_io_pins
circuit_breaker
mixer
mixer_module
pwm_limit
)
+111 -447
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -51,6 +51,7 @@ add_subdirectory(landing_slope)
add_subdirectory(led)
add_subdirectory(mathlib)
add_subdirectory(mixer)
add_subdirectory(mixer_module)
add_subdirectory(perf)
add_subdirectory(pid)
add_subdirectory(pwm_limit)
+35
View File
@@ -0,0 +1,35 @@
############################################################################
#
# Copyright (c) 2019 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(mixer_module mixer_module.cpp)
+463
View File
@@ -0,0 +1,463 @@
/****************************************************************************
*
* Copyright (c) 2019 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 "mixer_module.hpp"
#include <lib/circuit_breaker/circuit_breaker.h>
#include <px4_log.h>
using namespace time_literals;
MixingOutput::MixingOutput(OutputModuleInterface &interface, bool support_esc_calibration, bool ramp_up)
: ModuleParams(&interface),
_control_subs{
{&interface, ORB_ID(actuator_controls_0)},
{&interface, ORB_ID(actuator_controls_1)},
{&interface, ORB_ID(actuator_controls_2)},
{&interface, ORB_ID(actuator_controls_3)}
},
_support_esc_calibration(support_esc_calibration),
_interface(interface),
_control_latency_perf(perf_alloc(PC_ELAPSED, "control latency"))
{
/* initialize PWM limit lib */
pwm_limit_init(&_pwm_limit);
_pwm_limit.ramp_up = ramp_up;
/* Safely initialize armed flags */
_armed.armed = false;
_armed.prearmed = false;
_armed.ready_to_arm = false;
_armed.lockdown = false;
_armed.force_failsafe = false;
_armed.in_esc_calibration_mode = false;
// If there is no safety button, disable it on init.
#ifndef GPIO_BTN_SAFETY
_safety_off = true;
#endif
px4_sem_init(&_lock, 0, 1);
}
MixingOutput::~MixingOutput()
{
perf_free(_control_latency_perf);
delete _mixers;
px4_sem_destroy(&_lock);
}
void MixingOutput::printStatus()
{
perf_print_counter(_control_latency_perf);
PX4_INFO("Switched to rate_ctrl work queue: %i", (int)_wq_switched);
}
void MixingOutput::updateParams()
{
ModuleParams::updateParams();
bool safety_disabled = circuit_breaker_enabled("CBRK_IO_SAFETY", CBRK_IO_SAFETY_KEY);
if (safety_disabled) {
_safety_off = true;
}
// update mixer if we have one
if (_mixers) {
if (_param_mot_slew_max.get() <= FLT_EPSILON) {
_mixers->set_max_delta_out_once(0.f);
}
_mixers->set_thrust_factor(_param_thr_mdl_fac.get());
_mixers->set_airmode((Mixer::Airmode)_param_mc_airmode.get());
}
}
bool MixingOutput::updateSubscriptions(bool allow_wq_switch)
{
if (_groups_subscribed == _groups_required) {
return false;
}
// must be locked to potentially change WorkQueue
lock();
// first clear everything
_interface.ScheduleClear();
unregister();
// if subscribed to control group 0 or 1 then move to the rate_ctrl WQ
const bool sub_group_0 = (_groups_required & (1 << 0));
const bool sub_group_1 = (_groups_required & (1 << 1));
if (allow_wq_switch && !_wq_switched && (sub_group_0 || sub_group_1)) {
if (_interface.ChangeWorkQeue(px4::wq_configurations::rate_ctrl)) {
// let the new WQ handle the subscribe update
_wq_switched = true;
_interface.ScheduleNow();
unlock();
return false;
}
}
_groups_subscribed = _groups_required;
// subscribe to all required actuator control groups
for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
if (_groups_required & (1 << i)) {
PX4_DEBUG("subscribe to actuator_controls_%d", i);
if (!_control_subs[i].register_callback()) {
PX4_ERR("actuator_controls_%d register callback failed!", i);
}
}
}
setMaxTopicUpdateRate(_max_topic_update_interval_us);
// if nothing required keep periodic schedule (so the module can update other things)
if (_groups_required == 0) {
// TODO: this might need to be configurable depending on the module
_interface.ScheduleOnInterval(100_ms);
}
PX4_DEBUG("_groups_required 0x%08x", _groups_required);
PX4_DEBUG("_groups_subscribed 0x%08x", _groups_subscribed);
unlock();
return true;
}
void MixingOutput::setMaxTopicUpdateRate(unsigned max_topic_update_interval_us)
{
_max_topic_update_interval_us = max_topic_update_interval_us;
for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
if (_groups_subscribed & (1 << i)) {
_control_subs[i].set_interval_us(_max_topic_update_interval_us);
}
}
}
void MixingOutput::setAllMinValues(uint16_t value)
{
for (unsigned i = 0; i < MAX_ACTUATORS; i++) {
_min_value[i] = value;
}
}
void MixingOutput::setAllMaxValues(uint16_t value)
{
for (unsigned i = 0; i < MAX_ACTUATORS; i++) {
_max_value[i] = value;
}
}
void MixingOutput::setAllFailsafeValues(uint16_t value)
{
for (unsigned i = 0; i < MAX_ACTUATORS; i++) {
_failsafe_value[i] = value;
}
}
void MixingOutput::setAllDisarmedValues(uint16_t value)
{
for (unsigned i = 0; i < MAX_ACTUATORS; i++) {
_disarmed_value[i] = value;
}
}
void MixingOutput::unregister()
{
for (auto &control_sub : _control_subs) {
control_sub.unregister_callback();
}
}
bool MixingOutput::update()
{
if (!_mixers) {
// do nothing until we have a valid mixer
return false;
}
// check arming state
if (_armed_sub.update(&_armed)) {
_armed.in_esc_calibration_mode &= _support_esc_calibration;
/* Update the armed status and check that we're not locked down.
* We also need to arm throttle for the ESC calibration. */
_throttle_armed = (_safety_off && _armed.armed && !_armed.lockdown) || (_safety_off && _armed.in_esc_calibration_mode);
}
if (_param_mot_slew_max.get() > FLT_EPSILON) {
const hrt_abstime now = hrt_absolute_time();
const float dt = math::constrain((now - _time_last_mix) / 1e6f, 0.0001f, 0.02f);
_time_last_mix = now;
// maximum value the outputs of the multirotor mixer are allowed to change in this cycle
// factor 2 is needed because actuator outputs are in the range [-1,1]
const float delta_out_max = 2.0f * 1000.0f * dt / (_max_value[0] - _min_value[0]) / _param_mot_slew_max.get();
_mixers->set_max_delta_out_once(delta_out_max);
}
unsigned n_updates = 0;
/* get controls for required topics */
for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
if (_groups_subscribed & (1 << i)) {
if (_control_subs[i].copy(&_controls[i])) {
n_updates++;
}
/* During ESC calibration, we overwrite the throttle value. */
if (i == 0 && _armed.in_esc_calibration_mode) {
/* Set all controls to 0 */
memset(&_controls[i], 0, sizeof(_controls[i]));
/* except thrust to maximum. */
_controls[i].control[actuator_controls_s::INDEX_THROTTLE] = 1.0f;
/* Switch off the PWM limit ramp for the calibration. */
_pwm_limit.state = PWM_LIMIT_STATE_ON;
}
}
}
/* do mixing */
float outputs[MAX_ACTUATORS] {};
const unsigned mixed_num_outputs = _mixers->mix(outputs, MAX_ACTUATORS);
/* the PWM limit call takes care of out of band errors, NaN and constrains */
uint16_t output_limited[MAX_ACTUATORS] {};
pwm_limit_calc(_throttle_armed, armNoThrottle(), mixed_num_outputs, _reverse_output_mask,
_disarmed_value, _min_value, _max_value, outputs, output_limited, &_pwm_limit);
/* overwrite outputs in case of force_failsafe with _failsafe_value values */
if (_armed.force_failsafe) {
for (size_t i = 0; i < mixed_num_outputs; i++) {
output_limited[i] = _failsafe_value[i];
}
}
/* overwrite outputs in case of lockdown or parachute triggering with disarmed values */
if (_armed.lockdown || _armed.manual_lockdown) {
for (size_t i = 0; i < mixed_num_outputs; i++) {
output_limited[i] = _disarmed_value[i];
}
}
bool stop_motors = true;
if (mixed_num_outputs > 0) {
/* assume if one (here the 1.) motor is disarmed, all of them should be stopped */
stop_motors = (output_limited[0] == _disarmed_value[0]);
}
/* apply _param_mot_ordering */
reorderOutputs(output_limited);
/* now return the outputs to the driver */
_interface.updateOutputs(stop_motors, output_limited, mixed_num_outputs, n_updates);
actuator_outputs_s actuator_outputs{};
actuator_outputs.noutputs = mixed_num_outputs;
// zero unused outputs
for (size_t i = 0; i < mixed_num_outputs; ++i) {
actuator_outputs.output[i] = output_limited[i];
}
actuator_outputs.timestamp = hrt_absolute_time();
_outputs_pub.publish(actuator_outputs);
/* publish mixer status */
MultirotorMixer::saturation_status saturation_status;
saturation_status.value = _mixers->get_saturation_status();
if (saturation_status.flags.valid) {
multirotor_motor_limits_s motor_limits;
motor_limits.timestamp = actuator_outputs.timestamp;
motor_limits.saturation_status = saturation_status.value;
_to_mixer_status.publish(motor_limits);
}
// use first valid timestamp_sample for latency tracking
for (int i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
const bool required = _groups_required & (1 << i);
const hrt_abstime &timestamp_sample = _controls[i].timestamp_sample;
if (required && (timestamp_sample > 0)) {
perf_set_elapsed(_control_latency_perf, actuator_outputs.timestamp - timestamp_sample);
break;
}
}
// check safety button state
if (_safety_sub.updated()) {
safety_s safety;
if (_safety_sub.copy(&safety)) {
_safety_off = !safety.safety_switch_available || safety.safety_off;
}
}
return true;
}
void
MixingOutput::reorderOutputs(uint16_t values[MAX_ACTUATORS])
{
if (MAX_ACTUATORS < 4) {
return;
}
if ((MotorOrdering)_param_mot_ordering.get() == MotorOrdering::Betaflight) {
/*
* Betaflight default motor ordering:
* 4 2
* ^
* 3 1
*/
const uint16_t value_tmp[4] = {values[0], values[1], values[2], values[3] };
values[0] = value_tmp[3];
values[1] = value_tmp[0];
values[2] = value_tmp[1];
values[3] = value_tmp[2];
}
/* else: PX4, no need to reorder
* 3 1
* ^
* 2 4
*/
}
int MixingOutput::controlCallback(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &input)
{
const MixingOutput *output = (const MixingOutput *)handle;
input = output->_controls[control_group].control[control_index];
/* limit control input */
if (input > 1.0f) {
input = 1.0f;
} else if (input < -1.0f) {
input = -1.0f;
}
/* motor spinup phase - lock throttle to zero */
if (output->_pwm_limit.state == PWM_LIMIT_STATE_RAMP) {
if ((control_group == actuator_controls_s::GROUP_INDEX_ATTITUDE ||
control_group == actuator_controls_s::GROUP_INDEX_ATTITUDE_ALTERNATE) &&
control_index == actuator_controls_s::INDEX_THROTTLE) {
/* limit the throttle output to zero during motor spinup,
* as the motors cannot follow any demand yet
*/
input = 0.0f;
}
}
/* throttle not arming - mark throttle input as invalid */
if (output->armNoThrottle() && !output->_armed.in_esc_calibration_mode) {
if ((control_group == actuator_controls_s::GROUP_INDEX_ATTITUDE ||
control_group == actuator_controls_s::GROUP_INDEX_ATTITUDE_ALTERNATE) &&
control_index == actuator_controls_s::INDEX_THROTTLE) {
/* set the throttle to an invalid value */
input = NAN;
}
}
return 0;
}
void MixingOutput::resetMixer()
{
// TODO: thread-safe
lock();
if (_mixers != nullptr) {
delete _mixers;
_mixers = nullptr;
_groups_required = 0;
}
_interface.ScheduleNow();
unlock();
}
int MixingOutput::loadMixer(const char *buf, unsigned len)
{
// TODO: thread-safe
if (_mixers == nullptr) {
_mixers = new MixerGroup(controlCallback, (uintptr_t)this);
}
if (_mixers == nullptr) {
_groups_required = 0;
return -ENOMEM;
}
int ret = _mixers->load_from_buf(buf, len);
if (ret != 0) {
PX4_ERR("mixer load failed with %d", ret);
delete _mixers;
_mixers = nullptr;
_groups_required = 0;
return ret;
}
_mixers->groups_required(_groups_required);
PX4_DEBUG("loaded mixers \n%s\n", buf);
updateParams();
lock();
_interface.ScheduleNow();
unlock();
return ret;
}
+203
View File
@@ -0,0 +1,203 @@
/****************************************************************************
*
* Copyright (c) 2019 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 <board_config.h>
#include <lib/mixer/mixer.h>
#include <lib/perf/perf_counter.h>
#include <lib/pwm_limit/pwm_limit.h>
#include <px4_module_params.h>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <uORB/Publication.hpp>
#include <uORB/PublicationMulti.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/SubscriptionCallback.hpp>
#include <uORB/topics/actuator_armed.h>
#include <uORB/topics/actuator_controls.h>
#include <uORB/topics/actuator_outputs.h>
#include <uORB/topics/multirotor_motor_limits.h>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/safety.h>
/**
* @class OutputModuleInterface
* Base class for an output module.
*/
class OutputModuleInterface : public px4::ScheduledWorkItem, public ModuleParams
{
public:
static constexpr int MAX_ACTUATORS = DIRECT_PWM_OUTPUT_CHANNELS;
OutputModuleInterface(const px4::wq_config_t &config)
: px4::ScheduledWorkItem(config), ModuleParams(nullptr) {}
virtual void updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS],
unsigned num_outputs, unsigned num_control_groups_updated) = 0;
};
/**
* @class MixingOutput
* This handles the mixing, arming/disarming and all subscriptions required for that.
*
* It also drives the scheduling of the OutputModuleInterface (via uORB callbacks
* to reduce output latency).
*/
class MixingOutput : public ModuleParams
{
public:
static constexpr int MAX_ACTUATORS = OutputModuleInterface::MAX_ACTUATORS;
/**
* Contructor
* @param interface Parent module for scheduling, parameter updates and callbacks
* @param support_esc_calibration true if the output module supports ESC calibration via max, then min setting
* @param ramp_up true if motor ramp up from disarmed to min upon arming is wanted
*/
MixingOutput(OutputModuleInterface &interface, bool support_esc_calibration, bool ramp_up = true);
~MixingOutput();
void printStatus();
/**
* Call this regularly from Run(). It will call interface.updateOutputs().
* @return true if outputs were updated
*/
bool update();
/**
* Check for subscription updates (e.g. after a mixer is loaded).
* Call this at the very end of Run() if allow_wq_switch
* @param allow_wq_switch if true
* @return true if subscriptions got changed
*/
bool updateSubscriptions(bool allow_wq_switch);
/**
* unregister uORB subscription callbacks
*/
void unregister();
void setMaxTopicUpdateRate(unsigned max_topic_update_interval_us);
void resetMixer();
int loadMixer(const char *buf, unsigned len);
const actuator_armed_s &armed() const { return _armed; }
MixerGroup *mixers() const { return _mixers; }
void setAllFailsafeValues(uint16_t value);
void setAllDisarmedValues(uint16_t value);
void setAllMinValues(uint16_t value);
void setAllMaxValues(uint16_t value);
uint16_t &reverseOutputMask() { return _reverse_output_mask; }
uint16_t &failsafeValue(int index) { return _failsafe_value[index]; }
/** Disarmed values: if ramp_up is true, then disarmedValue < minValue needs to hold */
uint16_t &disarmedValue(int index) { return _disarmed_value[index]; }
uint16_t &minValue(int index) { return _min_value[index]; }
uint16_t &maxValue(int index) { return _max_value[index]; }
protected:
void updateParams() override;
private:
bool armNoThrottle() const
{
return (_armed.prearmed && !_armed.armed) || _armed.in_esc_calibration_mode;
}
static int controlCallback(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &input);
enum class MotorOrdering : int32_t {
PX4 = 0,
Betaflight = 1
};
/**
* Reorder PWM outputs according to _param_mot_ordering
* @param values PWM values to reorder
*/
inline void reorderOutputs(uint16_t values[MAX_ACTUATORS]);
void lock() { do {} while (px4_sem_wait(&_lock) != 0); }
void unlock() { px4_sem_post(&_lock); }
px4_sem_t _lock; /**< lock to protect access to work queue changes (includes ScheduleNow calls from another thread) */
uint16_t _failsafe_value[MAX_ACTUATORS] {};
uint16_t _disarmed_value[MAX_ACTUATORS] {};
uint16_t _min_value[MAX_ACTUATORS] {};
uint16_t _max_value[MAX_ACTUATORS] {};
uint16_t _reverse_output_mask{0}; ///< reverses the interval [min, max] -> [max, min], NOT motor direction
pwm_limit_t _pwm_limit;
uORB::Subscription _armed_sub{ORB_ID(actuator_armed)};
uORB::Subscription _safety_sub{ORB_ID(safety)};
uORB::SubscriptionCallbackWorkItem _control_subs[actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS];
uORB::PublicationMulti<actuator_outputs_s> _outputs_pub{ORB_ID(actuator_outputs), ORB_PRIO_DEFAULT};
uORB::PublicationMulti<multirotor_motor_limits_s> _to_mixer_status{ORB_ID(multirotor_motor_limits), ORB_PRIO_DEFAULT}; ///< mixer status flags
actuator_controls_s _controls[actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS] {};
actuator_armed_s _armed{};
hrt_abstime _time_last_mix{0};
unsigned _max_topic_update_interval_us{0}; ///< max _control_subs topic update interval (0=unlimited)
bool _safety_off{false}; ///< State of the safety button from the subscribed _safety_sub topic
bool _throttle_armed{false};
MixerGroup *_mixers{nullptr};
uint32_t _groups_required{0};
uint32_t _groups_subscribed{0};
const bool _support_esc_calibration;
bool _wq_switched{false};
OutputModuleInterface &_interface;
perf_counter_t _control_latency_perf;
DEFINE_PARAMETERS(
(ParamInt<px4::params::MC_AIRMODE>) _param_mc_airmode, ///< multicopter air-mode
(ParamFloat<px4::params::MOT_SLEW_MAX>) _param_mot_slew_max,
(ParamFloat<px4::params::THR_MDL_FAC>) _param_thr_mdl_fac, ///< thrust to pwm modelling factor
(ParamInt<px4::params::MOT_ORDERING>) _param_mot_ordering
)
};
+7 -1
View File
@@ -54,6 +54,7 @@ void pwm_limit_init(pwm_limit_t *limit)
{
limit->state = PWM_LIMIT_STATE_INIT;
limit->time_armed = 0;
limit->ramp_up = true;
}
void pwm_limit_calc(const bool armed, const bool pre_armed, const unsigned num_channels, const uint16_t reverse_mask,
@@ -81,7 +82,12 @@ void pwm_limit_calc(const bool armed, const bool pre_armed, const unsigned num_c
case PWM_LIMIT_STATE_OFF:
if (armed) {
limit->state = PWM_LIMIT_STATE_RAMP;
if (limit->ramp_up) {
limit->state = PWM_LIMIT_STATE_RAMP;
} else {
limit->state = PWM_LIMIT_STATE_ON;
}
/* reset arming time, used for ramp timing */
limit->time_armed = hrt_absolute_time();
+1
View File
@@ -67,6 +67,7 @@ enum pwm_limit_state {
typedef struct {
enum pwm_limit_state state;
uint64_t time_armed;
bool ramp_up; ///< if true, motors will ramp up from disarmed to min_pwm after arming
} pwm_limit_t;
__EXPORT void pwm_limit_init(pwm_limit_t *limit);