diff --git a/conf/airframes/tudelft/rot_wing_v3d.xml b/conf/airframes/tudelft/rot_wing_v3d.xml index 659b8e794d..4485dee5da 100644 --- a/conf/airframes/tudelft/rot_wing_v3d.xml +++ b/conf/airframes/tudelft/rot_wing_v3d.xml @@ -125,6 +125,7 @@ + @@ -236,25 +237,26 @@ - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - + + + + +
@@ -303,6 +305,9 @@ + + + diff --git a/conf/modules/actuators_bebop.xml b/conf/modules/actuators_bebop.xml index d29d96e480..c3b24e737f 100644 --- a/conf/modules/actuators_bebop.xml +++ b/conf/modules/actuators_bebop.xml @@ -23,7 +23,7 @@ - + diff --git a/conf/modules/actuators_disco.xml b/conf/modules/actuators_disco.xml index 675313ed85..672a538db9 100644 --- a/conf/modules/actuators_disco.xml +++ b/conf/modules/actuators_disco.xml @@ -21,7 +21,7 @@ - + diff --git a/conf/modules/pfc_actuators.xml b/conf/modules/pfc_actuators.xml new file mode 100644 index 0000000000..a8d9a3eb2d --- /dev/null +++ b/conf/modules/pfc_actuators.xml @@ -0,0 +1,36 @@ + + + + + + Preform a pre flight check of the actuators and validate by looking at the feedback. + + + + + + + + + + + + + + + @datalink,preflight_checks + +
+ +
+ + + + + + + + + + +
diff --git a/conf/modules/preflight_checks.xml b/conf/modules/preflight_checks.xml index cd4937d763..7658ddd207 100644 --- a/conf/modules/preflight_checks.xml +++ b/conf/modules/preflight_checks.xml @@ -18,6 +18,9 @@ + + @datalink +
diff --git a/conf/modules/sensors_hitl.xml b/conf/modules/sensors_hitl.xml index 067571d804..31797ec776 100644 --- a/conf/modules/sensors_hitl.xml +++ b/conf/modules/sensors_hitl.xml @@ -33,7 +33,7 @@ - + diff --git a/sw/airborne/boards/bebop/actuators.c b/sw/airborne/boards/bebop/actuators.c index 051ba1beb7..edc1e53780 100644 --- a/sw/airborne/boards/bebop/actuators.c +++ b/sw/airborne/boards/bebop/actuators.c @@ -141,11 +141,7 @@ void actuators_bebop_commit(void) // Send ABI message struct act_feedback_t feedback[4]; for (int i=0;i<4;i++) { -#ifdef SERVOS_BEBOP_OFFSET - feedback[i].idx = SERVOS_BEBOP_OFFSET + i; -#else - feedback[i].idx = SERVOS_DEFAULT_OFFSET + i; -#endif + feedback[i].idx = get_servo_idx(i); feedback[i].rpm = actuators_bebop.rpm_obs[i]; feedback[i].set.rpm = true; } diff --git a/sw/airborne/boards/disco/actuators.c b/sw/airborne/boards/disco/actuators.c index a429c4de94..47db6d574a 100644 --- a/sw/airborne/boards/disco/actuators.c +++ b/sw/airborne/boards/disco/actuators.c @@ -40,6 +40,11 @@ #include + +#ifndef SERVO_MOTOR_IDX +#warning "Disco actuators require a " +#endif + /** * private observation structure */ @@ -170,11 +175,7 @@ void actuators_disco_commit(void) // Send ABI message struct act_feedback_t feedback = {0}; -#ifdef SERVOS_DISCO_OFFSET - feedback.idx = SERVOS_DISCO_OFFSET; -#else - feedback.idx = SERVOS_DEFAULT_OFFSET; -#endif + feedback.idx = SERVO_MOTOR_IDX; feedback.rpm = actuators_disco.rpm_obs; feedback.set.rpm = true; AbiSendMsgACT_FEEDBACK(ACT_FEEDBACK_BOARD_ID, &feedback, 1); diff --git a/sw/airborne/modules/actuators/actuators_uavcan.c b/sw/airborne/modules/actuators/actuators_uavcan.c index 511b1c87f4..a55fad4805 100644 --- a/sw/airborne/modules/actuators/actuators_uavcan.c +++ b/sw/airborne/modules/actuators/actuators_uavcan.c @@ -49,13 +49,12 @@ struct actuators_uavcan_telem_t { float position; }; +/* The transmitted actuator values */ #ifdef SERVOS_UAVCAN1_NB int16_t actuators_uavcan1_values[SERVOS_UAVCAN1_NB]; -static struct actuators_uavcan_telem_t uavcan1_telem[SERVOS_UAVCAN1_NB] = {0}; #endif #ifdef SERVOS_UAVCAN2_NB int16_t actuators_uavcan2_values[SERVOS_UAVCAN2_NB]; -static struct actuators_uavcan_telem_t uavcan2_telem[SERVOS_UAVCAN2_NB] = {0}; #endif #ifdef SERVOS_UAVCAN1CMD_NB int16_t actuators_uavcan1cmd_values[SERVOS_UAVCAN1CMD_NB]; @@ -64,6 +63,23 @@ int16_t actuators_uavcan1cmd_values[SERVOS_UAVCAN1CMD_NB]; int16_t actuators_uavcan2cmd_values[SERVOS_UAVCAN2CMD_NB]; #endif +/* Set the actual telemetry length (ID's from actuators can't collide with the command version) */ +#if SERVOS_UAVCAN1CMD_NB > SERVOS_UAVCAN1_NB +#define UAVCAN1_TELEM_NB SERVOS_UAVCAN1CMD_NB +static struct actuators_uavcan_telem_t uavcan1_telem[SERVOS_UAVCAN1CMD_NB] = {0}; +#elif defined(SERVOS_UAVCAN1_NB) +#define UAVCAN1_TELEM_NB SERVOS_UAVCAN1_NB +static struct actuators_uavcan_telem_t uavcan1_telem[SERVOS_UAVCAN1_NB] = {0}; +#endif + +#if SERVOS_UAVCAN2CMD_NB > SERVOS_UAVCAN2_NB +#define UAVCAN2_TELEM_NB SERVOS_UAVCAN2CMD_NB +static struct actuators_uavcan_telem_t uavcan2_telem[SERVOS_UAVCAN2CMD_NB] = {0}; +#elif defined(SERVOS_UAVCAN2_NB) +#define UAVCAN2_TELEM_NB SERVOS_UAVCAN2_NB +static struct actuators_uavcan_telem_t uavcan2_telem[SERVOS_UAVCAN2_NB] = {0}; +#endif + /* UNUSED value for CMD */ #define UAVCAN_CMD_UNUSED (MIN_PPRZ-1) @@ -112,8 +128,8 @@ static struct actuators_uavcan_telem_t *actuators_uavcan_next_telem(void) { // Find the next set telemetry uint8_t offset = 0; -#ifdef SERVOS_UAVCAN1_NB - for(uint8_t i = esc_idx - offset; i < SERVOS_UAVCAN1_NB; i++) { +#ifdef UAVCAN1_TELEM_NB + for(uint8_t i = esc_idx - offset; i < UAVCAN1_TELEM_NB; i++) { if(uavcan1_telem[i].set) { old_idx = i + offset; esc_idx = i + offset + add_idx; @@ -122,10 +138,10 @@ static struct actuators_uavcan_telem_t *actuators_uavcan_next_telem(void) { esc_idx = i + offset + 1; } } - offset += SERVOS_UAVCAN1_NB; + offset += UAVCAN1_TELEM_NB; #endif -#ifdef SERVOS_UAVCAN2_NB - for(uint8_t i = esc_idx - offset; i < SERVOS_UAVCAN2_NB; i++) { +#ifdef UAVCAN2_TELEM_NB + for(uint8_t i = esc_idx - offset; i < UAVCAN2_TELEM_NB; i++) { if(uavcan2_telem[i].set) { old_idx = i + offset; esc_idx = i + offset + add_idx; @@ -134,7 +150,7 @@ static struct actuators_uavcan_telem_t *actuators_uavcan_next_telem(void) { esc_idx = i + offset + 1; } } - offset += SERVOS_UAVCAN2_NB; + offset += UAVCAN2_TELEM_NB; #endif // Going round or no telemetry found @@ -173,16 +189,16 @@ static void actuators_uavcan_esc_status_cb(struct uavcan_iface_t *iface, CanardR struct actuators_uavcan_telem_t *telem = NULL; uint8_t max_id = 0; -#ifdef SERVOS_UAVCAN1_NB +#ifdef UAVCAN1_TELEM_NB if (iface == &uavcan1) { telem = uavcan1_telem; - max_id = SERVOS_UAVCAN1_NB; + max_id = UAVCAN1_TELEM_NB; } #endif -#ifdef SERVOS_UAVCAN2_NB +#ifdef UAVCAN2_TELEM_NB if (iface == &uavcan2) { telem = uavcan2_telem; - max_id = SERVOS_UAVCAN2_NB; + max_id = UAVCAN2_TELEM_NB; } #endif @@ -206,37 +222,53 @@ static void actuators_uavcan_esc_status_cb(struct uavcan_iface_t *iface, CanardR #if UAVCAN_ACTUATORS_USE_CURRENT // Update total current electrical.current = 0; -#ifdef SERVOS_UAVCAN1_NB - for (uint8_t i = 0; i < SERVOS_UAVCAN1_NB; ++i) { +#ifdef UAVCAN1_TELEM_NB + for (uint8_t i = 0; i < UAVCAN1_TELEM_NB; ++i) { electrical.current += uavcan1_telem[i].current; } #endif -#ifdef SERVOS_UAVCAN2_NB - for (uint8_t i = 0; i < SERVOS_UAVCAN2_NB; ++i) { +#ifdef UAVCAN2_TELEM_NB + for (uint8_t i = 0; i < UAVCAN2_TELEM_NB; ++i) { electrical.current += uavcan2_telem[i].current; } #endif #endif // Feedback ABI RPM messages -#ifdef SERVOS_UAVCAN1_NB +#ifdef UAVCAN1_TELEM_NB if (iface == &uavcan1) { - struct act_feedback_t feedback; - feedback.idx = SERVOS_UAVCAN1_OFFSET + esc_idx; + struct act_feedback_t feedback = {0}; feedback.rpm = telem[esc_idx].rpm; feedback.set.rpm = true; +#ifdef SERVOS_UAVCAN1_NB + feedback.idx = get_servo_idx_UAVCAN1(esc_idx); +#endif +#ifdef SERVOS_UAVCAN1CMD_NB + if(esc_idx < SERVOS_UAVCAN1CMD_NB && actuators_uavcan1cmd_values[esc_idx] != UAVCAN_CMD_UNUSED) { + feedback.idx = get_servo_idx_UAVCAN1CMD(esc_idx); + } +#endif + // Send ABI message AbiSendMsgACT_FEEDBACK(ACT_FEEDBACK_UAVCAN_ID, &feedback, 1); } #endif -#ifdef SERVOS_UAVCAN2_NB +#ifdef UAVCAN2_TELEM_NB if (iface == &uavcan2) { - struct act_feedback_t feedback; - feedback.idx = SERVOS_UAVCAN2_OFFSET + esc_idx; + struct act_feedback_t feedback = {0}; feedback.rpm = telem[esc_idx].rpm; feedback.set.rpm = true; +#ifdef SERVOS_UAVCAN2_NB + feedback.idx = get_servo_idx_UAVCAN2(esc_idx); +#endif +#ifdef SERVOS_UAVCAN2CMD_NB + if(esc_idx < SERVOS_UAVCAN2CMD_NB && actuators_uavcan2cmd_values[esc_idx] != UAVCAN_CMD_UNUSED) { + feedback.idx = get_servo_idx_UAVCAN2CMD(esc_idx); + } +#endif + // Send ABI message AbiSendMsgACT_FEEDBACK(ACT_FEEDBACK_UAVCAN_ID, &feedback, 1); } @@ -253,16 +285,16 @@ static void actuators_uavcan_actuator_status_cb(struct uavcan_iface_t *iface, Ca struct actuators_uavcan_telem_t *telem = NULL; uint8_t max_id = 0; -#ifdef SERVOS_UAVCAN1_NB +#ifdef UAVCAN1_TELEM_NB if (iface == &uavcan1) { telem = uavcan1_telem; - max_id = SERVOS_UAVCAN1_NB; + max_id = UAVCAN1_TELEM_NB; } #endif -#ifdef SERVOS_UAVCAN2_NB +#ifdef UAVCAN2_TELEM_NB if (iface == &uavcan2) { telem = uavcan2_telem; - max_id = SERVOS_UAVCAN2_NB; + max_id = UAVCAN2_TELEM_NB; } #endif @@ -276,25 +308,41 @@ static void actuators_uavcan_actuator_status_cb(struct uavcan_iface_t *iface, Ca canardDecodeScalar(transfer, 8, 16, true, (void *)&tmp_float); telem[actuator_idx].position = canardConvertFloat16ToNativeFloat(tmp_float); -#ifdef SERVOS_UAVCAN1_NB +#ifdef UAVCAN1_TELEM_NB if (iface == &uavcan1) { - struct act_feedback_t feedback; - feedback.idx = SERVOS_UAVCAN1_OFFSET + actuator_idx; + struct act_feedback_t feedback = {0}; feedback.position = telem[actuator_idx].position; feedback.set.position = true; +#ifdef SERVOS_UAVCAN1_NB + feedback.idx = get_servo_idx_UAVCAN1(actuator_idx); +#endif +#ifdef SERVOS_UAVCAN1CMD_NB + if(actuator_idx < SERVOS_UAVCAN1CMD_NB && actuators_uavcan1cmd_values[actuator_idx] != UAVCAN_CMD_UNUSED) { + feedback.idx = get_servo_idx_UAVCAN1CMD(actuator_idx); + } +#endif + // Send ABI message AbiSendMsgACT_FEEDBACK(ACT_FEEDBACK_UAVCAN_ID, &feedback, 1); } #endif -#ifdef SERVOS_UAVCAN2_NB +#ifdef UAVCAN2_TELEM_NB if (iface == &uavcan2) { - struct act_feedback_t feedback; - feedback.idx = SERVOS_UAVCAN2_OFFSET + actuator_idx; + struct act_feedback_t feedback = {0}; feedback.position = telem[actuator_idx].position; feedback.set.position = true; +#ifdef SERVOS_UAVCAN2_NB + feedback.idx = get_servo_idx_UAVCAN2(actuator_idx); +#endif +#ifdef SERVOS_UAVCAN2CMD_NB + if(actuator_idx < SERVOS_UAVCAN2CMD_NB && actuators_uavcan2cmd_values[actuator_idx] != UAVCAN_CMD_UNUSED) { + feedback.idx = get_servo_idx_UAVCAN2CMD(actuator_idx); + } +#endif + // Send ABI message AbiSendMsgACT_FEEDBACK(ACT_FEEDBACK_UAVCAN_ID, &feedback, 1); } @@ -311,16 +359,16 @@ static void actuators_uavcan_device_temperature_cb(struct uavcan_iface_t *iface, struct actuators_uavcan_telem_t *telem = NULL; uint8_t max_id = 0; -#ifdef SERVOS_UAVCAN1_NB +#ifdef UAVCAN1_TELEM_NB if (iface == &uavcan1) { telem = uavcan1_telem; - max_id = SERVOS_UAVCAN1_NB; + max_id = UAVCAN1_TELEM_NB; } #endif -#ifdef SERVOS_UAVCAN2_NB +#ifdef UAVCAN2_TELEM_NB if (iface == &uavcan2) { telem = uavcan2_telem; - max_id = SERVOS_UAVCAN2_NB; + max_id = UAVCAN2_TELEM_NB; } #endif diff --git a/sw/airborne/modules/checks/pfc_actuators.c b/sw/airborne/modules/checks/pfc_actuators.c new file mode 100644 index 0000000000..333e7f4465 --- /dev/null +++ b/sw/airborne/modules/checks/pfc_actuators.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2023 Freek van Tienen + * + * This file is part of paparazzi + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, see + * . + */ + +/** + * @file "modules/checks/pfc_actuators.c" + * @author Freek van Tienen + * Checks the actuators with feedback before takeoff + */ + +#include "preflight_checks.h" +#include "core/abi.h" +#include "modules/datalink/telemetry.h" +#include + +/** + * Maximum error for the angle of the actuators (rad) +*/ +#ifndef PFC_ACTUATORS_MAX_ANGLE_ERROR +#define PFC_ACTUATORS_MAX_ANGLE_ERROR 0.1f +#endif + +/** + * Maximum error for the RPM of the actuators +*/ +#ifndef PFC_ACTUATORS_MAX_RPM_ERROR +#define PFC_ACTUATORS_MAX_RPM_ERROR 250.0f +#endif + +/** + * Enable debugging to set the expected feedback values +*/ +#ifndef PFC_ACTUATORS_DEBUG +#define PFC_ACTUATORS_DEBUG false +#endif + +/** + * @brief The status of the preflight checks +*/ +enum pfc_actuators_state_t { + PFC_ACTUATORS_STATE_INIT, + PFC_ACTUATORS_STATE_RUNNING, + PFC_ACTUATORS_STATE_SUCCESS, + PFC_ACTUATORS_STATE_ERROR +}; + +/** + * @brief The state of the actuator during the test +*/ +enum pfc_actuator_state_t { + PFC_ACTUATOR_STATE_WAIT, + PFC_ACTUATOR_STATE_LOW, + PFC_ACTUATOR_STATE_HIGH, +}; + +/** + * @brief The configuration struct of the actuator +*/ +struct pfc_actuator_t { + uint8_t feedback_id; ///< The feedback id of the actuator (255 for none) + uint8_t feedback_id2; ///< The secondary feedback id of the actuator (255 for none) + + int16_t low; ///< The low value to set the actuator to + int16_t high; ///< The high value to set the actuator to + float low_feedback; ///< The expected feedback value when the actuator is low + float high_feedback; ///< The expected feedback value when the actuator is high + + float timeout; ///< The timeout for the actuator to move +}; + +struct pfc_actuators_t { + enum pfc_actuators_state_t state; ///< The state of the preflight checks + + uint8_t act_idx; ///< The current actuator index + uint8_t act_nb; ///< The number of actuators + enum pfc_actuator_state_t act_state; ///< The state of the actuator (during the test) + float act_start_time; ///< The start time of the actuator (during the test) + + float last_feedback; ///< The last measured feedback value of the actuator + float last_feedback_err; ///< The last expected feedback error of the actuator (based on RPM/angle) + float last_feedback2; ///< The last measured secondary feedback value of the actuator + float last_feedback_err2; ///< The last expected secondary feedback error of the actuator (based on RPM/angle) +}; + +// Local variables and functions +static struct pfc_actuator_t pfc_acts[] = PFC_ACTUATORS; +static struct pfc_actuators_t pfc_actuators; +static struct preflight_check_t actuators_pfc; +static abi_event act_feedback_ev; +static void pfc_actuators_cb(struct preflight_result_t *result); +static void pfc_act_feedback_cb(uint8_t sender_id, struct act_feedback_t *feedback, uint8_t num_act); + +/** + * @brief Send an error message to the ground station + * @param fmt The format of the message + * @param ... The arguments for the format + */ +static void pfc_actuators_error(const char *fmt, ...) { + char msg[200]; + + // Add the error + va_list args; + va_start(args, fmt); + int rc = vsnprintf(msg, 200, fmt, args); + va_end(args); + + if(rc > 0) { + DOWNLINK_SEND_INFO_MSG(DefaultChannel, DefaultDevice, rc, msg); + } +} + +/** + * @brief Send a debug message to the ground station +*/ +#if PFC_ACTUATORS_DEBUG +#define pfc_actuators_debug pfc_actuators_error +#else +#define pfc_actuators_debug(...) +#endif + +/** + * @brief Register the preflight checks for the actuators + */ +void pfc_actuators_init(void) { + pfc_actuators.state = PFC_ACTUATORS_STATE_INIT; + pfc_actuators.act_state = PFC_ACTUATOR_STATE_WAIT; + pfc_actuators.act_idx = 0; + pfc_actuators.act_nb = sizeof(pfc_acts) / sizeof(struct pfc_actuator_t); + pfc_actuators.last_feedback = NAN; + pfc_actuators.last_feedback2 = NAN; + + preflight_check_register(&actuators_pfc, pfc_actuators_cb); + AbiBindMsgACT_FEEDBACK(ABI_BROADCAST, &act_feedback_ev, pfc_act_feedback_cb); +} + +/** + * @brief Move the actuators, should be put in the command laws + */ +void pfc_actuators_run(void) { + // Only actuate when running + if(pfc_actuators.state != PFC_ACTUATORS_STATE_RUNNING || pfc_actuators.act_idx >= pfc_actuators.act_nb) { + return; + } + + // Get the actuators + struct pfc_actuator_t *act = &pfc_acts[pfc_actuators.act_idx]; + + // Verify the result and continue + if((get_sys_time_float() - pfc_actuators.act_start_time) > act->timeout) { + switch(pfc_actuators.act_state) { + case PFC_ACTUATOR_STATE_WAIT: + pfc_actuators_debug("Actuator %d starting LOW", pfc_actuators.act_idx); + pfc_actuators.act_state = PFC_ACTUATOR_STATE_LOW; + pfc_actuators.act_start_time = get_sys_time_float(); + pfc_actuators.last_feedback = NAN; + pfc_actuators.last_feedback2 = NAN; + break; + case PFC_ACTUATOR_STATE_LOW: + // Check if the feedback is correct + if(act->feedback_id != 255 && + (pfc_actuators.last_feedback < (act->low_feedback - pfc_actuators.last_feedback_err) || pfc_actuators.last_feedback > (act->low_feedback + pfc_actuators.last_feedback_err))) { + pfc_actuators_error("Actuator %d not responding correctly LOW %.2f < %.2f < %.2f", pfc_actuators.act_idx, act->low_feedback - pfc_actuators.last_feedback_err, pfc_actuators.last_feedback, act->low_feedback + pfc_actuators.last_feedback_err); + pfc_actuators.state = PFC_ACTUATORS_STATE_ERROR; + } else if(act->feedback_id2 != 255 && + (pfc_actuators.last_feedback2 < (act->low_feedback - pfc_actuators.last_feedback_err2) || pfc_actuators.last_feedback2 > (act->low_feedback + pfc_actuators.last_feedback_err2))) { + pfc_actuators_error("Actuator %d not responding correctly LOW2 %.2f < %.2f < %.2f", pfc_actuators.act_idx, act->low_feedback - pfc_actuators.last_feedback_err2, pfc_actuators.last_feedback2, act->low_feedback + pfc_actuators.last_feedback_err2); + pfc_actuators.state = PFC_ACTUATORS_STATE_ERROR; + } + if(pfc_actuators.state != PFC_ACTUATORS_STATE_ERROR || PFC_ACTUATORS_DEBUG) { + pfc_actuators_debug("Actuator %d LOW ok %.2f, %.2f starting HIGH", pfc_actuators.act_idx, pfc_actuators.last_feedback, pfc_actuators.last_feedback2); + pfc_actuators.state = PFC_ACTUATORS_STATE_RUNNING; + pfc_actuators.act_state = PFC_ACTUATOR_STATE_HIGH; + pfc_actuators.act_start_time = get_sys_time_float(); + pfc_actuators.last_feedback = NAN; + pfc_actuators.last_feedback2 = NAN; + } + break; + case PFC_ACTUATOR_STATE_HIGH: + // Check if the feedback is correct + if(act->feedback_id != 255 && + (pfc_actuators.last_feedback < (act->high_feedback - pfc_actuators.last_feedback_err) || pfc_actuators.last_feedback > (act->high_feedback + pfc_actuators.last_feedback_err))) { + pfc_actuators_error("Actuator %d not responding correctly HIGH %.2f < %.2f < %.2f", pfc_actuators.act_idx, act->high_feedback - pfc_actuators.last_feedback_err, pfc_actuators.last_feedback, act->high_feedback + pfc_actuators.last_feedback_err); + pfc_actuators.state = PFC_ACTUATORS_STATE_ERROR; + } else if(act->feedback_id2 != 255 && + (pfc_actuators.last_feedback2 < (act->high_feedback - pfc_actuators.last_feedback_err2) || pfc_actuators.last_feedback2 > (act->high_feedback + pfc_actuators.last_feedback_err2))) { + pfc_actuators_error("Actuator %d not responding correctly HIGH2 %.2f < %.2f < %.2f", pfc_actuators.act_idx, act->high_feedback - pfc_actuators.last_feedback_err2, pfc_actuators.last_feedback2, act->high_feedback + pfc_actuators.last_feedback_err2); + pfc_actuators.state = PFC_ACTUATORS_STATE_ERROR; + } + if(pfc_actuators.state != PFC_ACTUATORS_STATE_ERROR || PFC_ACTUATORS_DEBUG) { + pfc_actuators_debug("Actuator %d HIGH ok %.2f, %.2f", pfc_actuators.act_idx, pfc_actuators.last_feedback, pfc_actuators.last_feedback2); + pfc_actuators.state = PFC_ACTUATORS_STATE_RUNNING; + pfc_actuators.act_state = PFC_ACTUATOR_STATE_WAIT; + pfc_actuators.act_start_time = 0; + pfc_actuators.act_idx++; + } + break; + } + } + + // Finished testing + if(pfc_actuators.act_idx >= pfc_actuators.act_nb) { + pfc_actuators.state = PFC_ACTUATORS_STATE_SUCCESS; + pfc_actuators_error("Actuators checks done %d", pfc_actuators.act_idx); + } +} + +/** + * @brief Start the actuator testing +*/ +void pfc_actuators_start(bool start) { + if(start && pfc_actuators.state != PFC_ACTUATORS_STATE_RUNNING) { + pfc_actuators.act_idx = 0; + pfc_actuators.act_start_time = 0; + pfc_actuators.state = PFC_ACTUATORS_STATE_RUNNING; + pfc_actuators.act_state = PFC_ACTUATOR_STATE_WAIT; + pfc_actuators.last_feedback = NAN; + pfc_actuators.last_feedback2 = NAN; + } + else if(!start && pfc_actuators.state == PFC_ACTUATORS_STATE_RUNNING) { + pfc_actuators.act_idx = 0; + pfc_actuators.act_start_time = 0; + pfc_actuators.state = PFC_ACTUATORS_STATE_INIT; + pfc_actuators.act_state = PFC_ACTUATOR_STATE_WAIT; + } +} + +/** + * @brief Get the actuator value in the command laws to move the actuator during the preflight checks + * @param idx The index of the actuator in the preflight checks struct + * @param value The value if no checks are performed on this actuator +*/ +int16_t pfc_actuators_value(uint8_t idx, int16_t value) { + if(idx != pfc_actuators.act_idx || pfc_actuators.state != PFC_ACTUATORS_STATE_RUNNING) { + return value; + } + + if(pfc_actuators.act_state == PFC_ACTUATOR_STATE_LOW) { + return pfc_acts[pfc_actuators.act_idx].low; + } + else if(pfc_actuators.act_state == PFC_ACTUATOR_STATE_HIGH) { + return pfc_acts[pfc_actuators.act_idx].high; + } + + return value; +} + +/** + * @brief Check the actuators with feedback + * + * @param result The result of the preflight checks + */ +static void pfc_actuators_cb(struct preflight_result_t *result) +{ + switch(pfc_actuators.state) { + case PFC_ACTUATORS_STATE_INIT: + preflight_error(result, "Actuators not checked perform checks first[%d]", pfc_actuators.act_nb); + break; + case PFC_ACTUATORS_STATE_RUNNING: + preflight_error(result, "Actuators are currently being checked[%d/%d]",pfc_actuators.act_idx, pfc_actuators.act_nb); + break; + case PFC_ACTUATORS_STATE_SUCCESS: + preflight_success(result, "Actuators checked and moved succesfully[%d]", pfc_actuators.act_nb); + break; + case PFC_ACTUATORS_STATE_ERROR: + preflight_error(result, "Actuators not responding correctly[%d/%d]",pfc_actuators.act_idx, pfc_actuators.act_nb); + break; + } +} + +/** + * @brief Callback for the actuator feedback +*/ +static void pfc_act_feedback_cb(uint8_t sender_id __attribute__((unused)), struct act_feedback_t *feedback, uint8_t num_act) { + if(pfc_actuators.state != PFC_ACTUATORS_STATE_RUNNING || pfc_actuators.act_idx >= pfc_actuators.act_nb) { + return; + } + + // Save the last feedback values + for(uint8_t i = 0; i < num_act; i++) { + if(feedback[i].idx == pfc_acts[pfc_actuators.act_idx].feedback_id && feedback[i].set.rpm) { + pfc_actuators.last_feedback = feedback[i].rpm; + pfc_actuators.last_feedback_err = PFC_ACTUATORS_MAX_RPM_ERROR; + } else if(feedback[i].idx == pfc_acts[pfc_actuators.act_idx].feedback_id && feedback[i].set.position) { + pfc_actuators.last_feedback = feedback[i].position; + pfc_actuators.last_feedback_err = PFC_ACTUATORS_MAX_ANGLE_ERROR; + } + + if(feedback[i].idx == pfc_acts[pfc_actuators.act_idx].feedback_id2 && feedback[i].set.rpm) { + pfc_actuators.last_feedback2 = feedback[i].rpm; + pfc_actuators.last_feedback_err2 = PFC_ACTUATORS_MAX_RPM_ERROR; + } else if(feedback[i].idx == pfc_acts[pfc_actuators.act_idx].feedback_id2 && feedback[i].set.position) { + pfc_actuators.last_feedback2 = feedback[i].position; + pfc_actuators.last_feedback_err2 = PFC_ACTUATORS_MAX_ANGLE_ERROR; + } + } +} diff --git a/sw/airborne/modules/checks/pfc_actuators.h b/sw/airborne/modules/checks/pfc_actuators.h new file mode 100644 index 0000000000..491c5aaab2 --- /dev/null +++ b/sw/airborne/modules/checks/pfc_actuators.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Freek van Tienen + * + * This file is part of paparazzi + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, see + * . + */ + +/** + * @file "modules/checks/pfc_actuators.h" + * @author Freek van Tienen + * Checks the actuators with feedback before takeoff + */ + +#ifndef PFC_ACTUATORS_H +#define PFC_ACTUATORS_H + +#include "std.h" + +extern void pfc_actuators_init(void); +extern void pfc_actuators_run(void); +extern void pfc_actuators_start(bool start); +extern int16_t pfc_actuators_value(uint8_t idx, int16_t value); + +#endif /* PFC_ACTUATORS_H */ diff --git a/sw/tools/generators/gen_airframe.ml b/sw/tools/generators/gen_airframe.ml index fd86ab79a5..ca3ef04877 100644 --- a/sw/tools/generators/gen_airframe.ml +++ b/sw/tools/generators/gen_airframe.ml @@ -164,14 +164,25 @@ let print_reverse_servo_table = fun out driver servos -> ) servos; fprintf out " default: return 0;\n"; fprintf out " };\n"; + fprintf out "}\n\n"; + fprintf out "static inline int get_servo_idx%s(int _idx) {\n" d; + fprintf out " switch (_idx) {\n"; + List.iter (fun c -> + let name = ExtXml.attrib c "name" in + fprintf out " case SERVO_%s_DRIVER_NO: return SERVO_%s_IDX;\n" name name; + ) servos; + fprintf out " default: return 0;\n"; + fprintf out " };\n"; fprintf out "}\n\n" let parse_servo = fun out driver c -> let shortname = ExtXml.attrib c "name" in let name = "SERVO_"^shortname and no_servo = int_of_string (ExtXml.attrib c "no") in + let global_idx = Hashtbl.length servos_drivers in define_out out (name^"_DRIVER_NO") (string_of_int no_servo); + define_out out (name^"_IDX") (string_of_int global_idx); let s_min = fos (ExtXml.attrib c "min" ) and neutral = fos (ExtXml.attrib c "neutral") @@ -194,7 +205,6 @@ let parse_servo = fun out driver c -> fprintf out "\n"; (* Memorize the associated driver (if any) and global index (insertion order) *) - let global_idx = Hashtbl.length servos_drivers in Hashtbl.add servos_drivers shortname (driver, global_idx) (* Characters checked in Gen_radio.checl_function_name *) @@ -207,7 +217,6 @@ let preprocess_value = fun s v prefix -> let print_actuators_idx = fun out -> Hashtbl.iter (fun s (d, i) -> - fprintf out "#define SERVO_%s_IDX %d\n" s i; (* Set servo macro *) fprintf out "#define Set_%s_Servo(_v) { \\\n" s; fprintf out " actuators[SERVO_%s_IDX] = Clip(_v, SERVO_%s_MIN, SERVO_%s_MAX); \\\n" s s s; @@ -315,10 +324,8 @@ let rec parse_section = fun out ac_id s -> let driver = ExtXml.attrib_or_default s "driver" "Default" in let servos = Xml.children s in let nb_servos = List.fold_right (fun s m -> max (int_of_string (ExtXml.attrib s "no")) m) servos min_int + 1 in - let servos_offset = Hashtbl.length servos_drivers in define_out out (sprintf "SERVOS_%s_NB" (String.uppercase_ascii driver)) (string_of_int nb_servos); - define_out out (sprintf "SERVOS_%s_OFFSET" (String.uppercase_ascii driver)) (string_of_int servos_offset); fprintf out "#include \"modules/actuators/actuators_%s.h\"\n" (String.lowercase_ascii driver); fprintf out "\n"; List.iter (parse_servo out driver) servos;