diff --git a/conf/airframes/ENAC/hybrid/ranger_1.xml b/conf/airframes/ENAC/hybrid/ranger_1.xml new file mode 100644 index 0000000000..61f724f340 --- /dev/null +++ b/conf/airframes/ENAC/hybrid/ranger_1.xml @@ -0,0 +1,356 @@ + + + + + + HEEWING Ranger T1 + Hybrid Quad/Plane with tilt motors + Tawaki v1.0 Chibios + Xbee API + Ublox + SBUS Futaba + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + +
+ +
+ + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +
+ +
diff --git a/conf/autopilot/heewing_vtol.xml b/conf/autopilot/heewing_vtol.xml new file mode 100644 index 0000000000..4958471e78 --- /dev/null +++ b/conf/autopilot/heewing_vtol.xml @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/modules/control_mixing_heewing.xml b/conf/modules/control_mixing_heewing.xml new file mode 100644 index 0000000000..89c499419d --- /dev/null +++ b/conf/modules/control_mixing_heewing.xml @@ -0,0 +1,23 @@ + + + + Control mixing specific to the Heewing T1 Ranger + +
+ +
+ + + + + + + + + + + + + + +
diff --git a/conf/modules/guidance_plane_pid_rotorcraft.xml b/conf/modules/guidance_plane_pid_rotorcraft.xml new file mode 100644 index 0000000000..b780a63744 --- /dev/null +++ b/conf/modules/guidance_plane_pid_rotorcraft.xml @@ -0,0 +1,58 @@ + + + + + + Basic guidance code for fixedwing planes using the rotorcraft firmware + with PID control, no airspeed control + +
+ + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + @stabilization,guidance_rotorcraft + guidance,attitude_command + +
+ +
+ + + + +
diff --git a/conf/modules/stabilization_plane_pid.xml b/conf/modules/stabilization_plane_pid.xml new file mode 100644 index 0000000000..9d684dfed9 --- /dev/null +++ b/conf/modules/stabilization_plane_pid.xml @@ -0,0 +1,42 @@ + + + + + + Basic fixed-wing PID controller for rotorcraft firmware using float euler implementation + +
+ + + + + + + + +
+
+ + + + + + + + + + + + + + stabilization_rotorcraft,@attitude_command + commands + +
+ +
+ + + + +
diff --git a/conf/simulator/jsbsim/aircraft/Systems/aerodynamics_ranger_t1.xml b/conf/simulator/jsbsim/aircraft/Systems/aerodynamics_ranger_t1.xml new file mode 100644 index 0000000000..f463213ea7 --- /dev/null +++ b/conf/simulator/jsbsim/aircraft/Systems/aerodynamics_ranger_t1.xml @@ -0,0 +1,204 @@ + + + + + + Lift due to alpha + + aero/qbar-psf + metrics/Sw-sqft + + aero/alpha-rad + + -1.57 0. + -0.20 -0.750 + 0.00 0.250 + 0.23 1.400 + 0.60 0.710 + 1.57 0. + +
+
+
+ +
+ + + + + Drag at zero lift + + aero/qbar-psf + metrics/Sw-sqft + 0.15 + + + + + Induced drag + + aero/qbar-psf + metrics/Sw-sqft + aero/cl-squared + 0.3 + + + + + + + + + Side force due to beta + + aero/qbar-psf + metrics/Sw-sqft + aero/beta-rad + -0.1 + + + + + + + + + Roll moment due to beta + + aero/qbar-psf + metrics/Sw-sqft + metrics/bw-ft + + aero/beta-rad + + -0.01 + + + + + Roll moment due to roll rate + + aero/qbar-psf + metrics/Sw-sqft + metrics/bw-ft + aero/bi2vel + velocities/p-aero-rad_sec + -0.4 + + + + + Roll moment due to yaw rate + + aero/qbar-psf + metrics/Sw-sqft + metrics/bw-ft + aero/bi2vel + velocities/r-aero-rad_sec + + 0. + + + + + Roll moment due to aileron + + aero/qbar-psf + metrics/Sw-sqft + metrics/bw-ft + fcs/aileron_lag + 0.17 + + + + + + + + + Pitch moment due to alpha + + aero/qbar-psf + metrics/Sw-sqft + metrics/cbarw-ft + aero/alpha-rad + -0.5 + + aero/alpha-rad + + -1.57 0. + -0.60 0.3 + 0.00 0. + 0.60 -0.3 + 1.57 0. + +
+
+
+ + + Pitch moment due to elevator + + aero/qbar-psf + metrics/Sw-sqft + metrics/cbarw-ft + fcs/elevator_lag + 1.1 + + + + + Pitch moment due to pitch rate + + aero/qbar-psf + metrics/Sw-sqft + metrics/cbarw-ft + aero/ci2vel + velocities/q-aero-rad_sec + -12 + + + +
+ + + + + Yaw moment due to beta + + aero/qbar-psf + metrics/Sw-sqft + metrics/bw-ft + + aero/beta-rad + + 0.12 + + + + + Yaw moment due to yaw rate + + aero/qbar-psf + metrics/Sw-sqft + metrics/bw-ft + aero/bi2vel + velocities/r-aero-rad_sec + -0.15 + + + + + Adverse yaw + + aero/qbar-psf + metrics/Sw-sqft + metrics/bw-ft + fcs/aileron_lag + -0.01 + + + + + +
diff --git a/conf/simulator/jsbsim/aircraft/ranger_t1.xml b/conf/simulator/jsbsim/aircraft/ranger_t1.xml new file mode 100644 index 0000000000..84eb6fde90 --- /dev/null +++ b/conf/simulator/jsbsim/aircraft/ranger_t1.xml @@ -0,0 +1,442 @@ + + + + + + ENAC + 23-01-2025 + 1.0 + Heewing Ranger T1 VTOL + + + + 0.09 + 0.73 + 0.15 + 0.029 + 0.4 + 0.01 + 0.4 + 0 + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + + + 0.005 + 0.005 + 0.015 + 0. + 0. + 0. + 0.67 + + 0 + 0 + 0 + + + + + + + -0.12 + 0.0 + -0.1 + + 0.8 + 0.5 + 500 + 100 + 1000 + 0.0 + NONE + 0 + + + + + 0.12 + 0.0 + -0.1 + + 0.8 + 0.5 + 500 + 100 + 1000 + 0.0 + NONE + 0 + + + + + 0.0 + 0.12 + -0.1 + + 0.8 + 0.5 + 500 + 100 + 1000 + 0.0 + NONE + 0 + + + + + 0.0 + -0.12 + -0.1 + + 0.8 + 0.5 + 500 + 100 + 1000 + 0.0 + NONE + 0 + + + + + fcs/motor_lag + fcs/servo_lag + fcs/tilt_min_rad + fcs/tilt_max_rad + + + + + fcs/MOTOR_RIGHT + + 0 + 1 + + fcs/motor_lag + fcs/motor_right_lag + + + fcs/MOTOR_LEFT + + 0 + 1 + + fcs/motor_lag + fcs/motor_left_lag + + + fcs/MOTOR_TAIL + + 0 + 1 + + fcs/motor_lag + fcs/motor_tail_lag + + + + fcs/ROLL + fcs/servo_lag + fcs/aileron_lag + + + fcs/PITCH + fcs/servo_lag + fcs/elevator_lag + + + + + + + + + fcs/TILT + fcs/YAW + + + fcs/tilt_right + + + fcs/tilt_right + false + + 0. + 1. + + + 0. + 1.73329 + + fcs/tilt_right_rad + + + fcs/tilt_right_rad + 3.5 + + fcs/tilt_min_rad + fcs/tilt_max_rad + + fcs/tilt_right_lag + + + + -fcs/tilt_right_lag + + external_reactions/motor_right/x + + + + -fcs/tilt_right_lag + + external_reactions/motor_right/z + + + + -fcs/tilt_right_lag + + external_reactions/motor_right_torque/l + + + + -fcs/tilt_right_lag + + external_reactions/motor_right_torque/n + + + + + + fcs/TILT + -fcs/YAW + + + fcs/tilt_left + + + fcs/tilt_left + false + + 0. + 1. + + + 0. + 1.73329 + + fcs/tilt_left_rad + + + fcs/tilt_left_rad + 3.5 + + fcs/tilt_min_rad + fcs/tilt_max_rad + + fcs/tilt_left_lag + + + + -fcs/tilt_left_rad + + external_reactions/motor_left/x + + + + -fcs/tilt_left_rad + + external_reactions/motor_left/z + + + + + -1. + -fcs/tilt_left_rad + + + external_reactions/motor_left_torque/l + + + + + -1. + -fcs/tilt_left_rad + + + external_reactions/motor_left_torque/n + + + + + + + + fcs/MOTOR_RIGHT + fcs/MOTOR_LEFT + fcs/MOTOR_TAIL + fcs/PITCH + fcs/ROLL + fcs/YAW + fcs/THRUST + fcs/TILT + fcs/motor_right_lag + fcs/motor_left_lag + fcs/motor_tail_lag + fcs/aileron_lag + fcs/elevator_lag + fcs/tilt_left_lag + fcs/tilt_right_lag + + fcs/motor_max_thrust + + fcs/motor_max_torque + + + + + + + fcs/motor_right_lag + fcs/motor_max_thrust + 0.224808943 + + + + -0.09 + 0.12 + 0. + + + 1 + 0 + 0 + + + + + + fcs/motor_left_lag + fcs/motor_max_thrust + 0.224808943 + + + + -0.09 + -0.12 + 0 + + + 1 + 0 + 0 + + + + + + fcs/motor_tail_lag + fcs/motor_max_thrust + 0.224808943 + + + + 0.25 + 0 + 0 + + + 0 + 0 + -1 + + + + + + + + + fcs/motor_right_lag + fcs/motor_max_torque + 0.738 + + + + 0.09 + 0.12 + 0. + + + 1 + 0 + 0 + + + + + + + + fcs/motor_left_lag + fcs/motor_max_torque + 0.738 + + + + 0.09 + -0.12 + 0. + + + -1 + 0 + 0 + + + + + + + fcs/motor_tail_lag + fcs/motor_max_torque + 0.738 + + + + -0.25 + 0 + 0 + + + 0 + 0 + -1 + + + + + + + + + + + diff --git a/sw/airborne/firmwares/rotorcraft/guidance/guidance_plane.c b/sw/airborne/firmwares/rotorcraft/guidance/guidance_plane.c new file mode 100644 index 0000000000..3faa6fd83a --- /dev/null +++ b/sw/airborne/firmwares/rotorcraft/guidance/guidance_plane.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2024 Gautier Hattenberger + * + * 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 firmwares/rotorcraft/guidance/guidance_plane.c + * Guidance controller for planes with PID + * compatible with the rotorcraft firmware + * no airspeed control + * + */ + +#include "firmwares/rotorcraft/guidance/guidance_plane.h" +#include "firmwares/rotorcraft/navigation.h" +#include "firmwares/rotorcraft/guidance.h" +#include "firmwares/rotorcraft/stabilization.h" +#include "generated/airframe.h" +#include "generated/flight_plan.h" +#include "modules/nav/waypoints.h" +#include "state.h" + +#ifndef GUIDANCE_PLANE_MAX_BANK +#define GUIDANCE_PLANE_MAX_BANK RadOfDeg(45.f) +#endif + +#ifndef GUIDANCE_PLANE_MAX_PITCH +#define GUIDANCE_PLANE_MAX_PITCH RadOfDeg(30.f) +#endif + +#ifndef GUIDANCE_PLANE_MIN_PITCH +#define GUIDANCE_PLANE_MIN_PITCH RadOfDeg(-20.f) +#endif + +#ifndef GUIDANCE_PLANCE_COURSE_PRE_BANK +#define GUIDANCE_PLANCE_COURSE_PRE_BANK 1.f +#endif + +#ifndef GUIDANCE_PLANE_MAX_CLIMB +#define GUIDANCE_PLANE_MAX_CLIMB 2.f +#endif + +#ifndef GUIDANCE_PLANE_PITCH_OF_VZ +#define GUIDANCE_PLANE_PITCH_OF_VZ RadOfDeg(5.f) +#endif + +#ifndef GUIDANCE_PLANE_PITCH_TRIM +#define GUIDANCE_PLANE_PITCH_TRIM RadOfDeg(0.f) +#endif + +#ifndef GUIDANCE_PLANE_CLIMB_THROTTLE_INCREMENT +#define GUIDANCE_PLANE_CLIMB_THROTTLE_INCREMENT 0.1f +#endif + +/* + * external variables + */ + +struct GuidancePlane guidance_plane; + +/* + * internal variables + */ + +static float pitch_sum_err; +#define GUIDANCE_PLANE_PITCH_MAX_SUM_ERR (RadOfDeg(10.)) + +static float throttle_sum_err; +#define GUIDANCE_PLANE_THROTTLE_MAX_SUM_ERR 0.4 + +#if PERIODIC_TELEMETRY +#include "modules/datalink/telemetry.h" + +#endif + +void guidance_plane_init(void) +{ + guidance_plane.roll_max_setpoint = GUIDANCE_PLANE_MAX_BANK; + guidance_plane.course_setpoint = 0.f; + guidance_plane.course_kp = GUIDANCE_PLANE_COURSE_KP; + guidance_plane.course_kd = GUIDANCE_PLANE_COURSE_KD; + guidance_plane.course_pre_bank_correction = GUIDANCE_PLANCE_COURSE_PRE_BANK; + guidance_plane.roll_cmd = 0.f; + + guidance_plane.pitch_max_setpoint = GUIDANCE_PLANE_MAX_PITCH; + guidance_plane.pitch_min_setpoint = GUIDANCE_PLANE_MIN_PITCH; + guidance_plane.climb_max_setpoint = GUIDANCE_PLANE_MAX_CLIMB; + guidance_plane.altitude_setpoint = 0.f; + guidance_plane.climb_setpoint = 0.f; + guidance_plane.climb_kp = GUIDANCE_PLANE_CLIMB_KP; + guidance_plane.pitch_trim = GUIDANCE_PLANE_PITCH_TRIM; + guidance_plane.pitch_of_vz = GUIDANCE_PLANE_PITCH_OF_VZ; + guidance_plane.climb_throttle_increment = GUIDANCE_PLANE_CLIMB_THROTTLE_INCREMENT; + guidance_plane.p_kp = GUIDANCE_PLANE_PITCH_KP; + guidance_plane.p_kd = GUIDANCE_PLANE_PITCH_KD; + guidance_plane.p_ki = GUIDANCE_PLANE_PITCH_KI; + guidance_plane.t_kp = GUIDANCE_PLANE_THROTTLE_KP; + guidance_plane.t_kd = GUIDANCE_PLANE_THROTTLE_KD; + guidance_plane.t_ki = GUIDANCE_PLANE_THROTTLE_KI; + guidance_plane.cruise_throttle = GUIDANCE_PLANE_NOMINAL_THROTTLE; + guidance_plane.pitch_cmd = 0.f; + guidance_plane.throttle_cmd = 0; + + pitch_sum_err = 0.f; + throttle_sum_err = 0.f; + +#if PERIODIC_TELEMETRY +#endif +} + +/** + * run horizontal control loop for position and speed control + */ +struct StabilizationSetpoint guidance_plane_attitude_from_nav(bool in_flight UNUSED) +{ + struct FloatEulers att_sp; + + // course control loop + if (nav.horizontal_mode == NAV_HORIZONTAL_MODE_NONE || nav.horizontal_mode == NAV_HORIZONTAL_MODE_GUIDED) { + guidance_plane.roll_cmd = 0.f; + guidance_plane.pitch_cmd = 0.f; + } else if (nav.horizontal_mode == NAV_HORIZONTAL_MODE_ATTITUDE) { + if (nav.setpoint_mode == NAV_SETPOINT_MODE_QUAT) { + struct FloatEulers e; + float_eulers_of_quat(&e, &nav.quat); + guidance_plane.roll_cmd = e.phi; + guidance_plane.pitch_cmd = e.theta; + } + else { + guidance_plane.roll_cmd = nav.roll; + guidance_plane.pitch_cmd = nav.pitch; + } + } else { + // update carrot for GCS display and convert ENU float -> NED int + // even if sp is changed later + // FIXME really needed here ? + guidance_h.sp.pos.x = POS_BFP_OF_REAL(nav.carrot.y); + guidance_h.sp.pos.y = POS_BFP_OF_REAL(nav.carrot.x); + + switch (nav.setpoint_mode) { + case NAV_SETPOINT_MODE_POS: + guidance_plane.course_setpoint = atan2f(nav.carrot.x - stateGetPositionEnu_f()->x, nav.carrot.y - stateGetPositionNed_f()->y); + break; + case NAV_SETPOINT_MODE_SPEED: + guidance_plane.course_setpoint = atan2f(nav.speed.x, nav.speed.y); + break; + case NAV_SETPOINT_MODE_ACCEL: + // not supported, flying towards HOME waypoint + guidance_plane.course_setpoint = atan2f(waypoint_get_x(WP_HOME) - stateGetPositionEnu_f()->x, + waypoint_get_y(WP_HOME) - stateGetPositionEnu_f()->y); + break; + default: + // nothing to do for other cases at the moment + break; + } + /* final attitude setpoint */ + + // Ground path error + static float last_err = 0.f; + float err = guidance_plane.course_setpoint - stateGetHorizontalSpeedDir_f(); + NormRadAngle(err); + float d_err = err - last_err; + last_err = err; + NormRadAngle(d_err); + //float course_pre_bank = 0.f; // TODO: compute if flying a circle -> pb is that it is coming from nav + // guidance_plane.course_pre_bank_correction * course_pre_bank + + guidance_plane.roll_cmd = guidance_plane.course_kp * err + guidance_plane.course_kd * d_err; + BoundAbs(guidance_plane.roll_cmd, guidance_plane.roll_max_setpoint); + } + + att_sp.phi = guidance_plane.roll_cmd; + att_sp.theta = guidance_plane.pitch_cmd; + att_sp.psi = stateGetNedToBodyEulers_f()->psi; + return stab_sp_from_eulers_f(&att_sp); +} + + +static void guidance_plane_set_pitch(bool in_flight) +{ + static float last_err = 0.f; + + if (!in_flight) { + pitch_sum_err = 0.f; + } + + // Compute errors + float err = guidance_plane.climb_setpoint - stateGetSpeedEnu_f()->z; + float d_err = err - last_err; + last_err = err; + + if (guidance_plane.p_ki > 0.) { + pitch_sum_err += err * (1. / PERIODIC_FREQUENCY); + BoundAbs(pitch_sum_err, GUIDANCE_PLANE_PITCH_MAX_SUM_ERR / guidance_plane.p_ki); + } + + // PID loop + feedforward ctl + guidance_plane.pitch_cmd = nav.pitch + + guidance_plane.pitch_trim + + guidance_plane.pitch_of_vz * guidance_plane.climb_setpoint + + guidance_plane.p_kp * err + + guidance_plane.p_kd * d_err + + guidance_plane.p_ki * pitch_sum_err; + +} + +static void guidance_plane_set_throttle(bool in_flight) +{ + static float last_err = 0.; + + if (!in_flight) { + throttle_sum_err = 0; + } + + // Compute errors + float err = guidance_plane.climb_setpoint - stateGetSpeedEnu_f()->z; + float d_err = err - last_err; + last_err = err; + + if (guidance_plane.t_ki > 0.) { + throttle_sum_err += err * (1. / PERIODIC_FREQUENCY); + BoundAbs(throttle_sum_err, GUIDANCE_PLANE_THROTTLE_MAX_SUM_ERR / guidance_plane.t_ki); + } + + // PID loop + feedforward ctl + float th_cmd = guidance_plane.cruise_throttle + + guidance_plane.climb_throttle_increment * guidance_plane.climb_setpoint + + guidance_plane.t_kp * err + + guidance_plane.t_kd * d_err + + guidance_plane.t_ki * throttle_sum_err; + + guidance_plane.throttle_cmd = TRIM_UPPRZ(MAX_PPRZ * th_cmd); + +} + +/** + * run vertical control loop for position and speed control + */ +struct ThrustSetpoint guidance_plane_thrust_from_nav(bool in_flight) +{ + if (nav.vertical_mode == NAV_VERTICAL_MODE_MANUAL) { + guidance_plane.throttle_cmd = (int32_t) nav.throttle; + } else { + if (nav.vertical_mode == NAV_VERTICAL_MODE_ALT) { + guidance_plane.altitude_setpoint = nav.nav_altitude; + float alt_err = guidance_plane.altitude_setpoint - stateGetPositionEnu_f()->z; + guidance_plane.climb_setpoint = guidance_plane.climb_kp * alt_err; + } else if (nav.vertical_mode == NAV_VERTICAL_MODE_CLIMB) { + guidance_plane.climb_setpoint = nav.climb; + } else { + guidance_plane.climb_setpoint = 0.f; + } + BoundAbs(guidance_plane.climb_setpoint, guidance_plane.climb_max_setpoint); + + guidance_plane_set_pitch(in_flight); + guidance_plane_set_throttle(in_flight); + } + + return th_sp_from_thrust_i(guidance_plane.throttle_cmd, THRUST_AXIS_X); +} + +void guidance_plane_enter(void) +{ + /* set nav_heading to current heading */ + //nav.heading = stateGetNedToBodyEulers_f()->psi; + + pitch_sum_err = 0.f; + throttle_sum_err = 0.f; +} + diff --git a/sw/airborne/firmwares/rotorcraft/guidance/guidance_plane.h b/sw/airborne/firmwares/rotorcraft/guidance/guidance_plane.h new file mode 100644 index 0000000000..4e4e9dcba5 --- /dev/null +++ b/sw/airborne/firmwares/rotorcraft/guidance/guidance_plane.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2025 Gautier Hattenberger + * + * 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 firmwares/rotorcraft/guidance/guidance_plane.h + * Guidance controller for planes in rotorcraft firmware + * using basic PID controller + * no airspeed control + * + */ + +#ifndef GUIDANCE_PLANE_H +#define GUIDANCE_PLANE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "std.h" +#include "math/pprz_algebra_int.h" +#include "math/pprz_algebra_float.h" + +struct GuidancePlane { + // horizontal + float roll_max_setpoint; + float pitch_max_setpoint; + float pitch_min_setpoint; + float course_setpoint; + // horizontal gains + float course_kp; + float course_kd; + float course_pre_bank_correction; + + // vertical + float altitude_setpoint; + float climb_max_setpoint; + float climb_setpoint; + float pitch_trim; + // vertical gains, pitch and throttle + float cruise_throttle; + float climb_kp; + float pitch_of_vz; + float climb_throttle_increment; + float p_kp; + float p_kd; + float p_ki; + float t_kp; + float t_kd; + float t_ki; + + // outputs + float roll_cmd; + float pitch_cmd; + int32_t throttle_cmd; // Throttle command (in pprz_t) +}; + +/** Guidance PID structyre + */ +extern struct GuidancePlane guidance_plane; + +extern void guidance_plane_init(void); +extern void guidance_plane_enter(void); +extern struct StabilizationSetpoint guidance_plane_attitude_from_nav(bool in_flight); +extern struct ThrustSetpoint guidance_plane_thrust_from_nav(bool in_flight); + +#ifdef __cplusplus +} +#endif + +#endif /* GUIDANCE_PLANE_H */ diff --git a/sw/airborne/firmwares/rotorcraft/stabilization/stabilization_attitude_plane_pid.c b/sw/airborne/firmwares/rotorcraft/stabilization/stabilization_attitude_plane_pid.c new file mode 100644 index 0000000000..e19dc8136f --- /dev/null +++ b/sw/airborne/firmwares/rotorcraft/stabilization/stabilization_attitude_plane_pid.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2024 Gautier Hattenberger + * + * 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 stabilization_attitude_plane_pid.c + * + * Basic fixed-wing attitude stabilization in euler float version. + */ + +#include "generated/airframe.h" + +#include "firmwares/rotorcraft/stabilization/stabilization_attitude_plane_pid.h" + +#include "std.h" +#include "paparazzi.h" +#include "math/pprz_algebra_float.h" +#include "state.h" + +struct PlaneAttitudeGains stab_plane_gains; +struct FloatEulers stab_plane_att_sum_err; + +static struct FloatEulers stab_att_sp_euler; + +float stab_plane_att_cmd[COMMANDS_NB]; + +#if PERIODIC_TELEMETRY +#include "modules/datalink/telemetry.h" + +static void send_att(struct transport_tx *trans, struct link_device *dev) +{ + struct FloatRates *body_rate = stateGetBodyRates_f(); + struct FloatEulers *att = stateGetNedToBodyEulers_f(); + float foo = 0.0; + pprz_msg_send_STAB_ATTITUDE_FLOAT(trans, dev, AC_ID, + &(body_rate->p), &(body_rate->q), &(body_rate->r), + &(att->phi), &(att->theta), &(att->psi), + &stab_att_sp_euler.phi, + &stab_att_sp_euler.theta, + &stab_att_sp_euler.psi, + &stab_plane_att_sum_err.phi, + &stab_plane_att_sum_err.theta, + &stab_plane_att_sum_err.psi, + &stab_plane_att_cmd[COMMAND_ROLL], + &stab_plane_att_cmd[COMMAND_PITCH], + &foo, &foo, &foo, &foo, + &stabilization.cmd[COMMAND_ROLL], + &stabilization.cmd[COMMAND_PITCH], + &stabilization.cmd[COMMAND_YAW], + &foo, &foo, &foo); +} + +#endif + +void stabilization_attitude_plane_pid_init(void) +{ + VECT3_ASSIGN(stab_plane_gains.p, + STABILIZATION_PLANE_PHI_PGAIN, + STABILIZATION_PLANE_THETA_PGAIN, + 0.f); + + VECT3_ASSIGN(stab_plane_gains.d, + STABILIZATION_PLANE_PHI_DGAIN, + STABILIZATION_PLANE_THETA_DGAIN, + 0.f); + + VECT3_ASSIGN(stab_plane_gains.i, + STABILIZATION_PLANE_PHI_IGAIN, + STABILIZATION_PLANE_THETA_IGAIN, + 0.f); + + FLOAT_EULERS_ZERO(stab_plane_att_sum_err); + +#if PERIODIC_TELEMETRY + register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_STAB_ATTITUDE_FLOAT, send_att); +#endif +} + +void stabilization_attitude_plane_pid_enter(void) +{ + FLOAT_EULERS_ZERO(stab_plane_att_sum_err); +} + +#define MAX_SUM_ERR 200 + +void stabilization_attitude_plane_pid_run(bool in_flight, struct StabilizationSetpoint *sp, struct ThrustSetpoint *thrust, int32_t *cmd) +{ + + /* Compute feedback */ + /* attitude error */ + stab_att_sp_euler = stab_sp_to_eulers_f(sp); + struct FloatEulers *att_float = stateGetNedToBodyEulers_f(); + struct FloatEulers att_err; + EULERS_DIFF(att_err, stab_att_sp_euler, *att_float); + att_err.psi = 0.f; + + /* update integrator */ + if (in_flight) { + EULERS_ADD(stab_plane_att_sum_err, att_err); + EULERS_BOUND_CUBE(stab_plane_att_sum_err, -MAX_SUM_ERR, MAX_SUM_ERR); + } else { + FLOAT_EULERS_ZERO(stab_plane_att_sum_err); + } + + /* rate error */ + struct FloatRates *rate_float = stateGetBodyRates_f(); + struct FloatRates rate_sp, rate_err; + FLOAT_RATES_ZERO(rate_sp); + RATES_DIFF(rate_err, rate_sp, *rate_float); + + /* PID */ + stab_plane_att_cmd[COMMAND_ROLL] = + stab_plane_gains.p.x * att_err.phi + + stab_plane_gains.d.x * rate_err.p + + stab_plane_gains.i.x * stab_plane_att_sum_err.phi; + + stab_plane_att_cmd[COMMAND_PITCH] = + stab_plane_gains.p.y * att_err.theta + + stab_plane_gains.d.y * rate_err.q + + stab_plane_gains.i.y * stab_plane_att_sum_err.theta; + + cmd[COMMAND_ROLL] = (int32_t)stab_plane_att_cmd[COMMAND_ROLL]; + cmd[COMMAND_PITCH] = (int32_t)stab_plane_att_cmd[COMMAND_PITCH]; + cmd[COMMAND_YAW] = 0; + cmd[COMMAND_THRUST] = th_sp_to_thrust_i(thrust, 0, THRUST_AXIS_X); + + /* bound the result */ + BoundAbs(cmd[COMMAND_ROLL], MAX_PPRZ); + BoundAbs(cmd[COMMAND_PITCH], MAX_PPRZ); + BoundAbs(cmd[COMMAND_YAW], MAX_PPRZ); + BoundAbs(cmd[COMMAND_THRUST], MAX_PPRZ); +} + diff --git a/sw/airborne/firmwares/rotorcraft/stabilization/stabilization_attitude_plane_pid.h b/sw/airborne/firmwares/rotorcraft/stabilization/stabilization_attitude_plane_pid.h new file mode 100644 index 0000000000..96f1327c44 --- /dev/null +++ b/sw/airborne/firmwares/rotorcraft/stabilization/stabilization_attitude_plane_pid.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 Gautier Hattenberger + * + * 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 stabilization_attitude_plane_pid.h + * + * Basic fixed-wing attitude stabilization in euler float version. + */ + +#ifndef STABILIZATION_ATTITUDE_PLANE_PID_H +#define STABILIZATION_ATTITUDE_PLANE_PID_H + +#include "firmwares/rotorcraft/stabilization/stabilization_attitude.h" + +struct PlaneAttitudeGains { + struct FloatVect3 p; + struct FloatVect3 i; + struct FloatVect3 d; +}; + +extern void stabilization_attitude_plane_pid_init(void); +extern void stabilization_attitude_plane_pid_enter(void); +extern void stabilization_attitude_plane_pid_run(bool in_flight, struct StabilizationSetpoint *sp, struct ThrustSetpoint *thrust, int32_t *cmd); + +extern struct PlaneAttitudeGains stab_plane_gains; +extern struct FloatEulers stab_plane_att_sum_err; + +#endif /* STABILIZATION_ATTITUDE_PLANE_PID_H */ diff --git a/sw/airborne/modules/ctrl/control_mixing_heewing.c b/sw/airborne/modules/ctrl/control_mixing_heewing.c new file mode 100644 index 0000000000..10eb7575cf --- /dev/null +++ b/sw/airborne/modules/ctrl/control_mixing_heewing.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2024 Gautier Hattenberger + * + * 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/ctrl/control_mixing_heewing.c" + * @author Gautier Hattenberger + * Control mixing specific to the Heewing T1 Ranger + */ + +#include "modules/ctrl/control_mixing_heewing.h" +#include "modules/radio_control/radio_control.h" +#include "generated/modules.h" +#include "modules/core/commands.h" +#include "modules/actuators/actuators.h" +#include "autopilot.h" +#include "state.h" +#include "firmwares/rotorcraft/stabilization.h" +#include "firmwares/rotorcraft/guidance.h" +#include "firmwares/rotorcraft/navigation.h" +#include "firmwares/rotorcraft/guidance/guidance_plane.h" +#include "firmwares/rotorcraft/stabilization/stabilization_indi.h" +#include "firmwares/rotorcraft/stabilization/stabilization_attitude_plane_pid.h" + +// Tilt position in forward flight +#ifndef CMH_TILT_FORWARD +#define CMH_TILT_FORWARD 0 +#endif + +// Tilt vertical position for hovering +#ifndef CMH_TILT_VERTICAL +#define CMH_TILT_VERTICAL 8700 +#endif + +// Max tilt differential for yaw +#ifndef CMH_TILT_DIFF_MAX +#define CMH_TILT_DIFF_MAX (MAX_PPRZ-CMH_TILT_VERTICAL) +#endif + +// Motor idle position +#ifndef CMH_MOTOR_IDLE +#define CMH_MOTOR_IDLE 800 +#endif + +// Time for forward transition +#ifndef CMH_TRANSITION_TIME +#define CMH_TRANSITION_TIME 4.f +#endif + +// Transition airspeed, set a negative value to disable +#ifndef CMH_TRANSITION_AIRSPEED +#define CMH_TRANSITION_AIRSPEED 10.f +#endif + +#define TRANSITION_TO_HOVER false +#define TRANSITION_TO_FORWARD true + +static float transition_ratio; +static const float transition_increment = 1.f / (CMH_TRANSITION_TIME * PERIODIC_FREQUENCY); + +static void transition_run(bool to_forward) { + if (to_forward && transition_ratio < 1.f) { + if (stateIsAirspeedValid()) { + if ((stateGetAirspeed_f() < CMH_TRANSITION_AIRSPEED && transition_ratio < 0.4f) + || stateGetAirspeed_f() >= CMH_TRANSITION_AIRSPEED) { + transition_ratio += transition_increment; + } + else if (stateGetAirspeed_f() < CMH_TRANSITION_AIRSPEED && transition_ratio > 0.4f) { + transition_ratio -= transition_increment; + } + } + } else if (!to_forward && transition_ratio > 0.f) { + transition_ratio = 0.f; // immediately switch back to hover + } + Bound(transition_ratio, 0.f, 1.f); +} + +static int32_t command_from_transition(int32_t hover_cmd, int32_t forward_cmd) { + Bound(transition_ratio, 0.f, 1.f); + return (int32_t) (hover_cmd + transition_ratio * (forward_cmd - hover_cmd)); +} + +void control_mixing_heewing_init(void) +{ + transition_ratio = 0.f; // 0. for hover to 1. for forward flight +} + +void control_mixing_heewing_manual(void) +{ + transition_run(TRANSITION_TO_FORWARD); + commands[COMMAND_ROLL] = radio_control_get(RADIO_ROLL); + commands[COMMAND_PITCH] = radio_control_get(RADIO_PITCH); + commands[COMMAND_YAW] = 0; + commands[COMMAND_TILT] = CMH_TILT_FORWARD; + commands[COMMAND_MOTOR_RIGHT] = radio_control_get(RADIO_THROTTLE); + commands[COMMAND_MOTOR_LEFT] = radio_control_get(RADIO_THROTTLE); + commands[COMMAND_MOTOR_TAIL] = MIN_PPRZ; + commands[COMMAND_THRUST] = (commands[COMMAND_MOTOR_RIGHT] + commands[COMMAND_MOTOR_LEFT]) / 2; + autopilot.throttle = commands[COMMAND_THRUST]; +} + +void control_mixing_heewing_attitude_direct(void) +{ + transition_run(TRANSITION_TO_HOVER); + commands[COMMAND_TILT] = CMH_TILT_VERTICAL; + commands[COMMAND_ROLL] = 0; + commands[COMMAND_PITCH] = 0; + struct ThrustSetpoint th_sp = guidance_v_run(autopilot_in_flight()); + stabilization_run(autopilot_in_flight(), &stabilization.rc_sp, &th_sp, stabilization.cmd); + if (autopilot_get_motors_on()) { + commands[COMMAND_MOTOR_RIGHT] = actuators_pprz[CMH_ACT_MOTOR_RIGHT]; + commands[COMMAND_MOTOR_LEFT] = actuators_pprz[CMH_ACT_MOTOR_LEFT]; + commands[COMMAND_MOTOR_TAIL] = actuators_pprz[CMH_ACT_MOTOR_TAIL]; + if (autopilot_in_flight()) { + commands[COMMAND_YAW] = actuators_pprz[CMH_ACT_YAW]; + } else { + commands[CMH_ACT_YAW] = 0; + } + commands[COMMAND_THRUST] = stabilization.cmd[COMMAND_THRUST]; + } else { + commands[COMMAND_MOTOR_RIGHT] = -MAX_PPRZ; + commands[COMMAND_MOTOR_LEFT] = -MAX_PPRZ; + commands[COMMAND_MOTOR_TAIL] = -MAX_PPRZ; + commands[COMMAND_YAW] = 0; + commands[COMMAND_THRUST] = 0; + } + autopilot.throttle = commands[COMMAND_THRUST]; +} + +void control_mixing_heewing_attitude_direct_enter(void) +{ + guidance_h_mode_changed(GUIDANCE_H_MODE_NONE); + guidance_v_mode_changed(GUIDANCE_V_MODE_RC_DIRECT); + stabilization_mode_changed(STABILIZATION_MODE_NONE, STABILIZATION_ATT_SUBMODE_HEADING); // force mode change to always reset heading + stabilization_mode_changed(STABILIZATION_MODE_ATTITUDE, STABILIZATION_ATT_SUBMODE_HEADING); +} + +void stabilization_indi_set_wls_settings(void) +{ + // Calculate the min and max increments + wls_stab_p.u_min[CMH_ACT_MOTOR_RIGHT] = CMH_MOTOR_IDLE; + wls_stab_p.u_max[CMH_ACT_MOTOR_RIGHT] = MAX_PPRZ; + wls_stab_p.u_pref[CMH_ACT_MOTOR_RIGHT] = act_pref[CMH_ACT_MOTOR_RIGHT]; + + wls_stab_p.u_min[CMH_ACT_MOTOR_LEFT] = CMH_MOTOR_IDLE; + wls_stab_p.u_max[CMH_ACT_MOTOR_LEFT] = MAX_PPRZ; + wls_stab_p.u_pref[CMH_ACT_MOTOR_LEFT] = act_pref[CMH_ACT_MOTOR_LEFT]; + + wls_stab_p.u_min[CMH_ACT_MOTOR_TAIL] = CMH_MOTOR_IDLE; + wls_stab_p.u_max[CMH_ACT_MOTOR_TAIL] = MAX_PPRZ; + wls_stab_p.u_pref[CMH_ACT_MOTOR_TAIL] = act_pref[CMH_ACT_MOTOR_TAIL]; + + wls_stab_p.u_min[CMH_ACT_YAW] = -CMH_TILT_DIFF_MAX; + wls_stab_p.u_max[CMH_ACT_YAW] = CMH_TILT_DIFF_MAX; + wls_stab_p.u_pref[CMH_ACT_YAW] = act_pref[CMH_ACT_YAW]; +} + +void control_mixing_heewing_attitude_plane_enter(void) +{ + // don't use forward submode to avoid pitch offset on RC input + guidance_h_mode_changed(GUIDANCE_H_MODE_NONE); + guidance_v_mode_changed(GUIDANCE_V_MODE_RC_DIRECT); + stabilization_mode_changed(STABILIZATION_MODE_ATTITUDE, STABILIZATION_ATT_SUBMODE_HEADING); + stabilization_attitude_plane_pid_enter(); +} + +void control_mixing_heewing_attitude_plane(void) +{ + transition_run(TRANSITION_TO_FORWARD); + + if (transition_ratio < 0.5f) { + struct ThrustSetpoint hover_th_sp = guidance_v_run(autopilot_in_flight()); + stabilization_run(autopilot_in_flight(), &stabilization.rc_sp, &hover_th_sp, stabilization.cmd); + } + struct ThrustSetpoint th_sp = th_sp_from_thrust_i(radio_control_get(RADIO_THROTTLE), THRUST_AXIS_X); + struct FloatEulers rc_sp = { + .phi = STABILIZATION_ATTITUDE_SP_MAX_PHI * radio_control_get(RADIO_ROLL) / MAX_PPRZ, + .theta = STABILIZATION_ATTITUDE_SP_MAX_THETA * radio_control_get(RADIO_PITCH) / MAX_PPRZ, + .psi = 0.f + }; + struct StabilizationSetpoint stab_sp = stab_sp_from_eulers_f(&rc_sp); + stabilization_attitude_plane_pid_run(transition_ratio < 0.8 ? false : autopilot_in_flight(), &stab_sp, &th_sp, stabilization.cmd); + + commands[COMMAND_ROLL] = stabilization.cmd[COMMAND_ROLL]; + commands[COMMAND_PITCH] = stabilization.cmd[COMMAND_PITCH]; + commands[COMMAND_THRUST] = stabilization.cmd[COMMAND_THRUST]; + if (autopilot_in_flight()) { + if (transition_ratio < 0.5f) { + // only hover stabilization + commands[COMMAND_MOTOR_RIGHT] = actuators_pprz[CMH_ACT_MOTOR_RIGHT]; + commands[COMMAND_MOTOR_LEFT] = actuators_pprz[CMH_ACT_MOTOR_LEFT]; + commands[COMMAND_MOTOR_TAIL] = actuators_pprz[CMH_ACT_MOTOR_TAIL]; + } else { + // blend with plane control + commands[COMMAND_MOTOR_RIGHT] = command_from_transition(actuators_pprz[CMH_ACT_MOTOR_RIGHT], stabilization.cmd[COMMAND_THRUST]); + commands[COMMAND_MOTOR_LEFT] = command_from_transition(actuators_pprz[CMH_ACT_MOTOR_LEFT], stabilization.cmd[COMMAND_THRUST]); + commands[COMMAND_MOTOR_TAIL] = command_from_transition(actuators_pprz[CMH_ACT_MOTOR_TAIL], 0); + } + } else { + commands[COMMAND_MOTOR_RIGHT] = stabilization.cmd[COMMAND_THRUST]; + commands[COMMAND_MOTOR_LEFT] = stabilization.cmd[COMMAND_THRUST]; + commands[COMMAND_MOTOR_TAIL] = 0; + } + commands[COMMAND_TILT] = command_from_transition(CMH_TILT_VERTICAL, CMH_TILT_FORWARD); + commands[COMMAND_YAW] = 0; + autopilot.throttle = commands[COMMAND_THRUST]; + +} + +void control_mixing_heewing_nav_enter(void) +{ + guidance_h_mode_changed(GUIDANCE_H_MODE_NAV); + guidance_v_mode_changed(GUIDANCE_V_MODE_NAV); + stabilization_mode_changed(STABILIZATION_MODE_ATTITUDE, STABILIZATION_ATT_SUBMODE_HEADING); +} + +void control_mixing_heewing_nav_run(void) +{ + if (nav.horizontal_mode == NAV_HORIZONTAL_MODE_ROUTE || + nav.horizontal_mode == NAV_HORIZONTAL_MODE_CIRCLE) { + transition_run(TRANSITION_TO_FORWARD); + + struct ThrustSetpoint th_sp = guidance_plane_thrust_from_nav(transition_ratio < 0.8f ? false : autopilot_in_flight()); + if (transition_ratio < 0.8f) { + guidance_plane.pitch_cmd = 0.f; + } + struct StabilizationSetpoint stab_sp = guidance_plane_attitude_from_nav(autopilot_in_flight()); + if (transition_ratio < 0.5f) { + stabilization_attitude_plane_pid_run(transition_ratio < 0.8f ? false : autopilot_in_flight(), &stab_sp, &th_sp, stabilization.cmd); + struct ThrustSetpoint hover_th_sp = guidance_v_run(autopilot_in_flight()); + struct FloatEulers eulers_sp = { .phi = 0.f , .theta = 0.f, .psi = stateGetNedToBodyEulers_f()->psi }; + struct StabilizationSetpoint hover_stab_sp = stab_sp_from_eulers_f(&eulers_sp); + stabilization_run(autopilot_in_flight(), &hover_stab_sp, &hover_th_sp, stabilization.cmd); // will overwrite COMMAND_THRUST + } else { + stabilization_attitude_plane_pid_run(transition_ratio < 0.8f ? false : autopilot_in_flight(), &stab_sp, &th_sp, stabilization.cmd); + } + nav.heading = stateGetNedToBodyEulers_f()->psi; // overwrite nav heading to avoid problems when transition to hover + + commands[COMMAND_TILT] = command_from_transition(CMH_TILT_VERTICAL, CMH_TILT_FORWARD); + commands[COMMAND_ROLL] = stabilization.cmd[COMMAND_ROLL]; + commands[COMMAND_PITCH] = stabilization.cmd[COMMAND_PITCH]; + commands[COMMAND_YAW] = command_from_transition(actuators_pprz[CMH_ACT_YAW], 0); + if (autopilot_get_motors_on()) { + if (transition_ratio < 0.5f) { + // only hover stabilization + commands[COMMAND_MOTOR_RIGHT] = actuators_pprz[CMH_ACT_MOTOR_RIGHT]; + commands[COMMAND_MOTOR_LEFT] = actuators_pprz[CMH_ACT_MOTOR_LEFT]; + commands[COMMAND_MOTOR_TAIL] = actuators_pprz[CMH_ACT_MOTOR_TAIL]; + } else { + // blend with plane control + commands[COMMAND_MOTOR_RIGHT] = command_from_transition(actuators_pprz[CMH_ACT_MOTOR_RIGHT], stabilization.cmd[COMMAND_THRUST]); + commands[COMMAND_MOTOR_LEFT] = command_from_transition(actuators_pprz[CMH_ACT_MOTOR_LEFT], stabilization.cmd[COMMAND_THRUST]); + commands[COMMAND_MOTOR_TAIL] = command_from_transition(actuators_pprz[CMH_ACT_MOTOR_TAIL], 0); + } + commands[COMMAND_THRUST] = stabilization.cmd[COMMAND_THRUST]; + } else { + commands[COMMAND_THRUST] = 0; + commands[COMMAND_MOTOR_RIGHT] = MIN_PPRZ; + commands[COMMAND_MOTOR_LEFT] = MIN_PPRZ; + commands[COMMAND_MOTOR_TAIL] = 0; + } + + } else { + // all other nav modes are in rotorcraft flight mode + transition_run(TRANSITION_TO_HOVER); + commands[COMMAND_TILT] = command_from_transition(CMH_TILT_VERTICAL, CMH_TILT_FORWARD); + commands[COMMAND_ROLL] = 0; + commands[COMMAND_PITCH] = 0; + + struct ThrustSetpoint th_sp = guidance_v_run(autopilot_in_flight()); + struct StabilizationSetpoint stab_sp = guidance_h_run(autopilot_in_flight()); + stabilization_run(autopilot_in_flight(), &stab_sp, &th_sp, stabilization.cmd); + + if (autopilot_get_motors_on()) { + commands[COMMAND_MOTOR_RIGHT] = actuators_pprz[CMH_ACT_MOTOR_RIGHT]; + commands[COMMAND_MOTOR_LEFT] = actuators_pprz[CMH_ACT_MOTOR_LEFT]; + commands[COMMAND_MOTOR_TAIL] = command_from_transition(actuators_pprz[CMH_ACT_MOTOR_TAIL], 0); + if (autopilot_in_flight()) { + commands[COMMAND_YAW] = actuators_pprz[CMH_ACT_YAW]; + } else { + commands[CMH_ACT_YAW] = 0; + } + commands[COMMAND_THRUST] = stabilization.cmd[COMMAND_THRUST]; + } else { + commands[COMMAND_MOTOR_RIGHT] = -MAX_PPRZ; + commands[COMMAND_MOTOR_LEFT] = -MAX_PPRZ; + commands[COMMAND_MOTOR_TAIL] = -MAX_PPRZ; + commands[COMMAND_YAW] = 0; + commands[COMMAND_THRUST] = 0; + } + } + autopilot.throttle = commands[COMMAND_THRUST]; +} diff --git a/sw/airborne/modules/ctrl/control_mixing_heewing.h b/sw/airborne/modules/ctrl/control_mixing_heewing.h new file mode 100644 index 0000000000..189c71d066 --- /dev/null +++ b/sw/airborne/modules/ctrl/control_mixing_heewing.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 Gautier Hattenberger + * + * 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/ctrl/control_mixing_heewing.h" + * @author Gautier Hattenberger + * Control mixing specific to the Heewing T1 Ranger + */ + +#ifndef CONTROL_MIXING_HEEWING_H +#define CONTROL_MIXING_HEEWING_H + +// INDI actuators output indexes +#define CMH_ACT_MOTOR_RIGHT 0 +#define CMH_ACT_MOTOR_LEFT 1 +#define CMH_ACT_MOTOR_TAIL 2 +#define CMH_ACT_YAW 3 + +extern void control_mixing_heewing_init(void); + +/** Direct manual control in plane style flight + */ +extern void control_mixing_heewing_manual(void); + +/** Stabilization in attitude direct mode + */ +extern void control_mixing_heewing_attitude_direct(void); +extern void control_mixing_heewing_attitude_direct_enter(void); +extern void control_mixing_heewing_attitude_plane(void); +extern void control_mixing_heewing_attitude_plane_enter(void); +extern void control_mixing_heewing_nav_enter(void); +extern void control_mixing_heewing_nav_run(void); + +#endif // CONTROL_MIXING_HEEWING_H