448 changes: 448 additions & 0 deletions conf/simulator/jsbsim/aircraft/rotwing25.xml

Large diffs are not rendered by default.

939 changes: 939 additions & 0 deletions conf/simulator/jsbsim/aircraft/rotwingv3b_fwd.xml

Large diffs are not rendered by default.

37 changes: 29 additions & 8 deletions conf/telemetry/highspeed_rotorcraft.xml
Expand Up @@ -47,6 +47,15 @@
<message name="AHRS_REF_QUAT" period="0.1"/>
<message name="INS_FLOW_INFO" period="0.1"/>
<message name="GPS_RTK" period="0.1"/>
<message name="STAB_ATTITUDE_INDI" period="0.05"/>
<message name="AOA" period="0.2"/>
<message name="ACTUATORS" period="0.02"/>
<message name="INDI_G" period="0.2"/>
<message name="APPROACH_MOVING_TARGET" period="0.1"/>
<message name="AIRSPEED_RAW" period="0.1"/>
<message name="TARGET_POS_INFO" period="0.1"/>
<message name="ROTATING_WING_STATE" period="0.2"/>
<message name="ROTORCRAFT_CMD" period="0.2"/>
</mode>

<mode name="calibration">
Expand Down Expand Up @@ -74,14 +83,16 @@
</mode>

<mode name="raw_sensors">
<message name="ROTORCRAFT_STATUS" period="1.2"/>
<message name="DL_VALUE" period="0.5"/>
<message name="ALIVE" period="2.1"/>
<message name="IMU_ACCEL_RAW" period=".05"/>
<message name="IMU_GYRO_RAW" period=".05"/>
<message name="IMU_MAG_RAW" period=".05"/>
<message name="BARO_RAW" period=".1"/>
<message name="ARDRONE_NAVDATA" period=".05"/>
<message name="ROTORCRAFT_STATUS" period="1.2"/>
<message name="DL_VALUE" period="0.5"/>
<message name="ALIVE" period="2.1"/>
<message name="IMU_ACCEL_RAW" period=".05"/>
<message name="IMU_GYRO_RAW" period=".05"/>
<message name="IMU_MAG_RAW" period=".05"/>
<message name="BARO_RAW" period=".1"/>
<message name="IMU_MAG_CURRENT_CALIBRATION" period=".05"/>
<message name="ARDRONE_NAVDATA" period=".05"/>
<message name="AIRSPEED_RAW" period="0.1"/>
</mode>

<mode name="scaled_sensors">
Expand Down Expand Up @@ -149,6 +160,7 @@
<mode name="vel_guidance" key_press="q">
<message name="ALIVE" period="0.9"/>
<message name="HYBRID_GUIDANCE" period="0.062"/>
<message name="GUIDANCE_INDI_HYBRID" period="0.1"/>
<!--<message name="STAB_ATTITUDE_REF" period="0.4"/>-->
<message name="ROTORCRAFT_FP" period="0.8"/>
<message name="ROTORCRAFT_STATUS" period="1.2"/>
Expand Down Expand Up @@ -242,6 +254,15 @@
<message name="ACTUATORS" period="0.002"/>
<message name="GPS_RTK" period="0.1"/>
<message name="IMU_HEATER" period="1.0"/>
<message name="AOA" period="0.02"/>
<message name="DOUBLET" period="0.002"/>
<message name="APPROACH_MOVING_TARGET" period="0.1"/>
<message name="TARGET_POS_INFO" period="0.1"/>
<message name="AIRSPEED_RAW" period="0.002"/>
<message name="AIRSPEED_WIND_ESTIMATOR_EKF" period="0.04"/>
<message name="AIRSPEED_WIND_ESTIMATOR_EKF_COV" period="0.04"/>
<message name="AIRSPEED_WIND_ESTIMATOR_EKF_FORCES" period="0.04"/>
<message name="ROTATING_WING_STATE" period="0.1"/>
</mode>
</process>

Expand Down
22 changes: 22 additions & 0 deletions conf/userconf/tudelft/conf.xml
Expand Up @@ -571,4 +571,26 @@
settings_modules="modules/air_data.xml modules/airspeed_ms45xx_i2c.xml modules/electrical.xml modules/gps.xml modules/gps_ublox.xml modules/gps_ubx_ucenter.xml modules/guidance_indi_hybrid.xml modules/guidance_rotorcraft.xml modules/imu_common.xml modules/ins_ekf2.xml modules/nav_rotorcraft.xml modules/stabilization_indi.xml"
gui_color="red"
/>
<aircraft
name="RotatingWingV3b"
ac_id="39"
airframe="airframes/tudelft/rot_wing_v3b.xml"
radio="radios/crossfire_sbus.xml"
telemetry="telemetry/highspeed_rotorcraft.xml"
flight_plan="flight_plans/tudelft/rotating_wing_EHVB.xml"
settings="settings/rotorcraft_basic.xml"
settings_modules="modules/AOA_pwm.xml modules/air_data.xml modules/airspeed_ms45xx_i2c.xml modules/approach_moving_target.xml modules/ctrl_eff_sched_rot_wing_v3b.xml modules/ekf_aw.xml modules/electrical.xml modules/gps.xml modules/gps_ublox.xml modules/guidance_indi_hybrid.xml modules/guidance_rotorcraft.xml modules/imu_common.xml modules/imu_heater.xml modules/ins_ekf2.xml modules/lidar_tfmini.xml modules/logger_sd_chibios.xml modules/nav_hybrid.xml modules/nav_rotorcraft.xml modules/rot_wing_automation.xml modules/stabilization_indi.xml modules/sys_id_auto_doublets.xml modules/sys_id_doublet.xml modules/target_pos.xml modules/wing_rotation_controller_v3b.xml"
gui_color="red"
/>
<aircraft
name="rot_wing_25kg"
ac_id="29"
airframe="airframes/tudelft/rot_wing_25kg.xml"
radio="radios/crossfire_sbus.xml"
telemetry="telemetry/highspeed_rotorcraft.xml"
flight_plan="flight_plans/tudelft/rotating_wing25kg_EHVB.xml"
settings="settings/rotorcraft_basic.xml"
settings_modules="modules/actuators_faulhaber.xml modules/air_data.xml modules/gps.xml modules/gps_ublox.xml modules/guidance_indi_rot_wing.xml modules/guidance_rotorcraft.xml modules/imu_common.xml modules/imu_heater.xml modules/ins_ekf2.xml modules/logger_sd_chibios.xml modules/nav_hybrid.xml modules/nav_rotorcraft.xml modules/rot_wing_auto_doublet.xml modules/stabilization_indi.xml modules/sys_id_doublet.xml"
gui_color="red"
/>
</conf>
4 changes: 2 additions & 2 deletions sw/airborne/boards/cube/orange/board.cfg
Expand Up @@ -140,8 +140,8 @@ PD09 UART3_RX UART AF:USART3_RX # TELEM2
PD10 SPI_SLAVE7 OUTPUT PUSHPULL SPEED_HIGH FLOATING LEVEL_HIGH # INV FRAM_CS
PD11 UART3_CTS PASSIVE # TELEM2
PD12 UART3_RTS PASSIVE # TELEM2
PD13 SERVO5 PWM AF:TIM4_CH2 ()
PD14 SERVO6 PWM AF:TIM4_CH3 ()
PD13 PWM_INPUT1 ICU AF:TIM4_CH2 () # SERVO5
PD14 PWM_INPUT2 ICU AF:TIM4_CH3 () # SERVO6
PD15 MPU_DRDY PASSIVE

PE00 UART8_RX UART AF:UART8_RX # GPS2
Expand Down
60 changes: 29 additions & 31 deletions sw/airborne/boards/cube/orange/board.h
Expand Up @@ -125,8 +125,8 @@
#define PD10_SPI_SLAVE7 10U
#define PD11_UART3_CTS 11U
#define PD12_UART3_RTS 12U
#define PD13_SERVO5 13U
#define PD14_SERVO6 14U
#define PD13_PWM_INPUT1 13U
#define PD14_PWM_INPUT2 14U
#define PD15_MPU_DRDY 15U

#define PE00_UART8_RX 0U
Expand Down Expand Up @@ -313,8 +313,8 @@
#define LINE_SPI_SLAVE7 PAL_LINE(GPIOD, 10U)
#define LINE_UART3_CTS PAL_LINE(GPIOD, 11U)
#define LINE_UART3_RTS PAL_LINE(GPIOD, 12U)
#define LINE_SERVO5 PAL_LINE(GPIOD, 13U)
#define LINE_SERVO6 PAL_LINE(GPIOD, 14U)
#define LINE_PWM_INPUT1 PAL_LINE(GPIOD, 13U)
#define LINE_PWM_INPUT2 PAL_LINE(GPIOD, 14U)
#define LINE_MPU_DRDY PAL_LINE(GPIOD, 15U)

#define LINE_UART8_RX PAL_LINE(GPIOE, 0U)
Expand Down Expand Up @@ -682,8 +682,8 @@
PIN_MODE_OUTPUT(PD10_SPI_SLAVE7) | \
PIN_MODE_INPUT(PD11_UART3_CTS) | \
PIN_MODE_INPUT(PD12_UART3_RTS) | \
PIN_MODE_ALTERNATE(PD13_SERVO5) | \
PIN_MODE_ALTERNATE(PD14_SERVO6) | \
PIN_MODE_ALTERNATE(PD13_PWM_INPUT1) | \
PIN_MODE_ALTERNATE(PD14_PWM_INPUT2) | \
PIN_MODE_INPUT(PD15_MPU_DRDY))

#define VAL_GPIOD_OTYPER (PIN_OTYPE_PUSHPULL(PD00_CAN1_RX) | \
Expand All @@ -699,8 +699,8 @@
PIN_OTYPE_PUSHPULL(PD10_SPI_SLAVE7) | \
PIN_OTYPE_OPENDRAIN(PD11_UART3_CTS) | \
PIN_OTYPE_OPENDRAIN(PD12_UART3_RTS) | \
PIN_OTYPE_PUSHPULL(PD13_SERVO5) | \
PIN_OTYPE_PUSHPULL(PD14_SERVO6) | \
PIN_OTYPE_PUSHPULL(PD13_PWM_INPUT1) | \
PIN_OTYPE_PUSHPULL(PD14_PWM_INPUT2) | \
PIN_OTYPE_OPENDRAIN(PD15_MPU_DRDY))

#define VAL_GPIOD_OSPEEDR (PIN_OSPEED_SPEED_HIGH(PD00_CAN1_RX) | \
Expand All @@ -716,8 +716,8 @@
PIN_OSPEED_SPEED_HIGH(PD10_SPI_SLAVE7) | \
PIN_OSPEED_SPEED_VERYLOW(PD11_UART3_CTS) | \
PIN_OSPEED_SPEED_VERYLOW(PD12_UART3_RTS) | \
PIN_OSPEED_SPEED_HIGH(PD13_SERVO5) | \
PIN_OSPEED_SPEED_HIGH(PD14_SERVO6) | \
PIN_OSPEED_SPEED_HIGH(PD13_PWM_INPUT1) | \
PIN_OSPEED_SPEED_HIGH(PD14_PWM_INPUT2) | \
PIN_OSPEED_SPEED_VERYLOW(PD15_MPU_DRDY))

#define VAL_GPIOD_PUPDR (PIN_PUPDR_FLOATING(PD00_CAN1_RX) | \
Expand All @@ -733,8 +733,8 @@
PIN_PUPDR_FLOATING(PD10_SPI_SLAVE7) | \
PIN_PUPDR_PULLDOWN(PD11_UART3_CTS) | \
PIN_PUPDR_PULLDOWN(PD12_UART3_RTS) | \
PIN_PUPDR_FLOATING(PD13_SERVO5) | \
PIN_PUPDR_FLOATING(PD14_SERVO6) | \
PIN_PUPDR_FLOATING(PD13_PWM_INPUT1) | \
PIN_PUPDR_FLOATING(PD14_PWM_INPUT2) | \
PIN_PUPDR_PULLDOWN(PD15_MPU_DRDY))

#define VAL_GPIOD_ODR (PIN_ODR_LEVEL_HIGH(PD00_CAN1_RX) | \
Expand All @@ -750,8 +750,8 @@
PIN_ODR_LEVEL_HIGH(PD10_SPI_SLAVE7) | \
PIN_ODR_LEVEL_HIGH(PD11_UART3_CTS) | \
PIN_ODR_LEVEL_HIGH(PD12_UART3_RTS) | \
PIN_ODR_LEVEL_LOW(PD13_SERVO5) | \
PIN_ODR_LEVEL_LOW(PD14_SERVO6) | \
PIN_ODR_LEVEL_HIGH(PD13_PWM_INPUT1) | \
PIN_ODR_LEVEL_HIGH(PD14_PWM_INPUT2) | \
PIN_ODR_LEVEL_HIGH(PD15_MPU_DRDY))

#define VAL_GPIOD_AFRL (PIN_AFIO_AF(PD00_CAN1_RX, 9) | \
Expand All @@ -768,8 +768,8 @@
PIN_AFIO_AF(PD10_SPI_SLAVE7, 0) | \
PIN_AFIO_AF(PD11_UART3_CTS, 0) | \
PIN_AFIO_AF(PD12_UART3_RTS, 0) | \
PIN_AFIO_AF(PD13_SERVO5, 2) | \
PIN_AFIO_AF(PD14_SERVO6, 2) | \
PIN_AFIO_AF(PD13_PWM_INPUT1, 2) | \
PIN_AFIO_AF(PD14_PWM_INPUT2, 2) | \
PIN_AFIO_AF(PD15_MPU_DRDY, 0))

#define VAL_GPIOE_MODER (PIN_MODE_ALTERNATE(PE00_UART8_RX) | \
Expand Down Expand Up @@ -1559,10 +1559,10 @@
#define AF_LINE_UART3_TX 7U
#define AF_PD09_UART3_RX 7U
#define AF_LINE_UART3_RX 7U
#define AF_PD13_SERVO5 2U
#define AF_LINE_SERVO5 2U
#define AF_PD14_SERVO6 2U
#define AF_LINE_SERVO6 2U
#define AF_PD13_PWM_INPUT1 2U
#define AF_LINE_PWM_INPUT1 2U
#define AF_PD14_PWM_INPUT2 2U
#define AF_LINE_PWM_INPUT2 2U
#define AF_PE00_UART8_RX 8U
#define AF_LINE_UART8_RX 8U
#define AF_PE01_UART8_TX 8U
Expand Down Expand Up @@ -1609,14 +1609,14 @@
#define ADC5_ADC 1
#define ADC5_ADC_FN INP
#define ADC5_ADC_INP 8
#define SERVO5_TIM 4
#define SERVO5_TIM_FN CH
#define SERVO5_TIM_CH 2
#define SERVO5_TIM_AF 2
#define SERVO6_TIM 4
#define SERVO6_TIM_FN CH
#define SERVO6_TIM_CH 3
#define SERVO6_TIM_AF 2
#define PWM_INPUT1_TIM 4
#define PWM_INPUT1_TIM_FN CH
#define PWM_INPUT1_TIM_CH 2
#define PWM_INPUT1_TIM_AF 2
#define PWM_INPUT2_TIM 4
#define PWM_INPUT2_TIM_FN CH
#define PWM_INPUT2_TIM_CH 3
#define PWM_INPUT2_TIM_AF 2
#define SERVO4_TIM 1
#define SERVO4_TIM_FN CH
#define SERVO4_TIM_CH 1
Expand Down Expand Up @@ -1658,15 +1658,13 @@
LINE_SPI_SLAVE5, \
LINE_SPI_SLAVE6, \
LINE_SPI_SLAVE7, \
LINE_SERVO5, \
LINE_SERVO6, \
LINE_SPI_SLAVE8, \
LINE_SERVO4, \
LINE_SERVO3, \
LINE_LED1, \
LINE_SERVO2, \
LINE_SERVO1
#define ENERGY_SAVE_INPUTS_SIZE 16
#define ENERGY_SAVE_INPUTS_SIZE 14

#define ENERGY_SAVE_LOWS \
LINE_VDD_5V_PERIPH_EN, \
Expand Down
4 changes: 2 additions & 2 deletions sw/airborne/boards/cube/orange/mcuconf.h
Expand Up @@ -339,7 +339,7 @@
#define STM32_ICU_USE_TIM1 FALSE
#define STM32_ICU_USE_TIM2 FALSE
#define STM32_ICU_USE_TIM3 FALSE
#define STM32_ICU_USE_TIM4 FALSE
#define STM32_ICU_USE_TIM4 TRUE
#define STM32_ICU_USE_TIM5 FALSE
#define STM32_ICU_USE_TIM8 FALSE
#define STM32_ICU_USE_TIM12 FALSE
Expand Down Expand Up @@ -367,7 +367,7 @@
#define STM32_PWM_USE_TIM1 TRUE
#define STM32_PWM_USE_TIM2 FALSE
#define STM32_PWM_USE_TIM3 FALSE
#define STM32_PWM_USE_TIM4 TRUE
#define STM32_PWM_USE_TIM4 FALSE
#define STM32_PWM_USE_TIM5 FALSE
#define STM32_PWM_USE_TIM8 FALSE
#define STM32_PWM_USE_TIM9 FALSE
Expand Down
207 changes: 175 additions & 32 deletions sw/airborne/firmwares/rotorcraft/guidance/guidance_indi_hybrid.c
Expand Up @@ -40,6 +40,13 @@
#include "modules/core/abi.h"
#include "firmwares/rotorcraft/stabilization/stabilization_attitude_rc_setpoint.h"

#ifndef GUIDANCE_INDI_QUADPLANE
#define GUIDANCE_INDI_QUADPLANE FALSE
#endif

#if GUIDANCE_INDI_QUADPLANE
#include "firmwares/rotorcraft/stabilization/wls/wls_alloc.h"
#endif

// The acceleration reference is calculated with these gains. If you use GPS,
// they are probably limited by the update rate of your GPS. The default
Expand All @@ -56,9 +63,14 @@
#endif

#ifndef GUIDANCE_INDI_MIN_PITCH
#if GUIDANCE_INDI_QUADPLANE
#define GUIDANCE_INDI_MIN_PITCH -20
#define GUIDANCE_INDI_MAX_PITCH 20
#else
#define GUIDANCE_INDI_MIN_PITCH -120
#define GUIDANCE_INDI_MAX_PITCH 25
#endif
#endif

#ifndef GUIDANCE_INDI_LIFTD_ASQ
#define GUIDANCE_INDI_LIFTD_ASQ 0.20
Expand Down Expand Up @@ -155,6 +167,7 @@ Butterworth2LowPass roll_filt;
Butterworth2LowPass pitch_filt;
Butterworth2LowPass thrust_filt;
Butterworth2LowPass accely_filt;
Butterworth2LowPass accel_bodyz_filt;

struct FloatVect2 desired_airspeed;

Expand All @@ -163,6 +176,34 @@ struct FloatMat33 Ga_inv;
struct FloatVect3 euler_cmd;

float filter_cutoff = GUIDANCE_INDI_FILTER_CUTOFF;
float bodyz_filter_cutoff = 0.2;

#if GUIDANCE_INDI_QUADPLANE
float Gmat_quadplane[3][4];
float *Bwls_quadplane[3];

int num_iter_quadplane= 0;
float guidance_indi_du[4];
float guidance_indi_du_min[4];
float guidance_indi_du_max[4];
float guidance_indi_du_pref[4];
float Wv_quadplane[3] = {10., 10., 10.};
float Wu_quadplane[4] = {10., 10., 100., 1.};

#ifndef GUIDANCE_INDI_THRUST_Z_EFF
#error "You need to define GUIDANCE_INDI_THRUST_Z_EFF"
#else
float guidance_indi_thrust_z_eff = GUIDANCE_INDI_THRUST_Z_EFF;
#endif

#ifndef GUIDANCE_INDI_THRUST_X_EFF
#error "You need to define GUIDANCE_INDI_THRUST_X_EFF"
#else
float guidance_indi_thrust_x_eff = GUIDANCE_INDI_THRUST_X_EFF;
#endif

float guidance_indi_pitch_pref_deg = 0;
#endif

float guidance_indi_hybrid_heading_sp = 0.f;
struct FloatEulers guidance_euler_cmd;
Expand All @@ -179,7 +220,12 @@ struct FloatVect3 indi_vel_sp = {0.0, 0.0, 0.0};
float time_of_vel_sp = 0.0;

void guidance_indi_propagate_filters(void);
#if GUIDANCE_INDI_QUADPLANE
// For Quadplane types
static void guidance_indi_calcg_wls(struct FloatVect3 a_diff);
#else
static void guidance_indi_calcg_wing(struct FloatMat33 *Gmat);
#endif

#if PERIODIC_TELEMETRY
#include "modules/datalink/telemetry.h"
Expand Down Expand Up @@ -222,6 +268,12 @@ void guidance_indi_init(void)
#if PERIODIC_TELEMETRY
register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_GUIDANCE_INDI_HYBRID, send_guidance_indi_hybrid);
#endif

#if GUIDANCE_INDI_QUADPLANE
for (int8_t i = 0; i < 3; i++) {
Bwls_quadplane[i] = Gmat_quadplane[i];
}
#endif
}

/**
Expand All @@ -238,6 +290,7 @@ void guidance_indi_enter(void) {
guidance_indi_hybrid_heading_sp = stateGetNedToBodyEulers_f()->psi;

float tau = 1.0 / (2.0 * M_PI * filter_cutoff);
float tau_bodyz = 1.0/(2.0*M_PI*bodyz_filter_cutoff);
float sample_time = 1.0 / PERIODIC_FREQUENCY;
for (int8_t i = 0; i < 3; i++) {
init_butterworth_2_low_pass(&filt_accel_ned[i], tau, sample_time, 0.0);
Expand All @@ -250,6 +303,7 @@ void guidance_indi_enter(void) {
init_butterworth_2_low_pass(&pitch_filt, tau, sample_time, eulers_zxy.theta);
init_butterworth_2_low_pass(&thrust_filt, tau, sample_time, thrust_in);
init_butterworth_2_low_pass(&accely_filt, tau, sample_time, 0.0);
init_butterworth_2_low_pass(&accel_bodyz_filt, tau_bodyz, sample_time, -9.81);
}

#include "firmwares/rotorcraft/navigation.h"
Expand Down Expand Up @@ -291,11 +345,6 @@ struct StabilizationSetpoint guidance_indi_run(struct FloatVect3 *accel_sp, floa
sp_accel.z = -(radio_control.values[RADIO_THROTTLE]-4500)*8.0/9600.0;
#endif

// Calculate matrix of partial derivatives
guidance_indi_calcg_wing(&Ga);
// Invert this matrix
MAT33_INV(Ga_inv, Ga);

struct FloatVect3 accel_filt;
accel_filt.x = filt_accel_ned[0].o[0];
accel_filt.y = filt_accel_ned[1].o[0];
Expand All @@ -319,11 +368,27 @@ struct StabilizationSetpoint guidance_indi_run(struct FloatVect3 *accel_sp, floa
#endif
#endif

#if GUIDANCE_INDI_QUADPLANE
// Perform WLS for quadplane types
guidance_indi_calcg_wls(a_diff);
euler_cmd.x = guidance_indi_du[0];
euler_cmd.y = guidance_indi_du[1];
euler_cmd.z = guidance_indi_du[2];
#else
// Calculate matrix of partial derivatives
guidance_indi_calcg_wing(&Ga);
// Invert this matrix
MAT33_INV(Ga_inv, Ga);

//Calculate roll,pitch and thrust command
MAT33_VECT3_MUL(euler_cmd, Ga_inv, a_diff);

#endif
struct FloatVect3 thrust_vect;
thrust_vect.x = 0.0; // Fill for quadplanes
#if GUIDANCE_INDI_QUADPLANE
thrust_vect.x = guidance_indi_du[3];
#else
thrust_vect.x = 0.0;
#endif
thrust_vect.y = 0.0;
thrust_vect.z = euler_cmd.z;
AbiSendMsgTHRUST(THRUST_INCREMENT_ID, thrust_vect);
Expand Down Expand Up @@ -654,48 +719,126 @@ void guidance_indi_propagate_filters(void) {
// Propagate filter for sideslip correction
float accely = ACCEL_FLOAT_OF_BFP(stateGetAccelBody_i()->y);
update_butterworth_2_low_pass(&accely_filt, accely);

// Propagate filter for thrust/lift estimate
float accelz = ACCEL_FLOAT_OF_BFP(stateGetAccelBody_i()->z);
update_butterworth_2_low_pass(&accel_bodyz_filt, accelz);
}

/**
* Calculate the matrix of partial derivatives of the roll, pitch and thrust
* w.r.t. the NED accelerations, taking into account the lift of a wing that is
* horizontal at -90 degrees pitch
* Perform WLS
*
* @param Gmat array to write the matrix to [3x3]
* @param a_diff 3D vector to write the acceration error
*/
void guidance_indi_calcg_wing(struct FloatMat33 *Gmat) {

void guidance_indi_calcg_wls(struct FloatVect3 a_diff) {
/*Pre-calculate sines and cosines*/
float sphi = sinf(eulers_zxy.phi);
float cphi = cosf(eulers_zxy.phi);
float stheta = sinf(eulers_zxy.theta);
float ctheta = cosf(eulers_zxy.theta);
float spsi = sinf(eulers_zxy.psi);
float cpsi = cosf(eulers_zxy.psi);
//minus gravity is a guesstimate of the thrust force, thrust measurement would be better

#ifndef GUIDANCE_INDI_PITCH_EFF_SCALING
#define GUIDANCE_INDI_PITCH_EFF_SCALING 1.0
#endif

/*Amount of lift produced by the wing*/
float pitch_lift = eulers_zxy.theta;
Bound(pitch_lift,-M_PI_2,0);
float lift = sinf(pitch_lift)*9.81;
float T = cosf(pitch_lift)*-9.81;
float lift_thrust_bz = accel_bodyz_filt.o[0]; // Sum of lift and thrust in boxy z axis (level flight)

// get the derivative of the lift wrt to theta
float liftd = guidance_indi_get_liftd(stateGetAirspeed_f(), eulers_zxy.theta);

RMAT_ELMT(*Gmat, 0, 0) = cphi*ctheta*spsi*T + cphi*spsi*lift;
RMAT_ELMT(*Gmat, 1, 0) = -cphi*ctheta*cpsi*T - cphi*cpsi*lift;
RMAT_ELMT(*Gmat, 2, 0) = -sphi*ctheta*T -sphi*lift;
RMAT_ELMT(*Gmat, 0, 1) = (ctheta*cpsi - sphi*stheta*spsi)*T*GUIDANCE_INDI_PITCH_EFF_SCALING + sphi*spsi*liftd;
RMAT_ELMT(*Gmat, 1, 1) = (ctheta*spsi + sphi*stheta*cpsi)*T*GUIDANCE_INDI_PITCH_EFF_SCALING - sphi*cpsi*liftd;
RMAT_ELMT(*Gmat, 2, 1) = -cphi*stheta*T*GUIDANCE_INDI_PITCH_EFF_SCALING + cphi*liftd;
RMAT_ELMT(*Gmat, 0, 2) = stheta*cpsi + sphi*ctheta*spsi;
RMAT_ELMT(*Gmat, 1, 2) = stheta*spsi - sphi*ctheta*cpsi;
RMAT_ELMT(*Gmat, 2, 2) = cphi*ctheta;
float liftd = guidance_indi_get_liftd(0.0f, 0.0f);

Gmat_quadplane[0][0] = -sphi*stheta*lift_thrust_bz;
Gmat_quadplane[1][0] = -cphi*lift_thrust_bz;
Gmat_quadplane[2][0] = -sphi*ctheta*lift_thrust_bz;

Gmat_quadplane[0][1] = cphi*ctheta*lift_thrust_bz*GUIDANCE_INDI_PITCH_EFF_SCALING;
Gmat_quadplane[1][1] = sphi*stheta*lift_thrust_bz*GUIDANCE_INDI_PITCH_EFF_SCALING - sphi*liftd;
Gmat_quadplane[2][1] = -cphi*stheta*lift_thrust_bz*GUIDANCE_INDI_PITCH_EFF_SCALING + cphi*liftd;

Gmat_quadplane[0][2] = cphi*stheta;
Gmat_quadplane[1][2] = -sphi;
Gmat_quadplane[2][2] = cphi*ctheta;

Gmat_quadplane[0][3] = ctheta;
Gmat_quadplane[1][3] = 0;
Gmat_quadplane[2][3] = -stheta;

// Perform WLS
// WLS Control Allocator

// Convert acceleration error to body axis system
float body_v[3];
body_v[0] = cpsi * a_diff.x + spsi * a_diff.y;
body_v[1] = -spsi * a_diff.x + cpsi * a_diff.y;
body_v[2] = a_diff.z;

guidance_indi_hybrid_set_wls_settings(body_v, roll_filt.o[0], pitch_filt.o[0]);

int16_t n_u = CA_N_U_OUTER;
int16_t n_v = CA_N_V_OUTER;

checknan("GUID-guidance_indi_du_min",guidance_indi_du_min,4);
checknan("GUID-guidance_indi_du_max",guidance_indi_du_max,4);
checknan("GUID-guidance_indi_du_pref",guidance_indi_du_pref,4);
checknan("GUID-body_v",body_v,3);
for (int i=0; i<3;i++){
checknan("GUID-Bwls_quadplane[]",Bwls_quadplane[i],4);
}
checknan("GUID-Wv_quadplane",Wv_quadplane,3);
checknan("GUID-Wu_quadplane",Wu_quadplane,4);

num_iter_quadplane =
wls_alloc(guidance_indi_du, body_v, guidance_indi_du_min, guidance_indi_du_max, Bwls_quadplane, 0, 0, Wv_quadplane, Wu_quadplane, guidance_indi_du_pref, 100000, 10, n_u, n_v);

int ret = checknan("GUID-p-guidance_indi_du",guidance_indi_du,4);
if (ret > 0) {
printmat("GUID-guidance_indi_du_min",guidance_indi_du_min,4);
printmat("GUID-guidance_indi_du_max",guidance_indi_du_max,4);
printmat("GUID-guidance_indi_du_pref",guidance_indi_du_pref,4);
printmat("GUID-body_v",body_v,3);
for (int i=0; i<3;i++){
printmat("GUID-Bwls_quadplane[]",Bwls_quadplane[i],4);
}
printmat("GUID-Wv_quadplane",Wv_quadplane,3);
printmat("GUID-Wu_quadplane",Wu_quadplane,4);

}
}


/**
*
* @param body_v
*
* WEAK function to set the quadplane wls settings
*/
void WEAK guidance_indi_hybrid_set_wls_settings(float body_v[3], float roll_angle, float pitch_angle)
{
float roll_limit_rad = GUIDANCE_H_MAX_BANK;
float max_pitch_limit_rad = RadOfDeg(GUIDANCE_INDI_MAX_PITCH);
float min_pitch_limit_rad = RadOfDeg(GUIDANCE_INDI_MIN_PITCH);

float pitch_pref_rad = RadOfDeg(guidance_indi_pitch_pref_deg);

// Set lower limits
guidance_indi_du_min[0] = -roll_limit_rad - roll_angle; //roll
guidance_indi_du_min[1] = min_pitch_limit_rad - pitch_angle; // pitch
guidance_indi_du_min[2] = (MAX_PPRZ - stabilization_cmd[COMMAND_THRUST]) * guidance_indi_thrust_z_eff;
guidance_indi_du_min[3] = -stabilization_cmd[COMMAND_THRUST_X]*guidance_indi_thrust_x_eff;

// Set upper limits limits
guidance_indi_du_max[0] = roll_limit_rad - roll_angle; //roll
guidance_indi_du_max[1] = max_pitch_limit_rad - pitch_angle; // pitch
guidance_indi_du_max[2] = -stabilization_cmd[COMMAND_THRUST] * guidance_indi_thrust_z_eff;
guidance_indi_du_max[3] = (MAX_PPRZ - stabilization_cmd[COMMAND_THRUST_X])*guidance_indi_thrust_x_eff;

// Set prefered states
guidance_indi_du_pref[0] = 0; // prefered delta roll angle
guidance_indi_du_pref[1] = -pitch_angle + pitch_pref_rad;// prefered delta pitch angle
guidance_indi_du_pref[2] = guidance_indi_du_max[2]; // Low thrust better for efficiency
guidance_indi_du_pref[3] = body_v[0]; // solve the body acceleration
}

/**
Expand Down Expand Up @@ -781,21 +924,21 @@ int32_t guidance_v_run_pos(bool in_flight UNUSED, struct VerticalGuidance *gv)
{
_gv = gv;
_v_mode = GUIDANCE_INDI_HYBRID_V_POS;
return (int32_t)thrust_in; // nothing to do
return (int32_t)stabilization_cmd[COMMAND_THRUST]; // nothing to do
}

int32_t guidance_v_run_speed(bool in_flight UNUSED, struct VerticalGuidance *gv)
{
_gv = gv;
_v_mode = GUIDANCE_INDI_HYBRID_V_SPEED;
return (int32_t)thrust_in; // nothing to do
return (int32_t)stabilization_cmd[COMMAND_THRUST]; // nothing to do
}

int32_t guidance_v_run_accel(bool in_flight UNUSED, struct VerticalGuidance *gv)
{
_gv = gv;
_v_mode = GUIDANCE_INDI_HYBRID_V_ACCEL;
return (int32_t)thrust_in; // nothing to do
return (int32_t)stabilization_cmd[COMMAND_THRUST]; // nothing to do
}

#endif
Expand Down
14 changes: 14 additions & 0 deletions sw/airborne/firmwares/rotorcraft/guidance/guidance_indi_hybrid.h
Expand Up @@ -57,6 +57,7 @@ enum GuidanceIndiHybrid_VMode {

extern struct StabilizationSetpoint guidance_indi_run(struct FloatVect3 *accep_sp, float heading_sp);
extern struct StabilizationSetpoint guidance_indi_run_mode(bool in_flight, struct HorizontalGuidance *gh, struct VerticalGuidance *gv, enum GuidanceIndiHybrid_HMode h_mode, enum GuidanceIndiHybrid_VMode v_mode);
extern void guidance_indi_hybrid_set_wls_settings(float body_v[3], float roll_angle, float pitch_angle);

struct guidance_indi_hybrid_params {
float pos_gain;
Expand All @@ -72,6 +73,19 @@ struct guidance_indi_hybrid_params {
extern struct FloatVect3 sp_accel;
extern struct FloatVect3 gi_speed_sp;

extern struct FloatRates ff_rates;
extern bool ff_rates_set;

extern float guidance_indi_pitch_pref_deg;

extern float Wu_quadplane[4];
extern float Wv_quadplane[3];
extern float guidance_indi_du_min[4];
extern float guidance_indi_du_max[4];
extern float guidance_indi_du_pref[4];
extern float guidance_indi_thrust_z_eff;
extern float guidance_indi_thrust_x_eff;

extern struct guidance_indi_hybrid_params gih_params;
extern float guidance_indi_specific_force_gain;
extern float guidance_indi_max_airspeed;
Expand Down
2 changes: 2 additions & 0 deletions sw/airborne/firmwares/rotorcraft/guidance/guidance_v.c
Expand Up @@ -232,6 +232,8 @@ void guidance_v_run(bool in_flight)
case GUIDANCE_V_MODE_RC_DIRECT:
guidance_v.z_sp = stateGetPositionNed_i()->z; // for display only
stabilization_cmd[COMMAND_THRUST] = guidance_v.rc_delta_t;

SetAutoCommandsFromRC(stabilization_cmd,radio_control.values);
break;

case GUIDANCE_V_MODE_RC_CLIMB:
Expand Down
151 changes: 144 additions & 7 deletions sw/airborne/firmwares/rotorcraft/stabilization/stabilization_indi.c
Expand Up @@ -81,6 +81,12 @@
#define INDI_HROTTLE_LIMIT_AIRSPEED_FWD 8.0
#endif

#if INDI_OUTPUTS > 4
#ifndef STABILIZATION_INDI_G1_THRUST_X
#error "You must define STABILIZATION_INDI_G1_THRUST_X for your number of INDI_OUTPUTS"
#endif
#endif

#ifdef SetCommandsFromRC
#warning SetAutoCommandsFromRC not used: STAB_INDI overwrites actuators
#endif
Expand All @@ -92,6 +98,8 @@ float indi_v[INDI_OUTPUTS];
float *Bwls[INDI_OUTPUTS];
int num_iter = 0;

int good = 512*30;

static void lms_estimation(void);
static void get_actuator_state(void);
static void calc_g1_element(float dx_error, int8_t i, int8_t j, float mu_extra);
Expand Down Expand Up @@ -131,6 +139,14 @@ bool act_is_servo[INDI_NUM_ACT] = STABILIZATION_INDI_ACT_IS_SERVO;
bool act_is_servo[INDI_NUM_ACT] = {0};
#endif

#ifdef STABILIZATION_INDI_ACT_IS_THRUSTER_X
bool act_is_thruster_x[INDI_NUM_ACT] = STABILIZATION_INDI_ACT_IS_THRUSTER_X;
#else
bool act_is_thruster_x[INDI_NUM_ACT] = {0};
#endif

bool act_is_thruster_z[INDI_NUM_ACT];

#ifdef STABILIZATION_INDI_ACT_PREF
// Preferred (neutral, least energy) actuator value
float act_pref[INDI_NUM_ACT] = STABILIZATION_INDI_ACT_PREF;
Expand All @@ -145,7 +161,11 @@ float act_dyn[INDI_NUM_ACT] = STABILIZATION_INDI_ACT_DYN;
static float Wv[INDI_OUTPUTS] = STABILIZATION_INDI_WLS_PRIORITIES;
#else
//State prioritization {W Roll, W pitch, W yaw, TOTAL THRUST}
static float Wv[INDI_OUTPUTS] = {1000, 1000, 1, 100};
#if INDI_OUTPUTS == 5
static float Wv[INDI_OUTPUTS] = {1000, 1000, 1, 100, 100};
#else
static float Wv[INDI_OUTPUTS] = {1000, 1000, 1, 100};
#endif
#endif

/**
Expand Down Expand Up @@ -190,6 +210,7 @@ float act_obs[INDI_NUM_ACT];

// Number of actuators used to provide thrust
int32_t num_thrusters;
int32_t num_thrusters_x;

struct Int32Eulers stab_att_sp_euler;
struct Int32Quat stab_att_sp_quat;
Expand All @@ -205,9 +226,17 @@ bool indi_thrust_increment_set = false;

float g1g2_pseudo_inv[INDI_NUM_ACT][INDI_OUTPUTS];
float g2[INDI_NUM_ACT] = STABILIZATION_INDI_G2; //scaled by INDI_G_SCALING
#if INDI_OUTPUTS == 5
float g1[INDI_OUTPUTS][INDI_NUM_ACT] = {STABILIZATION_INDI_G1_ROLL,
STABILIZATION_INDI_G1_PITCH, STABILIZATION_INDI_G1_YAW,
STABILIZATION_INDI_G1_THRUST, STABILIZATION_INDI_G1_THRUST_X
};
#else
float g1[INDI_OUTPUTS][INDI_NUM_ACT] = {STABILIZATION_INDI_G1_ROLL,
STABILIZATION_INDI_G1_PITCH, STABILIZATION_INDI_G1_YAW, STABILIZATION_INDI_G1_THRUST
};
#endif

float g1g2[INDI_OUTPUTS][INDI_NUM_ACT];
float g1_est[INDI_OUTPUTS][INDI_NUM_ACT];
float g2_est[INDI_NUM_ACT];
Expand All @@ -233,10 +262,10 @@ void sum_g1_g2(void);
#include "modules/datalink/telemetry.h"
static void send_indi_g(struct transport_tx *trans, struct link_device *dev)
{
pprz_msg_send_INDI_G(trans, dev, AC_ID, INDI_NUM_ACT, g1_est[0],
INDI_NUM_ACT, g1_est[1],
INDI_NUM_ACT, g1_est[2],
INDI_NUM_ACT, g1_est[3],
pprz_msg_send_INDI_G(trans, dev, AC_ID, INDI_NUM_ACT, g1g2[0],
INDI_NUM_ACT, g1g2[1],
INDI_NUM_ACT, g1g2[2],
INDI_NUM_ACT, g1g2[3],
INDI_NUM_ACT, g2_est);
}

Expand Down Expand Up @@ -324,8 +353,14 @@ void stabilization_indi_init(void)

// Assume all non-servos are delivering thrust
num_thrusters = INDI_NUM_ACT;
num_thrusters_x = 0;
for (i = 0; i < INDI_NUM_ACT; i++) {
num_thrusters -= act_is_servo[i];
num_thrusters -= act_is_thruster_x[i];

num_thrusters_x += act_is_thruster_x[i];

act_is_thruster_z[i] = !act_is_servo[i] && !act_is_thruster_x[i];
}

#if PERIODIC_TELEMETRY
Expand Down Expand Up @@ -467,6 +502,36 @@ void stabilization_indi_set_stab_sp(struct StabilizationSetpoint *sp)
stab_att_ff_rates = stab_sp_to_rates_f(sp);
}

int checknan(char* var, float* d, int size) {
int good = 1;
for (int i=0; i<size; i++) {
if (isnan(d[i])) {
good = 0;
}
}
if (good == 0) {
printf("%s:",var);
for (int i=0;i<size;i++) {
printf("%f,",d[i]);
if (isnan(d[i])) {
d[i] = 0;
}
}
printf("\n");
return 1;
}
return 0;
}

void printmat(char* var, float* d, int size) {
printf(" -> %s:",var);
for (int i=0;i<size;i++) {
printf("%f,",d[i]);
}
printf("\n");
}


/**
* @param att_err attitude error
* @param rate_control boolean that states if we are in rate control or attitude control
Expand All @@ -476,6 +541,14 @@ void stabilization_indi_set_stab_sp(struct StabilizationSetpoint *sp)
*/
void stabilization_indi_rate_run(struct FloatRates rate_sp, bool in_flight)
{
// Input protection
if (isnan(rate_sp.p) || isnan(rate_sp.q) || isnan(rate_sp.r)){
rate_sp.p = 0;
rate_sp.q = 0;
rate_sp.r = 0;
printf("RATE_SP-MAYDAY!\n");
}

/* Propagate the filter on the gyroscopes */
struct FloatRates *body_rates = stateGetBodyRates_f();
float rate_vect[3] = {body_rates->p, body_rates->q, body_rates->r};
Expand Down Expand Up @@ -536,6 +609,9 @@ void stabilization_indi_rate_run(struct FloatRates rate_sp, bool in_flight)
for (i = 0; i < INDI_NUM_ACT; i++) {
g2_times_du += g2[i] * indi_du[i];
}

checknan("G2", g2, INDI_NUM_ACT);

//G2 is scaled by INDI_G_SCALING to make it readable
g2_times_du = g2_times_du / INDI_G_SCALING;

Expand All @@ -554,15 +630,27 @@ void stabilization_indi_rate_run(struct FloatRates rate_sp, bool in_flight)
//update thrust command such that the current is correctly estimated
stabilization_cmd[COMMAND_THRUST] = 0;
for (i = 0; i < INDI_NUM_ACT; i++) {
stabilization_cmd[COMMAND_THRUST] += actuator_state[i] * -((int32_t) act_is_servo[i] - 1);
stabilization_cmd[COMMAND_THRUST] += actuator_state[i] * (int32_t) act_is_thruster_z[i];
}
stabilization_cmd[COMMAND_THRUST] /= num_thrusters;

#if INDI_OUTPUTS == 5
stabilization_cmd[COMMAND_THRUST_X] = 0;
for (i = 0; i < INDI_NUM_ACT; i++) {
stabilization_cmd[COMMAND_THRUST_X] += actuator_state[i] * (int32_t) act_is_thruster_x[i];
}
stabilization_cmd[COMMAND_THRUST_X] /= num_thrusters_x;
#endif

} else {
// incremental thrust
for (i = 0; i < INDI_NUM_ACT; i++) {
v_thrust.z +=
(stabilization_cmd[COMMAND_THRUST] - use_increment*actuator_state_filt_vect[i]) * Bwls[3][i];
#if INDI_OUTPUTS == 5
v_thrust.x +=
(stabilization_cmd[COMMAND_THRUST_X] - use_increment*actuator_state_filt_vect[i]) * Bwls[4][i];
#endif
}
}

Expand All @@ -571,6 +659,12 @@ void stabilization_indi_rate_run(struct FloatRates rate_sp, bool in_flight)
indi_v[1] = (angular_accel_ref.q - use_increment*angular_acceleration[1]);
indi_v[2] = (angular_accel_ref.r - use_increment*angular_acceleration[2] + g2_times_du);
indi_v[3] = v_thrust.z;
#if INDI_OUTPUTS == 5
indi_v[4] = v_thrust.x;
#endif

checknan("indi_v", indi_v, INDI_OUTPUTS);


#if STABILIZATION_INDI_ALLOCATION_PSEUDO_INVERSE
// Calculate the increment for each actuator
Expand All @@ -583,12 +677,47 @@ void stabilization_indi_rate_run(struct FloatRates rate_sp, bool in_flight)
#else
stabilization_indi_set_wls_settings(use_increment);

int16_t n_u = CA_N_U_INNER;
int16_t n_v = CA_N_V_INNER;

checknan("indi_du", indi_du, INDI_NUM_ACT);
checknan("indi_v", indi_v, INDI_OUTPUTS);
checknan("du_min_stab_indi", du_min_stab_indi, INDI_OUTPUTS);
checknan("du_max_stab_indi", du_max_stab_indi, INDI_OUTPUTS);
for (int i=0;i<INDI_OUTPUTS;i++) {
checknan("Bwls[]", Bwls[i], INDI_NUM_ACT);
}
checknan("Wv", Wv, INDI_OUTPUTS);
checknan("indi_Wu", indi_Wu, INDI_NUM_ACT);
checknan("du_pref_stab_indi", du_pref_stab_indi, INDI_NUM_ACT);

// WLS Control Allocator
num_iter =
wls_alloc(indi_du, indi_v, du_min_stab_indi, du_max_stab_indi, Bwls, 0, 0, Wv, indi_Wu, du_pref_stab_indi, 10000, 10);
wls_alloc(indi_du, indi_v, du_min_stab_indi, du_max_stab_indi, Bwls, 0, 0, Wv, indi_Wu, du_pref_stab_indi, 10000, 10, n_u, n_v);
#endif


int c = checknan("p-indi_du", indi_du, INDI_NUM_ACT);
//good--;
if ((c>0) || (good < 0)) {
if (good < 0) {
printf("REGULAR GOOD:\n");
}
good = 512*30;
printmat("indi_du", indi_du, INDI_NUM_ACT);
printmat("indi_v", indi_v, INDI_OUTPUTS);
printmat("du_min_stab_indi", du_min_stab_indi, INDI_NUM_ACT);
printmat("du_max_stab_indi", du_max_stab_indi, INDI_NUM_ACT);
for (int i=0;i<INDI_OUTPUTS;i++) {
printmat("Bwls[]", Bwls[i], INDI_NUM_ACT);
}
printmat("Wv", Wv, INDI_OUTPUTS);
printmat("indi_Wu", indi_Wu, INDI_NUM_ACT);
printmat("du_pref_stab_indi", du_pref_stab_indi, INDI_NUM_ACT);
}
checknan("p-actuator_state_filt_vect", actuator_state_filt_vect, INDI_NUM_ACT);
checknan("p-indi_u", indi_u, INDI_NUM_ACT);

if (in_flight) {
// Add the increments to the actuators
float_vect_sum(indi_u, actuator_state_filt_vect, indi_du, INDI_NUM_ACT);
Expand Down Expand Up @@ -635,6 +764,8 @@ void stabilization_indi_rate_run(struct FloatRates rate_sp, bool in_flight)
actuators_pprz[i] = (int16_t) indi_u[i];
}

checknan("p-indi_u", indi_u, INDI_NUM_ACT);

// Set the stab_cmd to 42 to indicate that it is not used
stabilization_cmd[COMMAND_ROLL] = 42;
stabilization_cmd[COMMAND_PITCH] = 42;
Expand Down Expand Up @@ -973,6 +1104,12 @@ static void thrust_cb(uint8_t UNUSED sender_id, struct FloatVect3 thrust_increme
{
indi_thrust_increment = thrust_increment;
indi_thrust_increment_set = true;
if (isnan(thrust_increment.x) || isnan(thrust_increment.y) || isnan(thrust_increment.z)) {
thrust_increment.x = 0;
thrust_increment.y = 0;
thrust_increment.z = 0;
printf("THRUST-MAYDAY!\n");
}
}

static void bound_g_mat(void)
Expand Down
Expand Up @@ -34,6 +34,7 @@ extern struct Int32Quat stab_att_sp_quat; ///< with #INT32_QUAT_FRAC
extern struct Int32Eulers stab_att_sp_euler; ///< with #INT32_ANGLE_FRAC
extern float g1g2[INDI_OUTPUTS][INDI_NUM_ACT];
extern float actuator_state_filt_vect[INDI_NUM_ACT];
extern bool act_is_servo[INDI_NUM_ACT];

extern bool indi_use_adaptive;

Expand All @@ -42,6 +43,14 @@ extern float du_max_stab_indi[INDI_NUM_ACT];
extern float du_pref_stab_indi[INDI_NUM_ACT];
extern float *Bwls[INDI_OUTPUTS];

extern int checknan(char* var, float* d, int size);
extern void printmat(char* var, float* d, int size);

extern float thrust_bx_eff;
extern float thrust_bx_act_dyn;
extern float actuator_thrust_bx_pprz;
extern float thrust_bx_state_filt;

extern float act_pref[INDI_NUM_ACT];

extern float indi_Wu[INDI_NUM_ACT];
Expand Down
119 changes: 90 additions & 29 deletions sw/airborne/firmwares/rotorcraft/stabilization/wls/wls_alloc.c
Expand Up @@ -49,15 +49,38 @@ void print_final_values(int n_u, int n_v, float* u, float** B, float* v, float*
void print_in_and_outputs(int n_c, int n_free, float** A_free_ptr, float* d, float* p_free);

// provide loop feedback
#ifndef WLS_VERBOSE
#define WLS_VERBOSE FALSE
#endif

// Problem size needs to be predefined to avoid having to use VLAs
#ifndef CA_N_V
#error CA_N_V needs to be defined!
#ifdef CA_N_V_INNER
#ifdef CA_N_V_OUTER

#if CA_N_V_INNER > CA_N_V_OUTER
#define CA_N_V CA_N_V_INNER
#else
#define CA_N_V CA_N_V_OUTER
#endif

#ifndef CA_N_U
#error CA_N_U needs to be defined!
#if CA_N_U_INNER > CA_N_U_OUTER
#define CA_N_U CA_N_U_INNER
#else
#define CA_N_U CA_N_U_OUTER
#endif

#else
#define CA_N_V CA_N_V_INNER
#define CA_N_U CA_N_U_INNER
#endif

#elif CA_N_U_OUTER
#define CA_N_V CA_N_V_OUTER
#define CA_N_U CA_N_U_OUTER
#else

#error CA_N_V_INNER or CA_N_V_OUTER needs to be defined!

#endif

#define CA_N_C (CA_N_U+CA_N_V)
Expand All @@ -84,6 +107,29 @@ void qr_solve_wrapper(int m, int n, float** A, float* b, float* x) {
qr_solve(m, n, in, b, x);
}

#if WLS_VERBOSE

void do_print(char* txt, float* d, int c) {
printf("\t%s[%d] = {%f", txt, c, d[0]);
for (int i = 1; i<c; i++) {
printf(",%f",d[i]);
}
printf("};\n");
}

void do_print_mat(char* txt, float** d, int u, int v) {
printf(" * %s[%d][%d]:\n", txt, u, v);
for (int i = 0; i<v; i++) {
printf("{%f",d[i][0]);
for (int j = 1; j<u; j++) {
printf(",%f",d[i][j]);
}
printf("}\n");
}
}

#endif

/**
* @brief active set algorithm for control allocation
*
Expand Down Expand Up @@ -111,14 +157,12 @@ void qr_solve_wrapper(int m, int n, float** A, float* b, float* x) {
*/
int wls_alloc(float* u, float* v, float* umin, float* umax, float** B,
float* u_guess, float* W_init, float* Wv, float* Wu, float* up,
float gamma_sq, int imax) {
float gamma_sq, int imax, int n_u, int n_v) {
// allocate variables, use defaults where parameters are set to 0
if(!gamma_sq) gamma_sq = 100000;
if(!imax) imax = 100;

int n_c = CA_N_C;
int n_u = CA_N_U;
int n_v = CA_N_V;
int n_c = n_u + n_v;

float A[CA_N_C][CA_N_U];
float A_free[CA_N_C][CA_N_U];
Expand Down Expand Up @@ -170,6 +214,19 @@ int wls_alloc(float* u, float* v, float* umin, float* umax, float** B,
}
}

#if WLS_VERBOSE

printf("================== WLS ==========================\n u=%d, v=%d\n",n_u,n_v);
do_print("u_min", umin, n_u);
do_print("u_max", umax, n_u);
do_print("u", u, n_u);
do_print("u_pref", up, n_u);
do_print("Wu", Wu, n_u);
do_print("Wv", Wv, n_v);
do_print_mat("B", B, n_u, n_v);

#endif

// fill up A, A_free, b and d
for (int i = 0; i < n_v; i++) {
// If Wv is a NULL pointer, use Wv = identity
Expand Down Expand Up @@ -205,7 +262,10 @@ int wls_alloc(float* u, float* v, float* umin, float* umax, float** B,
}


if (n_free) {
// Count the infeasible free actuators
n_infeasible = 0;

if (n_free > 0) {
// Still free variables left, calculate corresponding solution

// use a solver to find the solution to A_free*p_free = d
Expand All @@ -216,18 +276,15 @@ int wls_alloc(float* u, float* v, float* umin, float* umax, float** B,
print_in_and_outputs(n_c, n_free, A_free_ptr, d, p_free);
#endif

}
// Set the nonzero values of p and add to u_opt
for (int i = 0; i < n_free; i++) {
p[free_index[i]] = p_free[i];
u_opt[free_index[i]] += p_free[i];

// Set the nonzero values of p and add to u_opt
for (int i = 0; i < n_free; i++) {
p[free_index[i]] = p_free[i];
u_opt[free_index[i]] += p_free[i];
}
// check limits
n_infeasible = 0;
for (int i = 0; i < n_u; i++) {
if (u_opt[i] >= (umax[i] + 0.01) || u_opt[i] <= (umin[i] - 0.01)) {
infeasible_index[n_infeasible++] = i;
// check limits
if ( (u_opt[free_index[i]] > umax[free_index[i]] || u_opt[free_index[i]] < umin[free_index[i]])) {
infeasible_index[n_infeasible++] = free_index[i];
}
}
}

Expand Down Expand Up @@ -272,19 +329,22 @@ int wls_alloc(float* u, float* v, float* umin, float* umax, float** B,
return iter;
}
} else {
float alpha = INFINITY;
// scaling back actuator command (0-1)
float alpha = 1.0;
float alpha_tmp;
int id_alpha = 0;
int id_alpha = free_index[0];

// find the lowest distance from the limit among the free variables
for (int i = 0; i < n_free; i++) {
int id = free_index[i];
if(fabs(p[id]) > FLT_EPSILON) {
alpha_tmp = (p[id] < 0) ? (umin[id] - u[id]) / p[id]
: (umax[id] - u[id]) / p[id];
} else {
alpha_tmp = INFINITY;

alpha_tmp = (p[id] < 0) ? (umin[id] - u[id]) / p[id]
: (umax[id] - u[id]) / p[id];

if (isnan(alpha_tmp) || alpha_tmp < 0.f) {
alpha_tmp = 1.0f;
}

if (alpha_tmp < alpha) {
alpha = alpha_tmp;
id_alpha = id;
Expand All @@ -294,6 +354,7 @@ int wls_alloc(float* u, float* v, float* umin, float* umax, float** B,
// update input u = u + alpha*p
for (int i = 0; i < n_u; i++) {
u[i] += alpha * p[i];
Bound(u[i],umin[i],umax[i]);
}
// update d = d-alpha*A*p_free
for (int i = 0; i < n_c; i++) {
Expand All @@ -310,8 +371,8 @@ int wls_alloc(float* u, float* v, float* umin, float* umax, float** B,
free_index_lookup[id_alpha] = -1;
}
}
// solution failed, return negative one to indicate failure
return iter;
// solution failed, return negative iteration count to indicate failure
return -iter;
}

#if WLS_VERBOSE
Expand Down
Expand Up @@ -58,4 +58,4 @@ void qr_solve_wrapper(int m, int n, float** A, float* b, float* x);
*/
int wls_alloc(float* u, float* v, float* umin, float* umax, float** B,
float* u_guess, float* W_init, float* Wv, float* Wu,
float* ud, float gamma, int imax);
float* ud, float gamma, int imax, int n_u, int n_v);
518 changes: 518 additions & 0 deletions sw/airborne/modules/ctrl/ctrl_eff_sched_rot_wing_v3b.c

Large diffs are not rendered by default.

59 changes: 59 additions & 0 deletions sw/airborne/modules/ctrl/ctrl_eff_sched_rot_wing_v3b.h
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2022 D.C. van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
*
* 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
* <http://www.gnu.org/licenses/>.
*/

/** @file "modules/ctrl/ctrl_eff_sched_rot_wing_v3b.h"
* @author D.C. van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
* Crtl effectiveness scheduler for thr rotating wing drone V3
*/

#ifndef CTRL_EFF_SCHED_ROT_WING_V3B_H
#define CTRL_EFF_SCHED_ROT_WING_V3B_H

#include "std.h"

// Define settings
extern float lift_d_multiplier;
extern float g1_p_multiplier;
extern float g1_q_multiplier;
extern float g1_r_multiplier;
extern float g1_t_multiplier;

extern float pitch_angle_set;
extern float pitch_angle_range;

extern float rot_wing_aerodynamic_eff_const_g1_p[1];
extern float rot_wing_aerodynamic_eff_const_g1_q[1];
extern float rot_wing_aerodynamic_eff_const_g1_r[1];

extern bool wing_rotation_sched_activated;
extern bool pusher_sched_activated;

extern float pitch_priority_factor;
extern float roll_priority_factor;
extern float thrust_priority_factor;
extern float pusher_priority_factor;

extern bool hover_motors_active;
extern bool bool_disable_hover_motors;

extern void init_eff_scheduling(void);
extern void event_eff_scheduling(void);

#endif // CTRL_EFF_SCHED_ROT_WING_V3_H
2 changes: 1 addition & 1 deletion sw/airborne/modules/datalink/mavlink.c
Expand Up @@ -500,7 +500,7 @@ static void mavlink_send_heartbeat(struct transport_tx *trans, struct link_devic
}
mavlink_msg_heartbeat_send(MAVLINK_COMM_0,
mav_type,
MAV_AUTOPILOT_PPZ,
MAV_AUTOPILOT_ARDUPILOTMEGA,
mav_mode,
0, // custom_mode
mav_state);
Expand Down
1,559 changes: 1,559 additions & 0 deletions sw/airborne/modules/meteo/ekf_aw.cpp

Large diffs are not rendered by default.

145 changes: 145 additions & 0 deletions sw/airborne/modules/meteo/ekf_aw.h
@@ -0,0 +1,145 @@
#ifndef EKF_AW_H
#define EKF_AW_H

#ifdef __cplusplus
extern "C" {
#endif

#include "std.h"
#include "math/pprz_algebra_float.h"
#include "math/pprz_geodetic_float.h"

// Settings
struct ekfAwParameters {
// Q
float Q_accel; ///< accel process noise
float Q_gyro; ///< gyro process noise
float Q_mu; ///< wind process noise
float Q_k; ///< offset process noise

// R
float R_V_gnd; ///< speed measurement noise
float R_accel_filt[3]; ///< filtered accel measurement noise
float R_V_pitot; ///< airspeed measurement noise

// Model Based Parameters
float vehicle_mass;

// X Axis
float k_fx_drag[2]; // Temporary setting for fuselage + hover prop
float k_fx_fuselage[4];
float k_fx_hover[3];
float k_fx_wing[5];
float k_fx_push[3];
float k_fx_elev[3];

// Y Axis
float k_fy_beta;
float k_fy_v;
float k_fy_wing[5];

// Z Axis
float k_fz_fuselage[4];
float k_fz_wing[4];
float k_fz_hover[5];
float k_fz_elev[2];

// Other options
bool use_model[3];
bool use_pitot;
bool propagate_offset;
bool quick_convergence;
};

struct ekfHealth{
bool healthy;
uint16_t crashes_n;
};

extern struct ekfAwParameters ekf_aw_params;

// Init functions
extern void ekf_aw_init(void);
extern void ekf_aw_reset(void);

// Filtering functions
extern void ekf_aw_propagate(struct FloatVect3 *acc,struct FloatRates *gyro, struct FloatEulers *euler, float *pusher_RPM,float *hover_RPM_array, float *skew, float *elevator_angle, struct FloatVect3 * V_gnd, struct FloatVect3 *acc_filt, float *V_pitot,float dt);

// Getter/Setter functions
extern struct NedCoor_f ekf_aw_get_speed_body(void);
extern struct NedCoor_f ekf_aw_get_wind_ned(void);
extern struct NedCoor_f ekf_aw_get_offset(void);
extern struct FloatVect3 ekf_aw_get_innov_V_gnd(void);
extern struct FloatVect3 ekf_aw_get_innov_accel_filt(void);
extern float ekf_aw_get_innov_V_pitot(void);
extern void ekf_aw_get_meas_cov(float meas_cov[7]);
extern void ekf_aw_get_state_cov(float state_cov[9]);
extern void ekf_aw_get_process_cov(float process_cov[9]);
extern void ekf_aw_get_fuselage_force(float force[3]);
extern void ekf_aw_get_wing_force(float force[3]);
extern void ekf_aw_get_elevator_force(float force[3]);
extern void ekf_aw_get_hover_force(float force[3]);
extern void ekf_aw_get_pusher_force(float force[3]);
extern struct ekfAwParameters *ekf_aw_get_param_handle(void);

extern void ekf_aw_set_speed_body(struct NedCoor_f *s);
extern void ekf_aw_set_wind(struct NedCoor_f *s);
extern void ekf_aw_set_offset(struct NedCoor_f *s);
extern struct ekfHealth ekf_aw_get_health(void);

// Settings handlers
extern void ekf_aw_update_params(void);
extern void ekf_aw_reset_health(void);


// Handlers
#define ekf_aw_update_Q_accel(_v) { \
ekf_aw_params.Q_accel = _v; \
ekf_aw_update_params(); \
}

#define ekf_aw_update_Q_gyro(_v) { \
ekf_aw_params.Q_gyro = _v; \
ekf_aw_update_params(); \
}

#define ekf_aw_update_Q_mu(_v) { \
ekf_aw_params.Q_mu = _v; \
ekf_aw_update_params(); \
}

#define ekf_aw_update_Q_k(_v) { \
ekf_aw_params.Q_k = _v; \
ekf_aw_update_params(); \
}

#define ekf_aw_update_R_V_gnd(_v) { \
ekf_aw_params.R_V_gnd = _v; \
ekf_aw_update_params(); \
}

#define ekf_aw_update_R_accel_filt_x(_v) { \
ekf_aw_params.R_accel_filt[0] = _v; \
ekf_aw_update_params(); \
}

#define ekf_aw_update_R_accel_filt_y(_v) { \
ekf_aw_params.R_accel_filt[1] = _v; \
ekf_aw_update_params(); \
}

#define ekf_aw_update_R_accel_filt_z(_v) { \
ekf_aw_params.R_accel_filt[2] = _v; \
ekf_aw_update_params(); \
}

#define ekf_aw_update_R_V_pitot(_v) { \
ekf_aw_params.R_V_pitot = _v; \
ekf_aw_update_params(); \
}

#ifdef __cplusplus
}
#endif

#endif /* EKF_AW_H */
482 changes: 482 additions & 0 deletions sw/airborne/modules/meteo/ekf_aw_wrapper.c

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions sw/airborne/modules/meteo/ekf_aw_wrapper.h
@@ -0,0 +1,118 @@
#ifndef EKF_AW_WRAPPER_H
#define EKF_AW_WRAPPER_H

#include "std.h"
#include "math/pprz_algebra_int.h"
#include "math/pprz_algebra_float.h"
#include "math/pprz_geodetic_float.h"
#include "modules/meteo/ekf_aw.h"


// EKF structure
struct ekfAw {

// States
struct NedCoor_f wind;
struct NedCoor_f V_body;
struct NedCoor_f offset;

// Inputs
struct FloatVect3 acc; ///< Last accelerometer measurements
struct FloatRates gyro; ///< Last gyroscope measurements
struct FloatEulers euler; /// Euler angles

#define EKF_AW_RPM_HOVER_NUM 4
int32_t last_RPM_hover[EKF_AW_RPM_HOVER_NUM]; // Value obtained from ABI Callback
int32_t last_RPM_pusher; // Value obtained from ABI Callback
float RPM_hover[EKF_AW_RPM_HOVER_NUM]; /// Hover motor RPM
float RPM_pusher; /// Pusher motor RPM
float skew; /// Skew
float elevator_angle;


// Measurements
struct FloatVect3 Vg_NED; /// Ground Speed
struct FloatVect3 acc_filt; ///< Last accelerometer measurements
float V_pitot; /// Pitot tube airspeed

// Innovation
struct FloatVect3 innov_V_gnd;
struct FloatVect3 innov_acc_filt;
float innov_V_pitot;

// Covariance
float meas_cov[7];
float state_cov[9];
float process_cov[12];

// Forces
float fuselage_force[3];
float wing_force[3];
float elevator_force[3];
float hover_force[3];
float pusher_force[3];

// Other
bool reset;
bool in_air;
struct NedCoor_f wind_guess;
struct NedCoor_f offset_guess;
struct ekfHealth health;
uint64_t internal_clock;
uint64_t time_last_on_gnd;
uint64_t time_last_in_air;
bool override_start;
bool override_quick_convergence;

};

extern void ekf_aw_wrapper_init(void);
extern void ekf_aw_wrapper_periodic(void);
extern void ekf_aw_wrapper_fetch(void);

extern void set_in_air_status(bool);


extern float tau_filter_high;
extern float tau_filter_low;

extern struct ekfAw ekf_aw;

// Handlers
#define ekf_aw_wrapper_reset(_v) { \
ekf_aw.reset = false; \
ekf_aw_reset(); \
ekf_aw_reset_health(); \
}

#define ekf_aw_wrapper_set_wind_N(_v) { \
ekf_aw.wind_guess.x = _v; \
ekf_aw_set_wind(&ekf_aw.wind_guess); \
}

#define ekf_aw_wrapper_set_wind_E(_v) { \
ekf_aw.wind_guess.y = _v; \
ekf_aw_set_wind(&ekf_aw.wind_guess); \
}

#define ekf_aw_wrapper_set_wind_D(_v) { \
ekf_aw.wind_guess.z = _v; \
ekf_aw_set_wind(&ekf_aw.wind_guess); \
}

#define ekf_aw_wrapper_set_offset_x(_v) { \
ekf_aw.offset_guess.x = _v; \
ekf_aw_set_offset(&ekf_aw.offset_guess); \
}

#define ekf_aw_wrapper_set_offset_y(_v) { \
ekf_aw.offset_guess.y = _v; \
ekf_aw_set_offset(&ekf_aw.offset_guess); \
}

#define ekf_aw_wrapper_set_offset_z(_v) { \
ekf_aw.offset_guess.z = _v; \
ekf_aw_set_offset(&ekf_aw.offset_guess); \
}

#endif /* EKF_AW_WRAPPER_H */
1 change: 1 addition & 0 deletions sw/airborne/modules/radio_control/rc_datalink.h
Expand Up @@ -40,6 +40,7 @@
#define RADIO_YAW 2
#define RADIO_THROTTLE 3
#define RADIO_MODE 4
#define RADIO_AUX4 5

extern int8_t rc_dl_values[ RC_DL_NB_CHANNEL ];
extern volatile bool rc_dl_frame_available;
Expand Down
150 changes: 150 additions & 0 deletions sw/airborne/modules/rot_wing_drone/rot_wing_automation.c
@@ -0,0 +1,150 @@
/*
* Copyright (C) 2023 Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
*
* 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
* <http://www.gnu.org/licenses/>.
*/

/** @file "modules/rot_wing_drone/rot_wing_automation.c"
* @author Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
* Fucntions to automate the navigation and guidance of the rotating wing drone
*/

#include "modules/rot_wing_drone/rot_wing_automation.h"
#include "state.h"
#include "modules/datalink/telemetry.h"

#ifndef ROT_WING_AUTOMATION_TRANS_ACCEL
#define ROT_WING_AUTOMATION_TRANS_ACCEL 1.0
#endif

#ifndef ROT_WING_AUTOMATION_TRANS_DECEL
#define ROT_WING_AUTOMATION_TRANS_DECEL 0.5
#endif

#ifndef ROT_WING_AUTOMATION_TRANS_LENGTH
#define ROT_WING_AUTOMATION_TRANS_LENGTH 200.0
#endif

#ifndef ROT_WING_AUTOMATION_TRANS_AIRSPEED
#define ROT_WING_AUTOMATION_TRANS_AIRSPEED 15.0
#endif

struct rot_wing_automation rot_wing_a;

// declare function
inline void update_waypoint_rot_wing_automation(uint8_t wp_id, struct FloatVect3 * target_ned);

void init_rot_wing_automation(void)
{
rot_wing_a.trans_accel = ROT_WING_AUTOMATION_TRANS_ACCEL;
rot_wing_a.trans_decel = ROT_WING_AUTOMATION_TRANS_DECEL;
rot_wing_a.trans_length = ROT_WING_AUTOMATION_TRANS_LENGTH;
rot_wing_a.trans_airspeed = ROT_WING_AUTOMATION_TRANS_AIRSPEED;
rot_wing_a.transitioned = false;
}

// periodic function
void periodic_rot_wing_automation(void)
{
float airspeed = stateGetAirspeed_f();
if (airspeed > rot_wing_a.trans_airspeed)
{
rot_wing_a.transitioned = true;
} else {
rot_wing_a.transitioned = false;
}
}

// Update a waypoint such that you can see on the GCS where the drone wants to go
void update_waypoint_rot_wing_automation(uint8_t wp_id, struct FloatVect3 * target_ned) {

// Update the waypoint
struct EnuCoor_f target_enu;
ENU_OF_TO_NED(target_enu, *target_ned);
waypoint_set_enu(wp_id, &target_enu);

// Send waypoint update every half second
RunOnceEvery(100/2, {
// Send to the GCS that the waypoint has been moved
DOWNLINK_SEND_WP_MOVED_ENU(DefaultChannel, DefaultDevice, &wp_id,
&waypoints[wp_id].enu_i.x,
&waypoints[wp_id].enu_i.y,
&waypoints[wp_id].enu_i.z);
} );
}

// Function to visualize from flightplan, call repeadely
void rot_wing_vis_transition(uint8_t wp_transition_id, uint8_t wp_decel_id, uint8_t wp_end_id)
{
//state eulers in zxy order
struct FloatEulers eulers_zxy;

// get heading and cos and sin of heading
float_eulers_of_quat_zxy(&eulers_zxy, stateGetNedToBodyQuat_f());
float psi = eulers_zxy.psi;
float cpsi = cosf(psi);
float spsi = sinf(psi);

// get airspeed
float airspeed = stateGetAirspeed_f();
// get groundspeed
float ground_speed = stateGetHorizontalSpeedNorm_f();

// get drone position
struct NedCoor_f *drone_pos = stateGetPositionNed_f();

// Move reference waypoints
// Move end transition waypoint at the correct length
struct FloatVect3 end_transition_rel_pos;
VECT3_COPY(end_transition_rel_pos, *drone_pos);
end_transition_rel_pos.x = cpsi * rot_wing_a.trans_length;
end_transition_rel_pos.y = spsi * rot_wing_a.trans_length;
struct FloatVect3 end_transition_pos;
VECT3_SUM(end_transition_pos, end_transition_rel_pos, *drone_pos);
end_transition_pos.z = drone_pos->z;
update_waypoint_rot_wing_automation(wp_end_id, &end_transition_pos);

// Move transition waypoint
float airspeed_error = rot_wing_a.trans_airspeed - airspeed;
float transition_time = airspeed_error / rot_wing_a.trans_accel;
float average_ground_speed = ground_speed + airspeed_error / 2.;
float transition_distance = average_ground_speed * transition_time;

struct FloatVect3 transition_rel_pos;
VECT3_COPY(transition_rel_pos, *drone_pos);
transition_rel_pos.x = cpsi * transition_distance;
transition_rel_pos.y = spsi * transition_distance;
struct FloatVect3 transition_pos;
VECT3_SUM(transition_pos, transition_rel_pos, *drone_pos);
transition_pos.z = drone_pos->z;
update_waypoint_rot_wing_automation(wp_transition_id, &transition_pos);

// Move decel point
float final_groundspeed = ground_speed + airspeed_error;
float decel_time = final_groundspeed / rot_wing_a.trans_decel;
float decel_distance = (final_groundspeed / 2.) * decel_time;
float decel_distance_from_drone = rot_wing_a.trans_length - decel_distance;

struct FloatVect3 decel_rel_pos;
VECT3_COPY(decel_rel_pos, *drone_pos);
decel_rel_pos.x = cpsi * decel_distance_from_drone;
decel_rel_pos.y = spsi * decel_distance_from_drone;
struct FloatVect3 decel_pos;
VECT3_SUM(decel_pos, decel_rel_pos, *drone_pos);
decel_pos.z = drone_pos->z;
update_waypoint_rot_wing_automation(wp_decel_id, &decel_pos);
}
46 changes: 46 additions & 0 deletions sw/airborne/modules/rot_wing_drone/rot_wing_automation.h
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2023 Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
*
* 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
* <http://www.gnu.org/licenses/>.
*/

/** @file "modules/rot_wing_drone/rot_wing_automation.h"
* @author Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
* Fucntions to automate the navigation and guidance of the rotating wing drone
*/

#ifndef ROT_WING_AUTOMATION_H
#define ROT_WING_AUTOMATION_H

#include "std.h"
#include "math/pprz_algebra_float.h"

extern void init_rot_wing_automation(void);
extern void periodic_rot_wing_automation(void);
extern void rot_wing_vis_transition(uint8_t wp_transition_id, uint8_t wp_decel_id, uint8_t wp_end_id);

struct rot_wing_automation {
float trans_accel;
float trans_decel;
float trans_length;
float trans_airspeed;
bool transitioned;
};

extern struct rot_wing_automation rot_wing_a;

#endif // ROT_WING_AUTOMATION_H
393 changes: 393 additions & 0 deletions sw/airborne/modules/rot_wing_drone/rotwing_state.c
@@ -0,0 +1,393 @@
/*
* Copyright (C) 2023 Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
*
* 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
* <http://www.gnu.org/licenses/>.
*/

/** @file "modules/rotwing/rotwing_state.c"
* @author Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
* This module keeps track of the current state of a rotating wing drone and desired state set by the RC or flightplan. Paramters are being scheduled in each change of a current state and desired state. Functions are defined in this module to call the actual state and desired state and set a desired state.
*/

#include "modules/rot_wing_drone/rotwing_state.h"
#include "firmwares/rotorcraft/stabilization/stabilization_indi.h"
#include "firmwares/rotorcraft/guidance/guidance_indi_hybrid.h"
#include "firmwares/rotorcraft/autopilot_firmware.h"
#include "modules/rot_wing_drone/wing_rotation_controller_v3b.h"
#include "modules/actuators/actuators.h"
#include "modules/core/abi.h"

struct RotwingState rotwing_state;

// Quad state identification
#ifndef ROTWING_MIN_SKEW_ANGLE_DEG_QUAD
#define ROTWING_MIN_SKEW_ANGLE_DEG_QUAD 10.0
#endif
#ifndef ROTWING_MIN_SKEW_ANGLE_COUNTER
#define ROTWING_MIN_SKEW_ANGLE_COUNTER 10 // Minimum number of loops the skew angle is below ROTWING_MIN_SKEW_ANGLE_COUNTER
#endif

// Skewing state identification
#ifndef ROTWING_SKEWING_COUNTER
#define ROTWING_SKEWING_COUNTER 10 // Minimum number of loops the skew angle is in between QUAD and FW
#endif

// Half skew state identification
#ifndef ROTWING_HALF_SKEW_ANGLE_DEG
#define ROTWING_HALF_SKEW_ANGLE_DEG 55.0
#endif
#ifndef ROTWING_HALF_SKEW_ANGLE_RANG
#define ROTWING_HALF_SKEW_ANGLE_HALF_RANGE 10.0
#endif
#ifndef ROTWING_HALF_SKEW_COUNTER
#define ROTWING_HALF_SKEW_COUNTER 10 // Minimum number of loops the skew angle is at HALF_SKEW_ANGLE_DEG +/- ROTWING_HALF_SKEW_ANGLE_HALF_RANGE to trigger ROTWING_HALF_SKEW_ANGLE state
#endif

// FW state identification
#ifndef ROTWING_MIN_FW_SKEW_ANGLE_DEG
#define ROTWING_MIN_FW_SKEW_ANGLE_DEG 80.0 // Minimum wing angle to fly in fixed wing state
#endif
#ifndef ROTWING_MIN_FW_COUNTER
#define ROTWING_MIN_FW_COUNTER 10 // Minimum number of loops the skew angle is above the MIN_FW_SKEW_ANGLE
#endif

// FW idle state identification
#ifndef ROTWING_MIN_THRUST_IDLE
#define ROTWING_MIN_THRUST_IDLE 100
#endif
#ifndef ROTWING_MIN_THRUST_IDLE_COUNTER
#define ROTWING_MIN_THRUST_IDLE_COUNTER 10
#endif

// FW hov mot off state identification
#ifndef ROTWING_HOV_MOT_OFF_RPM_TH
#define ROTWING_HOV_MOT_OFF_RPM_TH 50
#endif
#ifndef ROTWING_HOV_MOT_OFF_COUNTER
#define ROTWING_HOV_MOT_OFF_COUNTER 10
#endif

#ifndef ROTWING_STATE_RPM_ID
#define ROTWING_STATE_RPM_ID ABI_BROADCAST
#endif
abi_event rotwing_state_rpm_ev;
static void rotwing_state_rpm_cb(uint8_t sender_id, struct rpm_act_t * rpm_message, uint8_t num_act);
#define ROTWING_STATE_NUM_HOVER_RPM 4
int32_t rotwing_state_hover_rpm[ROTWING_STATE_NUM_HOVER_RPM] = {0, 0, 0, 0};

bool rotwing_state_force_quad = false;

uint8_t rotwing_state_hover_counter = 0;
uint8_t rotwing_state_skewing_counter = 0;
uint8_t rotwing_state_fw_counter = 0;
uint8_t rotwing_state_fw_idle_counter = 0;
uint8_t rotwing_state_fw_m_off_counter = 0;

inline void rotwing_check_set_current_state(void);
inline void rotwing_update_desired_state(void);
inline void rotwing_switch_state(void);

#if PERIODIC_TELEMETRY
#include "modules/datalink/telemetry.h"
static void send_rotating_wing_state(struct transport_tx *trans, struct link_device *dev)
{
pprz_msg_send_ROTATING_WING_STATE(trans, dev, AC_ID,
&rotwing_state.current_state,
&rotwing_state.desired_state,
&wing_rotation.wing_angle_deg,
&wing_rotation.wing_angle_deg_sp,
&wing_rotation.adc_wing_rotation,
&wing_rotation.servo_pprz_cmd);
}
#endif // PERIODIC_TELEMETRY

void init_rotwing_state(void)
{
// Bind ABI messages
AbiBindMsgRPM(ROTWING_STATE_RPM_ID, &rotwing_state_rpm_ev, rotwing_state_rpm_cb);

// Start the drone in a desired hover state
rotwing_state.current_state = ROTWING_STATE_HOVER;
rotwing_state.desired_state = ROTWING_STATE_HOVER;

#if PERIODIC_TELEMETRY
register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_ROTATING_WING_STATE, send_rotating_wing_state);
#endif
}

void periodic_rotwing_state(void)
{
// Check and set the current state
rotwing_check_set_current_state();

// Check and update desired state
rotwing_update_desired_state();

// Check difference with desired state
// rotwing_switch_state();

//TODO: incorparate motor active / disbaling depending on called flight state
// Switch on motors if flight mode is attitude
if (guidance_h.mode == GUIDANCE_H_MODE_ATTITUDE) {
bool_disable_hover_motors = false;
} else if (guidance_h.mode == GUIDANCE_H_MODE_FORWARD) {
bool_disable_hover_motors = false;
}
}

// Function to request a state
void request_rotwing_state(uint8_t state)
{
if (state == ROTWING_STATE_HOVER) {
rotwing_state.current_state = ROTWING_STATE_HOVER;
} else if (state == ROTWING_STATE_FW) {
rotwing_state.current_state = ROTWING_STATE_FW;
}
}

void rotwing_check_set_current_state(void)
{
// if !in_flight, set state to hover
if (!autopilot.in_flight) {
rotwing_state_hover_counter = 0;
rotwing_state_skewing_counter = 0;
rotwing_state_fw_counter = 0;
rotwing_state_fw_idle_counter = 0;
rotwing_state_fw_m_off_counter = 0;
rotwing_state.current_state = ROTWING_STATE_HOVER;
return;
}

// States can be checked according to wing angle sensor, setpoints .....
uint8_t prev_state = rotwing_state.current_state;
switch (prev_state) {
case ROTWING_STATE_HOVER:
// Check if state needs to be set to skewing
if (wing_rotation.wing_angle_deg > ROTWING_MIN_SKEW_ANGLE_DEG_QUAD)
{
rotwing_state_skewing_counter++;
} else {
rotwing_state_skewing_counter = 0;
}

// Switch state if necessary
if (rotwing_state_skewing_counter > ROTWING_MIN_SKEW_ANGLE_COUNTER) {
rotwing_state.current_state = ROTWING_STATE_SKEWING;
rotwing_state_skewing_counter = 0;
}
break;

case ROTWING_STATE_SKEWING:
// Check if state needs to be set to hover
if (wing_rotation.wing_angle_deg < ROTWING_MIN_SKEW_ANGLE_DEG_QUAD)
{
rotwing_state_hover_counter++;
} else {
rotwing_state_hover_counter = 0;
}

// Check if state needs to be set to fixed wing
if (wing_rotation.wing_angle_deg > ROTWING_MIN_FW_SKEW_ANGLE_DEG)
{
rotwing_state_fw_counter++;
} else {
rotwing_state_fw_counter = 0;
}

// Switch state if necessary
if (rotwing_state_hover_counter > ROTWING_MIN_SKEW_ANGLE_COUNTER) {
rotwing_state.current_state = ROTWING_STATE_HOVER;
rotwing_state_hover_counter = 0;
}

if (rotwing_state_fw_counter > ROTWING_MIN_FW_COUNTER) {
rotwing_state.current_state = ROTWING_STATE_FW;
rotwing_state_fw_counter = 0;
}
break;

case ROTWING_STATE_FW:
// Check if state needs to be set to skewing
if (wing_rotation.wing_angle_deg < ROTWING_MIN_FW_SKEW_ANGLE_DEG) {
rotwing_state_skewing_counter++;
} else {
rotwing_state_skewing_counter = 0;
}

// Check if state needs to be set to fixed wing with hover motors idle (If hover thrust below threshold)
if (stabilization_cmd[COMMAND_THRUST] < ROTWING_MIN_THRUST_IDLE) {
rotwing_state_fw_idle_counter++;
} else {
rotwing_state_fw_idle_counter = 0;
}

// Switch state if necessary
if (rotwing_state_skewing_counter > ROTWING_MIN_FW_COUNTER) {
rotwing_state.current_state = ROTWING_STATE_SKEWING;
rotwing_state_skewing_counter = 0;
rotwing_state_fw_idle_counter = 0;
} else if (rotwing_state_fw_idle_counter > ROTWING_MIN_THRUST_IDLE_COUNTER
&& rotwing_state_skewing_counter == 0) {
rotwing_state.current_state = ROTWING_STATE_FW_HOV_MOT_IDLE;
rotwing_state_skewing_counter = 0;
rotwing_state_fw_idle_counter = 0;
}
break;

case ROTWING_STATE_FW_HOV_MOT_IDLE:
// Check if state needs to be set to fixed wing with hover motors activated
if (stabilization_cmd[COMMAND_THRUST] > ROTWING_MIN_THRUST_IDLE) {
rotwing_state_fw_counter++;
} else {
rotwing_state_fw_counter = 0;
}

// Check if state needs to be set to fixed wing with hover motors off (if zero rpm on hover motors)
if (rotwing_state_hover_rpm[0] == 0
&& rotwing_state_hover_rpm[1] == 0
&& rotwing_state_hover_rpm[2] == 0
&& rotwing_state_hover_rpm[3] == 0) {
rotwing_state_fw_m_off_counter++;
} else {
rotwing_state_fw_m_off_counter = 0;
}

// Switch state if necessary
if (rotwing_state_fw_counter > ROTWING_MIN_THRUST_IDLE_COUNTER) {
rotwing_state.current_state = ROTWING_STATE_FW;
rotwing_state_fw_counter = 0;
rotwing_state_fw_m_off_counter = 0;
} else if (rotwing_state_fw_m_off_counter > ROTWING_HOV_MOT_OFF_COUNTER
&& rotwing_state_fw_counter == 0) {
rotwing_state.current_state = ROTWING_STATE_FW_HOV_MOT_OFF;
rotwing_state_fw_counter = 0;
rotwing_state_fw_m_off_counter = 0;
}
break;

case ROTWING_STATE_FW_HOV_MOT_OFF:
// Check if state needs to be set to fixed wing with hover motors idle (if rpm on hover motors)
if (rotwing_state_hover_rpm[0] > ROTWING_HOV_MOT_OFF_RPM_TH
&& rotwing_state_hover_rpm[1] > ROTWING_HOV_MOT_OFF_RPM_TH
&& rotwing_state_hover_rpm[2] > ROTWING_HOV_MOT_OFF_RPM_TH
&& rotwing_state_hover_rpm[3] > ROTWING_HOV_MOT_OFF_RPM_TH) {
rotwing_state_fw_idle_counter++;
} else {
rotwing_state_fw_idle_counter = 0;
}

// Switch state if necessary
if (rotwing_state_fw_counter > ROTWING_MIN_THRUST_IDLE_COUNTER) {
rotwing_state.current_state = ROTWING_STATE_FW_HOV_MOT_IDLE;
rotwing_state_fw_idle_counter = 0;
}
break;

default:
break;
}
}

void rotwing_update_desired_state(void)
{
// If force_forward and nav selected, then the desired state if FW with hover motors off;
switch (guidance_h.mode) {
case GUIDANCE_H_MODE_NAV:
if (rotwing_state_force_quad) {
rotwing_state.desired_state = ROTWING_STATE_HOVER;
} else if (force_forward) {
rotwing_state.desired_state = ROTWING_STATE_FW_HOV_MOT_OFF;
} else {
rotwing_state.desired_state = ROTWING_STATE_FREE;
}
break;

case GUIDANCE_H_MODE_FORWARD:
rotwing_state.desired_state = ROTWING_STATE_FW_HOV_MOT_OFF;
break;

case GUIDANCE_H_MODE_ATTITUDE:
rotwing_state.desired_state = ROTWING_STATE_HOVER;
break;

default:
rotwing_state.desired_state = ROTWING_STATE_FREE;
break;
}
}

// Function that handles settings for switching state(s)
void rotwing_switch_state(void)
{
switch (rotwing_state.current_state) {
case ROTWING_STATE_HOVER:
if (rotwing_state.desired_state > ROTWING_STATE_HOVER) {
// if in hover state, but higher state requested, switch on wing_rotation scheduler
wing_rotation.airspeed_scheduling = true;
} else {
// if hover state desired and at the state, fix wing in quad
wing_rotation.airspeed_scheduling = false;
wing_rotation.wing_angle_deg_sp = 0;
}
break;

case ROTWING_STATE_SKEWING:
if (rotwing_state.desired_state > ROTWING_STATE_SKEWING) {
// Keep wing rotation scheduler on if skewing to a higher state
wing_rotation.airspeed_scheduling = true;
} else {
// go back to quad state if fulfilling conditions for skewing back (motors on, skewing back not too fast).
}
break;

case ROTWING_STATE_FW:
break;

case ROTWING_STATE_FW_HOV_MOT_IDLE:
if (rotwing_state.desired_state > ROTWING_STATE_FW_HOV_MOT_IDLE) {
// Switch off hover motors if in idle and desired state is higher
bool_disable_hover_motors = true;
} else if (rotwing_state.desired_state < ROTWING_STATE_FW_HOV_MOT_IDLE) {
// Allow hover motors to generate thrust when desired state is lower than current state
hover_motors_active = true;
} else {
// if idle desired and already at idle, let motors spin idle
bool_disable_hover_motors = false;
hover_motors_active = false;
}
break;

case ROTWING_STATE_FW_HOV_MOT_OFF:
if (rotwing_state.desired_state < ROTWING_STATE_FW_HOV_MOT_OFF) {
// Switch on the hover motors when going to a lower state
bool_disable_hover_motors = false;
}
wing_rotation.airspeed_scheduling = false;
wing_rotation.wing_angle_deg_sp = 90;
break;
}
}

static void rotwing_state_rpm_cb(uint8_t __attribute__((unused)) sender_id, struct rpm_act_t UNUSED * rpm_message, uint8_t UNUSED num_act_message)
{
for (int i=0; i<num_act_message; i++) {
// Sanity check that index is valid
if (rpm_message[i].actuator_idx < ROTWING_STATE_NUM_HOVER_RPM){
rotwing_state_hover_rpm[rpm_message->actuator_idx] = rpm_message->rpm;
}
}
}
53 changes: 53 additions & 0 deletions sw/airborne/modules/rot_wing_drone/rotwing_state.h
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2023 Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
*
* 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
* <http://www.gnu.org/licenses/>.
*/

/** @file "modules/rotwing/rotwing_state.h"
* @author Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
* This module keeps track of the current state of a rotating wing drone and desired state set by the RC or flightplan. Paramters are being scheduled in each change of a current state and desired state. Functions are defined in this module to call the actual state and desired state and set a desired state.
*/

#ifndef ROTWING_STATE_H
#define ROTWING_STATE_H

#include "std.h"

/** Rotwing States
*/
#define ROTWING_STATE_HOVER 0 // Wing is skewed in 0 degrees (quad)
#define ROTWING_STATE_SKEWING 1 // WIng is skewing
#define ROTWING_STATE_FW 2 // Wing is skewed at 90 degrees (fixed wing), hover motors have full authority
#define ROTWING_STATE_FW_HOV_MOT_IDLE 3 // Wing is skewed at 90 degrees (fixed wing), hover motors are forced to idle
#define ROTWING_STATE_FW_HOV_MOT_OFF 4 // Wing is skewed at 90 degrees (fixed wubg), hover motors are switched off
#define ROTWING_STATE_FREE 5 // This is a desired state for which the controller has to decide the desired state itself

struct RotwingState {
uint8_t current_state;
uint8_t desired_state;
};

extern struct RotwingState rotwing_state;

extern bool rotwing_state_force_quad;

extern void init_rotwing_state(void);
extern void periodic_rotwing_state(void);
extern void request_rotwing_state(uint8_t state);

#endif // ROTWING_STATE_H
262 changes: 262 additions & 0 deletions sw/airborne/modules/rot_wing_drone/wing_rotation_controller_v3b.c
@@ -0,0 +1,262 @@
/*
* Copyright (C) 2022 Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
*
* 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
* <http://www.gnu.org/licenses/>.
*/

/** @file "modules/rot_wing_drone/wing_rotation_controller_v3b.c"
* @author Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
* Module to control wing rotation servo command based on prefered angle setpoint
*/

#include "modules/rot_wing_drone/wing_rotation_controller_v3b.h"
#include "modules/radio_control/radio_control.h"
#include "firmwares/rotorcraft/guidance/guidance_h.h"

#include "state.h"

#include <stdlib.h>
#include "mcu_periph/adc.h"

/*
#ifndef WING_ROTATION_SERVO_IDX
#error "No WING_ROTATION_SERVO_IDX defined"
#endif
*/

#if !USE_NPS

#ifndef ADC_CHANNEL_WING_ROTATION_POSITION
#define ADC_CHANNEL_WING_ROTATION_POSITION ADC_5
#endif

#ifndef ADC_CHANNEL_WING_ROTATION_POSITION_NB_SAMPLES
#define ADC_CHANNEL_WING_ROTATION_POSITION_NB_SAMPLES 16
#endif

#endif // !USE_NPS

#ifndef WING_ROTATION_POSITION_ADC_0
#error "No WING_ROTATION_POSITION_ADC_0 defined"
#endif

#ifndef WING_ROTATION_POSITION_ADC_90
#error "No WING_ROTATION_POSITION_ADC_90 defined"
#endif

#ifndef WING_ROTATION_FIRST_DYN
#define WING_ROTATION_FIRST_DYN 0.001
#endif

#ifndef WING_ROTATION_SECOND_DYN
#define WING_ROTATION_SECOND_DYN 0.003
#endif

// Parameters
struct wing_rotation_controller wing_rotation;

bool in_transition = false;

#if !USE_NPS
static struct adc_buf buf_wing_rot_pos;
#endif

#if USE_NPS
#include "modules/actuators/actuators.h"
#endif

// Inline functions
inline void wing_rotation_to_rad(void);
inline void wing_rotation_update_sp(void);
inline void wing_rotation_compute_pprz_cmd(void);

void wing_rotation_init(void)
{
// your init code here
#if !USE_NPS
adc_buf_channel(ADC_CHANNEL_WING_ROTATION_POSITION, &buf_wing_rot_pos, ADC_CHANNEL_WING_ROTATION_POSITION_NB_SAMPLES);
#endif

// Init wing_rotation_controller struct
wing_rotation.servo_pprz_cmd = 0;
wing_rotation.adc_wing_rotation = 0;
wing_rotation.wing_angle_rad = 0;
wing_rotation.wing_angle_deg = 0;
wing_rotation.wing_angle_rad_sp = 0;
wing_rotation.wing_angle_deg_sp = 0;
wing_rotation.wing_rotation_speed = 0;
wing_rotation.wing_angle_virtual_deg_sp = 45;
wing_rotation.wing_rotation_first_order_dynamics = WING_ROTATION_FIRST_DYN;
wing_rotation.wing_rotation_second_order_dynamics = WING_ROTATION_SECOND_DYN;
wing_rotation.adc_wing_rotation_range = WING_ROTATION_POSITION_ADC_90 - WING_ROTATION_POSITION_ADC_0;
wing_rotation.airspeed_scheduling = false;
wing_rotation.airspeed_scheduling_nav = false;
wing_rotation.force_rotation_angle = false;
wing_rotation.transition_forward = false;
wing_rotation.forward_airspeed = 18.;

// Set wing angle to current wing angle
wing_rotation.initialized = false;
wing_rotation.init_loop_count = 0;
}

void wing_rotation_periodic(void)
{
// your periodic code here.
// freq = 1.0 Hz

// After 5 loops, set current setpoint and enable wing_rotation
if (!wing_rotation.initialized) {
wing_rotation.init_loop_count += 1;
if (wing_rotation.init_loop_count > 4) {
wing_rotation.initialized = true;
wing_rotation.wing_angle_rad_sp = M_PI * 0.25;
wing_rotation.wing_angle_deg_sp = wing_rotation.wing_angle_rad_sp / M_PI * 180.;
}
}
}

void wing_rotation_event(void)
{
// First check if safety switch is triggered
#ifdef WING_ROTATION_RESET_RADIO_CHANNEL
// Update wing_rotation deg setpoint when RESET switch triggered
if (radio_control.values[WING_ROTATION_RESET_RADIO_CHANNEL] > 1750)
{
wing_rotation.airspeed_scheduling = false;
wing_rotation.wing_angle_deg_sp = 0;
}
#endif

if (guidance_h.mode == GUIDANCE_H_MODE_FORWARD)
{
set_wing_rotation_scheduler(true);
} else if (guidance_h.mode == GUIDANCE_H_MODE_ATTITUDE)
{
set_wing_rotation_scheduler(false);
} else if (guidance_h.mode == GUIDANCE_H_MODE_NAV) {
set_wing_rotation_scheduler(wing_rotation.airspeed_scheduling_nav);
}

if (!wing_rotation.airspeed_scheduling) {
wing_rotation.transition_forward = false;
}

// Update Wing position sensor
wing_rotation_to_rad();

// Run control if initialized
if (wing_rotation.initialized) {

if (wing_rotation.airspeed_scheduling)
{
float wing_angle_scheduled_sp_deg = 0;
float airspeed = stateGetAirspeed_f();
if (airspeed < 8) {
in_transition = false;
wing_angle_scheduled_sp_deg = 0;
} else if (airspeed < 10 && in_transition) {
wing_angle_scheduled_sp_deg = 55;
} else if (airspeed > 10) {
wing_angle_scheduled_sp_deg = ((airspeed - 10.)) / 4. * 35. + 55.;
in_transition = true;
} else {
wing_angle_scheduled_sp_deg = 0;
}

Bound(wing_angle_scheduled_sp_deg, 0., 90.)
wing_rotation.wing_angle_deg_sp = wing_angle_scheduled_sp_deg;

}

wing_rotation_update_sp();

//int32_t servo_pprz_cmd; // Define pprz cmd

// Control the wing rotation position.
wing_rotation_compute_pprz_cmd();
//servo_pprz_cmd = wing_rotation.wing_angle_deg_sp / 90 * MAX_PPRZ;
//Bound(servo_pprz_cmd, 0, MAX_PPRZ);

//wing_rotation.servo_pprz_cmd = servo_pprz_cmd;
}
}

void wing_rotation_to_rad(void)
{
#if !USE_NPS
wing_rotation.adc_wing_rotation = buf_wing_rot_pos.sum / buf_wing_rot_pos.av_nb_sample;

wing_rotation.wing_angle_deg = 0.00247111 * (float)wing_rotation.adc_wing_rotation - 25.635294;
wing_rotation.wing_angle_rad = wing_rotation.wing_angle_deg / 180. * M_PI;

#else
// Copy setpoint as actual angle in simulation
wing_rotation.wing_angle_deg = wing_rotation.wing_angle_virtual_deg_sp;
wing_rotation.wing_angle_rad = wing_rotation.wing_angle_virtual_deg_sp / 180. * M_PI;
#endif
}

void wing_rotation_update_sp(void)
{
wing_rotation.wing_angle_rad_sp = wing_rotation.wing_angle_deg_sp / 180. * M_PI;
}

void wing_rotation_compute_pprz_cmd(void)
{
float angle_error = wing_rotation.wing_angle_deg_sp - wing_rotation.wing_angle_virtual_deg_sp;
float speed_sp = wing_rotation.wing_rotation_first_order_dynamics * angle_error;
float speed_error = speed_sp - wing_rotation.wing_rotation_speed;
wing_rotation.wing_rotation_speed += wing_rotation.wing_rotation_second_order_dynamics * speed_error;
wing_rotation.wing_angle_virtual_deg_sp += wing_rotation.wing_rotation_speed;

#if !USE_NPS
int32_t servo_pprz_cmd; // Define pprz cmd
servo_pprz_cmd = (int32_t)(wing_rotation.wing_angle_virtual_deg_sp / 90. * (float)MAX_PPRZ);
Bound(servo_pprz_cmd, 0, MAX_PPRZ);

wing_rotation.servo_pprz_cmd = servo_pprz_cmd;
#else
int32_t servo_pprz_cmd; // Define pprz cmd
servo_pprz_cmd = (int32_t)(wing_rotation.wing_angle_deg_sp / 90. * (float)MAX_PPRZ);
Bound(servo_pprz_cmd, 0, MAX_PPRZ);

wing_rotation.servo_pprz_cmd = servo_pprz_cmd;
actuators_pprz[INDI_NUM_ACT] = servo_pprz_cmd;
#endif
}

// Function to call in flightplan to switch scheduler on or off
bool set_wing_rotation_scheduler(bool rotation_scheduler_on)
{
if (rotation_scheduler_on)
{
wing_rotation.airspeed_scheduling = true;
} else {
wing_rotation.airspeed_scheduling = false;
if (!wing_rotation.force_rotation_angle) {
wing_rotation.wing_angle_deg_sp = 0;
}
}
return false;
}

bool set_wing_rotation_scheduler_nav(bool rotation_scheduler_on)
{
wing_rotation.airspeed_scheduling_nav = rotation_scheduler_on;
return false;
}
66 changes: 66 additions & 0 deletions sw/airborne/modules/rot_wing_drone/wing_rotation_controller_v3b.h
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2022 Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
*
* 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
* <http://www.gnu.org/licenses/>.
*/

/** @file "modules/rot_wing_drone/wing_rotation_controller.h"
* @author Dennis van Wijngaarden <D.C.vanWijngaarden@tudelft.nl>
* Module to control wing rotation servo command based on prefered angle setpoint
*/

#ifndef WING_ROTATION_CONTROLLER_V3B_H
#define WING_ROTATION_CONTROLLER_V3B_H

#include "std.h"

extern void wing_rotation_init(void);
extern void wing_rotation_periodic(void);
extern void wing_rotation_event(void);
extern bool set_wing_rotation_scheduler(bool rotation_scheduler_on);
extern bool set_wing_rotation_scheduler_nav(bool rotation_scheduler_on);

// Paramaters
struct wing_rotation_controller {
int32_t servo_pprz_cmd;
uint16_t adc_wing_rotation;
int16_t adc_wing_rotation_range;
float wing_angle_rad;
float wing_angle_deg;
float wing_angle_rad_sp;
float wing_angle_deg_sp;
float wing_rotation_speed;
float wing_angle_virtual_deg_sp;
float wing_rotation_first_order_dynamics;
float wing_rotation_second_order_dynamics;
bool initialized;
uint8_t init_loop_count;
bool airspeed_scheduling;
bool airspeed_scheduling_nav;
bool force_rotation_angle;
bool transition_forward;
float forward_airspeed;
};

extern float wing_rotation_sched_as_1;
extern float wing_rotation_sched_as_2;
extern float wing_rotation_sched_as_3;
extern float wing_rotation_sched_as_4;

extern struct wing_rotation_controller wing_rotation;

#endif // WING_ROTATION_CONTROLLER_V3B_H
2 changes: 1 addition & 1 deletion sw/ext/Makefile
Expand Up @@ -33,7 +33,7 @@ EXT_DIR=$(PAPARAZZI_SRC)/sw/ext
include $(PAPARAZZI_SRC)/conf/Makefile.arm-embedded-toolchain


all: libopencm3 luftboot chibios fatfs libsbp TRICAL hacl-c key_generator rustlink ecl matrix
all: libopencm3 luftboot chibios fatfs libsbp TRICAL hacl-c key_generator rustlink ecl matrix mavlink

# update (and init if needed) all submodules
update_submodules:
Expand Down
11 changes: 7 additions & 4 deletions sw/ground_segment/python/rot_wing_mon/rot_wing_viewer.py
Expand Up @@ -46,8 +46,8 @@ def __init__(self, msg):
self.rpm = float(msg['rpm'])
self.volt_b = float(msg['bat_volts'])
self.volt_m = float(msg['motor_volts'])
self.temperature = float(msg['temperature']) - 273.15
self.temperature_dev = float(msg['temperature_dev']) - 273.15
self.temperature = float(msg['temperature'])
self.temperature_dev = float(msg['temperature_dev'])
self.energy = float(msg['energy'])

def get_current(self):
Expand Down Expand Up @@ -140,7 +140,7 @@ def message_recv(self, ac_id, msg):
self.motors.fill_from_esc_msg(self.esc)
wx.CallAfter(self.update)

if msg.name == "STAB_ATTITUDE_FULL_INDI":
if msg.name == "STAB_ATTITUDE_INDI":
self.indi = INDIMessage(msg)
wx.CallAfter(self.update)

Expand Down Expand Up @@ -243,7 +243,10 @@ def OnPaint(self, e):
self.StatusBox(dc, dx, dy, 3, 0, m.get_temp(), m.get_temp_perc(), m.get_temp_color())
self.StatusBox(dc, dx, dy, 4, 0, m.get_temp_dev(), m.get_temp_dev_perc(), m.get_temp_dev_color())
try:
self.StatusBox(dc, dx, dy, 5, 0, self.indi.get_u(m.id), self.indi.get_u_perc(m.id), self.indi.get_u_color(m.id))
if m.id == 4:
self.StatusBox(dc, dx, dy, 5, 0, self.indi.get_u(8), self.indi.get_u_perc(8), self.indi.get_u_color(8))
else:
self.StatusBox(dc, dx, dy, 5, 0, self.indi.get_u(m.id), self.indi.get_u_perc(m.id), self.indi.get_u_color(m.id))
except:
pass

Expand Down
Expand Up @@ -478,7 +478,7 @@ def message_recv(self, ac_id, msg):
self.ahrs_ref_quat = AHRSRefQuatMessage(msg)
self.update_orientation_from_quat()

if msg.name == "ROT_WING_CONTROLLER":
if msg.name == "ROTATING_WING_STATE":
self.wing_rotation_controller = RotWingControllerMessage(msg)

if msg.name == "ACTUATORS":
Expand Down