forked from Archive/PX4-Autopilot
mixer_module: add support for test_motor (motor_test CLI command)
This commit is contained in:
parent
349469cf75
commit
0871802568
|
@ -56,6 +56,7 @@ px4_add_board(
|
||||||
led_control
|
led_control
|
||||||
mixer
|
mixer
|
||||||
motor_ramp
|
motor_ramp
|
||||||
|
motor_test
|
||||||
#mtd
|
#mtd
|
||||||
#nshterm
|
#nshterm
|
||||||
param
|
param
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
uint64 timestamp # time since system start (microseconds)
|
uint64 timestamp # time since system start (microseconds)
|
||||||
uint8 NUM_MOTOR_OUTPUTS = 8
|
uint8 NUM_MOTOR_OUTPUTS = 8
|
||||||
|
|
||||||
|
uint8 ACTION_STOP = 0 # stop motors (disable motor test mode)
|
||||||
|
uint8 ACTION_RUN = 1 # run motors (enable motor test mode)
|
||||||
|
|
||||||
|
uint8 action # one of ACTION_* (applies to all motors)
|
||||||
uint32 motor_number # number of motor to spin
|
uint32 motor_number # number of motor to spin
|
||||||
float32 value # output power, range [0..1]
|
float32 value # output power, range [0..1]
|
||||||
|
|
||||||
|
uint8 driver_instance # select output driver (for boards with multiple outputs, like IO+FMU)
|
||||||
|
|
||||||
uint8 ORB_QUEUE_LENGTH = 4
|
uint8 ORB_QUEUE_LENGTH = 4
|
||||||
|
|
|
@ -165,6 +165,8 @@ public:
|
||||||
bool telemetryEnabled() const { return _telemetry != nullptr; }
|
bool telemetryEnabled() const { return _telemetry != nullptr; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr uint16_t DISARMED_VALUE = 0;
|
||||||
|
|
||||||
enum class DShotConfig {
|
enum class DShotConfig {
|
||||||
Disabled = 0,
|
Disabled = 0,
|
||||||
DShot150 = 150,
|
DShot150 = 150,
|
||||||
|
@ -248,8 +250,8 @@ DShotOutput::DShotOutput() :
|
||||||
_cycle_perf(perf_alloc(PC_ELAPSED, "dshot: cycle")),
|
_cycle_perf(perf_alloc(PC_ELAPSED, "dshot: cycle")),
|
||||||
_cycle_interval_perf(perf_alloc(PC_INTERVAL, "dshot: cycle interval"))
|
_cycle_interval_perf(perf_alloc(PC_INTERVAL, "dshot: cycle interval"))
|
||||||
{
|
{
|
||||||
_mixing_output.setAllDisarmedValues(0);
|
_mixing_output.setAllDisarmedValues(DISARMED_VALUE);
|
||||||
_mixing_output.setAllMinValues(1);
|
_mixing_output.setAllMinValues(DISARMED_VALUE + 1);
|
||||||
_mixing_output.setAllMaxValues(DSHOT_MAX_THROTTLE);
|
_mixing_output.setAllMaxValues(DSHOT_MAX_THROTTLE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -286,6 +288,8 @@ DShotOutput::init()
|
||||||
PX4_ERR("FAILED registering class device");
|
PX4_ERR("FAILED registering class device");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_mixing_output.setDriverInstance(_class_instance);
|
||||||
|
|
||||||
// Getting initial parameter values
|
// Getting initial parameter values
|
||||||
update_params();
|
update_params();
|
||||||
|
|
||||||
|
@ -721,7 +725,12 @@ void DShotOutput::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < (int)num_outputs; i++) {
|
for (int i = 0; i < (int)num_outputs; i++) {
|
||||||
up_dshot_motor_data_set(i, math::min(outputs[i], (uint16_t)DSHOT_MAX_THROTTLE), i == requested_telemetry_index);
|
if (outputs[i] == DISARMED_VALUE) {
|
||||||
|
up_dshot_motor_command(i, DShot_cmd_motor_stop, i == requested_telemetry_index);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
up_dshot_motor_data_set(i, math::min(outputs[i], (uint16_t)DSHOT_MAX_THROTTLE), i == requested_telemetry_index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear commands when motors are running
|
// clear commands when motors are running
|
||||||
|
@ -805,8 +814,8 @@ void DShotOutput::update_params()
|
||||||
updateParams();
|
updateParams();
|
||||||
|
|
||||||
// we use a minimum value of 1, since 0 is for disarmed
|
// we use a minimum value of 1, since 0 is for disarmed
|
||||||
_mixing_output.setAllMinValues(math::constrain((int)(_param_dshot_min.get() * (float)DSHOT_MAX_THROTTLE), 1,
|
_mixing_output.setAllMinValues(math::constrain((int)(_param_dshot_min.get() * (float)DSHOT_MAX_THROTTLE),
|
||||||
DSHOT_MAX_THROTTLE));
|
DISARMED_VALUE + 1, DSHOT_MAX_THROTTLE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -253,6 +253,8 @@ PX4FMU::init()
|
||||||
PX4_ERR("FAILED registering class device");
|
PX4_ERR("FAILED registering class device");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_mixing_output.setDriverInstance(_class_instance);
|
||||||
|
|
||||||
/* force a reset of the update rate */
|
/* force a reset of the update rate */
|
||||||
_current_update_rate = 0;
|
_current_update_rate = 0;
|
||||||
|
|
||||||
|
|
|
@ -496,7 +496,14 @@ void TAP_ESC::cycle()
|
||||||
if (test_motor_updated) {
|
if (test_motor_updated) {
|
||||||
struct test_motor_s test_motor;
|
struct test_motor_s test_motor;
|
||||||
orb_copy(ORB_ID(test_motor), _test_motor_sub, &test_motor);
|
orb_copy(ORB_ID(test_motor), _test_motor_sub, &test_motor);
|
||||||
_outputs.output[test_motor.motor_number] = RPMSTOPPED + ((RPMMAX - RPMSTOPPED) * test_motor.value);
|
|
||||||
|
if (test_motor.action == test_motor_s::ACTION_STOP) {
|
||||||
|
_outputs.output[test_motor.motor_number] = RPMSTOPPED;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_outputs.output[test_motor.motor_number] = RPMSTOPPED + ((RPMMAX - RPMSTOPPED) * test_motor.value);
|
||||||
|
}
|
||||||
|
|
||||||
PX4_INFO("setting motor %i to %.1lf", test_motor.motor_number,
|
PX4_INFO("setting motor %i to %.1lf", test_motor.motor_number,
|
||||||
(double)_outputs.output[test_motor.motor_number]);
|
(double)_outputs.output[test_motor.motor_number]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -971,7 +971,7 @@ int UavcanNode::run()
|
||||||
orb_copy(ORB_ID(test_motor), _test_motor_sub, &_test_motor);
|
orb_copy(ORB_ID(test_motor), _test_motor_sub, &_test_motor);
|
||||||
|
|
||||||
// Update the test status and check that we're not locked down
|
// Update the test status and check that we're not locked down
|
||||||
_test_in_progress = (_test_motor.value > 0);
|
_test_in_progress = (_test_motor.action == test_motor_s::ACTION_RUN);
|
||||||
_esc_controller.arm_single_esc(_test_motor.motor_number, _test_in_progress);
|
_esc_controller.arm_single_esc(_test_motor.motor_number, _test_in_progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "mixer_module.hpp"
|
#include "mixer_module.hpp"
|
||||||
|
|
||||||
#include <lib/circuit_breaker/circuit_breaker.h>
|
#include <lib/circuit_breaker/circuit_breaker.h>
|
||||||
|
#include <uORB/PublicationQueued.hpp>
|
||||||
#include <px4_log.h>
|
#include <px4_log.h>
|
||||||
|
|
||||||
using namespace time_literals;
|
using namespace time_literals;
|
||||||
|
@ -72,6 +73,12 @@ _control_latency_perf(perf_alloc(PC_ELAPSED, "control latency"))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
px4_sem_init(&_lock, 0, 1);
|
px4_sem_init(&_lock, 0, 1);
|
||||||
|
|
||||||
|
// Enforce the existence of the test_motor topic, so we won't miss initial publications
|
||||||
|
test_motor_s test{};
|
||||||
|
uORB::PublicationQueued<test_motor_s> test_motor_pub{ORB_ID(test_motor)};
|
||||||
|
test_motor_pub.publish(test);
|
||||||
|
_motor_test.test_motor_sub.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
MixingOutput::~MixingOutput()
|
MixingOutput::~MixingOutput()
|
||||||
|
@ -86,12 +93,14 @@ void MixingOutput::printStatus() const
|
||||||
perf_print_counter(_control_latency_perf);
|
perf_print_counter(_control_latency_perf);
|
||||||
PX4_INFO("Switched to rate_ctrl work queue: %i", (int)_wq_switched);
|
PX4_INFO("Switched to rate_ctrl work queue: %i", (int)_wq_switched);
|
||||||
PX4_INFO("Mixer loaded: %s", _mixers ? "yes" : "no");
|
PX4_INFO("Mixer loaded: %s", _mixers ? "yes" : "no");
|
||||||
|
PX4_INFO("Driver instance: %i", _driver_instance);
|
||||||
|
|
||||||
PX4_INFO("Channel Configuration:");
|
PX4_INFO("Channel Configuration:");
|
||||||
|
|
||||||
for (unsigned i = 0; i < MAX_ACTUATORS; i++) {
|
for (unsigned i = 0; i < _max_num_outputs; i++) {
|
||||||
PX4_INFO("Channel %i: failsafe: %d, disarmed: %d, min: %d, max: %d", i, _failsafe_value[i], _disarmed_value[i],
|
int reordered_i = reorderedMotorIndex(i);
|
||||||
_min_value[i], _max_value[i]);
|
PX4_INFO("Channel %i: value: %i, failsafe: %d, disarmed: %d, min: %d, max: %d", reordered_i, _current_output_value[i],
|
||||||
|
_failsafe_value[reordered_i], _disarmed_value[reordered_i], _min_value[reordered_i], _max_value[reordered_i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +240,44 @@ void MixingOutput::updateOutputSlewrate()
|
||||||
_mixers->set_max_delta_out_once(delta_out_max);
|
_mixers->set_max_delta_out_once(delta_out_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned MixingOutput::motorTest()
|
||||||
|
{
|
||||||
|
test_motor_s test_motor;
|
||||||
|
bool had_update = false;
|
||||||
|
|
||||||
|
while (_motor_test.test_motor_sub.update(&test_motor)) {
|
||||||
|
if (test_motor.driver_instance != _driver_instance ||
|
||||||
|
hrt_elapsed_time(&test_motor.timestamp) > 100_ms) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_test_mode = test_motor.action == test_motor_s::ACTION_RUN;
|
||||||
|
|
||||||
|
if (in_test_mode != _motor_test.in_test_mode) {
|
||||||
|
// reset all outputs to disarmed on state change
|
||||||
|
for (int i = 0; i < MAX_ACTUATORS; ++i) {
|
||||||
|
_current_output_value[i] = _disarmed_value[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_test_mode) {
|
||||||
|
int idx = test_motor.motor_number;
|
||||||
|
|
||||||
|
if (idx < MAX_ACTUATORS) {
|
||||||
|
_current_output_value[reorderedMotorIndex(idx)] =
|
||||||
|
math::constrain<uint16_t>(_min_value[idx] + (uint16_t)((_max_value[idx] - _min_value[idx]) * test_motor.value),
|
||||||
|
_min_value[idx], _max_value[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_motor_test.in_test_mode = in_test_mode;
|
||||||
|
had_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (_motor_test.in_test_mode || had_update) ? _max_num_outputs : 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool MixingOutput::update()
|
bool MixingOutput::update()
|
||||||
{
|
{
|
||||||
if (!_mixers) {
|
if (!_mixers) {
|
||||||
|
@ -250,6 +297,24 @@ bool MixingOutput::update()
|
||||||
/* Update the armed status and check that we're not locked down.
|
/* Update the armed status and check that we're not locked down.
|
||||||
* We also need to arm throttle for the ESC calibration. */
|
* 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);
|
_throttle_armed = (_safety_off && _armed.armed && !_armed.lockdown) || (_safety_off && _armed.in_esc_calibration_mode);
|
||||||
|
|
||||||
|
if (_armed.armed) {
|
||||||
|
_motor_test.in_test_mode = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for motor test
|
||||||
|
if (!_armed.armed) {
|
||||||
|
unsigned num_motor_test = motorTest();
|
||||||
|
|
||||||
|
if (num_motor_test > 0) {
|
||||||
|
_interface.updateOutputs(false, _current_output_value, num_motor_test, 1);
|
||||||
|
actuator_outputs_s actuator_outputs{};
|
||||||
|
setAndPublishActuatorOutputs(num_motor_test, actuator_outputs);
|
||||||
|
checkSafetyButton();
|
||||||
|
handleCommands();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_param_mot_slew_max.get() > FLT_EPSILON) {
|
if (_param_mot_slew_max.get() > FLT_EPSILON) {
|
||||||
|
@ -285,15 +350,13 @@ bool MixingOutput::update()
|
||||||
const unsigned mixed_num_outputs = _mixers->mix(outputs, _max_num_outputs);
|
const unsigned mixed_num_outputs = _mixers->mix(outputs, _max_num_outputs);
|
||||||
|
|
||||||
/* the output limit call takes care of out of band errors, NaN and constrains */
|
/* the output limit call takes care of out of band errors, NaN and constrains */
|
||||||
uint16_t output_limited[MAX_ACTUATORS] {};
|
|
||||||
|
|
||||||
output_limit_calc(_throttle_armed, armNoThrottle(), mixed_num_outputs, _reverse_output_mask,
|
output_limit_calc(_throttle_armed, armNoThrottle(), mixed_num_outputs, _reverse_output_mask,
|
||||||
_disarmed_value, _min_value, _max_value, outputs, output_limited, &_output_limit);
|
_disarmed_value, _min_value, _max_value, outputs, _current_output_value, &_output_limit);
|
||||||
|
|
||||||
/* overwrite outputs in case of force_failsafe with _failsafe_value values */
|
/* overwrite outputs in case of force_failsafe with _failsafe_value values */
|
||||||
if (_armed.force_failsafe) {
|
if (_armed.force_failsafe) {
|
||||||
for (size_t i = 0; i < mixed_num_outputs; i++) {
|
for (size_t i = 0; i < mixed_num_outputs; i++) {
|
||||||
output_limited[i] = _failsafe_value[i];
|
_current_output_value[i] = _failsafe_value[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,28 +365,21 @@ bool MixingOutput::update()
|
||||||
/* overwrite outputs in case of lockdown or parachute triggering with disarmed values */
|
/* overwrite outputs in case of lockdown or parachute triggering with disarmed values */
|
||||||
if (_armed.lockdown || _armed.manual_lockdown) {
|
if (_armed.lockdown || _armed.manual_lockdown) {
|
||||||
for (size_t i = 0; i < mixed_num_outputs; i++) {
|
for (size_t i = 0; i < mixed_num_outputs; i++) {
|
||||||
output_limited[i] = _disarmed_value[i];
|
_current_output_value[i] = _disarmed_value[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_motors = true;
|
stop_motors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* apply _param_mot_ordering */
|
/* apply _param_mot_ordering */
|
||||||
reorderOutputs(output_limited);
|
reorderOutputs(_current_output_value);
|
||||||
|
|
||||||
/* now return the outputs to the driver */
|
/* now return the outputs to the driver */
|
||||||
_interface.updateOutputs(stop_motors, output_limited, mixed_num_outputs, n_updates);
|
_interface.updateOutputs(stop_motors, _current_output_value, mixed_num_outputs, n_updates);
|
||||||
|
|
||||||
|
|
||||||
actuator_outputs_s actuator_outputs{};
|
actuator_outputs_s actuator_outputs{};
|
||||||
actuator_outputs.noutputs = mixed_num_outputs;
|
setAndPublishActuatorOutputs(mixed_num_outputs, actuator_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);
|
|
||||||
|
|
||||||
publishMixerStatus(actuator_outputs);
|
publishMixerStatus(actuator_outputs);
|
||||||
updateLatencyPerfCounter(actuator_outputs);
|
updateLatencyPerfCounter(actuator_outputs);
|
||||||
|
@ -346,6 +402,19 @@ MixingOutput::checkSafetyButton()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MixingOutput::setAndPublishActuatorOutputs(unsigned num_outputs, actuator_outputs_s &actuator_outputs)
|
||||||
|
{
|
||||||
|
actuator_outputs.noutputs = num_outputs;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_outputs; ++i) {
|
||||||
|
actuator_outputs.output[i] = _current_output_value[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
actuator_outputs.timestamp = hrt_absolute_time();
|
||||||
|
_outputs_pub.publish(actuator_outputs);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MixingOutput::publishMixerStatus(const actuator_outputs_s &actuator_outputs)
|
MixingOutput::publishMixerStatus(const actuator_outputs_s &actuator_outputs)
|
||||||
{
|
{
|
||||||
|
@ -404,7 +473,7 @@ MixingOutput::reorderOutputs(uint16_t values[MAX_ACTUATORS])
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
int MixingOutput::reorderedMotorIndex(int index)
|
int MixingOutput::reorderedMotorIndex(int index) const
|
||||||
{
|
{
|
||||||
if ((MotorOrdering)_param_mot_ordering.get() == MotorOrdering::Betaflight) {
|
if ((MotorOrdering)_param_mot_ordering.get() == MotorOrdering::Betaflight) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#include <uORB/topics/multirotor_motor_limits.h>
|
#include <uORB/topics/multirotor_motor_limits.h>
|
||||||
#include <uORB/topics/parameter_update.h>
|
#include <uORB/topics/parameter_update.h>
|
||||||
#include <uORB/topics/safety.h>
|
#include <uORB/topics/safety.h>
|
||||||
|
#include <uORB/topics/test_motor.h>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,6 +103,8 @@ public:
|
||||||
|
|
||||||
~MixingOutput();
|
~MixingOutput();
|
||||||
|
|
||||||
|
void setDriverInstance(uint8_t instance) { _driver_instance = instance; }
|
||||||
|
|
||||||
void printStatus() const;
|
void printStatus() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,7 +166,7 @@ public:
|
||||||
* @param index motor index in [0, num_motors-1]
|
* @param index motor index in [0, num_motors-1]
|
||||||
* @return reordered motor index. When out of range, the input index is returned
|
* @return reordered motor index. When out of range, the input index is returned
|
||||||
*/
|
*/
|
||||||
int reorderedMotorIndex(int index);
|
int reorderedMotorIndex(int index) const;
|
||||||
|
|
||||||
void setIgnoreLockdown(bool ignore_lockdown) { _ignore_lockdown = ignore_lockdown; }
|
void setIgnoreLockdown(bool ignore_lockdown) { _ignore_lockdown = ignore_lockdown; }
|
||||||
|
|
||||||
|
@ -178,8 +181,11 @@ private:
|
||||||
return (_armed.prearmed && !_armed.armed) || _armed.in_esc_calibration_mode;
|
return (_armed.prearmed && !_armed.armed) || _armed.in_esc_calibration_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned motorTest();
|
||||||
|
|
||||||
void updateOutputSlewrate();
|
void updateOutputSlewrate();
|
||||||
void checkSafetyButton();
|
void checkSafetyButton();
|
||||||
|
void setAndPublishActuatorOutputs(unsigned num_outputs, actuator_outputs_s &actuator_outputs);
|
||||||
void publishMixerStatus(const actuator_outputs_s &actuator_outputs);
|
void publishMixerStatus(const actuator_outputs_s &actuator_outputs);
|
||||||
void updateLatencyPerfCounter(const actuator_outputs_s &actuator_outputs);
|
void updateLatencyPerfCounter(const actuator_outputs_s &actuator_outputs);
|
||||||
|
|
||||||
|
@ -218,6 +224,7 @@ private:
|
||||||
uint16_t _disarmed_value[MAX_ACTUATORS] {};
|
uint16_t _disarmed_value[MAX_ACTUATORS] {};
|
||||||
uint16_t _min_value[MAX_ACTUATORS] {};
|
uint16_t _min_value[MAX_ACTUATORS] {};
|
||||||
uint16_t _max_value[MAX_ACTUATORS] {};
|
uint16_t _max_value[MAX_ACTUATORS] {};
|
||||||
|
uint16_t _current_output_value[MAX_ACTUATORS] {}; ///< current output values (reordered)
|
||||||
uint16_t _reverse_output_mask{0}; ///< reverses the interval [min, max] -> [max, min], NOT motor direction
|
uint16_t _reverse_output_mask{0}; ///< reverses the interval [min, max] -> [max, min], NOT motor direction
|
||||||
output_limit_t _output_limit;
|
output_limit_t _output_limit;
|
||||||
|
|
||||||
|
@ -246,8 +253,15 @@ private:
|
||||||
const bool _support_esc_calibration;
|
const bool _support_esc_calibration;
|
||||||
|
|
||||||
bool _wq_switched{false};
|
bool _wq_switched{false};
|
||||||
|
uint8_t _driver_instance{0}; ///< for boards that supports multiple outputs (e.g. PX4IO + FMU)
|
||||||
const uint8_t _max_num_outputs;
|
const uint8_t _max_num_outputs;
|
||||||
|
|
||||||
|
struct MotorTest {
|
||||||
|
uORB::Subscription test_motor_sub{ORB_ID(test_motor)};
|
||||||
|
bool in_test_mode{false};
|
||||||
|
};
|
||||||
|
MotorTest _motor_test;
|
||||||
|
|
||||||
OutputModuleInterface &_interface;
|
OutputModuleInterface &_interface;
|
||||||
|
|
||||||
perf_counter_t _control_latency_perf;
|
perf_counter_t _control_latency_perf;
|
||||||
|
|
|
@ -48,20 +48,27 @@
|
||||||
|
|
||||||
extern "C" __EXPORT int motor_test_main(int argc, char *argv[]);
|
extern "C" __EXPORT int motor_test_main(int argc, char *argv[]);
|
||||||
|
|
||||||
static void motor_test(unsigned channel, float value);
|
static void motor_test(unsigned channel, float value, uint8_t driver_instance);
|
||||||
static void usage(const char *reason);
|
static void usage(const char *reason);
|
||||||
|
|
||||||
void motor_test(unsigned channel, float value)
|
void motor_test(unsigned channel, float value, uint8_t driver_instance)
|
||||||
{
|
{
|
||||||
test_motor_s test_motor{};
|
test_motor_s test_motor{};
|
||||||
test_motor.timestamp = hrt_absolute_time();
|
test_motor.timestamp = hrt_absolute_time();
|
||||||
test_motor.motor_number = channel;
|
test_motor.motor_number = channel;
|
||||||
test_motor.value = value;
|
test_motor.value = value;
|
||||||
|
test_motor.action = value >= 0.f ? test_motor_s::ACTION_RUN : test_motor_s::ACTION_STOP;
|
||||||
|
test_motor.driver_instance = driver_instance;
|
||||||
|
|
||||||
uORB::PublicationQueued<test_motor_s> test_motor_pub{ORB_ID(test_motor)};
|
uORB::PublicationQueued<test_motor_s> test_motor_pub{ORB_ID(test_motor)};
|
||||||
test_motor_pub.publish(test_motor);
|
test_motor_pub.publish(test_motor);
|
||||||
|
|
||||||
PX4_INFO("motor %d set to %.2f", channel, (double)value);
|
if (test_motor.action == test_motor_s::ACTION_STOP) {
|
||||||
|
PX4_INFO("motors stop command sent");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
PX4_INFO("motor %d set to %.2f", channel, (double)value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usage(const char *reason)
|
static void usage(const char *reason)
|
||||||
|
@ -70,15 +77,20 @@ static void usage(const char *reason)
|
||||||
PX4_WARN("%s", reason);
|
PX4_WARN("%s", reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
PRINT_MODULE_DESCRIPTION("Utility to test motors.\n"
|
PRINT_MODULE_DESCRIPTION(
|
||||||
"\n"
|
R"DESCR_STR(
|
||||||
"Note: this can only be used for drivers which support the motor_test uorb topic (currently uavcan and tap_esc)\n"
|
Utility to test motors.
|
||||||
);
|
|
||||||
|
WARNING: remove all props before using this command.
|
||||||
|
|
||||||
|
Note: this can only be used for drivers which support the motor_test uorb topic (not px4io).
|
||||||
|
)DESCR_STR");
|
||||||
|
|
||||||
PRINT_MODULE_USAGE_NAME("motor_test", "command");
|
PRINT_MODULE_USAGE_NAME("motor_test", "command");
|
||||||
PRINT_MODULE_USAGE_COMMAND_DESCR("test", "Set motor(s) to a specific output value");
|
PRINT_MODULE_USAGE_COMMAND_DESCR("test", "Set motor(s) to a specific output value");
|
||||||
PRINT_MODULE_USAGE_PARAM_INT('m', -1, 0, 7, "Motor to test (0...7, all if not specified)", true);
|
PRINT_MODULE_USAGE_PARAM_INT('m', -1, 0, 7, "Motor to test (0...7, all if not specified)", true);
|
||||||
PRINT_MODULE_USAGE_PARAM_INT('p', 0, 0, 100, "Power (0...100)", true);
|
PRINT_MODULE_USAGE_PARAM_INT('p', 0, 0, 100, "Power (0...100)", true);
|
||||||
|
PRINT_MODULE_USAGE_PARAM_INT('i', 0, 0, 4, "driver instance", true);
|
||||||
PRINT_MODULE_USAGE_COMMAND_DESCR("stop", "Stop all motors");
|
PRINT_MODULE_USAGE_COMMAND_DESCR("stop", "Stop all motors");
|
||||||
PRINT_MODULE_USAGE_COMMAND_DESCR("iterate", "Iterate all motors starting and stopping one after the other");
|
PRINT_MODULE_USAGE_COMMAND_DESCR("iterate", "Iterate all motors starting and stopping one after the other");
|
||||||
|
|
||||||
|
@ -89,14 +101,19 @@ int motor_test_main(int argc, char *argv[])
|
||||||
int channel = -1; //default to all channels
|
int channel = -1; //default to all channels
|
||||||
unsigned long lval;
|
unsigned long lval;
|
||||||
float value = 0.0f;
|
float value = 0.0f;
|
||||||
|
uint8_t driver_instance = 0;
|
||||||
int ch;
|
int ch;
|
||||||
|
|
||||||
int myoptind = 1;
|
int myoptind = 1;
|
||||||
const char *myoptarg = NULL;
|
const char *myoptarg = NULL;
|
||||||
|
|
||||||
while ((ch = px4_getopt(argc, argv, "m:p:", &myoptind, &myoptarg)) != EOF) {
|
while ((ch = px4_getopt(argc, argv, "i:m:p:", &myoptind, &myoptarg)) != EOF) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
driver_instance = (uint8_t)strtol(myoptarg, NULL, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
/* Read in motor number */
|
/* Read in motor number */
|
||||||
channel = (int)strtol(myoptarg, NULL, 0);
|
channel = (int)strtol(myoptarg, NULL, 0);
|
||||||
|
@ -124,17 +141,17 @@ int motor_test_main(int argc, char *argv[])
|
||||||
|
|
||||||
if (myoptind >= 0 && myoptind < argc) {
|
if (myoptind >= 0 && myoptind < argc) {
|
||||||
if (strcmp("stop", argv[myoptind]) == 0) {
|
if (strcmp("stop", argv[myoptind]) == 0) {
|
||||||
channel = -1;
|
channel = 0;
|
||||||
value = 0.f;
|
value = -1.f;
|
||||||
|
|
||||||
} else if (strcmp("iterate", argv[myoptind]) == 0) {
|
} else if (strcmp("iterate", argv[myoptind]) == 0) {
|
||||||
value = 0.3f;
|
value = 0.15f;
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
motor_test(i, value);
|
motor_test(i, value, driver_instance);
|
||||||
usleep(500000);
|
px4_usleep(500000);
|
||||||
motor_test(i, 0.f);
|
motor_test(i, -1.f, driver_instance);
|
||||||
usleep(10000);
|
px4_usleep(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
run_test = false;
|
run_test = false;
|
||||||
|
@ -154,12 +171,12 @@ int motor_test_main(int argc, char *argv[])
|
||||||
if (run_test) {
|
if (run_test) {
|
||||||
if (channel < 0) {
|
if (channel < 0) {
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
motor_test(i, value);
|
motor_test(i, value, driver_instance);
|
||||||
usleep(10000);
|
px4_usleep(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
motor_test(channel, value);
|
motor_test(channel, value, driver_instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue