44 #include "actuatorsettings.h"
45 #include "systemsettings.h"
46 #include "actuatordesired.h"
47 #include "actuatorcommand.h"
48 #include "flightstatus.h"
49 #include "mixersettings.h"
50 #include "cameradesired.h"
51 #include "manualcontrolcommand.h"
57 #define MAX_QUEUE_SIZE 2
59 #if defined(PIOS_ACTUATOR_STACK_SIZE)
60 #define STACK_SIZE_BYTES PIOS_ACTUATOR_STACK_SIZE
62 #define STACK_SIZE_BYTES 1336
65 #define TASK_PRIORITY PIOS_THREAD_PRIO_HIGHEST
66 #define FAILSAFE_TIMEOUT_MS 100
68 #ifndef MAX_MIX_ACTUATORS
69 #define MAX_MIX_ACTUATORS ACTUATORCOMMAND_CHANNEL_NUMELEM
74 DONT_BUILD_IF((MIXERSETTINGS_MIXER1VECTOR_NUMELEM - MIXERSETTINGS_MIXER1VECTOR_ACCESSORY0) < MANUALCONTROLCOMMAND_ACCESSORY_NUMELEM, AccessoryMismatch);
76 #define MIXER_SCALE 128
77 #define ACTUATOR_EPSILON 0.00001f
105 static float curve2[MIXERSETTINGS_THROTTLECURVE2_NUMELEM];
136 #define DSHOT_COMMAND_DONTSPIN 0
137 #define DSHOT_COMMAND_NO3DMODE 9
138 #define DSHOT_COMMAND_3DMODE 10
140 #define DSHOT_COUNT_EXCESSIVE 24
224 if (ActuatorSettingsInitialize() == -1) {
227 ActuatorSettingsConnectCallbackCtx(
UAVObjCbSetFlag, &settings_updated);
230 if (MixerSettingsInitialize() == -1) {
236 if (SystemSettingsInitialize() == -1) {
242 if (ActuatorDesiredInitialize() == -1) {
247 ActuatorDesiredConnectQueue(queue);
250 if (ActuatorCommandInitialize() == -1) {
254 #if defined(MIXERSTATUS_DIAGNOSTICS)
256 if (MixerStatusInitialize() == -1) {
266 SystemSettingsAirframeTypeOptions airframe_type,
267 MixerSettingsCurve2SourceOptions source,
273 case MIXERSETTINGS_CURVE2SOURCE_THROTTLE:
276 case MIXERSETTINGS_CURVE2SOURCE_ROLL:
277 return desired->Roll;
279 case MIXERSETTINGS_CURVE2SOURCE_PITCH:
280 return desired->Pitch;
282 case MIXERSETTINGS_CURVE2SOURCE_YAW:
285 case MIXERSETTINGS_CURVE2SOURCE_COLLECTIVE:
286 if (airframe_type == SYSTEMSETTINGS_AIRFRAMETYPE_HELICP)
288 return desired->Thrust;
290 ManualControlCommandCollectiveGet(&tmp);
293 case MIXERSETTINGS_CURVE2SOURCE_ACCESSORY0:
294 case MIXERSETTINGS_CURVE2SOURCE_ACCESSORY1:
295 case MIXERSETTINGS_CURVE2SOURCE_ACCESSORY2:
298 int idx = source - MIXERSETTINGS_CURVE2SOURCE_ACCESSORY0;
304 if (idx >= MANUALCONTROLCOMMAND_ACCESSORY_NUMELEM) {
308 float accessories[MANUALCONTROLCOMMAND_ACCESSORY_NUMELEM];
310 ManualControlCommandAccessoryGet(accessories);
312 return accessories[idx];
321 int16_t (*vals)[MIXERSETTINGS_MIXER1VECTOR_NUMELEM],
322 MixerSettingsMixer1TypeOptions
type,
323 float scale_adjustment)
325 types_mixer[mixnum] =
type;
327 mixnum *= MIXERSETTINGS_MIXER1VECTOR_NUMELEM;
329 if ((type != MIXERSETTINGS_MIXER1TYPE_SERVO) &&
330 (type != MIXERSETTINGS_MIXER1TYPE_MOTOR)) {
331 for (
int i = 0;
i < MIXERSETTINGS_MIXER1VECTOR_NUMELEM;
i++) {
333 motor_mixer[mixnum+
i] = 0;
336 for (
int i = 0;
i < MIXERSETTINGS_MIXER1VECTOR_NUMELEM;
i++) {
340 if (type == MIXERSETTINGS_MIXER1TYPE_MOTOR) {
354 motor_mixer[mixnum + MIXERSETTINGS_MIXER1VECTOR_ROLL] *=
356 motor_mixer[mixnum + MIXERSETTINGS_MIXER1VECTOR_PITCH] *=
358 motor_mixer[mixnum + MIXERSETTINGS_MIXER1VECTOR_YAW] *=
365 #define compute_one_token_paste(b) compute_one_mixer(b-1, &mixerSettings.Mixer ## b ## Vector, mixerSettings.Mixer ## b ## Type, scale_adjustment)
369 float scale_adjustment = 1.0f;
370 const float output_gain = actuatorSettings.MotorInputOutputGain;
371 const float curve_fit = actuatorSettings.MotorInputOutputCurveFit;
373 if ((output_gain < 1.0
f) && (output_gain >= 0.01
f)) {
374 scale_adjustment = powf(1 / output_gain, 1 / curve_fit);
377 MixerSettingsData mixerSettings;
379 MixerSettingsGet(&mixerSettings);
381 #if MAX_MIX_ACTUATORS > 0
384 #if MAX_MIX_ACTUATORS > 1
387 #if MAX_MIX_ACTUATORS > 2
390 #if MAX_MIX_ACTUATORS > 3
393 #if MAX_MIX_ACTUATORS > 4
396 #if MAX_MIX_ACTUATORS > 5
399 #if MAX_MIX_ACTUATORS > 6
402 #if MAX_MIX_ACTUATORS > 7
405 #if MAX_MIX_ACTUATORS > 8
408 #if MAX_MIX_ACTUATORS > 9
414 ActuatorDesiredData *desired,
415 float val1,
float val2,
416 float (*cmd_vector)[MIXERSETTINGS_MIXER1VECTOR_NUMELEM])
418 (*cmd_vector)[MIXERSETTINGS_MIXER1VECTOR_THROTTLECURVE1] = val1;
419 (*cmd_vector)[MIXERSETTINGS_MIXER1VECTOR_THROTTLECURVE2] = val2;
420 (*cmd_vector)[MIXERSETTINGS_MIXER1VECTOR_ROLL] = desired->Roll;
421 (*cmd_vector)[MIXERSETTINGS_MIXER1VECTOR_PITCH] = desired->Pitch;
422 (*cmd_vector)[MIXERSETTINGS_MIXER1VECTOR_YAW] = desired->Yaw;
430 float *desired_vect,
float dT,
431 bool armed,
bool spin_while_armed,
bool stabilize_now,
433 float *maxpoweradd_bucket)
435 float min_chan = INFINITY;
436 float max_chan = -INFINITY;
439 ActuatorCommandData command;
456 float maxpoweradd_softlimit =
MAX(
457 2 * actuatorSettings.LowPowerStabilizationMaxPowerAdd,
458 fabsf(desired_vect[MIXERSETTINGS_MIXER1VECTOR_THROTTLECURVE1]))
462 if (*maxpoweradd_bucket < maxpoweradd_softlimit) {
463 *maxpoweradd_bucket += actuatorSettings.LowPowerStabilizationMaxPowerAdd * dT;
470 *maxpoweradd_bucket = alpha * maxpoweradd_softlimit +
471 (1-alpha) * (*maxpoweradd_bucket);
484 if (flip_over_mode) {
488 bool neg_throttle = desired_vect[MIXERSETTINGS_MIXER1VECTOR_THROTTLECURVE1] < 0.0f;
491 switch (types_mixer[ct]) {
492 case MIXERSETTINGS_MIXER1TYPE_DISABLED:
499 case MIXERSETTINGS_MIXER1TYPE_SERVO:
502 case MIXERSETTINGS_MIXER1TYPE_MOTOR:
505 motor_vect[ct] = -motor_vect[ct];
508 min_chan = fminf(min_chan, motor_vect[ct]);
509 max_chan = fmaxf(max_chan, motor_vect[ct]);
511 if (motor_vect[ct] < 0.0
f) {
512 neg_clip += motor_vect[ct];
517 case MIXERSETTINGS_MIXER1TYPE_CAMERAPITCH:
518 if (CameraDesiredHandle()) {
519 CameraDesiredPitchGet(
525 case MIXERSETTINGS_MIXER1TYPE_CAMERAROLL:
526 if (CameraDesiredHandle()) {
527 CameraDesiredRollGet(
533 case MIXERSETTINGS_MIXER1TYPE_CAMERAYAW:
534 if (CameraDesiredHandle()) {
535 CameraDesiredRollGet(
554 if ((max_chan - min_chan) > 1.0
f) {
555 gain = 1.0f / (max_chan - min_chan);
562 if (max_chan > 1.0
f) {
563 offset = 1.0f - max_chan;
564 }
else if (min_chan < 0.0
f) {
568 neg_clip /= num_motors;
579 offset = neg_clip + maxpoweradd;
585 offset =
MIN(-min_chan, offset);
592 *maxpoweradd_bucket -= (offset - neg_clip) * dT;
595 bool active_command =
false;
601 if (types_mixer[ct] == MIXERSETTINGS_MIXER1TYPE_MOTOR) {
603 motor_vect[ct] = NAN;
604 }
else if (!stabilize_now) {
605 if ((!spin_while_armed) || (flip_over_mode)) {
607 motor_vect[ct] = NAN;
617 motor_vect[ct] = motor_vect[ct] * gain +
offset;
619 if (motor_vect[ct] > 0) {
622 motor_vect[ct] =
powapprox(motor_vect[ct], actuatorSettings.MotorInputOutputCurveFit);
623 motor_vect[ct] *= actuatorSettings.MotorInputOutputGain;
626 if (!flip_over_mode) {
629 motor_vect[ct] = NAN;
634 motor_vect[ct] = -motor_vect[ct];
644 command.UpdateTime = 1000.0f*dT;
646 ActuatorCommandMaxUpdateTimeGet(&command.MaxUpdateTime);
648 if (command.UpdateTime > command.MaxUpdateTime)
649 command.MaxUpdateTime = 1000.0f*dT;
652 command.LowPowerStabilizationReserve = *maxpoweradd_bucket;
655 if (!ActuatorCommandReadOnly()) {
656 ActuatorCommandSet(&command);
660 ActuatorCommandGet(&command);
671 float (*desired_vect)[MIXERSETTINGS_MIXER1VECTOR_NUMELEM],
672 bool *armed,
bool *spin_while_armed,
bool *stabilize_now,
673 bool *flip_over_mode)
675 static float manual_throt = -1;
676 float throttle_val = 0;
677 ActuatorDesiredData desired;
679 static FlightStatusData flightStatus;
681 ActuatorDesiredGet(&desired);
683 if (flight_status_updated) {
684 FlightStatusGet(&flightStatus);
685 flight_status_updated =
false;
688 if (manual_control_cmd_updated) {
691 ManualControlCommandThrottleGet(&manual_throt);
692 manual_control_cmd_updated =
false;
693 ManualControlCommandAccessoryGet(
694 &(*desired_vect)[MIXERSETTINGS_MIXER1VECTOR_ACCESSORY0]);
697 *armed = flightStatus.Armed == FLIGHTSTATUS_ARMED_ARMED;
698 *spin_while_armed = actuatorSettings.MotorsSpinWhileArmed == ACTUATORSETTINGS_MOTORSSPINWHILEARMED_TRUE;
699 *flip_over_mode = desired.FlipOverThrustMode == ACTUATORDESIRED_FLIPOVERTHRUSTMODE_TRUE;
701 if (airframe_type == SYSTEMSETTINGS_AIRFRAMETYPE_HELICP) {
705 if (flightStatus.FlightMode != FLIGHTSTATUS_FLIGHTMODE_FAILSAFE) {
706 throttle_val = manual_throt;
709 throttle_val = desired.Thrust;
716 *stabilize_now = throttle_val != 0.0f;
718 if (*flip_over_mode) {
723 if ((desired.Pitch == 0) && (desired.Roll == 0) &&
724 (desired.Yaw == 0)) {
725 *stabilize_now =
false;
730 float val1 = throttle_val;
736 curve2, MIXERSETTINGS_THROTTLECURVE2_NUMELEM);
743 ActuatorSettingsGet(&actuatorSettings);
746 ACTUATORSETTINGS_TIMERUPDATEFREQ_NUMELEM,
747 actuatorSettings.ChannelMax,
748 actuatorSettings.ChannelMin);
753 if (actuatorSettings.ChannelDeadband[
i]) {
754 desired_3d_mask |= (1 <<
i);
758 hangtime_leakybucket_timeconstant = actuatorSettings.LowPowerStabilizationTimeConstant;
777 FlightStatusConnectCallbackCtx(
UAVObjCbSetFlag, &flight_status_updated);
778 ManualControlCommandConnectCallbackCtx(
UAVObjCbSetFlag, &manual_control_cmd_updated);
789 float desired_vect[MIXERSETTINGS_MIXER1VECTOR_NUMELEM] = { 0 };
792 float maxpoweradd_bucket = 0.0f;
794 bool prev_armed =
false;
801 if (settings_updated) {
804 SystemSettingsAirframeTypeGet(&airframe_type);
808 MixerSettingsThrottleCurve2Get(curve2);
809 MixerSettingsCurve2SourceGet(&curve2_src);
810 settings_updated =
false;
830 if (this_systime > last_systime) {
831 dT = (this_systime - last_systime) / 1000.0
f;
837 last_systime = this_systime;
841 uint32_t exp_time = this_systime + 100;
857 if ((exp_time - this_systime) > 100) {
867 ACTUATORSETTINGS_TIMERUPDATEFREQ_NUMELEM,
868 actuatorSettings.ChannelMax,
869 actuatorSettings.ChannelMin);
876 bool armed, spin_while_armed, stabilize_now, flip_over_mode;
883 &spin_while_armed, &stabilize_now,
890 MIXERSETTINGS_MIXER1VECTOR_NUMELEM,
896 if (armed != prev_armed) {
897 if (armed && desired_3d_mask) {
915 dT, armed, spin_while_armed, stabilize_now,
916 flip_over_mode, &maxpoweradd_bucket);
941 if (active_command) {
947 if (!isfinite(value)) {
953 bool reversed =
false;
955 #define DSHOT_DEADBAND_NORMAL 0
956 #define DSHOT_DEADBAND_3D 1
957 #define DSHOT_DEADBAND_3DREV 2
959 switch (actuatorSettings.ChannelDeadband[idx]) {
974 float neutral = actuatorSettings.ChannelNeutral[idx];
978 int16_t val = roundf((2047 - neutral) * value) + neutral;
1002 negative = !reversed;
1004 negative = reversed;
1009 int16_t val = roundf((999 - neutral) * value + neutral);
1040 float max = actuatorSettings.ChannelMax[idx];
1041 float min = actuatorSettings.ChannelMin[idx];
1042 float neutral = actuatorSettings.ChannelNeutral[idx];
1043 float deadband = actuatorSettings.ChannelDeadband[idx] / 2.0f;
1047 if (!isfinite(value)) {
1061 deadband = -deadband;
1064 if (value >= 0.0
f) {
1065 valueScaled = value * (max - neutral - deadband)
1066 + neutral + deadband;
1068 valueScaled = value * (neutral - min - deadband)
1069 + neutral - deadband;
1073 if (valueScaled > max) valueScaled =
max;
1074 if (valueScaled < min) valueScaled =
min;
1076 if (valueScaled < max) valueScaled =
max;
1077 if (valueScaled > min) valueScaled =
min;
1085 switch (types_mixer[idx]) {
1086 case MIXERSETTINGS_MIXER1TYPE_MOTOR:
1088 case MIXERSETTINGS_MIXER1TYPE_SERVO:
1089 return actuatorSettings.ChannelNeutral[idx];
1090 case MIXERSETTINGS_MIXER1TYPE_DISABLED:
1103 float Channel[ACTUATORCOMMAND_CHANNEL_NUMELEM] = {0};
1106 AlarmsSet(SYSTEMALARMS_ALARM_ACTUATOR, SYSTEMALARMS_ALARM_CRITICAL);
1112 Channel[n] = fs_val;
1120 ActuatorCommandChannelSet(Channel);
static void compute_one_mixer(int mixnum, int16_t(*vals)[MIXERSETTINGS_MIXER1VECTOR_NUMELEM], MixerSettingsMixer1TypeOptions type, float scale_adjustment)
uint32_t PIOS_Thread_Systime(void)
static volatile bool manual_control_cmd_updated
#define matrix_mul_check(a, b, out, arows, acolsbrows, bcols)
struct pios_queue * PIOS_Queue_Create(size_t queue_length, size_t item_size)
static struct pios_thread * taskHandle
static float curve2[MIXERSETTINGS_THROTTLECURVE2_NUMELEM]
static float motor_mixer[MAX_MIX_ACTUATORS *MIXERSETTINGS_MIXER1VECTOR_NUMELEM]
bool PIOS_Servo_IsDshot(uint8_t servo)
static MixerSettingsCurve2SourceOptions curve2_src
static uint32_t channel_mask
#define DSHOT_DEADBAND_NORMAL
DONT_BUILD_IF(ACTUATORSETTINGS_TIMERUPDATEFREQ_NUMELEM > PIOS_SERVO_MAX_BANKS, TooManyServoBanks)
static float scale_channel(float value, int idx, bool active_cmd)
bool PIOS_WDG_RegisterFlag(uint16_t flag_requested)
Register a module against the watchdog.
bool PIOS_WDG_UpdateFlag(uint16_t flag)
Function called by modules to indicate they are still running.
void UAVObjCbSetFlag(const UAVObjEvent *objEv, void *ctx, void *obj, int len)
int32_t AlarmsSet(SystemAlarmsAlarmElem alarm, SystemAlarmsAlarmOptions severity)
bool PIOS_Queue_Receive(struct pios_queue *queuep, void *itemp, uint32_t timeout_ms)
static ActuatorSettingsData actuatorSettings
static float scale_channel_dshot(float value, int idx, bool active_command)
static float hangtime_leakybucket_timeconstant
void PIOS_Servo_Set(uint8_t servo, float position)
static float get_curve2_source(ActuatorDesiredData *desired, SystemSettingsAirframeTypeOptions airframe_type, MixerSettingsCurve2SourceOptions source, float throttle_val)
#define DSHOT_DEADBAND_3DREV
static void actuator_task(void *parameters)
Main Actuator module task.
static void compute_mixer()
static volatile bool flight_status_updated
#define DSHOT_DEADBAND_3D
static MixerSettingsMixer1TypeOptions types_mixer[MAX_MIX_ACTUATORS]
static void set_failsafe()
#define DSHOT_COMMAND_3DMODE
int actuator_send_dshot_command(uint8_t cmd_id, uint8_t num_to_send, uint16_t channel_mask)
void apply_channel_deadband(float *value, float deadband)
Apply deadband to Roll/Pitch/Yaw channels.
#define FAILSAFE_TIMEOUT_MS
static float channel_failsafe_value(int idx)
static volatile struct dshot_command pending_cmd
struct pios_thread * PIOS_Thread_Create(void(*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
static volatile bool settings_updated
#define compute_one_token_paste(b)
int32_t TaskMonitorAdd(TaskInfoRunningElem task, struct pios_thread *threadp)
static struct dshot_command cur_cmd
#define PIOS_WDG_ACTUATOR
static uint16_t desired_3d_mask
static void fill_desired_vector(ActuatorDesiredData *desired, float val1, float val2, float(*cmd_vector)[MIXERSETTINGS_MIXER1VECTOR_NUMELEM])
int PIOS_Servo_SetMode(const uint16_t *out_rate, const int banks, const uint16_t *channel_max, const uint16_t *channel_min)
PIOS_Servo_SetMode Sets the PWM output frequency and resolution. An output rate of 0 indicates Synchr...
static float collective_curve(const float input, const float *curve, uint8_t num_points)
#define MAX_MIX_ACTUATORS
void PIOS_Thread_Sleep(uint32_t time_ms)
MODULE_HIPRI_INITCALL(ActuatorInitialize, ActuatorStart)
#define DSHOT_COUNT_EXCESSIVE
static void post_process_scale_and_commit(float *motor_vect, float *desired_vect, float dT, bool armed, bool spin_while_armed, bool stabilize_now, bool flip_over_mode, float *maxpoweradd_bucket)
static SystemSettingsAirframeTypeOptions airframe_type
float linear_interpolate(float const input, float const *curve, uint8_t num_points, const float input_min, const float input_max)
static bool actuator_get_dshot_command(bool armed)
#define PIOS_SERVO_MAX_BANKS
Includes PiOS and core architecture components.
int32_t AlarmsClear(SystemAlarmsAlarmElem alarm)
static void actuator_settings_update()
void PIOS_Servo_Update(void)
static struct pios_queue * queue
int32_t ActuatorStart()
Module initialization.
#define PIOS_Assert(test)
static int actuator_send_dshot_command_now(uint8_t cmd_id, uint8_t num_to_send, uint16_t channel_mask)
static void normalize_input_data(uint32_t this_systime, float(*desired_vect)[MIXERSETTINGS_MIXER1VECTOR_NUMELEM], bool *armed, bool *spin_while_armed, bool *stabilize_now, bool *flip_over_mode)
int32_t ActuatorInitialize()
Module initialization.