2015-11-24 04:24:04 -04:00
# include "Plane.h"
const AP_Param : : GroupInfo QuadPlane : : var_info [ ] = {
2015-12-26 06:40:40 -04:00
// @Param: ENABLE
// @DisplayName: Enable QuadPlane
2016-04-22 07:20:45 -03:00
// @Description: This enables QuadPlane functionality, assuming multicopter motors start on output 5. If this is set to 2 then when starting AUTO mode it will initially be in VTOL AUTO mode.
// @Values: 0:Disable,1:Enable,2:Enable VTOL AUTO
2015-12-26 06:40:40 -04:00
// @User: Standard
2015-12-29 07:43:43 -04:00
AP_GROUPINFO_FLAGS ( " ENABLE " , 1 , QuadPlane , enable , 0 , AP_PARAM_FLAG_ENABLE ) ,
2015-12-26 06:40:40 -04:00
// @Group: M_
2015-11-24 04:24:04 -04:00
// @Path: ../libraries/AP_Motors/AP_MotorsMulticopter.cpp
2017-02-27 02:39:46 -04:00
AP_SUBGROUPVARPTR ( motors , " M_ " , 2 , QuadPlane , plane . quadplane . motors_var_info ) ,
2015-11-24 04:24:04 -04:00
2016-02-15 04:45:56 -04:00
// 3 ~ 8 were used by quadplane attitude control PIDs
2015-11-24 04:24:04 -04:00
// @Param: ANGLE_MAX
// @DisplayName: Angle Max
2016-04-28 21:23:26 -03:00
// @Description: Maximum lean angle in all VTOL flight modes
2017-05-02 10:36:25 -03:00
// @Units: cdeg
2015-11-24 04:24:04 -04:00
// @Range: 1000 8000
// @User: Advanced
2016-04-28 21:23:26 -03:00
AP_GROUPINFO ( " ANGLE_MAX " , 10 , QuadPlane , aparm . angle_max , 3000 ) ,
2015-11-24 04:24:04 -04:00
// @Param: TRANSITION_MS
// @DisplayName: Transition time
2015-12-26 04:51:05 -04:00
// @Description: Transition time in milliseconds after minimum airspeed is reached
2017-05-02 10:36:25 -03:00
// @Units: ms
2015-11-24 04:24:04 -04:00
// @Range: 0 30000
// @User: Advanced
2015-12-26 06:40:40 -04:00
AP_GROUPINFO ( " TRANSITION_MS " , 11 , QuadPlane , transition_time_ms , 5000 ) ,
2015-11-24 04:24:04 -04:00
2018-01-24 08:14:15 -04:00
// 12 ~ 16 were used by position, velocity and acceleration PIDs
2018-12-18 00:52:22 -04:00
// @Group: P
2015-11-24 04:24:04 -04:00
// @Path: ../libraries/AC_AttitudeControl/AC_PosControl.cpp
2015-12-26 06:40:40 -04:00
AP_SUBGROUPPTR ( pos_control , " P " , 17 , QuadPlane , AC_PosControl ) ,
2015-12-26 03:45:42 -04:00
// @Param: VELZ_MAX
// @DisplayName: Pilot maximum vertical speed
// @Description: The maximum vertical velocity the pilot may request in cm/s
2017-05-02 10:36:25 -03:00
// @Units: cm/s
2015-12-26 03:45:42 -04:00
// @Range: 50 500
// @Increment: 10
// @User: Standard
2015-12-26 06:40:40 -04:00
AP_GROUPINFO ( " VELZ_MAX " , 18 , QuadPlane , pilot_velocity_z_max , 250 ) ,
2015-11-24 04:24:04 -04:00
2015-12-26 03:45:42 -04:00
// @Param: ACCEL_Z
// @DisplayName: Pilot vertical acceleration
// @Description: The vertical acceleration used when pilot is controlling the altitude
// @Units: cm/s/s
// @Range: 50 500
// @Increment: 10
// @User: Standard
2015-12-26 06:40:40 -04:00
AP_GROUPINFO ( " ACCEL_Z " , 19 , QuadPlane , pilot_accel_z , 250 ) ,
2015-12-26 03:45:42 -04:00
2015-12-26 04:51:05 -04:00
// @Group: WP_
// @Path: ../libraries/AC_WPNav/AC_WPNav.cpp
2015-12-26 06:40:40 -04:00
AP_SUBGROUPPTR ( wp_nav , " WP_ " , 20 , QuadPlane , AC_WPNav ) ,
// @Param: RC_SPEED
// @DisplayName: RC output speed in Hz
// @Description: This is the PWM refresh rate in Hz for QuadPlane quad motors
// @Units: Hz
// @Range: 50 500
// @Increment: 10
// @User: Standard
AP_GROUPINFO ( " RC_SPEED " , 21 , QuadPlane , rc_speed , 490 ) ,
// @Param: THR_MIN_PWM
// @DisplayName: Minimum PWM output
// @Description: This is the minimum PWM output for the quad motors
2017-09-18 15:05:45 -03:00
// @Units: PWM
2015-12-26 06:40:40 -04:00
// @Range: 800 2200
// @Increment: 1
// @User: Standard
AP_GROUPINFO ( " THR_MIN_PWM " , 22 , QuadPlane , thr_min_pwm , 1000 ) ,
// @Param: THR_MAX_PWM
// @DisplayName: Maximum PWM output
// @Description: This is the maximum PWM output for the quad motors
2017-09-18 15:05:45 -03:00
// @Units: PWM
2015-12-26 06:40:40 -04:00
// @Range: 800 2200
// @Increment: 1
// @User: Standard
2016-01-01 20:56:31 -04:00
AP_GROUPINFO ( " THR_MAX_PWM " , 23 , QuadPlane , thr_max_pwm , 2000 ) ,
2015-12-26 06:40:40 -04:00
// @Param: ASSIST_SPEED
// @DisplayName: Quadplane assistance speed
// @Description: This is the speed below which the quad motors will provide stability and lift assistance in fixed wing modes. Zero means no assistance except during transition
// @Units: m/s
// @Range: 0 100
// @Increment: 0.1
// @User: Standard
AP_GROUPINFO ( " ASSIST_SPEED " , 24 , QuadPlane , assist_speed , 0 ) ,
2016-01-02 00:25:49 -04:00
// @Param: YAW_RATE_MAX
// @DisplayName: Maximum yaw rate
2017-10-19 06:59:02 -03:00
// @Description: This is the maximum yaw rate for pilot input on rudder stick in degrees/second
2017-05-02 10:36:25 -03:00
// @Units: deg/s
2016-01-02 00:25:49 -04:00
// @Range: 50 500
// @Increment: 1
// @User: Standard
AP_GROUPINFO ( " YAW_RATE_MAX " , 25 , QuadPlane , yaw_rate_max , 100 ) ,
2016-01-02 02:55:56 -04:00
// @Param: LAND_SPEED
// @DisplayName: Land speed
// @Description: The descent speed for the final stage of landing in cm/s
// @Units: cm/s
// @Range: 30 200
// @Increment: 10
// @User: Standard
AP_GROUPINFO ( " LAND_SPEED " , 26 , QuadPlane , land_speed_cms , 50 ) ,
// @Param: LAND_FINAL_ALT
// @DisplayName: Land final altitude
// @Description: The altitude at which we should switch to Q_LAND_SPEED descent rate
// @Units: m
// @Range: 0.5 50
// @Increment: 0.1
// @User: Standard
AP_GROUPINFO ( " LAND_FINAL_ALT " , 27 , QuadPlane , land_final_alt , 6 ) ,
2016-01-06 00:19:57 -04:00
2016-06-16 00:02:41 -03:00
// 28 was used by THR_MID
2016-01-15 01:49:49 -04:00
// @Param: TRAN_PIT_MAX
// @DisplayName: Transition max pitch
// @Description: Maximum pitch during transition to auto fixed wing flight
// @User: Standard
// @Range: 0 30
2017-05-02 10:36:25 -03:00
// @Units: deg
2016-01-15 01:49:49 -04:00
// @Increment: 1
AP_GROUPINFO ( " TRAN_PIT_MAX " , 29 , QuadPlane , transition_pitch_max , 3 ) ,
2016-02-07 20:00:19 -04:00
2016-12-30 19:50:01 -04:00
// frame class was moved from 30 when consolidating AP_Motors classes
# define FRAME_CLASS_OLD_IDX 30
2016-02-07 20:00:19 -04:00
// @Param: FRAME_CLASS
// @DisplayName: Frame Class
// @Description: Controls major frame class for multicopter component
2017-06-10 05:41:22 -03:00
// @Values: 0:Undefined, 1:Quad, 2:Hexa, 3:Octa, 4:OctaQuad, 5:Y6, 7:Tri, 10: TailSitter
2016-02-07 20:00:19 -04:00
// @User: Standard
2016-12-30 19:50:01 -04:00
AP_GROUPINFO ( " FRAME_CLASS " , 46 , QuadPlane , frame_class , 1 ) ,
2016-02-07 20:00:19 -04:00
// @Param: FRAME_TYPE
// @DisplayName: Frame Type (+, X or V)
// @Description: Controls motor mixing for multicopter component
2019-04-22 15:00:10 -03:00
// @Values: 0:Plus, 1:X, 2:V, 3:H, 4:V-Tail, 5:A-Tail, 10:Y6B, 11:Y6F, 12:BetaFlightX, 13:DJIX, 14:ClockwiseX, 15: I
2016-02-07 20:00:19 -04:00
// @User: Standard
AP_GROUPINFO ( " FRAME_TYPE " , 31 , QuadPlane , frame_type , 1 ) ,
2016-04-20 02:13:20 -03:00
// @Param: VFWD_GAIN
// @DisplayName: Forward velocity hold gain
2016-06-03 22:04:03 -03:00
// @Description: Controls use of forward motor in vtol modes. If this is zero then the forward motor will not be used for position control in VTOL modes. A value of 0.05 is a good place to start if you want to use the forward motor for position control. No forward motor will be used in QSTABILIZE or QHOVER modes. Use QLOITER for position hold with the forward motor.
2016-04-20 02:13:20 -03:00
// @Range: 0 0.5
// @Increment: 0.01
// @User: Standard
AP_GROUPINFO ( " VFWD_GAIN " , 32 , QuadPlane , vel_forward . gain , 0 ) ,
2016-04-20 03:23:17 -03:00
// @Param: WVANE_GAIN
// @DisplayName: Weathervaning gain
2016-06-03 22:04:03 -03:00
// @Description: This controls the tendency to yaw to face into the wind. A value of 0.1 is to start with and will give a slow turn into the wind. Use a value of 0.4 for more rapid response. The weathervaning works by turning into the direction of roll.
2016-04-20 03:23:17 -03:00
// @Range: 0 1
// @Increment: 0.01
// @User: Standard
AP_GROUPINFO ( " WVANE_GAIN " , 33 , QuadPlane , weathervane . gain , 0 ) ,
2016-04-21 08:52:25 -03:00
// @Param: WVANE_MINROLL
// @DisplayName: Weathervaning min roll
// @Description: This set the minimum roll in degrees before active weathervaning will start. This may need to be larger if your aircraft has bad roll trim.
// @Range: 0 10
// @Increment: 0.1
// @User: Standard
AP_GROUPINFO ( " WVANE_MINROLL " , 34 , QuadPlane , weathervane . min_roll , 1 ) ,
2016-04-29 02:31:08 -03:00
// @Param: RTL_ALT
// @DisplayName: QRTL return altitude
// @Description: The altitude which QRTL mode heads to initially
// @Units: m
// @Range: 1 200
// @Increment: 1
// @User: Standard
AP_GROUPINFO ( " RTL_ALT " , 35 , QuadPlane , qrtl_alt , 15 ) ,
2016-04-29 03:31:22 -03:00
// @Param: RTL_MODE
// @DisplayName: VTOL RTL mode
2017-10-30 02:51:28 -03:00
// @Description: If this is set to 1 then an RTL will change to QRTL when within RTL_RADIUS meters of the RTL destination
2016-04-29 03:31:22 -03:00
// @Values: 0:Disabled,1:Enabled
// @User: Standard
AP_GROUPINFO ( " RTL_MODE " , 36 , QuadPlane , rtl_mode , 0 ) ,
2016-05-01 05:07:52 -03:00
// @Param: TILT_MASK
// @DisplayName: Tiltrotor mask
// @Description: This is a bitmask of motors that are tiltable in a tiltrotor (or tiltwing). The mask is in terms of the standard motor order for the frame type.
// @User: Standard
AP_GROUPINFO ( " TILT_MASK " , 37 , QuadPlane , tilt . tilt_mask , 0 ) ,
2017-02-24 06:05:27 -04:00
// @Param: TILT_RATE_UP
// @DisplayName: Tiltrotor upwards tilt rate
// @Description: This is the maximum speed at which the motor angle will change for a tiltrotor when moving from forward flight to hover
2017-05-02 10:36:25 -03:00
// @Units: deg/s
2016-05-01 05:07:52 -03:00
// @Increment: 1
// @Range: 10 300
// @User: Standard
2017-02-24 06:05:27 -04:00
AP_GROUPINFO ( " TILT_RATE_UP " , 38 , QuadPlane , tilt . max_rate_up_dps , 40 ) ,
2016-05-01 05:07:52 -03:00
// @Param: TILT_MAX
// @DisplayName: Tiltrotor maximum VTOL angle
// @Description: This is the maximum angle of the tiltable motors at which multicopter control will be enabled. Beyond this angle the plane will fly solely as a fixed wing aircraft and the motors will tilt to their maximum angle at the TILT_RATE
2017-05-02 10:36:25 -03:00
// @Units: deg
2016-05-01 05:07:52 -03:00
// @Increment: 1
// @Range: 20 80
// @User: Standard
AP_GROUPINFO ( " TILT_MAX " , 39 , QuadPlane , tilt . max_angle_deg , 45 ) ,
2016-05-05 19:27:47 -03:00
// @Param: GUIDED_MODE
// @DisplayName: Enable VTOL in GUIDED mode
// @Description: This enables use of VTOL in guided mode. When enabled the aircraft will switch to VTOL flight when the guided destination is reached and hover at the destination.
// @Values: 0:Disabled,1:Enabled
// @User: Standard
AP_GROUPINFO ( " GUIDED_MODE " , 40 , QuadPlane , guided_mode , 0 ) ,
2016-05-07 19:11:00 -03:00
2016-06-16 00:02:41 -03:00
// 41 was used by THR_MIN
2016-06-02 00:10:39 -03:00
// @Param: ESC_CAL
// @DisplayName: ESC Calibration
// @Description: This is used to calibrate the throttle range of the VTOL motors. Please read http://ardupilot.org/plane/docs/quadplane-esc-calibration.html before using. This parameter is automatically set back to 0 on every boot. This parameter only takes effect in QSTABILIZE mode. When set to 1 the output of all motors will come directly from the throttle stick when armed, and will be zero when disarmed. When set to 2 the output of all motors will be maximum when armed and zero when disarmed. Make sure you remove all properllers before using.
// @Values: 0:Disabled,1:ThrottleInput,2:FullInput
// @User: Standard
AP_GROUPINFO ( " ESC_CAL " , 42 , QuadPlane , esc_calibration , 0 ) ,
2016-06-03 19:35:09 -03:00
// @Param: VFWD_ALT
// @DisplayName: Forward velocity alt cutoff
// @Description: Controls altitude to disable forward velocity assist when below this relative altitude. This is useful to keep the forward velocity propeller from hitting the ground. Rangefinder height data is incorporated when available.
2018-12-27 10:48:30 -04:00
// @Units: m
2016-06-03 19:35:09 -03:00
// @Range: 0 10
// @Increment: 0.25
// @User: Standard
AP_GROUPINFO ( " VFWD_ALT " , 43 , QuadPlane , vel_forward_alt_cutoff , 0 ) ,
2016-07-25 02:46:17 -03:00
// @Param: LAND_ICE_CUT
// @DisplayName: Cut IC engine on landing
// @Description: This controls stopping an internal combustion engine in the final landing stage of a VTOL. This is important for aircraft where the forward thrust engine may experience prop-strike if left running during landing. This requires the engine controls are enabled using the ICE_* parameters.
// @Values: 0:Disabled,1:Enabled
// @User: Standard
AP_GROUPINFO ( " LAND_ICE_CUT " , 44 , QuadPlane , land_icengine_cut , 1 ) ,
2016-04-20 03:23:17 -03:00
2016-08-29 03:44:54 -03:00
// @Param: ASSIST_ANGLE
// @DisplayName: Quadplane assistance angle
// @Description: This is the angular error in attitude beyond which the quadplane VTOL motors will provide stability assistance. This will only be used if Q_ASSIST_SPEED is also non-zero. Assistance will be given if the attitude is outside the normal attitude limits by at least 5 degrees and the angular error in roll or pitch is greater than this angle for at least 1 second. Set to zero to disable angle assistance.
2017-05-02 10:36:25 -03:00
// @Units: deg
2016-08-29 03:44:54 -03:00
// @Range: 0 90
// @Increment: 1
// @User: Standard
AP_GROUPINFO ( " ASSIST_ANGLE " , 45 , QuadPlane , assist_angle , 30 ) ,
2016-07-03 09:02:38 -03:00
// @Param: TILT_TYPE
// @DisplayName: Tiltrotor type
2019-03-18 21:00:25 -03:00
// @Description: This is the type of tiltrotor when TILT_MASK is non-zero. A continuous tiltrotor can tilt the rotors to any angle on demand. A binary tiltrotor assumes a retract style servo where the servo is either fully forward or fully up. In both cases the servo can't move faster than Q_TILT_RATE. A vectored yaw tiltrotor will use the tilt of the motors to control yaw in hover, Bicopter tiltrottor must use the tailsitter frame class (10)
// @Values: 0:Continuous,1:Binary,2:VectoredYaw,3:Bicopter
2017-01-22 18:29:51 -04:00
AP_GROUPINFO ( " TILT_TYPE " , 47 , QuadPlane , tilt . tilt_type , TILT_TYPE_CONTINUOUS ) ,
2017-02-11 04:12:56 -04:00
2017-02-24 01:47:09 -04:00
// @Param: TAILSIT_ANGLE
2017-02-11 04:12:56 -04:00
// @DisplayName: Tailsitter transition angle
// @Description: This is the angle at which tailsitter aircraft will change from VTOL control to fixed wing control.
// @Range: 5 80
2017-11-05 02:25:00 -04:00
AP_GROUPINFO ( " TAILSIT_ANGLE " , 48 , QuadPlane , tailsitter . transition_angle , 45 ) ,
2017-02-24 06:05:27 -04:00
// @Param: TILT_RATE_DN
// @DisplayName: Tiltrotor downwards tilt rate
// @Description: This is the maximum speed at which the motor angle will change for a tiltrotor when moving from hover to forward flight. When this is zero the Q_TILT_RATE_UP value is used.
2017-05-02 10:36:25 -03:00
// @Units: deg/s
2017-02-24 06:05:27 -04:00
// @Increment: 1
// @Range: 10 300
// @User: Standard
AP_GROUPINFO ( " TILT_RATE_DN " , 49 , QuadPlane , tilt . max_rate_down_dps , 0 ) ,
2017-02-24 01:47:09 -04:00
// @Param: TAILSIT_INPUT
// @DisplayName: Tailsitter input type
2019-03-03 13:54:23 -04:00
// @Description: This controls whether stick input when hovering as a tailsitter follows the conventions for fixed wing hovering or multicopter hovering. When multicopter input is selected the roll stick will roll the aircraft in earth frame and yaw stick will yaw in earth frame. When using fixed wing input the roll and yaw sticks are swapped so that the roll stick controls earth-frame yaw and rudder controls earth-frame roll. When body-frame roll is selected, the yaw stick controls earth-frame yaw rate and the roll stick controls roll in the tailsitter's body frame.
2019-03-16 09:51:05 -03:00
// @Values: 0:MultiCopterInput,1:FixedWingInput,2:BodyFrameRoll_M,3:BodyFrameRoll_P
2017-02-24 01:47:09 -04:00
AP_GROUPINFO ( " TAILSIT_INPUT " , 50 , QuadPlane , tailsitter . input_type , TAILSITTER_INPUT_MULTICOPTER ) ,
2017-02-24 03:02:34 -04:00
// @Param: TAILSIT_MASK
// @DisplayName: Tailsitter input mask
// @Description: This controls what channels have full manual control when hovering as a tailsitter and the Q_TAILSIT_MASKCH channel in high. This can be used to teach yourself to prop-hang a 3D plane by learning one or more channels at a time.
// @Bitmask: 0:Aileron,1:Elevator,2:Throttle,3:Rudder
AP_GROUPINFO ( " TAILSIT_MASK " , 51 , QuadPlane , tailsitter . input_mask , 0 ) ,
// @Param: TAILSIT_MASKCH
// @DisplayName: Tailsitter input mask channel
// @Description: This controls what input channel will activate the Q_TAILSIT_MASK mask. When this channel goes above 1700 then the pilot will have direct manual control of the output channels specified in Q_TAILSIT_MASK. Set to zero to disable.
// @Values: 0:Disabled,1:Channel1,2:Channel2,3:Channel3,4:Channel4,5:Channel5,6:Channel6,7:Channel7,8:Channel8
AP_GROUPINFO ( " TAILSIT_MASKCH " , 52 , QuadPlane , tailsitter . input_mask_chan , 0 ) ,
2017-04-09 21:01:58 -03:00
// @Param: TAILSIT_VFGAIN
// @DisplayName: Tailsitter vector thrust gain in forward flight
// @Description: This controls the amount of vectored thrust control used in forward flight for a vectored tailsitter
// @Range: 0 1
// @Increment: 0.01
AP_GROUPINFO ( " TAILSIT_VFGAIN " , 53 , QuadPlane , tailsitter . vectored_forward_gain , 0 ) ,
// @Param: TAILSIT_VHGAIN
// @DisplayName: Tailsitter vector thrust gain in hover
// @Description: This controls the amount of vectored thrust control used in hover for a vectored tailsitter
// @Range: 0 1
// @Increment: 0.01
AP_GROUPINFO ( " TAILSIT_VHGAIN " , 54 , QuadPlane , tailsitter . vectored_hover_gain , 0.5 ) ,
2017-04-22 22:35:25 -03:00
// @Param: TILT_YAW_ANGLE
// @DisplayName: Tilt minimum angle for vectored yaw
2019-03-18 21:00:25 -03:00
// @Description: This is the angle of the tilt servos when in VTOL mode and at minimum output. This needs to be set for Q_TILT_TYPE=3 to enable vectored control for yaw of tricopter tilt quadplanes. This is also used to limit the forwards travel of bicopter tilts when in VTOL modes
2017-04-22 22:35:25 -03:00
// @Range: 0 30
AP_GROUPINFO ( " TILT_YAW_ANGLE " , 55 , QuadPlane , tilt . tilt_yaw_angle , 0 ) ,
2017-05-04 18:34:27 -03:00
// @Param: TAILSIT_VHPOW
// @DisplayName: Tailsitter vector thrust gain power
// @Description: This controls the amount of extra pitch given to the vectored control when at high pitch errors
// @Range: 0 4
// @Increment: 0.1
AP_GROUPINFO ( " TAILSIT_VHPOW " , 56 , QuadPlane , tailsitter . vectored_hover_power , 2.5 ) ,
2017-10-20 01:39:44 -03:00
// @Param: MAV_TYPE
// @DisplayName: MAVLink type identifier
// @Description: This controls the mavlink type given in HEARTBEAT messages. For some GCS types a particular setting will be needed for correct operation.
// @Values: 0:AUTO,1:FIXED_WING,2:QUADROTOR,3:COAXIAL,4:HELICOPTER,7:AIRSHIP,8:FREE_BALLOON,9:ROCKET,10:GROUND_ROVER,11:SURFACE_BOAT,12:SUBMARINE,16:FLAPPING_WING,17:KITE,19:VTOL_DUOROTOR,20:VTOL_QUADROTOR,21:VTOL_TILTROTOR
AP_GROUPINFO ( " MAV_TYPE " , 57 , QuadPlane , mav_type , 0 ) ,
2017-10-29 03:31:09 -03:00
// @Param: OPTIONS
// @DisplayName: quadplane options
2019-06-11 15:00:32 -03:00
// @Description: This provides a set of additional control options for quadplanes. LevelTransition means that the wings should be held level to within LEVEL_ROLL_LIMIT degrees during transition to fixed wing flight, and the vehicle will not use the vertical lift motors to climb during the transition. If AllowFWTakeoff bit is not set then fixed wing takeoff on quadplanes will instead perform a VTOL takeoff. If AllowFWLand bit is not set then fixed wing land on quadplanes will instead perform a VTOL land. If respect takeoff frame is not set the vehicle will interpret all takeoff waypoints as an altitude above the corrent position. When Use QRTL is set it will replace QLAND with QRTL for failsafe actions when in VTOL modes.
// @Bitmask: 0:LevelTransition,1:AllowFWTakeoff,2:AllowFWLand,3:Respect takeoff frame types,4:Use a fixed wing approach for VTOL landings,5:Use QRTL instead of QLAND for failsafe when in VTOL modes.
2017-10-29 03:31:09 -03:00
AP_GROUPINFO ( " OPTIONS " , 58 , QuadPlane , options , 0 ) ,
2017-11-05 05:44:42 -04:00
AP_SUBGROUPEXTENSION ( " " , 59 , QuadPlane , var_info2 ) ,
2018-12-07 03:52:05 -04:00
2015-11-24 04:24:04 -04:00
AP_GROUPEND
} ;
2017-11-05 05:44:42 -04:00
// second table of user settable parameters for quadplanes, this
// allows us to go beyond the 64 parameter limit
const AP_Param : : GroupInfo QuadPlane : : var_info2 [ ] = {
// @Param: TRANS_DECEL
// @DisplayName: Transition deceleration
// @Description: This is deceleration rate that will be used in calculating the stopping distance when transitioning from fixed wing flight to multicopter flight.
// @Units: m/s/s
// @Increment: 0.1
// @Range: 0.2 5
// @User: Standard
AP_GROUPINFO ( " TRANS_DECEL " , 1 , QuadPlane , transition_decel , 2.0 ) ,
2018-03-27 23:24:05 -03:00
// @Group: LOIT_
// @Path: ../libraries/AC_WPNav/AC_Loiter.cpp
AP_SUBGROUPPTR ( loiter_nav , " LOIT_ " , 2 , QuadPlane , AC_Loiter ) ,
2018-04-15 08:31:53 -03:00
// @Param: TAILSIT_THSCMX
// @DisplayName: Maximum control throttle scaling value
// @Description: Maximum value of throttle scaling for tailsitter velocity scaling, reduce this value to remove low thorottle D ossilaitons
// @Range: 1 5
// @User: Standard
AP_GROUPINFO ( " TAILSIT_THSCMX " , 3 , QuadPlane , tailsitter . throttle_scale_max , 5 ) ,
2018-09-25 12:13:34 -03:00
2018-11-12 01:00:00 -04:00
// @Param: TRIM_PITCH
2018-09-25 12:13:34 -03:00
// @DisplayName: Quadplane AHRS trim pitch
2018-11-12 01:00:00 -04:00
// @Description: This sets the compensation for the pitch angle trim difference between forward and vertical flight pitch, NOTE! this is relative to forward flight trim not mounting locaiton. For tailsitters this is relative to a baseline of 90 degrees.
2018-09-25 12:13:34 -03:00
// @Units: deg
2018-11-12 01:00:00 -04:00
// @Range: -10 +10
// @Increment: 0.1
// @User: Advanced
2018-09-25 12:13:34 -03:00
// @RebootRequired: True
2018-11-12 01:00:00 -04:00
AP_GROUPINFO ( " TRIM_PITCH " , 4 , QuadPlane , ahrs_trim_pitch , 0 ) ,
2018-09-25 12:13:34 -03:00
2018-10-06 11:40:27 -03:00
// @Param: TAILSIT_RLL_MX
// @DisplayName: Maximum Roll angle
// @Description: Maximum Allowed roll angle for tailsitters. If this is zero then Q_ANGLE_MAX is used.
// @Units: deg
// @Range: 0 80
// @User: Standard
AP_GROUPINFO ( " TAILSIT_RLL_MX " , 5 , QuadPlane , tailsitter . max_roll_angle , 0 ) ,
2018-12-07 03:52:05 -04:00
# if QAUTOTUNE_ENABLED
// @Group: AUTOTUNE_
// @Path: qautotune.cpp
AP_SUBGROUPINFO ( qautotune , " AUTOTUNE_ " , 6 , QuadPlane , QAutoTune ) ,
# endif
2018-11-16 17:53:43 -04:00
// @Param: FW_LND_APR_RAD
// @DisplayName: Quadplane fixed wing landing approach radius
// @Description: This provides the radius used, when using a fixed wing landing approach. If set to 0 then the WP_LOITER_RAD will be selected.
// @Units: m
// @Range: 0 200
// @Increment: 5
// @User: Advanced
AP_GROUPINFO ( " FW_LND_APR_RAD " , 7 , QuadPlane , fw_land_approach_radius , 0 ) ,
2018-12-14 22:40:43 -04:00
// @Param: TRANS_FAIL
// @DisplayName: Quadplane transition failure time
// @Description: Maximum time allowed for forward transitions, exceeding this time will cancel the transition and the aircraft will immediately change to QLAND. 0 for no limit.
// @Units: s
// @Range: 0 20
// @Increment: 1
// @User: Advanced
AP_GROUPINFO ( " TRANS_FAIL " , 8 , QuadPlane , transition_failure , 0 ) ,
2019-01-17 13:54:15 -04:00
// @Param: TAILSIT_MOTMX
// @DisplayName: Tailsiter mask
// @Description: Bitmask of motors to remain active in forward flight for a 'copter' tailsitter. Non-zero indicates airframe is a tailsitter which pitches forward 90 degrees in forward flight modes.
// @User: Standard
// @Bitmask: 0:Motor 1,1:Motor 2,2:Motor 3,3:Motor 4, 4:Motor 5,5:Motor 6,6:Motor 7,7:Motor 8
AP_GROUPINFO ( " TAILSIT_MOTMX " , 9 , QuadPlane , tailsitter . motor_mask , 0 ) ,
2019-04-06 15:38:40 -03:00
// @Param: THROTTLE_EXPO
// @DisplayName: Throttle expo strength
// @Description: Amount of curvature in throttle curve: 0 is linear, 1 is cubic
// @Range: 0 1
// @Increment: .1
// @User: Advanced
AP_GROUPINFO ( " THROTTLE_EXPO " , 10 , QuadPlane , throttle_expo , 0.2 ) ,
2019-03-24 00:35:13 -03:00
// @Param: ACRO_RLL_RATE
// @DisplayName: QACRO mode roll rate
// @Description: The maximum roll rate at full stick deflection in QACRO mode
// @Units: deg/s
// @Range: 10 500
// @Increment: 1
// @User: Standard
AP_GROUPINFO ( " ACRO_RLL_RATE " , 11 , QuadPlane , acro_roll_rate , 360 ) ,
// @Param: ACRO_PIT_RATE
// @DisplayName: QACRO mode pitch rate
// @Description: The maximum pitch rate at full stick deflection in QACRO mode
// @Units: deg/s
// @Range: 10 500
// @Increment: 1
// @User: Standard
AP_GROUPINFO ( " ACRO_PIT_RATE " , 12 , QuadPlane , acro_pitch_rate , 180 ) ,
// @Param: ACRO_YAW_RATE
// @DisplayName: QACRO mode yaw rate
// @Description: The maximum yaw rate at full stick deflection in QACRO mode
// @Units: deg/s
// @Range: 10 500
// @Increment: 1
// @User: Standard
AP_GROUPINFO ( " ACRO_YAW_RATE " , 13 , QuadPlane , acro_yaw_rate , 90 ) ,
2018-12-15 01:23:56 -04:00
// @Param: TKOFF_FAIL_SCL
// @DisplayName: Takeoff time failure scalar
// @Description: Scalar for how long past the expected takeoff time a takeoff should be considered as failed and the vehicle will switch to QLAND. If set to 0 there is no limit on takeoff time.
// @Range: 1.1 5.0
// @Increment: 5.1
// @User: Advanced
AP_GROUPINFO ( " TKOFF_FAIL_SCL " , 14 , QuadPlane , takeoff_failure_scalar , 0 ) ,
2018-12-15 16:47:49 -04:00
// @Param: TKOFF_ARSP_LIM
// @DisplayName: Takeoff airspeed limit
// @Description: Airspeed limit during takeoff. If the airspeed exceeds this level the vehicle will switch to QLAND. This is useful for ensuring that you don't takeoff into excessively strong wind. If set to 0 there is no limit on airspeed during takeoff.
// @Units: m/s
// @Range: 0 20
// @Increment: 1
// @User: Advanced
AP_GROUPINFO ( " TKOFF_ARSP_LIM " , 15 , QuadPlane , maximum_takeoff_airspeed , 0 ) ,
2019-11-01 07:44:23 -03:00
// @Param: ASSIST_ALT
// @DisplayName: Quadplane assistance altitude
// @Description: This is the altitude below which quadplane assistance will be triggered. This acts the same way as Q_ASSIST_ANGLE and Q_ASSIST_SPEED, but triggers if the aircraft drops below the given altitude while the VTOL motors are not running. A value of zero disables this feature. The altutude is calculated as being above ground level. The height above ground is given from a Lidar used if available and RNGFND_LANDING=1. Otherwise it comes from terrain data if TERRAIN_FOLLOW=1 and comes from height above home otherwise.
// @Units: m
// @Range: 0 120
// @Increment: 1
// @User: Standard
AP_GROUPINFO ( " ASSIST_ALT " , 16 , QuadPlane , assist_alt , 0 ) ,
2017-11-05 05:44:42 -04:00
AP_GROUPEND
} ;
2017-04-03 19:18:30 -03:00
/*
defaults for all quadplanes
*/
2018-11-28 20:54:55 -04:00
static const struct AP_Param : : defaults_table_struct defaults_table [ ] = {
2016-04-01 02:44:49 -03:00
{ " Q_A_RAT_RLL_P " , 0.25 } ,
{ " Q_A_RAT_RLL_I " , 0.25 } ,
2019-06-27 06:36:57 -03:00
{ " Q_A_RAT_RLL_FLTD " , 10.0 } ,
2016-04-01 02:44:49 -03:00
{ " Q_A_RAT_PIT_P " , 0.25 } ,
{ " Q_A_RAT_PIT_I " , 0.25 } ,
2019-06-27 06:36:57 -03:00
{ " Q_A_RAT_PIT_FLTD " , 10.0 } ,
2017-02-13 06:35:41 -04:00
{ " Q_M_SPOOL_TIME " , 0.25 } ,
2018-06-09 04:41:02 -03:00
{ " Q_LOIT_ANG_MAX " , 15.0 } ,
{ " Q_LOIT_ACC_MAX " , 250.0 } ,
{ " Q_LOIT_BRK_ACCEL " , 50.0 } ,
{ " Q_LOIT_BRK_JERK " , 250 } ,
{ " Q_LOIT_SPEED " , 500 } ,
2016-04-01 02:40:06 -03:00
} ;
2017-04-03 19:18:30 -03:00
/*
extra defaults for tailsitters
*/
2018-11-28 20:54:55 -04:00
static const struct AP_Param : : defaults_table_struct defaults_table_tailsitter [ ] = {
2017-04-03 19:18:30 -03:00
{ " KFF_RDDRMIX " , 0.02 } ,
{ " Q_A_RAT_PIT_FF " , 0.2 } ,
{ " Q_A_RAT_YAW_FF " , 0.2 } ,
2019-09-08 17:18:06 -03:00
{ " Q_A_RAT_YAW_I " , 0.18 } ,
{ " Q_A_ANGLE_BOOST " , 0 } ,
2017-04-03 19:18:30 -03:00
{ " LIM_PITCH_MAX " , 3000 } ,
{ " LIM_PITCH_MIN " , - 3000 } ,
{ " MIXING_GAIN " , 1.0 } ,
2017-04-05 00:37:49 -03:00
{ " RUDD_DT_GAIN " , 10 } ,
2017-11-05 02:25:00 -04:00
{ " Q_TRANSITION_MS " , 2000 } ,
2017-04-03 19:18:30 -03:00
} ;
2018-01-24 00:40:49 -04:00
/*
conversion table for quadplane parameters
*/
const AP_Param : : ConversionInfo q_conversion_table [ ] = {
{ Parameters : : k_param_quadplane , 4044 , AP_PARAM_FLOAT , " Q_P_POSZ_P " } , // Q_PZ_P
{ Parameters : : k_param_quadplane , 4045 , AP_PARAM_FLOAT , " Q_P_POSXY_P " } , // Q_PXY_P
{ Parameters : : k_param_quadplane , 4046 , AP_PARAM_FLOAT , " Q_P_VELXY_P " } , // Q_VXY_P
{ Parameters : : k_param_quadplane , 78 , AP_PARAM_FLOAT , " Q_P_VELXY_I " } , // Q_VXY_I
{ Parameters : : k_param_quadplane , 142 , AP_PARAM_FLOAT , " Q_P_VELXY_IMAX " } , // Q_VXY_IMAX
{ Parameters : : k_param_quadplane , 206 , AP_PARAM_FLOAT , " Q_P_VELXY_FILT " } , // Q_VXY_FILT_HZ
{ Parameters : : k_param_quadplane , 4047 , AP_PARAM_FLOAT , " Q_P_VELZ_P " } , // Q_VZ_P
2018-01-27 02:41:35 -04:00
{ Parameters : : k_param_quadplane , 4048 , AP_PARAM_FLOAT , " Q_P_ACCZ_P " } , // Q_AZ_P
{ Parameters : : k_param_quadplane , 80 , AP_PARAM_FLOAT , " Q_P_ACCZ_I " } , // Q_AZ_I
{ Parameters : : k_param_quadplane , 144 , AP_PARAM_FLOAT , " Q_P_ACCZ_D " } , // Q_AZ_D
{ Parameters : : k_param_quadplane , 336 , AP_PARAM_FLOAT , " Q_P_ACCZ_IMAX " } , // Q_AZ_IMAX
2019-06-27 06:36:57 -03:00
{ Parameters : : k_param_quadplane , 400 , AP_PARAM_FLOAT , " Q_P_ACCZ_FLTD " } , // Q_AZ_FILT
2018-01-27 02:41:35 -04:00
{ Parameters : : k_param_quadplane , 464 , AP_PARAM_FLOAT , " Q_P_ACCZ_FF " } , // Q_AZ_FF
2018-03-27 23:24:05 -03:00
{ Parameters : : k_param_quadplane , 276 , AP_PARAM_FLOAT , " Q_LOIT_SPEED " } , // Q_WP_LOIT_SPEED
{ Parameters : : k_param_quadplane , 468 , AP_PARAM_FLOAT , " Q_LOIT_BRK_JERK " } , // Q_WP_LOIT_JERK
{ Parameters : : k_param_quadplane , 532 , AP_PARAM_FLOAT , " Q_LOIT_ACC_MAX " } , // Q_WP_LOIT_MAXA
{ Parameters : : k_param_quadplane , 596 , AP_PARAM_FLOAT , " Q_LOIT_BRK_ACCEL " } , // Q_WP_LOIT_MINA
2019-06-27 06:36:57 -03:00
{ Parameters : : k_param_q_attitude_control , 385 , AP_PARAM_FLOAT , " Q_A_RAT_RLL_FLTD " } , // Q_A_RAT_RLL_FILT
{ Parameters : : k_param_q_attitude_control , 386 , AP_PARAM_FLOAT , " Q_A_RAT_PIT_FLTD " } , // Q_A_RAT_PIT_FILT
{ Parameters : : k_param_q_attitude_control , 387 , AP_PARAM_FLOAT , " Q_A_RAT_YAW_FLTE " } , // Q_A_RAT_YAW_FILT
{ Parameters : : k_param_q_attitude_control , 449 , AP_PARAM_FLOAT , " Q_A_RAT_RLL_FF " } , // Q_A_RAT_RLL_FF
{ Parameters : : k_param_q_attitude_control , 450 , AP_PARAM_FLOAT , " Q_A_RAT_PIT_FF " } , // Q_A_RAT_PIT_FF
{ Parameters : : k_param_q_attitude_control , 451 , AP_PARAM_FLOAT , " Q_A_RAT_YAW_FF " } , // Q_A_RAT_YAW_FILT
2018-01-24 00:40:49 -04:00
} ;
2015-11-24 04:24:04 -04:00
QuadPlane : : QuadPlane ( AP_AHRS_NavEKF & _ahrs ) :
ahrs ( _ahrs )
{
AP_Param : : setup_object_defaults ( this , var_info ) ;
2017-11-05 05:44:42 -04:00
AP_Param : : setup_object_defaults ( this , var_info2 ) ;
2015-11-24 04:24:04 -04:00
}
2016-02-07 20:00:19 -04:00
// setup default motors for the frame class
void QuadPlane : : setup_default_channels ( uint8_t num_motors )
{
for ( uint8_t i = 0 ; i < num_motors ; i + + ) {
2017-10-23 00:16:14 -03:00
SRV_Channels : : set_aux_channel_default ( SRV_Channels : : get_motor_function ( i ) , CH_5 + i ) ;
2016-02-07 20:00:19 -04:00
}
}
2016-01-06 03:17:08 -04:00
bool QuadPlane : : setup ( void )
2015-11-24 04:24:04 -04:00
{
2016-01-06 03:17:08 -04:00
if ( initialised ) {
return true ;
}
if ( ! enable | | hal . util - > get_soft_armed ( ) ) {
return false ;
2015-12-26 06:40:40 -04:00
}
2016-04-02 17:49:38 -03:00
float loop_delta_t = 1.0 / plane . scheduler . get_loop_rate_hz ( ) ;
2016-12-30 19:50:01 -04:00
2017-01-09 04:19:42 -04:00
enum AP_Motors : : motor_frame_class motor_class ;
2017-02-11 04:12:56 -04:00
enum Rotation rotation = ROTATION_NONE ;
2019-02-04 13:14:01 -04:00
2016-12-30 19:50:01 -04:00
/*
cope with upgrade from old AP_Motors values for frame_class
*/
AP_Int8 old_class ;
const AP_Param : : ConversionInfo cinfo { Parameters : : k_param_quadplane , FRAME_CLASS_OLD_IDX , AP_PARAM_INT8 , nullptr } ;
if ( AP_Param : : find_old_parameter ( & cinfo , & old_class ) & & ! frame_class . load ( ) ) {
uint8_t new_value = 0 ;
// map from old values to new values
switch ( old_class . get ( ) ) {
case 0 :
new_value = AP_Motors : : MOTOR_FRAME_QUAD ;
break ;
case 1 :
new_value = AP_Motors : : MOTOR_FRAME_HEXA ;
break ;
case 2 :
new_value = AP_Motors : : MOTOR_FRAME_OCTA ;
break ;
case 3 :
new_value = AP_Motors : : MOTOR_FRAME_OCTAQUAD ;
break ;
case 4 :
new_value = AP_Motors : : MOTOR_FRAME_Y6 ;
break ;
}
frame_class . set_and_save ( new_value ) ;
}
2016-04-02 17:49:38 -03:00
2016-01-06 03:17:08 -04:00
if ( hal . util - > available_memory ( ) <
2018-08-24 02:42:37 -03:00
4096 + sizeof ( * motors ) + sizeof ( * attitude_control ) + sizeof ( * pos_control ) + sizeof ( * wp_nav ) + sizeof ( * ahrs_view ) + sizeof ( * loiter_nav ) ) {
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Not enough memory for quadplane " ) ;
2016-01-06 03:17:08 -04:00
goto failed ;
}
2016-02-07 20:00:19 -04:00
2015-12-26 06:40:40 -04:00
/*
dynamically allocate the key objects for quadplane . This ensures
that the objects don ' t affect the vehicle unless enabled and
also saves memory when not in use
*/
2017-01-09 04:19:42 -04:00
motor_class = ( enum AP_Motors : : motor_frame_class ) frame_class . get ( ) ;
switch ( motor_class ) {
2016-12-12 09:08:16 -04:00
case AP_Motors : : MOTOR_FRAME_QUAD :
2016-02-07 20:00:19 -04:00
setup_default_channels ( 4 ) ;
break ;
2016-12-12 09:08:16 -04:00
case AP_Motors : : MOTOR_FRAME_HEXA :
2016-02-07 20:00:19 -04:00
setup_default_channels ( 6 ) ;
break ;
2016-12-12 09:08:16 -04:00
case AP_Motors : : MOTOR_FRAME_OCTA :
case AP_Motors : : MOTOR_FRAME_OCTAQUAD :
2016-02-07 20:00:19 -04:00
setup_default_channels ( 8 ) ;
break ;
2016-12-12 09:08:16 -04:00
case AP_Motors : : MOTOR_FRAME_Y6 :
2016-04-21 21:28:06 -03:00
setup_default_channels ( 7 ) ;
2016-03-12 19:04:25 -04:00
break ;
2017-01-09 04:19:42 -04:00
case AP_Motors : : MOTOR_FRAME_TRI :
SRV_Channels : : set_default_function ( CH_5 , SRV_Channel : : k_motor1 ) ;
SRV_Channels : : set_default_function ( CH_6 , SRV_Channel : : k_motor2 ) ;
SRV_Channels : : set_default_function ( CH_8 , SRV_Channel : : k_motor4 ) ;
SRV_Channels : : set_default_function ( CH_11 , SRV_Channel : : k_motor7 ) ;
2017-02-07 19:47:17 -04:00
AP_Param : : set_frame_type_flags ( AP_PARAM_FRAME_TRICOPTER ) ;
2017-01-09 04:19:42 -04:00
break ;
2017-02-11 01:50:03 -04:00
case AP_Motors : : MOTOR_FRAME_TAILSITTER :
break ;
2016-02-07 20:00:19 -04:00
default :
2017-03-18 08:10:20 -03:00
hal . console - > printf ( " Unknown frame class %u - using QUAD \n " , ( unsigned ) frame_class . get ( ) ) ;
frame_class . set ( AP_Motors : : MOTOR_FRAME_QUAD ) ;
setup_default_channels ( 4 ) ;
break ;
2016-02-07 20:00:19 -04:00
}
2017-02-11 01:50:03 -04:00
2019-01-17 13:54:15 -04:00
if ( tailsitter . motor_mask = = 0 ) {
// this is a normal quadplane
switch ( motor_class ) {
case AP_Motors : : MOTOR_FRAME_TRI :
motors = new AP_MotorsTri ( plane . scheduler . get_loop_rate_hz ( ) , rc_speed ) ;
motors_var_info = AP_MotorsTri : : var_info ;
break ;
case AP_Motors : : MOTOR_FRAME_TAILSITTER :
// this is a duo-motor tailsitter (vectored thrust if tilt.tilt_mask != 0)
motors = new AP_MotorsTailsitter ( plane . scheduler . get_loop_rate_hz ( ) , rc_speed ) ;
motors_var_info = AP_MotorsTailsitter : : var_info ;
2019-03-18 21:00:25 -03:00
if ( tilt . tilt_type ! = TILT_TYPE_BICOPTER ) {
rotation = ROTATION_PITCH_90 ;
}
2019-01-17 13:54:15 -04:00
break ;
default :
motors = new AP_MotorsMatrix ( plane . scheduler . get_loop_rate_hz ( ) , rc_speed ) ;
motors_var_info = AP_MotorsMatrix : : var_info ;
break ;
}
} else {
// this is a copter tailsitter with motor layout specified by frame_class and frame_type
// tilting motors are not supported (tiltrotor control variables are ignored)
if ( tilt . tilt_mask ! = 0 ) {
hal . console - > printf ( " Warning tilting motors not supported, setting tilt_mask to zero \n " ) ;
tilt . tilt_mask . set ( 0 ) ;
}
2017-02-11 04:12:56 -04:00
rotation = ROTATION_PITCH_90 ;
2019-01-17 13:54:15 -04:00
motors = new AP_MotorsMatrixTS ( plane . scheduler . get_loop_rate_hz ( ) , rc_speed ) ;
motors_var_info = AP_MotorsMatrixTS : : var_info ;
2017-01-09 04:19:42 -04:00
}
2019-05-01 19:44:00 -03:00
2016-10-06 09:47:33 -03:00
const static char * strUnableToAllocate = " Unable to allocate " ;
2015-12-26 06:40:40 -04:00
if ( ! motors ) {
2016-10-06 09:47:33 -03:00
hal . console - > printf ( " %s motors \n " , strUnableToAllocate ) ;
2016-01-06 03:17:08 -04:00
goto failed ;
2015-12-26 06:40:40 -04:00
}
2017-02-11 04:12:56 -04:00
2017-02-27 02:39:46 -04:00
AP_Param : : load_object_from_eeprom ( motors , motors_var_info ) ;
2017-02-11 04:12:56 -04:00
// create the attitude view used by the VTOL code
2018-11-12 01:00:00 -04:00
ahrs_view = ahrs . create_view ( rotation , ahrs_trim_pitch ) ;
2017-02-11 04:12:56 -04:00
if ( ahrs_view = = nullptr ) {
goto failed ;
}
2018-09-25 12:13:34 -03:00
2017-02-11 04:12:56 -04:00
attitude_control = new AC_AttitudeControl_Multi ( * ahrs_view , aparm , * motors , loop_delta_t ) ;
2015-12-26 06:40:40 -04:00
if ( ! attitude_control ) {
2016-10-06 09:47:33 -03:00
hal . console - > printf ( " %s attitude_control \n " , strUnableToAllocate ) ;
2016-01-06 03:17:08 -04:00
goto failed ;
2015-12-26 06:40:40 -04:00
}
AP_Param : : load_object_from_eeprom ( attitude_control , attitude_control - > var_info ) ;
2018-01-15 07:59:09 -04:00
pos_control = new AC_PosControl ( * ahrs_view , inertial_nav , * motors , * attitude_control ) ;
2015-12-26 06:40:40 -04:00
if ( ! pos_control ) {
2016-10-06 09:47:33 -03:00
hal . console - > printf ( " %s pos_control \n " , strUnableToAllocate ) ;
2016-01-06 03:17:08 -04:00
goto failed ;
2015-12-26 06:40:40 -04:00
}
AP_Param : : load_object_from_eeprom ( pos_control , pos_control - > var_info ) ;
2017-02-11 18:34:08 -04:00
wp_nav = new AC_WPNav ( inertial_nav , * ahrs_view , * pos_control , * attitude_control ) ;
2016-10-26 23:54:15 -03:00
if ( ! wp_nav ) {
2016-10-06 09:47:33 -03:00
hal . console - > printf ( " %s wp_nav \n " , strUnableToAllocate ) ;
2016-01-06 03:17:08 -04:00
goto failed ;
2015-12-26 06:40:40 -04:00
}
AP_Param : : load_object_from_eeprom ( wp_nav , wp_nav - > var_info ) ;
2018-03-27 23:24:05 -03:00
loiter_nav = new AC_Loiter ( inertial_nav , * ahrs_view , * pos_control , * attitude_control ) ;
if ( ! loiter_nav ) {
hal . console - > printf ( " %s loiter_nav \n " , strUnableToAllocate ) ;
goto failed ;
}
AP_Param : : load_object_from_eeprom ( loiter_nav , loiter_nav - > var_info ) ;
2016-12-12 09:08:16 -04:00
motors - > init ( ( AP_Motors : : motor_frame_class ) frame_class . get ( ) , ( AP_Motors : : motor_frame_type ) frame_type . get ( ) ) ;
2016-06-09 10:19:18 -03:00
motors - > set_throttle_range ( thr_min_pwm , thr_max_pwm ) ;
2015-12-26 06:40:40 -04:00
motors - > set_update_rate ( rc_speed ) ;
motors - > set_interlock ( true ) ;
2016-04-02 17:49:38 -03:00
pos_control - > set_dt ( loop_delta_t ) ;
2016-09-01 08:32:24 -03:00
attitude_control - > parameter_sanity_check ( ) ;
2015-12-26 06:40:40 -04:00
2019-10-17 03:03:48 -03:00
// setup the trim of any motors used by AP_Motors so I/O board
2016-01-01 22:29:05 -04:00
// failsafe will disable motors
2016-05-06 05:09:07 -03:00
for ( uint8_t i = 0 ; i < 8 ; i + + ) {
2017-10-23 00:16:14 -03:00
SRV_Channel : : Aux_servo_function_t func = SRV_Channels : : get_motor_function ( i ) ;
2016-10-22 07:27:57 -03:00
SRV_Channels : : set_failsafe_pwm ( func , thr_min_pwm ) ;
2016-01-01 22:29:05 -04:00
}
2015-12-26 06:40:40 -04:00
transition_state = TRANSITION_DONE ;
2019-05-01 19:44:00 -03:00
if ( tilt . tilt_mask ! = 0 ) {
// setup tilt compensation
motors - > set_thrust_compensation_callback ( FUNCTOR_BIND_MEMBER ( & QuadPlane : : tilt_compensate , void , float * , uint8_t ) ) ;
if ( tilt . tilt_type = = TILT_TYPE_VECTORED_YAW ) {
// setup tilt servos for vectored yaw
SRV_Channels : : set_range ( SRV_Channel : : k_tiltMotorLeft , 1000 ) ;
SRV_Channels : : set_range ( SRV_Channel : : k_tiltMotorRight , 1000 ) ;
}
2017-04-22 22:35:25 -03:00
}
2016-04-01 02:40:06 -03:00
setup_defaults ( ) ;
2018-01-24 00:40:49 -04:00
AP_Param : : convert_old_parameters ( & q_conversion_table [ 0 ] , ARRAY_SIZE ( q_conversion_table ) ) ;
2019-01-17 13:54:15 -04:00
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " QuadPlane initialised " ) ;
2016-01-06 03:17:08 -04:00
initialised = true ;
return true ;
failed :
initialised = false ;
enable . set ( 0 ) ;
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " QuadPlane setup failed " ) ;
2016-01-06 03:17:08 -04:00
return false ;
2015-11-24 04:24:04 -04:00
}
2017-04-03 19:18:30 -03:00
/*
setup default parameters from defaults_table
*/
void QuadPlane : : setup_defaults ( void )
{
2018-11-28 20:54:55 -04:00
AP_Param : : set_defaults_from_table ( defaults_table , ARRAY_SIZE ( defaults_table ) ) ;
2017-04-03 19:18:30 -03:00
enum AP_Motors : : motor_frame_class motor_class ;
motor_class = ( enum AP_Motors : : motor_frame_class ) frame_class . get ( ) ;
if ( motor_class = = AP_Motors : : MOTOR_FRAME_TAILSITTER ) {
2018-11-28 20:54:55 -04:00
AP_Param : : set_defaults_from_table ( defaults_table_tailsitter , ARRAY_SIZE ( defaults_table_tailsitter ) ) ;
2017-04-03 19:18:30 -03:00
}
2016-06-02 00:10:39 -03:00
// reset ESC calibration
if ( esc_calibration ! = 0 ) {
esc_calibration . set_and_save ( 0 ) ;
}
2016-04-01 02:40:06 -03:00
}
2016-06-02 00:10:39 -03:00
// run ESC calibration
void QuadPlane : : run_esc_calibration ( void )
{
if ( ! motors - > armed ( ) ) {
motors - > set_throttle_passthrough_for_esc_calibration ( 0 ) ;
AP_Notify : : flags . esc_calibration = false ;
return ;
}
if ( ! AP_Notify : : flags . esc_calibration ) {
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Starting ESC calibration " ) ;
2016-06-02 00:10:39 -03:00
}
AP_Notify : : flags . esc_calibration = true ;
switch ( esc_calibration ) {
case 1 :
// throttle based calibration
2018-11-09 18:38:43 -04:00
motors - > set_throttle_passthrough_for_esc_calibration ( plane . get_throttle_input ( ) * 0.01f ) ;
2016-06-02 00:10:39 -03:00
break ;
case 2 :
// full range calibration
motors - > set_throttle_passthrough_for_esc_calibration ( 1 ) ;
break ;
}
}
2015-12-26 03:45:42 -04:00
// init quadplane stabilize mode
void QuadPlane : : init_stabilize ( void )
{
2015-12-26 05:13:20 -04:00
throttle_wait = false ;
2015-12-26 03:45:42 -04:00
}
2017-03-13 04:45:46 -03:00
/*
ask the multicopter attitude control to match the roll and pitch rates being demanded by the
fixed wing controller if not in a pure VTOL mode
*/
2017-07-10 01:00:45 -03:00
void QuadPlane : : multicopter_attitude_rate_update ( float yaw_rate_cds )
2017-03-13 04:45:46 -03:00
{
2018-09-13 21:02:18 -03:00
check_attitude_relax ( ) ;
2019-04-27 17:03:37 -03:00
// tailsitter-only bodyframe roll control options
if ( is_tailsitter ( ) ) {
2019-03-16 09:51:05 -03:00
if ( tailsitter . input_type = = TAILSITTER_INPUT_BF_ROLL_M ) {
2019-03-03 13:54:23 -04:00
// Angle mode attitude control for pitch and body-frame roll, rate control for yaw.
// this version interprets the first argument as yaw rate and the third as roll angle
// because it is intended to be used with Q_TAILSIT_INPUT=1 where the roll and yaw sticks
// act in the tailsitter's body frame (i.e. roll is MC/earth frame yaw and
// yaw is MC/earth frame roll)
2019-03-16 09:51:05 -03:00
attitude_control - > input_euler_rate_yaw_euler_angle_pitch_bf_roll_m ( plane . nav_roll_cd ,
plane . nav_pitch_cd ,
yaw_rate_cds ) ;
2019-04-27 17:03:37 -03:00
return ;
2019-03-16 09:51:05 -03:00
} else if ( tailsitter . input_type = = TAILSITTER_INPUT_BF_ROLL_P ) {
attitude_control - > input_euler_rate_yaw_euler_angle_pitch_bf_roll_p ( plane . nav_roll_cd ,
plane . nav_pitch_cd ,
yaw_rate_cds ) ;
2019-04-27 17:03:37 -03:00
return ;
2019-03-03 13:54:23 -04:00
}
2019-04-27 17:03:37 -03:00
}
// normal control modes for VTOL and FW flight
if ( in_vtol_mode ( ) | | is_tailsitter ( ) ) {
// use euler angle attitude control
attitude_control - > input_euler_angle_roll_pitch_euler_rate_yaw ( plane . nav_roll_cd ,
plane . nav_pitch_cd ,
yaw_rate_cds ) ;
2017-03-13 04:45:46 -03:00
} else {
// use the fixed wing desired rates
2019-06-27 06:31:52 -03:00
float roll_rate = plane . rollController . get_pid_info ( ) . target ;
float pitch_rate = plane . pitchController . get_pid_info ( ) . target ;
2018-03-09 05:48:55 -04:00
attitude_control - > input_rate_bf_roll_pitch_yaw_2 ( roll_rate * 100.0f , pitch_rate * 100.0f , yaw_rate_cds ) ;
2017-03-13 04:45:46 -03:00
}
}
2015-12-26 04:27:13 -04:00
// hold in stabilize with given throttle
void QuadPlane : : hold_stabilize ( float throttle_in )
2015-12-26 04:51:05 -04:00
{
2015-11-24 04:24:04 -04:00
// call attitude controller
2017-07-10 01:00:45 -03:00
multicopter_attitude_rate_update ( get_desired_yaw_rate_cds ( ) ) ;
2015-11-24 04:24:04 -04:00
2016-01-05 19:32:25 -04:00
if ( throttle_in < = 0 ) {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : GROUND_IDLE ) ;
2018-12-28 02:35:08 -04:00
attitude_control - > set_throttle_out ( 0 , true , 0 ) ;
if ( ! is_tailsitter ( ) ) {
2017-04-13 23:48:08 -03:00
// always stabilize with tailsitters so we can do belly takeoffs
2018-12-28 02:35:08 -04:00
attitude_control - > relax_attitude_controllers ( ) ;
2017-04-13 23:48:08 -03:00
}
2016-01-05 19:32:25 -04:00
} else {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2016-01-05 19:32:25 -04:00
attitude_control - > set_throttle_out ( throttle_in , true , 0 ) ;
}
2015-12-26 04:27:13 -04:00
}
// quadplane stabilize mode
void QuadPlane : : control_stabilize ( void )
{
2016-06-02 00:10:39 -03:00
// special check for ESC calibration in QSTABILIZE
if ( esc_calibration ! = 0 ) {
run_esc_calibration ( ) ;
return ;
}
// normal QSTABILIZE mode
2019-04-06 15:38:40 -03:00
float pilot_throttle_scaled = get_pilot_throttle ( ) ;
2015-12-26 04:27:13 -04:00
hold_stabilize ( pilot_throttle_scaled ) ;
2015-11-24 04:24:04 -04:00
}
2017-03-13 04:04:09 -03:00
// run the multicopter Z controller
void QuadPlane : : run_z_controller ( void )
{
2018-01-24 20:38:07 -04:00
const uint32_t now = AP_HAL : : millis ( ) ;
2017-03-13 04:04:09 -03:00
if ( now - last_pidz_active_ms > 2000 ) {
2017-04-07 22:49:18 -03:00
// set alt target to current height on transition. This
// starts the Z controller off with the right values
2017-09-18 15:21:01 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Reset alt target to %.1f " , ( double ) inertial_nav . get_altitude ( ) / 100 ) ;
2017-09-05 19:22:38 -03:00
set_alt_target_current ( ) ;
2017-04-07 22:49:18 -03:00
pos_control - > set_desired_velocity_z ( inertial_nav . get_velocity_z ( ) ) ;
// initialize vertical speeds and leash lengths
2018-09-20 02:44:42 -03:00
pos_control - > set_max_speed_z ( - pilot_velocity_z_max , pilot_velocity_z_max ) ;
pos_control - > set_max_accel_z ( pilot_accel_z ) ;
2017-04-07 22:49:18 -03:00
2017-03-13 04:04:09 -03:00
// it has been two seconds since we last ran the Z
// controller. We need to assume the integrator may be way off
2017-04-05 07:00:29 -03:00
// the base throttle we start at is the current throttle we are using
2018-04-10 19:07:24 -03:00
// note that AC_PosControl::run_z_controller() adds the Z pid (_pid_accel_z) output to _motors.get_throttle_hover()
2017-04-23 23:05:24 -03:00
float base_throttle = constrain_float ( motors - > get_throttle ( ) - motors - > get_throttle_hover ( ) , - 1 , 1 ) * 1000 ;
2018-01-15 07:59:09 -04:00
pos_control - > get_accel_z_pid ( ) . set_integrator ( base_throttle ) ;
2017-04-08 01:39:55 -03:00
last_pidz_init_ms = now ;
2017-03-13 04:04:09 -03:00
}
last_pidz_active_ms = now ;
pos_control - > update_z_controller ( ) ;
}
2018-09-13 21:02:18 -03:00
/*
check if we should relax the attitude controllers
We relax them whenever we will be using them after a period of
inactivity
*/
void QuadPlane : : check_attitude_relax ( void )
{
uint32_t now = AP_HAL : : millis ( ) ;
2018-09-14 21:03:05 -03:00
if ( now - last_att_control_ms > 100 ) {
2018-09-13 21:02:18 -03:00
attitude_control - > relax_attitude_controllers ( ) ;
}
last_att_control_ms = now ;
}
2019-02-17 14:42:05 -04:00
/*
init QACRO mode
*/
void QuadPlane : : init_qacro ( void )
{
throttle_wait = false ;
transition_state = TRANSITION_DONE ;
attitude_control - > relax_attitude_controllers ( ) ;
}
2015-12-26 03:45:42 -04:00
// init quadplane hover mode
void QuadPlane : : init_hover ( void )
{
// initialize vertical speeds and leash lengths
2018-09-20 02:44:42 -03:00
pos_control - > set_max_speed_z ( - pilot_velocity_z_max , pilot_velocity_z_max ) ;
pos_control - > set_max_accel_z ( pilot_accel_z ) ;
2015-12-26 03:45:42 -04:00
// initialise position and desired velocity
2017-09-05 19:22:38 -03:00
set_alt_target_current ( ) ;
2015-12-26 06:40:40 -04:00
pos_control - > set_desired_velocity_z ( inertial_nav . get_velocity_z ( ) ) ;
2015-12-26 05:13:20 -04:00
init_throttle_wait ( ) ;
2015-12-26 03:45:42 -04:00
}
2017-04-18 09:40:06 -03:00
/*
check for an EKF yaw reset
*/
void QuadPlane : : check_yaw_reset ( void )
{
2019-01-13 00:24:51 -04:00
if ( ! initialised ) {
return ;
}
2017-04-18 09:40:06 -03:00
float yaw_angle_change_rad = 0.0f ;
uint32_t new_ekfYawReset_ms = ahrs . getLastYawResetAngle ( yaw_angle_change_rad ) ;
if ( new_ekfYawReset_ms ! = ekfYawReset_ms ) {
2018-01-26 23:45:46 -04:00
attitude_control - > inertial_frame_reset ( ) ;
2017-04-18 09:40:06 -03:00
ekfYawReset_ms = new_ekfYawReset_ms ;
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " EKF yaw reset %.2f " , ( double ) degrees ( yaw_angle_change_rad ) ) ;
2017-04-18 09:40:06 -03:00
}
}
2015-12-26 04:27:13 -04:00
/*
hold hover with target climb rate
*/
void QuadPlane : : hold_hover ( float target_climb_rate )
2015-12-26 03:45:42 -04:00
{
2016-02-04 00:03:39 -04:00
// motors use full range
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2016-02-04 00:03:39 -04:00
2015-12-26 03:45:42 -04:00
// initialize vertical speeds and acceleration
2018-09-20 02:44:42 -03:00
pos_control - > set_max_speed_z ( - pilot_velocity_z_max , pilot_velocity_z_max ) ;
pos_control - > set_max_accel_z ( pilot_accel_z ) ;
2015-12-26 03:45:42 -04:00
// call attitude controller
2017-07-10 01:00:45 -03:00
multicopter_attitude_rate_update ( get_desired_yaw_rate_cds ( ) ) ;
2015-12-26 03:45:42 -04:00
// call position controller
2015-12-26 06:40:40 -04:00
pos_control - > set_alt_target_from_climb_rate_ff ( target_climb_rate , plane . G_Dt , false ) ;
2017-03-13 04:04:09 -03:00
run_z_controller ( ) ;
2015-12-26 04:27:13 -04:00
}
2019-04-06 15:38:40 -03:00
float QuadPlane : : get_pilot_throttle ( )
{
2019-03-24 00:35:13 -03:00
// get scaled throttle input
float throttle_in = plane . channel_throttle - > get_control_in ( ) ;
// normalize to [0,1]
throttle_in / = plane . channel_throttle - > get_range ( ) ;
2019-04-06 15:38:40 -03:00
2019-10-02 22:14:34 -03:00
if ( is_positive ( throttle_expo ) ) {
// get hover throttle level [0,1]
float thr_mid = motors - > get_throttle_hover ( ) ;
float thrust_curve_expo = constrain_float ( throttle_expo , 0.0f , 1.0f ) ;
2019-04-06 15:38:40 -03:00
2019-10-02 22:14:34 -03:00
// this puts mid stick at hover throttle
return throttle_curve ( thr_mid , thrust_curve_expo , throttle_in ) ; ;
} else {
return throttle_in ;
}
2019-04-06 15:38:40 -03:00
}
2019-02-17 14:42:05 -04:00
/*
control QACRO mode
*/
void QuadPlane : : control_qacro ( void )
{
if ( throttle_wait ) {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : GROUND_IDLE ) ;
2018-12-28 02:35:08 -04:00
attitude_control - > set_throttle_out ( 0 , true , 0 ) ;
attitude_control - > relax_attitude_controllers ( ) ;
2019-02-17 14:42:05 -04:00
} else {
check_attitude_relax ( ) ;
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2019-02-17 14:42:05 -04:00
// convert the input to the desired body frame rate
float target_roll = 0 ;
2019-03-24 00:35:13 -03:00
float target_pitch = plane . channel_pitch - > norm_input ( ) * acro_pitch_rate * 100.0f ;
2019-02-17 14:42:05 -04:00
float target_yaw = 0 ;
if ( is_tailsitter ( ) ) {
// Note that the 90 degree Y rotation for copter mode swaps body-frame roll and yaw
// acro_roll_rate param applies to yaw in copter frame
2019-03-24 00:35:13 -03:00
target_roll = plane . channel_rudder - > norm_input ( ) * acro_roll_rate * 100.0f ;
target_yaw = - plane . channel_roll - > norm_input ( ) * acro_yaw_rate * 100.0f ;
2019-02-17 14:42:05 -04:00
} else {
2019-03-24 00:35:13 -03:00
target_roll = plane . channel_roll - > norm_input ( ) * acro_roll_rate * 100.0f ;
target_yaw = plane . channel_rudder - > norm_input ( ) * acro_yaw_rate * 100.0 ;
2019-02-17 14:42:05 -04:00
}
2019-04-06 15:38:40 -03:00
float throttle_out = get_pilot_throttle ( ) ;
2019-02-17 14:42:05 -04:00
// run attitude controller
2019-03-06 17:03:33 -04:00
if ( plane . g . acro_locking ) {
attitude_control - > input_rate_bf_roll_pitch_yaw_3 ( target_roll , target_pitch , target_yaw ) ;
} else {
attitude_control - > input_rate_bf_roll_pitch_yaw_2 ( target_roll , target_pitch , target_yaw ) ;
}
2019-02-17 14:42:05 -04:00
// output pilot's throttle without angle boost
attitude_control - > set_throttle_out ( throttle_out , false , 10.0f ) ;
}
}
2015-12-26 04:27:13 -04:00
/*
control QHOVER mode
*/
void QuadPlane : : control_hover ( void )
{
2015-12-26 05:13:20 -04:00
if ( throttle_wait ) {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : GROUND_IDLE ) ;
2018-12-28 02:35:08 -04:00
attitude_control - > set_throttle_out ( 0 , true , 0 ) ;
attitude_control - > relax_attitude_controllers ( ) ;
2015-12-26 06:40:40 -04:00
pos_control - > relax_alt_hold_controllers ( 0 ) ;
2015-12-26 05:13:20 -04:00
} else {
hold_hover ( get_pilot_desired_climb_rate_cms ( ) ) ;
}
2015-12-26 03:45:42 -04:00
}
2015-12-26 04:51:05 -04:00
void QuadPlane : : init_loiter ( void )
{
2018-02-09 23:52:46 -04:00
// initialise loiter
2018-09-18 23:50:51 -03:00
loiter_nav - > clear_pilot_desired_acceleration ( ) ;
2018-06-09 04:41:02 -03:00
loiter_nav - > init_target ( ) ;
2015-12-26 04:51:05 -04:00
// initialize vertical speed and acceleration
2018-09-20 02:44:42 -03:00
pos_control - > set_max_speed_z ( - pilot_velocity_z_max , pilot_velocity_z_max ) ;
pos_control - > set_max_accel_z ( pilot_accel_z ) ;
2015-12-26 04:51:05 -04:00
// initialise position and desired velocity
2017-09-05 19:22:38 -03:00
set_alt_target_current ( ) ;
2015-12-26 06:40:40 -04:00
pos_control - > set_desired_velocity_z ( inertial_nav . get_velocity_z ( ) ) ;
2015-12-26 05:13:20 -04:00
init_throttle_wait ( ) ;
2017-09-08 04:54:55 -03:00
// remember initial pitch
loiter_initial_pitch_cd = MAX ( plane . ahrs . pitch_sensor , 0 ) ;
2017-11-05 05:44:42 -04:00
// prevent re-init of target position
last_loiter_ms = AP_HAL : : millis ( ) ;
2015-12-26 04:51:05 -04:00
}
2019-02-22 02:04:35 -04:00
void QuadPlane : : init_qland ( void )
2016-03-09 03:20:41 -04:00
{
init_loiter ( ) ;
throttle_wait = false ;
2016-05-05 19:27:47 -03:00
poscontrol . state = QPOS_LAND_DESCEND ;
2016-06-16 05:17:46 -03:00
landing_detect . lower_limit_start_ms = 0 ;
2019-02-01 23:49:02 -04:00
landing_detect . land_start_ms = 0 ;
2016-03-09 03:20:41 -04:00
}
2016-01-01 06:39:36 -04:00
// helper for is_flying()
bool QuadPlane : : is_flying ( void )
{
if ( ! available ( ) ) {
return false ;
}
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_guided & & guided_takeoff ) {
2017-09-06 04:58:08 -03:00
return true ;
}
2016-07-24 17:08:36 -03:00
if ( motors - > get_throttle ( ) > 0.01f & & ! motors - > limit . throttle_lower ) {
2016-01-01 06:39:36 -04:00
return true ;
}
2018-04-25 04:21:11 -03:00
if ( in_tailsitter_vtol_transition ( ) ) {
return true ;
}
2016-01-01 06:39:36 -04:00
return false ;
}
2015-12-26 06:40:40 -04:00
// crude landing detector to prevent tipover
bool QuadPlane : : should_relax ( void )
{
2018-08-24 02:42:37 -03:00
const uint32_t tnow = millis ( ) ;
2016-06-09 10:20:50 -03:00
bool motor_at_lower_limit = motors - > limit . throttle_lower & & attitude_control - > is_throttle_mix_min ( ) ;
2016-07-24 17:08:36 -03:00
if ( motors - > get_throttle ( ) < 0.01f ) {
2016-01-01 07:09:11 -04:00
motor_at_lower_limit = true ;
}
2018-08-24 02:42:37 -03:00
2015-12-26 06:40:40 -04:00
if ( ! motor_at_lower_limit ) {
2016-06-16 05:17:46 -03:00
landing_detect . lower_limit_start_ms = 0 ;
2019-02-01 23:49:02 -04:00
landing_detect . land_start_ms = 0 ;
2018-08-24 02:42:37 -03:00
return false ;
} else if ( landing_detect . lower_limit_start_ms = = 0 ) {
landing_detect . lower_limit_start_ms = tnow ;
2015-12-26 06:40:40 -04:00
}
2018-08-24 02:42:37 -03:00
return ( tnow - landing_detect . lower_limit_start_ms ) > 1000 ;
2015-12-26 06:40:40 -04:00
}
2016-04-22 07:20:06 -03:00
// see if we are flying in vtol
2018-08-24 02:42:37 -03:00
bool QuadPlane : : is_flying_vtol ( void ) const
2016-04-22 07:20:06 -03:00
{
2016-07-26 18:43:16 -03:00
if ( ! available ( ) ) {
return false ;
}
2019-04-09 09:17:25 -03:00
if ( motors - > get_spool_state ( ) = = AP_Motors : : SpoolState : : SHUT_DOWN ) {
2019-02-19 05:59:19 -04:00
// assume that with no motor outputs we're not flying in VTOL mode
return false ;
}
2016-07-24 17:08:36 -03:00
if ( motors - > get_throttle ( ) > 0.01f ) {
// if we are demanding more than 1% throttle then don't consider aircraft landed
return true ;
}
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_qacro ) {
2019-02-17 14:42:05 -04:00
return true ;
}
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_guided & & guided_takeoff ) {
2017-09-06 04:58:08 -03:00
return true ;
}
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_qstabilize | | plane . control_mode = = & plane . mode_qhover | | plane . control_mode = = & plane . mode_qloiter | | plane . control_mode = = & plane . mode_qautotune ) {
2016-07-24 17:08:36 -03:00
// in manual flight modes only consider aircraft landed when pilot demanded throttle is zero
2018-11-09 18:38:43 -04:00
return plane . get_throttle_input ( ) > 0 ;
2016-07-24 17:08:36 -03:00
}
2016-06-16 05:17:46 -03:00
if ( in_vtol_mode ( ) & & millis ( ) - landing_detect . lower_limit_start_ms > 5000 ) {
2016-07-24 17:08:36 -03:00
// use landing detector
2016-04-22 07:20:06 -03:00
return true ;
}
return false ;
}
2016-04-02 08:45:51 -03:00
/*
smooth out descent rate for landing to prevent a jerk as we get to
land_final_alt .
*/
2018-08-24 02:42:37 -03:00
float QuadPlane : : landing_descent_rate_cms ( float height_above_ground ) const
2016-04-02 08:45:51 -03:00
{
2019-01-24 01:03:12 -04:00
float ret = linear_interpolate ( land_speed_cms , wp_nav - > get_default_speed_down ( ) ,
2016-04-02 08:45:51 -03:00
height_above_ground ,
2017-09-08 03:43:32 -03:00
land_final_alt , land_final_alt + 6 ) ;
2016-04-02 08:45:51 -03:00
return ret ;
}
2015-12-26 04:51:05 -04:00
// run quadplane loiter controller
void QuadPlane : : control_loiter ( )
{
2015-12-26 05:13:20 -04:00
if ( throttle_wait ) {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : GROUND_IDLE ) ;
2018-12-28 02:35:08 -04:00
attitude_control - > set_throttle_out ( 0 , true , 0 ) ;
attitude_control - > relax_attitude_controllers ( ) ;
2015-12-26 06:40:40 -04:00
pos_control - > relax_alt_hold_controllers ( 0 ) ;
2018-09-18 23:50:51 -03:00
loiter_nav - > clear_pilot_desired_acceleration ( ) ;
2018-03-27 23:24:05 -03:00
loiter_nav - > init_target ( ) ;
2015-12-26 05:13:20 -04:00
return ;
}
2015-12-26 06:40:40 -04:00
2018-09-13 21:02:18 -03:00
check_attitude_relax ( ) ;
2015-12-26 06:40:40 -04:00
if ( should_relax ( ) ) {
2018-03-27 23:24:05 -03:00
loiter_nav - > soften_for_landing ( ) ;
2015-12-26 06:40:40 -04:00
}
2016-01-01 03:18:53 -04:00
2018-08-24 02:42:37 -03:00
const uint32_t now = AP_HAL : : millis ( ) ;
if ( now - last_loiter_ms > 500 ) {
2018-09-18 23:50:51 -03:00
loiter_nav - > clear_pilot_desired_acceleration ( ) ;
2018-03-27 23:24:05 -03:00
loiter_nav - > init_target ( ) ;
2016-01-01 03:18:53 -04:00
}
2018-08-24 02:42:37 -03:00
last_loiter_ms = now ;
2016-02-04 00:03:39 -04:00
// motors use full range
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2016-02-04 00:03:39 -04:00
2015-12-26 04:51:05 -04:00
// initialize vertical speed and acceleration
2018-09-20 02:44:42 -03:00
pos_control - > set_max_speed_z ( - pilot_velocity_z_max , pilot_velocity_z_max ) ;
pos_control - > set_max_accel_z ( pilot_accel_z ) ;
2015-12-26 04:51:05 -04:00
// process pilot's roll and pitch input
2018-03-27 23:24:05 -03:00
loiter_nav - > set_pilot_desired_acceleration ( plane . channel_roll - > get_control_in ( ) ,
plane . channel_pitch - > get_control_in ( ) ,
plane . G_Dt ) ;
2015-12-26 04:51:05 -04:00
// run loiter controller
2018-09-05 03:48:15 -03:00
loiter_nav - > update ( ) ;
2015-12-26 04:51:05 -04:00
// nav roll and pitch are controller by loiter controller
2018-03-27 23:24:05 -03:00
plane . nav_roll_cd = loiter_nav - > get_roll ( ) ;
plane . nav_pitch_cd = loiter_nav - > get_pitch ( ) ;
2015-12-26 04:51:05 -04:00
2017-11-05 06:35:32 -04:00
if ( now - last_pidz_init_ms < ( uint32_t ) transition_time_ms * 2 & & ! is_tailsitter ( ) ) {
2017-09-08 04:54:55 -03:00
// we limit pitch during initial transition
float pitch_limit_cd = linear_interpolate ( loiter_initial_pitch_cd , aparm . angle_max ,
now ,
2017-11-05 06:35:32 -04:00
last_pidz_init_ms , last_pidz_init_ms + transition_time_ms * 2 ) ;
2017-09-08 04:54:55 -03:00
if ( plane . nav_pitch_cd > pitch_limit_cd ) {
plane . nav_pitch_cd = pitch_limit_cd ;
pos_control - > set_limit_accel_xy ( ) ;
}
}
// call attitude controller with conservative smoothing gain of 4.0f
attitude_control - > input_euler_angle_roll_pitch_euler_rate_yaw ( plane . nav_roll_cd ,
plane . nav_pitch_cd ,
2017-07-10 01:00:45 -03:00
get_desired_yaw_rate_cds ( ) ) ;
2017-09-08 04:54:55 -03:00
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_qland ) {
2019-02-01 23:49:02 -04:00
if ( poscontrol . state < QPOS_LAND_FINAL & & check_land_final ( ) ) {
2016-05-05 19:27:47 -03:00
poscontrol . state = QPOS_LAND_FINAL ;
2016-09-28 16:18:38 -03:00
// cut IC engine if enabled
if ( land_icengine_cut ! = 0 ) {
plane . g2 . ice_control . engine_control ( 0 , 0 , 0 ) ;
}
2016-03-09 03:20:41 -04:00
}
2019-02-01 23:49:02 -04:00
float height_above_ground = plane . relative_ground_altitude ( plane . g . rangefinder_landing ) ;
2016-05-05 19:27:47 -03:00
float descent_rate = ( poscontrol . state = = QPOS_LAND_FINAL ) ? land_speed_cms : landing_descent_rate_cms ( height_above_ground ) ;
2016-03-09 03:20:41 -04:00
pos_control - > set_alt_target_from_climb_rate ( - descent_rate , plane . G_Dt , true ) ;
check_land_complete ( ) ;
2019-01-15 13:46:13 -04:00
} else if ( plane . control_mode = = & plane . mode_guided & & guided_takeoff ) {
2017-09-06 04:58:08 -03:00
pos_control - > set_alt_target_from_climb_rate_ff ( 0 , plane . G_Dt , false ) ;
2016-03-09 03:20:41 -04:00
} else {
// update altitude target and call position controller
pos_control - > set_alt_target_from_climb_rate_ff ( get_pilot_desired_climb_rate_cms ( ) , plane . G_Dt , false ) ;
}
2017-03-13 04:04:09 -03:00
run_z_controller ( ) ;
2015-12-26 04:51:05 -04:00
}
/*
2016-01-09 18:42:46 -04:00
get pilot input yaw rate in cd / s
2015-12-26 04:51:05 -04:00
*/
2018-08-24 02:42:37 -03:00
float QuadPlane : : get_pilot_input_yaw_rate_cds ( void ) const
2016-01-09 18:42:46 -04:00
{
2019-02-02 06:13:35 -04:00
if ( plane . get_throttle_input ( ) < = 0 & & ! plane . auto_throttle_mode & &
2019-03-06 23:12:56 -04:00
plane . arming . get_rudder_arming_type ( ) ! = AP_Arming : : RudderArming : : IS_DISABLED ) {
2016-01-09 18:42:46 -04:00
// the user may be trying to disarm
return 0 ;
}
// add in rudder input
2017-02-24 01:47:09 -04:00
return plane . channel_rudder - > get_control_in ( ) * yaw_rate_max / 45 ;
2016-01-09 18:42:46 -04:00
}
/*
get overall desired yaw rate in cd / s
*/
float QuadPlane : : get_desired_yaw_rate_cds ( void )
2015-12-26 04:51:05 -04:00
{
2016-01-02 00:25:49 -04:00
float yaw_cds = 0 ;
2015-12-26 06:40:40 -04:00
if ( assisted_flight ) {
// use bank angle to get desired yaw rate
2016-01-09 18:42:46 -04:00
yaw_cds + = desired_auto_yaw_rate_cds ( ) ;
2015-12-26 06:40:40 -04:00
}
2018-11-09 18:38:43 -04:00
if ( plane . get_throttle_input ( ) < = 0 & & ! plane . auto_throttle_mode ) {
2016-01-02 00:16:31 -04:00
// the user may be trying to disarm
return 0 ;
}
2016-01-09 18:42:46 -04:00
// add in pilot input
yaw_cds + = get_pilot_input_yaw_rate_cds ( ) ;
2016-04-20 03:23:17 -03:00
// add in weathervaning
yaw_cds + = get_weathervane_yaw_rate_cds ( ) ;
2016-01-02 00:25:49 -04:00
return yaw_cds ;
2015-12-26 04:51:05 -04:00
}
// get pilot desired climb rate in cm/s
2018-08-24 02:42:37 -03:00
float QuadPlane : : get_pilot_desired_climb_rate_cms ( void ) const
2015-12-26 04:51:05 -04:00
{
2017-10-27 17:21:28 -03:00
if ( plane . failsafe . rc_failsafe | | plane . failsafe . throttle_counter > 0 ) {
2015-12-26 06:40:40 -04:00
// descend at 0.5m/s for now
return - 50 ;
}
uint16_t dead_zone = plane . channel_throttle - > get_dead_zone ( ) ;
ArduPlane: Fix up after refactoring RC_Channel class
Further to refactor of RC_Channel class which included
adding get_xx set_xx methods, change reads and writes to the public members
to calls to get and set functionsss
old public member(int16_t) get function -> int16_t set function (int16_t)
(expression where c is an object of type RC_Channel)
c.radio_in c.get_radio_in() c.set_radio_in(v)
c.control_in c.get_control_in() c.set_control_in(v)
c.servo_out c.get_servo_out() c.set_servo_out(v)
c.pwm_out c.get_pwm_out() // use existing
c.radio_out c.get_radio_out() c.set_radio_out(v)
c.radio_max c.get_radio_max() c.set_radio_max(v)
c.radio_min c.get_radio_min() c.set_radio_min(v)
c.radio_trim c.get_radio_trim() c.set_radio_trim(v);
c.min_max_configured() // return true if min and max are configured
Because data members of RC_Channels are now private and so cannot be written directly
some overloads are provided in the Plane classes to provide the old functionality
new overload Plane::stick_mix_channel(RC_Channel *channel)
which forwards to the previously existing
void stick_mix_channel(RC_Channel *channel, int16_t &servo_out);
new overload Plane::channel_output_mixer(Rc_Channel* , RC_Channel*)const
which forwards to
(uint8_t mixing_type, int16_t & chan1, int16_t & chan2)const;
Rename functions
RC_Channel_aux::set_radio_trim(Aux_servo_function_t function)
to RC_Channel_aux::set_trim_to_radio_in_for(Aux_servo_function_t function)
RC_Channel_aux::set_servo_out(Aux_servo_function_t function, int16_t value)
to RC_Channel_aux::set_servo_out_for(Aux_servo_function_t function, int16_t value)
Rationale:
RC_Channel is a complicated class, which combines
several functionalities dealing with stick inputs
in pwm and logical units, logical and actual actuator
outputs, unit conversion etc, etc
The intent of this PR is to clarify existing use of
the class. At the basic level it should now be possible
to grep all places where private variable is set by
searching for the set_xx function.
(The wider purpose is to provide a more generic and
logically simpler method of output mixing. This is a small step)
2016-05-08 05:33:02 -03:00
uint16_t trim = ( plane . channel_throttle - > get_radio_max ( ) + plane . channel_throttle - > get_radio_min ( ) ) / 2 ;
2015-12-26 06:40:40 -04:00
return pilot_velocity_z_max * plane . channel_throttle - > pwm_to_angle_dz_trim ( dead_zone , trim ) / 100.0f ;
2015-12-26 04:51:05 -04:00
}
2015-12-26 05:13:20 -04:00
/*
initialise throttle_wait based on throttle and is_flying ( )
*/
void QuadPlane : : init_throttle_wait ( void )
{
2018-11-09 18:38:43 -04:00
if ( plane . get_throttle_input ( ) > = 10 | |
2015-12-26 05:13:20 -04:00
plane . is_flying ( ) ) {
throttle_wait = false ;
} else {
throttle_wait = true ;
}
}
2015-11-24 04:24:04 -04:00
// set motor arming
void QuadPlane : : set_armed ( bool armed )
{
2015-12-26 06:40:40 -04:00
if ( ! initialised ) {
return ;
}
motors - > armed ( armed ) ;
}
/*
estimate desired climb rate for assistance ( in cm / s )
*/
2018-08-24 02:42:37 -03:00
float QuadPlane : : assist_climb_rate_cms ( void ) const
2015-12-26 06:40:40 -04:00
{
2016-01-20 02:21:54 -04:00
float climb_rate ;
2015-12-26 06:40:40 -04:00
if ( plane . auto_throttle_mode ) {
2016-01-20 02:21:54 -04:00
// use altitude_error_cm, spread over 10s interval
2018-09-17 21:35:24 -03:00
climb_rate = plane . altitude_error_cm * 0.1f ;
2016-01-20 02:21:54 -04:00
} else {
// otherwise estimate from pilot input
climb_rate = plane . g . flybywire_climb_rate * ( plane . nav_pitch_cd / ( float ) plane . aparm . pitch_limit_max_cd ) ;
2018-11-09 18:38:43 -04:00
climb_rate * = plane . get_throttle_input ( ) ;
2015-11-24 04:24:04 -04:00
}
2019-01-24 01:03:12 -04:00
climb_rate = constrain_float ( climb_rate , - wp_nav - > get_default_speed_down ( ) , wp_nav - > get_default_speed_up ( ) ) ;
2017-04-08 01:39:55 -03:00
// bring in the demanded climb rate over 2 seconds
2018-09-17 21:35:24 -03:00
const uint32_t ramp_up_time_ms = 2000 ;
2018-01-24 20:38:07 -04:00
const uint32_t dt_since_start = last_pidz_active_ms - last_pidz_init_ms ;
2018-09-17 21:35:24 -03:00
if ( dt_since_start < ramp_up_time_ms ) {
climb_rate = linear_interpolate ( 0 , climb_rate , dt_since_start , 0 , ramp_up_time_ms ) ;
2017-04-08 01:39:55 -03:00
}
2015-12-26 06:40:40 -04:00
return climb_rate ;
2015-11-24 04:24:04 -04:00
}
2015-12-26 06:40:40 -04:00
/*
calculate desired yaw rate for assistance
*/
2018-08-24 02:42:37 -03:00
float QuadPlane : : desired_auto_yaw_rate_cds ( void ) const
2015-12-26 06:40:40 -04:00
{
float aspeed ;
2016-08-07 21:48:36 -03:00
if ( ! ahrs . airspeed_estimate ( & aspeed ) | | aspeed < plane . aparm . airspeed_min ) {
aspeed = plane . aparm . airspeed_min ;
2015-12-26 06:40:40 -04:00
}
if ( aspeed < 1 ) {
aspeed = 1 ;
}
float yaw_rate = degrees ( GRAVITY_MSS * tanf ( radians ( plane . nav_roll_cd * 0.01f ) ) / aspeed ) * 100 ;
return yaw_rate ;
}
2015-11-24 04:24:04 -04:00
2016-08-29 03:44:54 -03:00
/*
return true if the quadplane should provide stability assistance
*/
bool QuadPlane : : assistance_needed ( float aspeed )
{
if ( assist_speed < = 0 ) {
// assistance disabled
in_angle_assist = false ;
angle_error_start_ms = 0 ;
return false ;
}
2019-11-01 07:44:23 -03:00
2016-08-29 03:44:54 -03:00
if ( aspeed < assist_speed ) {
// assistance due to Q_ASSIST_SPEED
in_angle_assist = false ;
angle_error_start_ms = 0 ;
return true ;
}
2019-11-01 07:44:23 -03:00
const uint32_t now = AP_HAL : : millis ( ) ;
/*
optional assistance when altitude is too close to the ground
*/
if ( assist_alt > 0 ) {
float height_above_ground = plane . relative_ground_altitude ( plane . g . rangefinder_landing ) ;
if ( height_above_ground < assist_alt ) {
if ( alt_error_start_ms = = 0 ) {
alt_error_start_ms = now ;
}
if ( now - alt_error_start_ms > 500 ) {
// we've been below assistant alt for 0.5s
if ( ! in_alt_assist ) {
in_alt_assist = true ;
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Alt assist %.1fm " , height_above_ground ) ;
}
return true ;
}
} else {
in_alt_assist = false ;
alt_error_start_ms = 0 ;
}
}
2016-08-29 03:44:54 -03:00
if ( assist_angle < = 0 ) {
in_angle_assist = false ;
angle_error_start_ms = 0 ;
return false ;
}
/*
now check if we should provide assistance due to attitude error
*/
const uint16_t allowed_envelope_error_cd = 500U ;
if ( labs ( ahrs . roll_sensor ) < = plane . aparm . roll_limit_cd + allowed_envelope_error_cd & &
ahrs . pitch_sensor < plane . aparm . pitch_limit_max_cd + allowed_envelope_error_cd & &
2017-04-18 08:26:13 -03:00
ahrs . pitch_sensor > - ( allowed_envelope_error_cd - plane . aparm . pitch_limit_min_cd ) ) {
2016-08-29 03:44:54 -03:00
// we are inside allowed attitude envelope
in_angle_assist = false ;
angle_error_start_ms = 0 ;
return false ;
}
2018-07-28 00:43:26 -03:00
int32_t max_angle_cd = 100U * assist_angle ;
2016-08-29 03:44:54 -03:00
if ( ( labs ( ahrs . roll_sensor - plane . nav_roll_cd ) < max_angle_cd & &
labs ( ahrs . pitch_sensor - plane . nav_pitch_cd ) < max_angle_cd ) ) {
// not beyond angle error
angle_error_start_ms = 0 ;
in_angle_assist = false ;
return false ;
}
2019-11-01 07:44:23 -03:00
2016-08-29 03:44:54 -03:00
if ( angle_error_start_ms = = 0 ) {
2018-01-24 20:38:07 -04:00
angle_error_start_ms = now ;
2016-08-29 03:44:54 -03:00
}
2018-01-24 20:38:07 -04:00
bool ret = ( now - angle_error_start_ms ) > = 1000U ;
2016-08-29 03:44:54 -03:00
if ( ret & & ! in_angle_assist ) {
in_angle_assist = true ;
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Angle assist r=%d p=%d " ,
2016-08-29 03:44:54 -03:00
( int ) ( ahrs . roll_sensor / 100 ) ,
( int ) ( ahrs . pitch_sensor / 100 ) ) ;
}
return ret ;
}
2015-11-24 04:24:04 -04:00
/*
2015-12-26 04:27:13 -04:00
update for transition from quadplane to fixed wing mode
2015-11-24 04:24:04 -04:00
*/
void QuadPlane : : update_transition ( void )
{
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_manual | |
plane . control_mode = = & plane . mode_acro | |
plane . control_mode = = & plane . mode_training ) {
2016-01-03 01:46:34 -04:00
// in manual modes quad motors are always off
2017-02-11 01:50:03 -04:00
if ( ! tilt . motors_active & & ! is_tailsitter ( ) ) {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : SHUT_DOWN ) ;
2016-05-01 20:33:54 -03:00
motors - > output ( ) ;
}
2015-12-26 04:27:13 -04:00
transition_state = TRANSITION_DONE ;
2018-12-14 22:40:43 -04:00
transition_start_ms = 0 ;
transition_low_airspeed_ms = 0 ;
2016-10-11 03:04:22 -03:00
assisted_flight = false ;
2015-12-26 04:27:13 -04:00
return ;
}
2018-12-14 22:40:43 -04:00
const uint32_t now = millis ( ) ;
if ( ! hal . util - > get_soft_armed ( ) ) {
// reset the failure timer if we haven't started transitioning
transition_start_ms = now ;
} else if ( ( transition_state ! = TRANSITION_DONE ) & &
( transition_start_ms ! = 0 ) & &
( transition_failure > 0 ) & &
( ( now - transition_start_ms ) > ( ( uint32_t ) transition_failure * 1000 ) ) ) {
gcs ( ) . send_text ( MAV_SEVERITY_CRITICAL , " Transition failed, exceeded time limit " ) ;
2019-01-15 13:46:13 -04:00
plane . set_mode ( plane . mode_qland , MODE_REASON_VTOL_FAILED_TRANSITION ) ;
2018-12-14 22:40:43 -04:00
}
2015-12-26 06:40:40 -04:00
float aspeed ;
bool have_airspeed = ahrs . airspeed_estimate ( & aspeed ) ;
2017-11-19 01:05:01 -04:00
// tailsitters use angle wait, not airspeed wait
if ( is_tailsitter ( ) & & transition_state = = TRANSITION_AIRSPEED_WAIT ) {
transition_state = TRANSITION_ANGLE_WAIT_FW ;
}
2015-12-26 06:40:40 -04:00
/*
see if we should provide some assistance
*/
2016-08-29 03:44:54 -03:00
if ( have_airspeed & &
assistance_needed ( aspeed ) & &
2017-02-11 04:12:56 -04:00
! is_tailsitter ( ) & &
2017-07-30 01:53:38 -03:00
hal . util - > get_soft_armed ( ) & &
2017-10-20 01:13:52 -03:00
( ( plane . auto_throttle_mode & & ! plane . throttle_suppressed ) | |
2018-11-09 18:38:43 -04:00
plane . get_throttle_input ( ) > 0 | |
2016-01-01 03:18:53 -04:00
plane . is_flying ( ) ) ) {
2015-12-26 06:40:40 -04:00
// the quad should provide some assistance to the plane
2016-06-10 04:37:14 -03:00
if ( transition_state ! = TRANSITION_AIRSPEED_WAIT ) {
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Transition started airspeed %.1f " , ( double ) aspeed ) ;
2016-06-10 04:37:14 -03:00
}
2015-12-26 06:40:40 -04:00
transition_state = TRANSITION_AIRSPEED_WAIT ;
2018-12-14 22:40:43 -04:00
if ( transition_start_ms = = 0 ) {
transition_start_ms = now ;
}
2015-12-26 06:40:40 -04:00
assisted_flight = true ;
} else {
assisted_flight = false ;
}
2016-01-09 06:50:59 -04:00
2017-02-11 04:12:56 -04:00
if ( is_tailsitter ( ) ) {
2017-10-30 01:19:38 -03:00
if ( transition_state = = TRANSITION_ANGLE_WAIT_FW & &
tailsitter_transition_fw_complete ( ) ) {
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Transition FW done " ) ;
2017-02-11 04:12:56 -04:00
transition_state = TRANSITION_DONE ;
2018-12-14 22:40:43 -04:00
transition_start_ms = 0 ;
transition_low_airspeed_ms = 0 ;
2017-02-11 04:12:56 -04:00
}
}
2018-09-13 07:57:10 -03:00
// if rotors are fully forward then we are not transitioning,
// unless we are waiting for airspeed to increase (in which case
// the tilt will decrease rapidly)
if ( tiltrotor_fully_fwd ( ) & & transition_state ! = TRANSITION_AIRSPEED_WAIT ) {
2017-01-22 22:06:31 -04:00
transition_state = TRANSITION_DONE ;
2018-12-14 22:40:43 -04:00
transition_start_ms = 0 ;
transition_low_airspeed_ms = 0 ;
2017-01-22 22:06:31 -04:00
}
2016-01-09 06:50:59 -04:00
if ( transition_state < TRANSITION_TIMER ) {
// set a single loop pitch limit in TECS
2016-05-27 02:48:14 -03:00
if ( plane . ahrs . groundspeed ( ) < 3 ) {
// until we have some ground speed limit to zero pitch
plane . TECS_controller . set_pitch_max_limit ( 0 ) ;
} else {
plane . TECS_controller . set_pitch_max_limit ( transition_pitch_max ) ;
}
2016-01-20 03:30:48 -04:00
} else if ( transition_state < TRANSITION_DONE ) {
plane . TECS_controller . set_pitch_max_limit ( ( transition_pitch_max + 1 ) * 2 ) ;
2016-01-09 06:50:59 -04:00
}
2016-10-04 21:52:52 -03:00
if ( transition_state < TRANSITION_DONE ) {
// during transition we ask TECS to use a synthetic
// airspeed. Otherwise the pitch limits will throw off the
// throttle calculation which is driven by pitch
plane . TECS_controller . use_synthetic_airspeed ( ) ;
}
2015-12-26 06:40:40 -04:00
2015-12-26 04:27:13 -04:00
switch ( transition_state ) {
case TRANSITION_AIRSPEED_WAIT : {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2015-12-26 04:27:13 -04:00
// we hold in hover until the required airspeed is reached
2015-12-26 05:13:20 -04:00
if ( transition_start_ms = = 0 ) {
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Transition airspeed wait " ) ;
2018-12-14 22:40:43 -04:00
transition_start_ms = now ;
2015-12-26 05:13:20 -04:00
}
2018-12-14 22:40:43 -04:00
transition_low_airspeed_ms = now ;
2016-08-07 21:48:36 -03:00
if ( have_airspeed & & aspeed > plane . aparm . airspeed_min & & ! assisted_flight ) {
2015-12-26 04:27:13 -04:00
transition_state = TRANSITION_TIMER ;
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Transition airspeed reached %.1f " , ( double ) aspeed ) ;
2015-12-26 04:27:13 -04:00
}
2016-01-01 05:42:10 -04:00
assisted_flight = true ;
2018-09-18 00:40:12 -03:00
// do not allow a climb on the quad motors during transition
// a climb would add load to the airframe, and prolongs the
// transition
float climb_rate_cms = assist_climb_rate_cms ( ) ;
if ( options & OPTION_LEVEL_TRANSITION ) {
climb_rate_cms = MIN ( climb_rate_cms , 0.0f ) ;
}
hold_hover ( climb_rate_cms ) ;
2018-01-05 08:58:40 -04:00
// set desired yaw to current yaw in both desired angle and
// rate request. This reduces wing twist in transition due to
// multicopter yaw demands
attitude_control - > set_yaw_target_to_current_heading ( ) ;
attitude_control - > rate_bf_yaw_target ( ahrs . get_gyro ( ) . z ) ;
2015-12-26 06:40:40 -04:00
last_throttle = motors - > get_throttle ( ) ;
2017-01-28 19:40:56 -04:00
// reset integrators while we are below target airspeed as we
// may build up too much while still primarily under
// multicopter control
plane . pitchController . reset_I ( ) ;
plane . rollController . reset_I ( ) ;
// give full authority to attitude control
attitude_control - > set_throttle_mix_max ( ) ;
2015-12-26 04:27:13 -04:00
break ;
}
case TRANSITION_TIMER : {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2015-12-26 04:27:13 -04:00
// after airspeed is reached we degrade throttle over the
// transition time, but continue to stabilize
2018-12-14 22:40:43 -04:00
const uint32_t transition_timer_ms = now - transition_low_airspeed_ms ;
if ( transition_timer_ms > ( unsigned ) transition_time_ms ) {
2015-12-26 04:27:13 -04:00
transition_state = TRANSITION_DONE ;
2018-12-14 22:40:43 -04:00
transition_start_ms = 0 ;
transition_low_airspeed_ms = 0 ;
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Transition done " ) ;
2015-12-26 04:27:13 -04:00
}
2016-06-10 04:35:43 -03:00
float trans_time_ms = ( float ) transition_time_ms . get ( ) ;
2018-12-14 22:40:43 -04:00
float transition_scale = ( trans_time_ms - transition_timer_ms ) / trans_time_ms ;
2017-01-28 19:40:56 -04:00
float throttle_scaled = last_throttle * transition_scale ;
// set zero throttle mix, to give full authority to
// throttle. This ensures that the fixed wing controllers get
// a chance to learn the right integrators during the transition
attitude_control - > set_throttle_mix_value ( 0.5 * transition_scale ) ;
2017-01-24 02:59:28 -04:00
if ( throttle_scaled < 0.01 ) {
// ensure we don't drop all the way to zero or the motors
// will stop stabilizing
throttle_scaled = 0.01 ;
2015-12-26 04:27:13 -04:00
}
2016-01-01 00:36:03 -04:00
assisted_flight = true ;
2015-12-26 04:27:13 -04:00
hold_stabilize ( throttle_scaled ) ;
2018-01-05 08:58:40 -04:00
// set desired yaw to current yaw in both desired angle and
// rate request while waiting for transition to
// complete. Navigation should be controlled by fixed wing
// control surfaces at this stage
attitude_control - > set_yaw_target_to_current_heading ( ) ;
attitude_control - > rate_bf_yaw_target ( ahrs . get_gyro ( ) . z ) ;
2015-12-26 04:27:13 -04:00
break ;
}
2017-10-30 01:19:38 -03:00
case TRANSITION_ANGLE_WAIT_FW : {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2017-02-11 04:12:56 -04:00
assisted_flight = true ;
2017-11-05 02:25:00 -04:00
// calculate transition rate in degrees per
// millisecond. Assume we want to get to the transition angle
// in half the transition time
float transition_rate = tailsitter . transition_angle / float ( transition_time_ms / 2 ) ;
2019-01-21 18:38:07 -04:00
uint32_t dt = now - transition_start_ms ;
2017-11-05 02:25:00 -04:00
plane . nav_pitch_cd = constrain_float ( ( - transition_rate * dt ) * 100 , - 8500 , 0 ) ;
2017-10-30 01:19:38 -03:00
plane . nav_roll_cd = 0 ;
2018-09-13 21:02:18 -03:00
check_attitude_relax ( ) ;
attitude_control - > input_euler_angle_roll_pitch_euler_rate_yaw ( plane . nav_roll_cd ,
2017-10-30 01:19:38 -03:00
plane . nav_pitch_cd ,
2017-07-10 01:00:45 -03:00
0 ) ;
2017-10-30 01:19:38 -03:00
attitude_control - > set_throttle_out ( motors - > get_throttle_hover ( ) , true , 0 ) ;
2017-02-11 04:12:56 -04:00
break ;
}
2017-10-30 01:19:38 -03:00
case TRANSITION_ANGLE_WAIT_VTOL :
// nothing to do, this is handled in the fw attitude controller
2018-09-17 21:35:24 -03:00
return ;
2017-10-30 01:19:38 -03:00
2015-12-26 04:27:13 -04:00
case TRANSITION_DONE :
2017-02-11 01:50:03 -04:00
if ( ! tilt . motors_active & & ! is_tailsitter ( ) ) {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : SHUT_DOWN ) ;
2016-05-01 20:33:54 -03:00
motors - > output ( ) ;
}
2018-09-17 21:35:24 -03:00
return ;
2015-11-24 04:24:04 -04:00
}
2018-09-17 21:35:24 -03:00
motors_output ( ) ;
2016-09-20 22:46:15 -03:00
}
2015-11-24 04:24:04 -04:00
/*
update motor output for quadplane
*/
void QuadPlane : : update ( void )
{
2016-01-06 03:17:08 -04:00
if ( ! setup ( ) ) {
2015-12-26 06:40:40 -04:00
return ;
}
2019-02-04 13:14:01 -04:00
if ( ( ahrs_view ! = NULL ) & & ! is_equal ( _last_ahrs_trim_pitch , ahrs_trim_pitch . get ( ) ) ) {
_last_ahrs_trim_pitch = ahrs_trim_pitch . get ( ) ;
ahrs_view - > set_pitch_trim ( _last_ahrs_trim_pitch ) ;
}
2019-01-08 21:05:40 -04:00
# if ADVANCED_FAILSAFE == ENABLED
2019-08-21 03:01:17 -03:00
if ( plane . afs . should_crash_vehicle ( ) & & ! plane . afs . terminating_vehicle_via_landing ( ) ) {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : SHUT_DOWN ) ;
2016-07-22 05:36:28 -03:00
motors - > output ( ) ;
return ;
}
2019-01-08 21:05:40 -04:00
# endif
2016-07-22 05:36:28 -03:00
2016-03-12 19:05:10 -04:00
if ( motor_test . running ) {
motor_test_output ( ) ;
return ;
}
2017-01-28 19:40:56 -04:00
2019-02-25 20:25:11 -04:00
if ( SRV_Channels : : get_emergency_stop ( ) ) {
attitude_control - > reset_rate_controller_I_terms ( ) ;
}
2017-10-29 03:08:43 -03:00
if ( ! hal . util - > get_soft_armed ( ) ) {
/*
make sure we don ' t have any residual control from previous flight stages
*/
2018-08-16 20:59:41 -03:00
if ( is_tailsitter ( ) ) {
// tailsitters only relax I terms, to make ground testing easier
attitude_control - > reset_rate_controller_I_terms ( ) ;
} else {
// otherwise full relax
attitude_control - > relax_attitude_controllers ( ) ;
}
2017-10-29 03:08:43 -03:00
pos_control - > relax_alt_hold_controllers ( 0 ) ;
}
2016-03-09 03:20:41 -04:00
if ( ! in_vtol_mode ( ) ) {
2015-11-24 04:24:04 -04:00
update_transition ( ) ;
} else {
2018-01-24 20:38:07 -04:00
const uint32_t now = AP_HAL : : millis ( ) ;
2017-10-30 01:19:38 -03:00
2015-12-26 06:40:40 -04:00
assisted_flight = false ;
2015-12-26 05:13:20 -04:00
// output to motors
2016-03-24 22:11:56 -03:00
motors_output ( ) ;
2017-10-30 01:19:38 -03:00
if ( now - last_vtol_mode_ms > 1000 & & is_tailsitter ( ) ) {
/*
we are just entering a VTOL mode as a tailsitter , set
our transition state so the fixed wing controller brings
the nose up before we start trying to fly as a
multicopter
*/
transition_state = TRANSITION_ANGLE_WAIT_VTOL ;
transition_start_ms = now ;
} else if ( is_tailsitter ( ) & &
transition_state = = TRANSITION_ANGLE_WAIT_VTOL ) {
if ( tailsitter_transition_vtol_complete ( ) ) {
/*
we have completed transition to VTOL as a tailsitter ,
setup for the back transition when needed
*/
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Transition VTOL done " ) ;
transition_state = TRANSITION_ANGLE_WAIT_FW ;
transition_start_ms = now ;
}
2016-01-01 03:18:53 -04:00
} else {
2017-10-30 01:19:38 -03:00
/*
setup the transition state appropriately for next time we go into a non - VTOL mode
*/
transition_start_ms = 0 ;
2018-12-14 22:40:43 -04:00
transition_low_airspeed_ms = 0 ;
2017-10-30 01:19:38 -03:00
if ( throttle_wait & & ! plane . is_flying ( ) ) {
transition_state = TRANSITION_DONE ;
} else if ( is_tailsitter ( ) ) {
/*
setup for the transition back to fixed wing for later
*/
transition_state = TRANSITION_ANGLE_WAIT_FW ;
transition_start_ms = now ;
} else {
/*
setup for airspeed wait for later
*/
transition_state = TRANSITION_AIRSPEED_WAIT ;
}
last_throttle = motors - > get_throttle ( ) ;
2016-01-01 03:18:53 -04:00
}
2017-10-30 01:19:38 -03:00
last_vtol_mode_ms = now ;
2015-12-26 05:13:20 -04:00
}
// disable throttle_wait when throttle rises above 10%
2015-12-26 06:40:40 -04:00
if ( throttle_wait & &
2018-11-09 18:38:43 -04:00
( plane . get_throttle_input ( ) > 10 | |
2017-10-27 17:21:28 -03:00
plane . failsafe . rc_failsafe | |
plane . failsafe . throttle_counter > 0 ) ) {
2015-12-26 05:13:20 -04:00
throttle_wait = false ;
2015-11-24 04:24:04 -04:00
}
2016-05-01 05:07:52 -03:00
tiltrotor_update ( ) ;
2015-11-24 04:24:04 -04:00
}
2015-12-26 06:40:40 -04:00
2016-08-01 19:21:44 -03:00
/*
see if motors should be shutdown . If they should be then change AP_Motors state to
2019-04-09 09:17:25 -03:00
AP_Motors : : DesiredSpoolState : : SHUT_DOWN
2016-08-01 19:21:44 -03:00
This is a safety check to prevent accidental motor runs on the
ground , such as if RC fails and QRTL is started
*/
2019-02-20 20:01:32 -04:00
void QuadPlane : : update_throttle_suppression ( void )
2016-08-01 19:21:44 -03:00
{
// if the motors have been running in the last 2 seconds then
// allow them to run now
if ( AP_HAL : : millis ( ) - last_motors_active_ms < 2000 ) {
return ;
}
// see if motors are already disabled
2019-04-09 09:17:25 -03:00
if ( motors - > get_desired_spool_state ( ) < AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) {
2016-08-01 19:21:44 -03:00
return ;
}
// if the users throttle is above zero then allow motors to run
2018-11-09 18:38:43 -04:00
if ( plane . get_throttle_input ( ) ! = 0 ) {
2016-08-01 19:21:44 -03:00
return ;
}
// if we are in a fixed wing auto throttle mode and we have
// unsuppressed the throttle then allow motors to run
if ( plane . auto_throttle_mode & & ! plane . throttle_suppressed ) {
return ;
}
// if our vertical velocity is greater than 1m/s then allow motors to run
if ( fabsf ( inertial_nav . get_velocity_z ( ) ) > 100 ) {
return ;
}
// if we are more than 5m from home altitude then allow motors to run
if ( plane . relative_ground_altitude ( plane . g . rangefinder_landing ) > 5 ) {
return ;
}
// allow for takeoff
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_auto & & is_vtol_takeoff ( plane . mission . get_current_nav_cmd ( ) . id ) ) {
2016-08-01 19:21:44 -03:00
return ;
}
// motors should be in the spin when armed state to warn user they could become active
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : GROUND_IDLE ) ;
2016-08-01 19:21:44 -03:00
motors - > set_throttle ( 0 ) ;
last_motors_active_ms = 0 ;
}
2018-04-10 19:07:24 -03:00
// update estimated throttle required to hover (if necessary)
// called at 100hz
void QuadPlane : : update_throttle_hover ( )
{
2019-02-20 20:01:32 -04:00
if ( ! available ( ) ) {
2018-04-10 19:07:24 -03:00
return ;
}
// if not armed or landed exit
if ( ! motors - > armed ( ) | | ! is_flying_vtol ( ) ) {
return ;
}
// do not update while climbing or descending
if ( ! is_zero ( pos_control - > get_desired_velocity ( ) . z ) ) {
return ;
}
2019-04-06 15:38:40 -03:00
// do not update if quadplane forward motor is running (wing may be generating lift)
if ( ! is_tailsitter ( ) & & ( SRV_Channels : : get_output_scaled ( SRV_Channel : : k_throttle ) ! = 0 ) ) {
return ;
}
2019-10-03 21:28:08 -03:00
// don't update if Z controller not running
const uint32_t now = AP_HAL : : millis ( ) ;
if ( now - last_pidz_active_ms > 20 ) {
return ;
}
2018-04-10 19:07:24 -03:00
// get throttle output
float throttle = motors - > get_throttle ( ) ;
2019-10-03 21:28:08 -03:00
float aspeed ;
// calc average throttle if we are in a level hover and low airspeed
2018-04-10 19:07:24 -03:00
if ( throttle > 0.0f & & fabsf ( inertial_nav . get_velocity_z ( ) ) < 60 & &
2019-10-03 21:28:08 -03:00
labs ( ahrs_view - > roll_sensor ) < 500 & & labs ( ahrs_view - > pitch_sensor ) < 500 & &
ahrs . airspeed_estimate ( & aspeed ) & & aspeed < plane . aparm . airspeed_min * 0.3 ) {
2018-04-10 19:07:24 -03:00
// Can we set the time constant automatically
motors - > update_throttle_hover ( 0.01f ) ;
}
}
2016-03-24 22:11:56 -03:00
/*
output motors and do any copter needed
*/
2018-09-17 21:35:24 -03:00
void QuadPlane : : motors_output ( bool run_rate_controller )
2016-03-24 22:11:56 -03:00
{
2018-09-17 21:35:24 -03:00
if ( run_rate_controller ) {
attitude_control - > rate_controller_run ( ) ;
}
2019-01-08 21:05:40 -04:00
# if ADVANCED_FAILSAFE == ENABLED
2019-08-21 03:01:17 -03:00
if ( ! hal . util - > get_soft_armed ( ) | |
( plane . afs . should_crash_vehicle ( ) & & ! plane . afs . terminating_vehicle_via_landing ( ) ) | |
SRV_Channels : : get_emergency_stop ( ) ) {
2019-01-08 21:05:40 -04:00
# else
if ( ! hal . util - > get_soft_armed ( ) | | SRV_Channels : : get_emergency_stop ( ) ) {
# endif
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : SHUT_DOWN ) ;
2016-07-22 05:36:28 -03:00
motors - > output ( ) ;
return ;
}
2019-01-15 13:46:13 -04:00
if ( esc_calibration & & AP_Notify : : flags . esc_calibration & & plane . control_mode = = & plane . mode_qstabilize ) {
2016-06-02 00:10:39 -03:00
// output is direct from run_esc_calibration()
return ;
}
2016-08-01 19:21:44 -03:00
2017-10-30 01:19:38 -03:00
if ( in_tailsitter_vtol_transition ( ) ) {
/*
don ' t run the motor outputs while in tailsitter - > vtol
transition . That is taken care of by the fixed wing
stabilisation code
*/
return ;
}
2016-08-01 19:21:44 -03:00
// see if motors should be shut down
2019-02-20 20:01:32 -04:00
update_throttle_suppression ( ) ;
2016-08-01 19:21:44 -03:00
2016-03-24 22:11:56 -03:00
motors - > output ( ) ;
2019-04-09 09:17:25 -03:00
if ( motors - > armed ( ) & & motors - > get_spool_state ( ) ! = AP_Motors : : SpoolState : : SHUT_DOWN ) {
2018-01-24 20:38:07 -04:00
const uint32_t now = AP_HAL : : millis ( ) ;
2019-10-19 12:19:29 -03:00
// log RATE at main loop rate
plane . logger . Write_Rate ( ahrs_view , * motors , * attitude_control , * pos_control ) ;
// log QTUN at 25 Hz
if ( now - last_qtun_log_ms > 40 ) {
last_qtun_log_ms = now ;
Log_Write_QControl_Tuning ( ) ;
}
// log CTRL at 10 Hz
2016-05-27 01:16:44 -03:00
if ( now - last_ctrl_log_ms > 100 ) {
2019-02-20 19:39:33 -04:00
last_ctrl_log_ms = now ;
2016-05-27 01:16:44 -03:00
attitude_control - > control_monitor_log ( ) ;
}
2016-04-03 20:50:15 -03:00
}
2016-08-01 19:21:44 -03:00
// remember when motors were last active for throttle suppression
if ( motors - > get_throttle ( ) > 0.01f | | tilt . motors_active ) {
last_motors_active_ms = AP_HAL : : millis ( ) ;
}
2016-03-24 22:11:56 -03:00
}
2015-12-26 06:40:40 -04:00
/*
update control mode for quadplane modes
*/
void QuadPlane : : control_run ( void )
{
if ( ! initialised ) {
return ;
}
2019-01-15 13:46:13 -04:00
switch ( plane . control_mode - > mode_number ( ) ) {
case Mode : : Number : : QACRO :
2019-02-17 14:42:05 -04:00
control_qacro ( ) ;
2019-05-03 20:37:10 -03:00
break ;
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QSTABILIZE :
2015-12-26 06:40:40 -04:00
control_stabilize ( ) ;
break ;
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QHOVER :
2015-12-26 06:40:40 -04:00
control_hover ( ) ;
break ;
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QLOITER :
case Mode : : Number : : QLAND :
2015-12-26 06:40:40 -04:00
control_loiter ( ) ;
2016-04-29 02:31:08 -03:00
break ;
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QRTL :
2016-04-29 02:31:08 -03:00
control_qrtl ( ) ;
break ;
2018-12-07 03:52:05 -04:00
# if QAUTOTUNE_ENABLED
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QAUTOTUNE :
2018-12-07 03:52:05 -04:00
qautotune . run ( ) ;
break ;
# endif
2015-12-26 06:40:40 -04:00
default :
break ;
}
2019-05-04 11:52:53 -03:00
2015-12-26 06:40:40 -04:00
// we also stabilize using fixed wing surfaces
float speed_scaler = plane . get_speed_scaler ( ) ;
2019-05-04 11:52:53 -03:00
if ( plane . control_mode - > mode_number ( ) = = Mode : : Number : : QACRO ) {
plane . stabilize_acro ( speed_scaler ) ;
} else {
plane . stabilize_roll ( speed_scaler ) ;
plane . stabilize_pitch ( speed_scaler ) ;
}
2015-12-26 06:40:40 -04:00
}
/*
enter a quadplane mode
*/
bool QuadPlane : : init_mode ( void )
{
2016-01-06 03:17:08 -04:00
if ( ! setup ( ) ) {
return false ;
}
2015-12-26 06:40:40 -04:00
if ( ! initialised ) {
2017-07-09 00:49:39 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_CRITICAL , " QuadPlane mode refused " ) ;
2015-12-26 06:40:40 -04:00
return false ;
}
2016-06-02 00:10:39 -03:00
AP_Notify : : flags . esc_calibration = false ;
2019-01-15 13:46:13 -04:00
switch ( plane . control_mode - > mode_number ( ) ) {
case Mode : : Number : : QSTABILIZE :
2015-12-26 06:40:40 -04:00
init_stabilize ( ) ;
break ;
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QHOVER :
2015-12-26 06:40:40 -04:00
init_hover ( ) ;
break ;
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QLOITER :
2015-12-26 06:40:40 -04:00
init_loiter ( ) ;
break ;
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QLAND :
2019-02-22 02:04:35 -04:00
init_qland ( ) ;
2016-03-09 03:20:41 -04:00
break ;
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QRTL :
2016-04-29 02:31:08 -03:00
init_qrtl ( ) ;
break ;
2019-01-15 13:46:13 -04:00
case Mode : : Number : : GUIDED :
2017-09-06 04:58:08 -03:00
guided_takeoff = false ;
break ;
2018-12-07 03:52:05 -04:00
# if QAUTOTUNE_ENABLED
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QAUTOTUNE :
2018-12-07 03:52:05 -04:00
return qautotune . init ( ) ;
# endif
2019-01-15 13:46:13 -04:00
case Mode : : Number : : QACRO :
2019-02-17 14:42:05 -04:00
init_qacro ( ) ;
break ;
2015-12-26 06:40:40 -04:00
default :
break ;
}
return true ;
}
/*
handle a MAVLink DO_VTOL_TRANSITION
*/
2016-04-22 05:22:31 -03:00
bool QuadPlane : : handle_do_vtol_transition ( enum MAV_VTOL_STATE state )
2015-12-26 06:40:40 -04:00
{
if ( ! available ( ) ) {
2017-07-08 22:15:58 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_NOTICE , " VTOL not available " ) ;
2016-04-22 05:22:31 -03:00
return false ;
2015-12-26 06:40:40 -04:00
}
2019-01-15 13:46:13 -04:00
if ( plane . control_mode ! = & plane . mode_auto ) {
2017-07-08 22:15:58 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_NOTICE , " VTOL transition only in AUTO " ) ;
2016-04-22 05:22:31 -03:00
return false ;
2015-12-26 06:40:40 -04:00
}
2016-04-22 05:22:31 -03:00
switch ( state ) {
2015-12-26 06:40:40 -04:00
case MAV_VTOL_STATE_MC :
if ( ! plane . auto_state . vtol_mode ) {
2017-07-08 22:15:58 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_NOTICE , " Entered VTOL mode " ) ;
2015-12-26 06:40:40 -04:00
}
plane . auto_state . vtol_mode = true ;
2016-04-22 05:22:31 -03:00
return true ;
2015-12-26 06:40:40 -04:00
case MAV_VTOL_STATE_FW :
if ( plane . auto_state . vtol_mode ) {
2017-07-08 22:15:58 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_NOTICE , " Exited VTOL mode " ) ;
2015-12-26 06:40:40 -04:00
}
plane . auto_state . vtol_mode = false ;
2016-04-22 05:22:31 -03:00
return true ;
default :
break ;
2015-12-26 06:40:40 -04:00
}
2017-07-08 22:15:58 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_NOTICE , " Invalid VTOL mode " ) ;
2016-04-22 05:22:31 -03:00
return false ;
2015-12-26 06:40:40 -04:00
}
2016-01-01 06:39:36 -04:00
/*
are we in a VTOL auto state ?
*/
2017-10-30 01:19:38 -03:00
bool QuadPlane : : in_vtol_auto ( void ) const
2016-01-01 06:39:36 -04:00
{
2019-02-20 20:01:32 -04:00
if ( ! available ( ) ) {
2016-05-05 22:28:26 -03:00
return false ;
}
2019-01-15 13:46:13 -04:00
if ( plane . control_mode ! = & plane . mode_auto ) {
2016-01-01 06:39:36 -04:00
return false ;
}
if ( plane . auto_state . vtol_mode ) {
return true ;
}
2017-10-29 03:31:09 -03:00
uint16_t id = plane . mission . get_current_nav_cmd ( ) . id ;
switch ( id ) {
2016-01-01 06:39:36 -04:00
case MAV_CMD_NAV_VTOL_TAKEOFF :
return true ;
2016-05-11 02:57:41 -03:00
case MAV_CMD_NAV_LOITER_UNLIM :
case MAV_CMD_NAV_LOITER_TIME :
2017-10-21 03:30:40 -03:00
case MAV_CMD_NAV_LOITER_TURNS :
case MAV_CMD_NAV_LOITER_TO_ALT :
2016-05-11 02:57:41 -03:00
return plane . auto_state . vtol_loiter ;
2017-10-29 03:31:09 -03:00
case MAV_CMD_NAV_TAKEOFF :
return is_vtol_takeoff ( id ) ;
2018-09-25 01:11:26 -03:00
case MAV_CMD_NAV_VTOL_LAND :
2017-10-29 03:31:09 -03:00
case MAV_CMD_NAV_LAND :
return is_vtol_land ( id ) ;
2016-01-01 06:39:36 -04:00
default :
return false ;
}
}
2016-02-20 05:20:27 -04:00
/*
are we in a VTOL mode ?
*/
2017-10-30 01:19:38 -03:00
bool QuadPlane : : in_vtol_mode ( void ) const
2016-02-20 05:20:27 -04:00
{
2019-02-20 20:01:32 -04:00
if ( ! available ( ) ) {
2016-04-09 05:48:22 -03:00
return false ;
}
2019-01-15 13:46:13 -04:00
return ( plane . control_mode = = & plane . mode_qstabilize | |
plane . control_mode = = & plane . mode_qhover | |
plane . control_mode = = & plane . mode_qloiter | |
plane . control_mode = = & plane . mode_qland | |
plane . control_mode = = & plane . mode_qrtl | |
plane . control_mode = = & plane . mode_qacro | |
plane . control_mode = = & plane . mode_qautotune | |
( ( plane . control_mode = = & plane . mode_guided | | plane . control_mode = = & plane . mode_avoidADSB ) & & plane . auto_state . vtol_loiter ) | |
2016-02-20 05:20:27 -04:00
in_vtol_auto ( ) ) ;
}
2016-04-28 23:53:20 -03:00
2015-12-26 06:40:40 -04:00
/*
2016-04-28 23:53:20 -03:00
main landing controller . Used for landing and RTL .
2015-12-26 06:40:40 -04:00
*/
2016-05-05 19:27:47 -03:00
void QuadPlane : : vtol_position_controller ( void )
2015-12-26 06:40:40 -04:00
{
2016-01-09 01:26:13 -04:00
if ( ! setup ( ) ) {
return ;
}
2016-04-01 03:29:51 -03:00
2016-04-28 23:53:20 -03:00
setup_target_position ( ) ;
2015-12-26 06:40:40 -04:00
2016-04-28 23:53:20 -03:00
const Location & loc = plane . next_WP_loc ;
2017-04-22 23:49:48 -03:00
2018-09-13 21:02:18 -03:00
check_attitude_relax ( ) ;
2018-03-09 02:28:02 -04:00
// horizontal position control
2018-02-09 23:52:46 -04:00
switch ( poscontrol . state ) {
2016-04-28 23:53:20 -03:00
2016-05-05 19:27:47 -03:00
case QPOS_POSITION1 : {
2019-04-08 10:16:20 -03:00
const Vector2f diff_wp = plane . current_loc . get_distance_NE ( loc ) ;
2019-02-22 02:04:35 -04:00
const float distance = diff_wp . length ( ) ;
2019-07-25 02:47:55 -03:00
Vector2f groundspeed = ahrs . groundspeed_vector ( ) ;
float speed_towards_target = distance > 1 ? ( diff_wp . normalized ( ) * groundspeed ) : 0 ;
2016-05-05 19:27:47 -03:00
if ( poscontrol . speed_scale < = 0 ) {
2016-04-02 06:54:01 -03:00
// initialise scaling so we start off targeting our
2016-04-03 20:50:15 -03:00
// current linear speed towards the target. If this is
// less than the wpnav speed then the wpnav speed is used
// land_speed_scale is then used to linearly change
// velocity as we approach the waypoint, aiming for zero
// speed at the waypoint
2016-05-07 18:34:30 -03:00
// setup land_speed_scale so at current distance we
// maintain speed towards target, and slow down as we
// approach
2016-04-20 05:07:04 -03:00
// max_speed will control how fast we will fly. It will always decrease
2019-01-24 01:03:12 -04:00
poscontrol . max_speed = MAX ( speed_towards_target , wp_nav - > get_default_speed_xy ( ) * 0.01 ) ;
2016-05-05 19:27:47 -03:00
poscontrol . speed_scale = poscontrol . max_speed / MAX ( distance , 1 ) ;
2016-04-02 06:54:01 -03:00
}
2016-04-10 07:36:20 -03:00
// run fixed wing navigation
2016-04-28 23:53:20 -03:00
plane . nav_controller - > update_waypoint ( plane . prev_WP_loc , loc ) ;
2016-04-10 07:36:20 -03:00
/*
calculate target velocity , not dropping it below 2 m / s
*/
const float final_speed = 2.0f ;
2016-05-05 19:27:47 -03:00
Vector2f target_speed_xy = diff_wp * poscontrol . speed_scale ;
2016-04-20 05:07:04 -03:00
float target_speed = target_speed_xy . length ( ) ;
2016-05-07 18:34:30 -03:00
if ( distance < 1 ) {
// prevent numerical error before switching to POSITION2
target_speed_xy ( 0.1 , 0.1 ) ;
}
2016-04-20 05:07:04 -03:00
if ( target_speed < final_speed ) {
// until we enter the loiter we always aim for at least 2m/s
target_speed_xy = target_speed_xy . normalized ( ) * final_speed ;
2016-05-05 19:27:47 -03:00
poscontrol . max_speed = final_speed ;
} else if ( target_speed > poscontrol . max_speed ) {
2016-04-20 05:07:04 -03:00
// we never speed up during landing approaches
2016-05-05 19:27:47 -03:00
target_speed_xy = target_speed_xy . normalized ( ) * poscontrol . max_speed ;
2016-04-20 05:07:04 -03:00
} else {
2016-05-05 19:27:47 -03:00
poscontrol . max_speed = target_speed ;
2016-04-10 07:36:20 -03:00
}
2016-04-20 05:07:04 -03:00
pos_control - > set_desired_velocity_xy ( target_speed_xy . x * 100 ,
target_speed_xy . y * 100 ) ;
2016-04-10 07:36:20 -03:00
2018-02-09 23:52:46 -04:00
// reset position controller xy target to current position
// because we only want velocity control (no position control)
2016-04-10 07:36:20 -03:00
const Vector3f & curr_pos = inertial_nav . get_position ( ) ;
pos_control - > set_xy_target ( curr_pos . x , curr_pos . y ) ;
2018-02-09 23:52:46 -04:00
pos_control - > set_desired_accel_xy ( 0.0f , 0.0f ) ;
// run horizontal velocity controller
2018-09-05 03:48:15 -03:00
pos_control - > update_vel_controller_xy ( ) ;
2018-02-09 23:52:46 -04:00
2016-04-02 06:54:01 -03:00
// nav roll and pitch are controller by position controller
plane . nav_roll_cd = pos_control - > get_roll ( ) ;
plane . nav_pitch_cd = pos_control - > get_pitch ( ) ;
2016-04-10 07:36:20 -03:00
/*
limit the pitch down with an expanding envelope . This
prevents the velocity controller demanding nose down during
the initial slowdown if the target velocity curve is higher
than the actual velocity curve ( for a high drag
aircraft ) . Nose down will cause a lot of downforce on the
wings which will draw a lot of current and also cause the
2019-07-25 02:47:55 -03:00
aircraft to lose altitude rapidly . pitch limit varies also with speed
to prevent inability to progress to position if moving from a loiter
to landing
2016-04-10 07:36:20 -03:00
*/
2019-07-25 02:47:55 -03:00
float minlimit = linear_interpolate ( - aparm . angle_max , - 300 ,
speed_towards_target ,
wp_nav - > get_default_speed_xy ( ) * 0.01 ,
wp_nav - > get_default_speed_xy ( ) * 0.015 ) ;
float pitch_limit_cd = linear_interpolate ( minlimit , plane . aparm . pitch_limit_min_cd ,
2016-04-10 07:36:20 -03:00
plane . auto_state . wp_proportion , 0 , 1 ) ;
if ( plane . nav_pitch_cd < pitch_limit_cd ) {
plane . nav_pitch_cd = pitch_limit_cd ;
// tell the pos controller we have limited the pitch to
// stop integrator buildup
pos_control - > set_limit_accel_xy ( ) ;
}
2016-04-02 07:19:06 -03:00
2016-04-02 06:54:01 -03:00
// call attitude controller
2016-06-16 23:48:25 -03:00
attitude_control - > input_euler_angle_roll_pitch_euler_rate_yaw ( plane . nav_roll_cd ,
2016-04-02 06:54:01 -03:00
plane . nav_pitch_cd ,
2017-07-10 01:00:45 -03:00
desired_auto_yaw_rate_cds ( ) + get_weathervane_yaw_rate_cds ( ) ) ;
2016-04-10 07:36:20 -03:00
if ( plane . auto_state . wp_proportion > = 1 | |
plane . auto_state . wp_distance < 5 ) {
2016-05-05 19:27:47 -03:00
poscontrol . state = QPOS_POSITION2 ;
2018-09-18 23:50:51 -03:00
loiter_nav - > clear_pilot_desired_acceleration ( ) ;
2018-03-27 23:24:05 -03:00
loiter_nav - > init_target ( ) ;
2017-07-08 22:15:58 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " VTOL position2 started v=%.1f d=%.1f " ,
2016-04-14 20:22:41 -03:00
( double ) ahrs . groundspeed ( ) , ( double ) plane . auto_state . wp_distance ) ;
2016-04-02 06:54:01 -03:00
}
2016-04-28 23:53:20 -03:00
break ;
}
2016-05-05 19:27:47 -03:00
case QPOS_POSITION2 :
case QPOS_LAND_DESCEND :
2016-01-15 01:49:49 -04:00
/*
2018-02-09 23:52:46 -04:00
for final land repositioning and descent we run the position controller
2016-01-15 01:49:49 -04:00
*/
2018-02-09 23:52:46 -04:00
2016-01-09 18:42:46 -04:00
// also run fixed wing navigation
2016-04-28 23:53:20 -03:00
plane . nav_controller - > update_waypoint ( plane . prev_WP_loc , loc ) ;
2018-02-09 23:52:46 -04:00
FALLTHROUGH ;
2016-01-15 01:49:49 -04:00
2018-02-09 23:52:46 -04:00
case QPOS_LAND_FINAL :
// set position controller desired velocity and acceleration to zero
pos_control - > set_desired_velocity_xy ( 0.0f , 0.0f ) ;
pos_control - > set_desired_accel_xy ( 0.0f , 0.0f ) ;
// set position control target and update
2019-02-22 02:04:35 -04:00
if ( should_relax ( ) ) {
loiter_nav - > soften_for_landing ( ) ;
} else {
pos_control - > set_xy_target ( poscontrol . target . x , poscontrol . target . y ) ;
}
2018-09-05 03:48:15 -03:00
pos_control - > update_xy_controller ( ) ;
2016-01-15 01:49:49 -04:00
// nav roll and pitch are controller by position controller
2018-02-09 23:52:46 -04:00
plane . nav_roll_cd = pos_control - > get_roll ( ) ;
plane . nav_pitch_cd = pos_control - > get_pitch ( ) ;
2016-01-09 18:42:46 -04:00
2016-01-15 01:49:49 -04:00
// call attitude controller
2016-06-16 23:48:25 -03:00
attitude_control - > input_euler_angle_roll_pitch_euler_rate_yaw ( plane . nav_roll_cd ,
2018-02-09 23:52:46 -04:00
plane . nav_pitch_cd ,
get_pilot_input_yaw_rate_cds ( ) + get_weathervane_yaw_rate_cds ( ) ) ;
2016-04-28 23:53:20 -03:00
break ;
2016-01-09 18:42:46 -04:00
2016-05-05 19:27:47 -03:00
case QPOS_LAND_COMPLETE :
2016-04-28 23:53:20 -03:00
// nothing to do
break ;
2016-01-02 02:55:56 -04:00
}
2015-12-26 06:40:40 -04:00
2016-04-28 23:53:20 -03:00
// now height control
2016-05-05 19:27:47 -03:00
switch ( poscontrol . state ) {
case QPOS_POSITION1 :
2017-10-21 03:30:40 -03:00
case QPOS_POSITION2 : {
bool vtol_loiter_auto = false ;
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_auto ) {
2017-10-21 03:30:40 -03:00
switch ( plane . mission . get_current_nav_cmd ( ) . id ) {
case MAV_CMD_NAV_LOITER_UNLIM :
case MAV_CMD_NAV_LOITER_TIME :
case MAV_CMD_NAV_LOITER_TURNS :
case MAV_CMD_NAV_LOITER_TO_ALT :
vtol_loiter_auto = true ;
break ;
}
}
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_qrtl | | plane . control_mode = = & plane . mode_guided | | vtol_loiter_auto ) {
2016-04-29 02:31:08 -03:00
plane . ahrs . get_position ( plane . current_loc ) ;
2016-04-29 02:50:45 -03:00
float target_altitude = plane . next_WP_loc . alt ;
2016-05-05 19:27:47 -03:00
if ( poscontrol . slow_descent ) {
2016-04-29 02:50:45 -03:00
// gradually descend as we approach target
2019-04-12 05:23:04 -03:00
plane . auto_state . wp_proportion = plane . current_loc . line_path_proportion ( plane . prev_WP_loc , plane . next_WP_loc ) ;
2016-04-29 02:50:45 -03:00
target_altitude = linear_interpolate ( plane . prev_WP_loc . alt ,
plane . next_WP_loc . alt ,
plane . auto_state . wp_proportion ,
0 , 1 ) ;
}
2017-09-05 19:22:38 -03:00
adjust_alt_target ( target_altitude - plane . home . alt ) ;
2016-04-29 02:31:08 -03:00
} else {
pos_control - > set_alt_target_from_climb_rate ( 0 , plane . G_Dt , false ) ;
}
2016-04-28 23:53:20 -03:00
break ;
2017-10-21 03:30:40 -03:00
}
2015-12-26 06:40:40 -04:00
2016-05-05 19:27:47 -03:00
case QPOS_LAND_DESCEND : {
2016-06-11 01:37:44 -03:00
float height_above_ground = plane . relative_ground_altitude ( plane . g . rangefinder_landing ) ;
2016-04-28 23:53:20 -03:00
pos_control - > set_alt_target_from_climb_rate ( - landing_descent_rate_cms ( height_above_ground ) ,
plane . G_Dt , true ) ;
2016-01-01 05:42:10 -04:00
break ;
2016-04-28 23:53:20 -03:00
}
2016-05-05 19:27:47 -03:00
case QPOS_LAND_FINAL :
2016-04-28 23:53:20 -03:00
pos_control - > set_alt_target_from_climb_rate ( - land_speed_cms , plane . G_Dt , true ) ;
2016-01-01 05:42:10 -04:00
break ;
2016-04-28 23:53:20 -03:00
2016-05-05 19:27:47 -03:00
case QPOS_LAND_COMPLETE :
2016-01-01 05:42:10 -04:00
break ;
}
2017-03-13 04:04:09 -03:00
run_z_controller ( ) ;
2015-12-26 06:40:40 -04:00
}
2016-01-01 05:42:10 -04:00
2016-04-28 23:53:20 -03:00
/*
setup the target position based on plane . next_WP_loc
*/
void QuadPlane : : setup_target_position ( void )
{
const Location & loc = plane . next_WP_loc ;
2019-06-28 18:31:44 -03:00
Location origin ;
if ( ! ahrs . get_origin ( origin ) ) {
origin . zero ( ) ;
}
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2017-07-10 01:00:45 -03:00
2019-04-08 10:16:20 -03:00
const Vector2f diff2d = origin . get_distance_NE ( loc ) ;
2016-05-05 19:27:47 -03:00
poscontrol . target . x = diff2d . x * 100 ;
poscontrol . target . y = diff2d . y * 100 ;
poscontrol . target . z = plane . next_WP_loc . alt - origin . alt ;
2016-04-28 23:53:20 -03:00
2018-01-24 20:38:07 -04:00
const uint32_t now = AP_HAL : : millis ( ) ;
2019-04-08 08:45:29 -03:00
if ( ! loc . same_latlon_as ( last_auto_target ) | |
2016-04-28 23:53:20 -03:00
plane . next_WP_loc . alt ! = last_auto_target . alt | |
2018-01-24 20:38:07 -04:00
now - last_loiter_ms > 500 ) {
2016-05-05 19:27:47 -03:00
wp_nav - > set_wp_destination ( poscontrol . target ) ;
2016-04-28 23:53:20 -03:00
last_auto_target = loc ;
}
2018-01-24 20:38:07 -04:00
last_loiter_ms = now ;
2016-04-28 23:53:20 -03:00
// setup vertical speed and acceleration
2018-09-20 02:44:42 -03:00
pos_control - > set_max_speed_z ( - pilot_velocity_z_max , pilot_velocity_z_max ) ;
pos_control - > set_max_accel_z ( pilot_accel_z ) ;
2016-04-28 23:53:20 -03:00
}
/*
run takeoff controller to climb vertically
*/
void QuadPlane : : takeoff_controller ( void )
{
/*
2018-02-09 23:52:46 -04:00
for takeoff we use the position controller
2016-04-28 23:53:20 -03:00
*/
2018-09-13 21:02:18 -03:00
check_attitude_relax ( ) ;
2016-04-28 23:53:20 -03:00
setup_target_position ( ) ;
2018-02-09 23:52:46 -04:00
// set position controller desired velocity and acceleration to zero
pos_control - > set_desired_velocity_xy ( 0.0f , 0.0f ) ;
pos_control - > set_desired_accel_xy ( 0.0f , 0.0f ) ;
// set position control target and update
pos_control - > set_xy_target ( poscontrol . target . x , poscontrol . target . y ) ;
2018-09-05 03:48:15 -03:00
pos_control - > update_xy_controller ( ) ;
2018-02-09 23:52:46 -04:00
2016-04-28 23:53:20 -03:00
// nav roll and pitch are controller by position controller
plane . nav_roll_cd = pos_control - > get_roll ( ) ;
plane . nav_pitch_cd = pos_control - > get_pitch ( ) ;
2018-02-09 23:52:46 -04:00
attitude_control - > input_euler_angle_roll_pitch_euler_rate_yaw ( plane . nav_roll_cd ,
plane . nav_pitch_cd ,
get_pilot_input_yaw_rate_cds ( ) + get_weathervane_yaw_rate_cds ( ) ) ;
2019-01-24 01:03:12 -04:00
pos_control - > set_alt_target_from_climb_rate ( wp_nav - > get_default_speed_up ( ) , plane . G_Dt , true ) ;
2017-03-13 04:04:09 -03:00
run_z_controller ( ) ;
2016-04-28 23:53:20 -03:00
}
/*
run waypoint controller between prev_WP_loc and next_WP_loc
*/
void QuadPlane : : waypoint_controller ( void )
{
setup_target_position ( ) ;
2018-09-13 21:02:18 -03:00
check_attitude_relax ( ) ;
2016-04-28 23:53:20 -03:00
/*
this is full copter control of auto flight
*/
// run wpnav controller
wp_nav - > update_wpnav ( ) ;
// call attitude controller
attitude_control - > input_euler_angle_roll_pitch_yaw ( wp_nav - > get_roll ( ) ,
wp_nav - > get_pitch ( ) ,
wp_nav - > get_yaw ( ) ,
2017-07-10 01:00:45 -03:00
true ) ;
2016-04-28 23:53:20 -03:00
// nav roll and pitch are controller by loiter controller
plane . nav_roll_cd = wp_nav - > get_roll ( ) ;
plane . nav_pitch_cd = wp_nav - > get_pitch ( ) ;
// climb based on altitude error
pos_control - > set_alt_target_from_climb_rate_ff ( assist_climb_rate_cms ( ) , plane . G_Dt , false ) ;
2017-03-13 04:04:09 -03:00
run_z_controller ( ) ;
2016-04-28 23:53:20 -03:00
}
/*
handle auto - mode when auto_state . vtol_mode is true
*/
2019-10-01 11:11:48 -03:00
void QuadPlane : : control_auto ( void )
2016-04-28 23:53:20 -03:00
{
if ( ! setup ( ) ) {
return ;
}
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2016-04-28 23:53:20 -03:00
2017-10-29 03:31:09 -03:00
uint16_t id = plane . mission . get_current_nav_cmd ( ) . id ;
switch ( id ) {
2016-05-11 02:57:41 -03:00
case MAV_CMD_NAV_VTOL_TAKEOFF :
2017-10-29 03:31:09 -03:00
case MAV_CMD_NAV_TAKEOFF :
if ( is_vtol_takeoff ( id ) ) {
takeoff_controller ( ) ;
}
break ;
2018-09-25 01:11:26 -03:00
case MAV_CMD_NAV_VTOL_LAND :
2017-10-29 03:31:09 -03:00
case MAV_CMD_NAV_LAND :
if ( is_vtol_land ( id ) ) {
vtol_position_controller ( ) ;
}
2016-05-11 02:57:41 -03:00
break ;
case MAV_CMD_NAV_LOITER_UNLIM :
case MAV_CMD_NAV_LOITER_TIME :
2017-10-21 03:30:40 -03:00
case MAV_CMD_NAV_LOITER_TURNS :
case MAV_CMD_NAV_LOITER_TO_ALT :
2016-05-05 19:27:47 -03:00
vtol_position_controller ( ) ;
2016-05-11 02:57:41 -03:00
break ;
default :
2016-04-28 23:53:20 -03:00
waypoint_controller ( ) ;
2016-05-11 02:57:41 -03:00
break ;
2016-04-28 23:53:20 -03:00
}
}
2016-04-29 02:31:08 -03:00
/*
handle QRTL mode
*/
void QuadPlane : : control_qrtl ( void )
{
2016-05-05 19:27:47 -03:00
vtol_position_controller ( ) ;
if ( poscontrol . state > = QPOS_POSITION2 ) {
2016-04-29 02:31:08 -03:00
// change target altitude to home alt
plane . next_WP_loc . alt = plane . home . alt ;
verify_vtol_land ( ) ;
}
}
/*
handle QRTL mode
*/
void QuadPlane : : init_qrtl ( void )
{
// use do_RTL() to setup next_WP_loc
plane . do_RTL ( plane . home . alt + qrtl_alt * 100UL ) ;
plane . prev_WP_loc = plane . current_loc ;
2016-05-05 19:27:47 -03:00
poscontrol . slow_descent = ( plane . current_loc . alt > plane . next_WP_loc . alt ) ;
poscontrol . state = QPOS_POSITION1 ;
poscontrol . speed_scale = 0 ;
2018-02-09 23:52:46 -04:00
pos_control - > set_desired_accel_xy ( 0.0f , 0.0f ) ;
2018-08-21 03:26:42 -03:00
pos_control - > init_xy_controller ( ) ;
2016-04-29 02:31:08 -03:00
}
2016-01-01 05:42:10 -04:00
/*
start a VTOL takeoff
*/
bool QuadPlane : : do_vtol_takeoff ( const AP_Mission : : Mission_Command & cmd )
{
2016-01-09 01:26:13 -04:00
if ( ! setup ( ) ) {
2016-01-01 05:42:10 -04:00
return false ;
}
2016-04-01 03:29:51 -03:00
2019-01-10 21:22:41 -04:00
// we always use the current location in XY for takeoff. The altitude defaults
// to relative to current height, but if Q_OPTIONS is set to respect takeoff frame
// then it will use normal frame handling for height
Location loc = cmd . content . location ;
loc . lat = 0 ;
loc . lng = 0 ;
plane . set_next_WP ( loc ) ;
2018-08-13 17:16:45 -03:00
if ( options & OPTION_RESPECT_TAKEOFF_FRAME ) {
if ( plane . current_loc . alt > = plane . next_WP_loc . alt ) {
// we are above the takeoff already, no need to do anything
return false ;
}
} else {
plane . next_WP_loc . alt = plane . current_loc . alt + cmd . content . location . alt ;
}
2016-01-01 05:42:10 -04:00
throttle_wait = false ;
2016-01-08 19:47:41 -04:00
// set target to current position
2018-09-18 23:50:51 -03:00
loiter_nav - > clear_pilot_desired_acceleration ( ) ;
2018-03-27 23:24:05 -03:00
loiter_nav - > init_target ( ) ;
2016-01-08 19:47:41 -04:00
// initialize vertical speed and acceleration
2018-09-20 02:44:42 -03:00
pos_control - > set_max_speed_z ( - pilot_velocity_z_max , pilot_velocity_z_max ) ;
pos_control - > set_max_accel_z ( pilot_accel_z ) ;
2016-01-08 19:47:41 -04:00
// initialise position and desired velocity
2017-09-05 19:22:38 -03:00
set_alt_target_current ( ) ;
2016-01-08 19:47:41 -04:00
pos_control - > set_desired_velocity_z ( inertial_nav . get_velocity_z ( ) ) ;
2016-01-01 05:42:10 -04:00
// also update nav_controller for status output
plane . nav_controller - > update_waypoint ( plane . prev_WP_loc , plane . next_WP_loc ) ;
2018-12-15 01:23:56 -04:00
// calculate the time required to complete a takeoff
// this may be conservative and accept extra time due to clamping
// derived from the following latex equations if you want a nicely formatted view
// t_{accel} = \frac{V_max - V_z}{a}
// d_{accel} = V_z*t_{accel} + \frac{1}{2}*a*t_{accel}^2
// d_{remaining} = d_{total} - d_{accel}
// t_{constant} = \frac{d_{remaining}}{V_z}
// t = max(t_{accel}, 0) + max(t_{constant}, 0)
const float d_total = ( plane . next_WP_loc . alt - plane . current_loc . alt ) * 0.01f ;
const float accel_m_s_s = MAX ( 10 , pilot_accel_z ) * 0.01f ;
const float vel_max = MAX ( 10 , pilot_velocity_z_max ) * 0.01f ;
const float vel_z = inertial_nav . get_velocity_z ( ) * 0.01f ;
const float t_accel = ( vel_max - vel_z ) / accel_m_s_s ;
const float d_accel = vel_z * t_accel + 0.5f * accel_m_s_s * sq ( t_accel ) ;
const float d_remaining = d_total - d_accel ;
const float t_constant = d_remaining / vel_max ;
const float travel_time = MAX ( t_accel , 0 ) + MAX ( t_constant , 0 ) ;
// setup the takeoff failure handling code
takeoff_start_time_ms = millis ( ) ;
takeoff_time_limit_ms = MAX ( travel_time * takeoff_failure_scalar * 1000 , 5000 ) ; // minimum time 5 seconds
2016-01-01 05:42:10 -04:00
return true ;
}
/*
start a VTOL landing
*/
bool QuadPlane : : do_vtol_land ( const AP_Mission : : Mission_Command & cmd )
{
2016-01-09 01:26:13 -04:00
if ( ! setup ( ) ) {
2016-01-01 05:42:10 -04:00
return false ;
}
2018-09-17 21:35:24 -03:00
attitude_control - > reset_rate_controller_I_terms ( ) ;
2018-01-15 07:59:09 -04:00
pos_control - > get_accel_z_pid ( ) . reset_I ( ) ;
2017-11-20 09:02:22 -04:00
pos_control - > get_vel_xy_pid ( ) . reset_I ( ) ;
2016-01-15 01:49:49 -04:00
2016-01-01 06:57:31 -04:00
plane . set_next_WP ( cmd . content . location ) ;
2016-01-02 02:55:56 -04:00
// initially aim for current altitude
plane . next_WP_loc . alt = plane . current_loc . alt ;
2016-05-05 19:27:47 -03:00
poscontrol . state = QPOS_POSITION1 ;
poscontrol . speed_scale = 0 ;
2018-02-09 23:52:46 -04:00
pos_control - > set_desired_accel_xy ( 0.0f , 0.0f ) ;
2018-08-21 03:26:42 -03:00
pos_control - > init_xy_controller ( ) ;
2016-04-10 07:36:20 -03:00
2016-01-15 01:49:49 -04:00
throttle_wait = false ;
2016-06-16 05:17:46 -03:00
landing_detect . lower_limit_start_ms = 0 ;
2019-02-01 23:49:02 -04:00
landing_detect . land_start_ms = 0 ;
2017-09-05 19:22:38 -03:00
set_alt_target_current ( ) ;
2018-09-25 01:11:26 -03:00
plane . crash_state . is_crashed = false ;
2016-01-01 07:09:11 -04:00
2016-01-01 05:42:10 -04:00
// also update nav_controller for status output
plane . nav_controller - > update_waypoint ( plane . prev_WP_loc , plane . next_WP_loc ) ;
return true ;
}
/*
check if a VTOL takeoff has completed
*/
bool QuadPlane : : verify_vtol_takeoff ( const AP_Mission : : Mission_Command & cmd )
{
2016-01-01 07:09:11 -04:00
if ( ! available ( ) ) {
return true ;
}
2018-12-15 01:23:56 -04:00
const uint32_t now = millis ( ) ;
// reset takeoff start time if we aren't armed, as we won't have made any progress
if ( ! hal . util - > get_soft_armed ( ) ) {
takeoff_start_time_ms = now ;
}
// check for failure conditions
if ( is_positive ( takeoff_failure_scalar ) & & ( ( now - takeoff_start_time_ms ) > takeoff_time_limit_ms ) ) {
gcs ( ) . send_text ( MAV_SEVERITY_CRITICAL , " Failed to complete takeoff within time limit " ) ;
plane . set_mode ( plane . mode_qland , MODE_REASON_VTOL_FAILED_TAKEOFF ) ;
return false ;
}
2018-12-15 16:47:49 -04:00
if ( is_positive ( maximum_takeoff_airspeed ) & & ( plane . airspeed . get_airspeed ( ) > maximum_takeoff_airspeed ) ) {
gcs ( ) . send_text ( MAV_SEVERITY_CRITICAL , " Failed to complete takeoff, excessive wind " ) ;
plane . set_mode ( plane . mode_qland , MODE_REASON_VTOL_FAILED_TAKEOFF ) ;
return false ;
}
2016-01-08 19:47:41 -04:00
if ( plane . current_loc . alt < plane . next_WP_loc . alt ) {
2016-01-01 05:42:10 -04:00
return false ;
}
2017-11-19 01:05:01 -04:00
transition_state = is_tailsitter ( ) ? TRANSITION_ANGLE_WAIT_FW : TRANSITION_AIRSPEED_WAIT ;
2016-04-02 05:53:48 -03:00
plane . TECS_controller . set_pitch_max_limit ( transition_pitch_max ) ;
2017-09-05 19:22:38 -03:00
set_alt_target_current ( ) ;
2016-08-29 05:04:42 -03:00
plane . complete_auto_takeoff ( ) ;
2016-01-01 05:42:10 -04:00
return true ;
}
2016-06-16 05:17:46 -03:00
/*
2019-02-01 23:49:02 -04:00
a landing detector based on change in altitude over a timeout
2016-06-16 05:17:46 -03:00
*/
2019-02-01 23:49:02 -04:00
bool QuadPlane : : land_detector ( uint32_t timeout_ms )
2016-03-09 03:20:41 -04:00
{
2018-01-24 20:38:07 -04:00
const uint32_t now = AP_HAL : : millis ( ) ;
2016-06-16 05:17:46 -03:00
bool might_be_landed = ( landing_detect . lower_limit_start_ms ! = 0 & &
now - landing_detect . lower_limit_start_ms > 1000 ) ;
if ( ! might_be_landed ) {
landing_detect . land_start_ms = 0 ;
2019-02-01 23:49:02 -04:00
return false ;
2016-06-16 05:17:46 -03:00
}
float height = inertial_nav . get_altitude ( ) * 0.01f ;
if ( landing_detect . land_start_ms = = 0 ) {
landing_detect . land_start_ms = now ;
landing_detect . vpos_start_m = height ;
}
2019-02-01 23:49:02 -04:00
2016-06-16 05:17:46 -03:00
// we only consider the vehicle landed when the motors have been
2019-02-01 23:49:02 -04:00
// at minimum for timeout_ms+1000 and the vertical position estimate has not
// changed by more than 20cm for timeout_ms
2016-07-08 05:38:43 -03:00
if ( fabsf ( height - landing_detect . vpos_start_m ) > 0.2 ) {
2016-06-16 05:17:46 -03:00
// height has changed, call off landing detection
landing_detect . land_start_ms = 0 ;
2019-02-01 23:49:02 -04:00
return false ;
2016-06-16 05:17:46 -03:00
}
2019-02-01 23:49:02 -04:00
if ( ( now - landing_detect . land_start_ms ) < timeout_ms | |
( now - landing_detect . lower_limit_start_ms ) < ( timeout_ms + 1000 ) ) {
2016-06-16 05:17:46 -03:00
// not landed yet
2019-02-01 23:49:02 -04:00
return false ;
}
return true ;
}
/*
check if a landing is complete
*/
void QuadPlane : : check_land_complete ( void )
{
if ( poscontrol . state ! = QPOS_LAND_FINAL ) {
// only apply to final landing phase
2016-06-16 05:17:46 -03:00
return ;
2016-03-09 03:20:41 -04:00
}
2019-02-01 23:49:02 -04:00
if ( land_detector ( 4000 ) ) {
2019-05-03 20:35:12 -03:00
plane . arming . disarm ( ) ;
2019-02-01 23:49:02 -04:00
poscontrol . state = QPOS_LAND_COMPLETE ;
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Land complete " ) ;
// reload target airspeed which could have been modified by the mission
plane . aparm . airspeed_cruise_cm . load ( ) ;
}
}
/*
check if we should switch from QPOS_LAND_DESCEND to QPOS_LAND_FINAL
*/
bool QuadPlane : : check_land_final ( void )
{
float height_above_ground = plane . relative_ground_altitude ( plane . g . rangefinder_landing ) ;
if ( height_above_ground < land_final_alt ) {
return true ;
}
/*
also apply landing detector , in case we have landed in descent
phase . Use a longer threshold
*/
return land_detector ( 6000 ) ;
2016-03-09 03:20:41 -04:00
}
2016-01-01 05:42:10 -04:00
/*
check if a VTOL landing has completed
*/
2016-04-29 02:31:08 -03:00
bool QuadPlane : : verify_vtol_land ( void )
2016-01-01 05:42:10 -04:00
{
2016-01-01 07:09:11 -04:00
if ( ! available ( ) ) {
return true ;
}
2016-05-05 19:27:47 -03:00
if ( poscontrol . state = = QPOS_POSITION2 & &
2016-01-02 02:55:56 -04:00
plane . auto_state . wp_distance < 2 ) {
2016-05-05 19:27:47 -03:00
poscontrol . state = QPOS_LAND_DESCEND ;
2017-07-08 22:15:58 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Land descend started " ) ;
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_auto ) {
2019-02-23 22:53:52 -04:00
// set height to mission height, so we can use the mission
// WP height for triggering land final if no rangefinder
// available
plane . set_next_WP ( plane . mission . get_current_nav_cmd ( ) . content . location ) ;
} else {
plane . set_next_WP ( plane . next_WP_loc ) ;
plane . next_WP_loc . alt = ahrs . get_home ( ) . alt ;
}
2016-01-02 02:55:56 -04:00
}
// at land_final_alt begin final landing
2019-02-01 23:49:02 -04:00
if ( poscontrol . state = = QPOS_LAND_DESCEND & & check_land_final ( ) ) {
2016-05-05 19:27:47 -03:00
poscontrol . state = QPOS_LAND_FINAL ;
2016-07-25 02:46:17 -03:00
// cut IC engine if enabled
if ( land_icengine_cut ! = 0 ) {
plane . g2 . ice_control . engine_control ( 0 , 0 , 0 ) ;
}
2017-07-08 22:15:58 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Land final started " ) ;
2016-01-02 02:55:56 -04:00
}
2016-03-09 03:20:41 -04:00
check_land_complete ( ) ;
2016-01-01 05:42:10 -04:00
return false ;
}
2016-03-24 22:33:19 -03:00
// Write a control tuning packet
void QuadPlane : : Log_Write_QControl_Tuning ( )
{
2018-12-21 03:42:59 -04:00
float des_alt_m = 0.0f ;
int16_t target_climb_rate_cms = 0 ;
2019-01-15 13:46:13 -04:00
if ( plane . control_mode ! = & plane . mode_qstabilize ) {
2018-12-21 03:42:59 -04:00
des_alt_m = pos_control - > get_alt_target ( ) / 100.0f ;
target_climb_rate_cms = pos_control - > get_vel_target_z ( ) ;
}
2016-03-24 22:33:19 -03:00
struct log_QControl_Tuning pkt = {
LOG_PACKET_HEADER_INIT ( LOG_QTUN_MSG ) ,
time_us : AP_HAL : : micros64 ( ) ,
2018-12-21 03:42:59 -04:00
throttle_in : attitude_control - > get_throttle_in ( ) ,
2016-03-24 22:33:19 -03:00
angle_boost : attitude_control - > angle_boost ( ) ,
throttle_out : motors - > get_throttle ( ) ,
2018-12-21 03:42:59 -04:00
throttle_hover : motors - > get_throttle_hover ( ) ,
desired_alt : des_alt_m ,
2016-03-24 22:33:19 -03:00
inav_alt : inertial_nav . get_altitude ( ) / 100.0f ,
2018-12-21 03:42:59 -04:00
baro_alt : int32_t ( plane . barometer . get_altitude ( ) * 100 ) ,
target_climb_rate : target_climb_rate_cms ,
climb_rate : int16_t ( inertial_nav . get_velocity_z ( ) ) ,
2017-04-18 09:25:14 -03:00
throttle_mix : attitude_control - > get_throttle_mix ( ) ,
2016-03-24 22:33:19 -03:00
} ;
2019-01-18 00:23:42 -04:00
plane . logger . WriteBlock ( & pkt , sizeof ( pkt ) ) ;
2018-03-01 01:44:44 -04:00
// write multicopter position control message
pos_control - > write_log ( ) ;
2016-03-24 22:33:19 -03:00
}
2016-04-20 02:13:20 -03:00
/*
calculate the forward throttle percentage . The forward throttle can
be used to assist with position hold and with landing approach . It
reduces the need for down pitch which reduces load on the vertical
lift motors .
*/
int8_t QuadPlane : : forward_throttle_pct ( void )
{
/*
in non - VTOL modes or modes without a velocity controller . We
don ' t use it in QHOVER or QSTABILIZE as they are the primary
recovery modes for a quadplane and need to be as simple as
possible . They will drift with the wind
*/
if ( ! in_vtol_mode ( ) | |
! motors - > armed ( ) | |
vel_forward . gain < = 0 | |
2019-01-15 13:46:13 -04:00
plane . control_mode = = & plane . mode_qstabilize | |
plane . control_mode = = & plane . mode_qhover | |
plane . control_mode = = & plane . mode_qautotune | |
2019-04-09 09:17:25 -03:00
motors - > get_desired_spool_state ( ) < AP_Motors : : DesiredSpoolState : : GROUND_IDLE ) {
2016-04-20 02:13:20 -03:00
return 0 ;
}
2016-06-02 18:57:10 -03:00
float deltat = ( AP_HAL : : millis ( ) - vel_forward . last_ms ) * 0.001f ;
2016-04-20 02:13:20 -03:00
if ( deltat > 1 | | deltat < 0 ) {
vel_forward . integrator = 0 ;
deltat = 0.1 ;
}
if ( deltat < 0.1 ) {
// run at 10Hz
return vel_forward . last_pct ;
}
2016-06-02 18:57:10 -03:00
vel_forward . last_ms = AP_HAL : : millis ( ) ;
2016-04-20 02:13:20 -03:00
// work out the desired speed in forward direction
const Vector3f & desired_velocity_cms = pos_control - > get_desired_velocity ( ) ;
Vector3f vel_ned ;
if ( ! plane . ahrs . get_velocity_NED ( vel_ned ) ) {
// we don't know our velocity? EKF must be pretty sick
vel_forward . last_pct = 0 ;
2016-06-03 19:35:09 -03:00
vel_forward . integrator = 0 ;
2016-04-20 02:13:20 -03:00
return 0 ;
}
Vector3f vel_error_body = ahrs . get_rotation_body_to_ned ( ) . transposed ( ) * ( ( desired_velocity_cms * 0.01f ) - vel_ned ) ;
// find component of velocity error in fwd body frame direction
float fwd_vel_error = vel_error_body * Vector3f ( 1 , 0 , 0 ) ;
// scale forward velocity error by maximum airspeed
2016-08-07 21:48:36 -03:00
fwd_vel_error / = MAX ( plane . aparm . airspeed_max , 5 ) ;
2016-04-20 02:13:20 -03:00
// add in a component from our current pitch demand. This tends to
// move us to zero pitch. Assume that LIM_PITCH would give us the
// WP nav speed.
2019-01-24 01:03:12 -04:00
fwd_vel_error - = ( wp_nav - > get_default_speed_xy ( ) * 0.01f ) * plane . nav_pitch_cd / ( float ) plane . aparm . pitch_limit_max_cd ;
2016-04-20 02:13:20 -03:00
if ( should_relax ( ) & & vel_ned . length ( ) < 1 ) {
// we may be landed
fwd_vel_error = 0 ;
vel_forward . integrator * = 0.95f ;
}
// integrator as throttle percentage (-100 to 100)
vel_forward . integrator + = fwd_vel_error * deltat * vel_forward . gain * 100 ;
2016-06-13 18:31:50 -03:00
// inhibit reverse throttle and allow petrol engines with min > 0
2018-11-09 18:38:43 -04:00
int8_t fwd_throttle_min = plane . have_reverse_thrust ( ) ? 0 : plane . aparm . throttle_min ;
2016-06-13 18:31:50 -03:00
vel_forward . integrator = constrain_float ( vel_forward . integrator , fwd_throttle_min , plane . aparm . throttle_max ) ;
2019-02-22 20:20:20 -04:00
2019-02-23 22:53:52 -04:00
if ( in_vtol_land_approach ( ) ) {
2019-02-22 20:20:20 -04:00
// when we are doing horizontal positioning in a VTOL land
// we always allow the fwd motor to run. Otherwise a bad
// lidar could cause the aircraft not to be able to
// approach the landing point when landing below the takeoff point
vel_forward . last_pct = vel_forward . integrator ;
2019-09-11 04:20:04 -03:00
} else if ( in_vtol_land_final ( ) & & motors - > limit . throttle_lower ) {
2019-09-11 01:02:49 -03:00
// we're in the settling phase of landing, disable fwd motor
vel_forward . last_pct = 0 ;
vel_forward . integrator = 0 ;
2019-02-22 20:20:20 -04:00
} else {
// If we are below alt_cutoff then scale down the effect until
// it turns off at alt_cutoff and decay the integrator
float alt_cutoff = MAX ( 0 , vel_forward_alt_cutoff ) ;
float height_above_ground = plane . relative_ground_altitude ( plane . g . rangefinder_landing ) ;
vel_forward . last_pct = linear_interpolate ( 0 , vel_forward . integrator ,
height_above_ground , alt_cutoff , alt_cutoff + 2 ) ;
}
2016-06-03 19:35:09 -03:00
if ( vel_forward . last_pct = = 0 ) {
// if the percent is 0 then decay the integrator
vel_forward . integrator * = 0.95f ;
}
2016-04-20 02:13:20 -03:00
return vel_forward . last_pct ;
}
2016-04-20 03:23:17 -03:00
/*
get weathervaning yaw rate in cd / s
*/
float QuadPlane : : get_weathervane_yaw_rate_cds ( void )
{
/*
we only do weathervaning in modes where we are doing VTOL
position control . We also don ' t do it if the pilot has given any
yaw input in the last 3 seconds .
*/
if ( ! in_vtol_mode ( ) | |
! motors - > armed ( ) | |
weathervane . gain < = 0 | |
2019-01-15 13:46:13 -04:00
plane . control_mode = = & plane . mode_qstabilize | |
plane . control_mode = = & plane . mode_qhover | |
plane . control_mode = = & plane . mode_qautotune ) {
2016-04-20 03:23:17 -03:00
weathervane . last_output = 0 ;
return 0 ;
}
2018-08-24 02:42:37 -03:00
const uint32_t tnow = millis ( ) ;
ArduPlane: Fix up after refactoring RC_Channel class
Further to refactor of RC_Channel class which included
adding get_xx set_xx methods, change reads and writes to the public members
to calls to get and set functionsss
old public member(int16_t) get function -> int16_t set function (int16_t)
(expression where c is an object of type RC_Channel)
c.radio_in c.get_radio_in() c.set_radio_in(v)
c.control_in c.get_control_in() c.set_control_in(v)
c.servo_out c.get_servo_out() c.set_servo_out(v)
c.pwm_out c.get_pwm_out() // use existing
c.radio_out c.get_radio_out() c.set_radio_out(v)
c.radio_max c.get_radio_max() c.set_radio_max(v)
c.radio_min c.get_radio_min() c.set_radio_min(v)
c.radio_trim c.get_radio_trim() c.set_radio_trim(v);
c.min_max_configured() // return true if min and max are configured
Because data members of RC_Channels are now private and so cannot be written directly
some overloads are provided in the Plane classes to provide the old functionality
new overload Plane::stick_mix_channel(RC_Channel *channel)
which forwards to the previously existing
void stick_mix_channel(RC_Channel *channel, int16_t &servo_out);
new overload Plane::channel_output_mixer(Rc_Channel* , RC_Channel*)const
which forwards to
(uint8_t mixing_type, int16_t & chan1, int16_t & chan2)const;
Rename functions
RC_Channel_aux::set_radio_trim(Aux_servo_function_t function)
to RC_Channel_aux::set_trim_to_radio_in_for(Aux_servo_function_t function)
RC_Channel_aux::set_servo_out(Aux_servo_function_t function, int16_t value)
to RC_Channel_aux::set_servo_out_for(Aux_servo_function_t function, int16_t value)
Rationale:
RC_Channel is a complicated class, which combines
several functionalities dealing with stick inputs
in pwm and logical units, logical and actual actuator
outputs, unit conversion etc, etc
The intent of this PR is to clarify existing use of
the class. At the basic level it should now be possible
to grep all places where private variable is set by
searching for the set_xx function.
(The wider purpose is to provide a more generic and
logically simpler method of output mixing. This is a small step)
2016-05-08 05:33:02 -03:00
if ( plane . channel_rudder - > get_control_in ( ) ! = 0 ) {
2018-08-24 02:42:37 -03:00
weathervane . last_pilot_input_ms = tnow ;
2016-04-20 03:23:17 -03:00
weathervane . last_output = 0 ;
return 0 ;
}
2018-08-24 02:42:37 -03:00
if ( tnow - weathervane . last_pilot_input_ms < 3000 ) {
2016-04-20 03:23:17 -03:00
weathervane . last_output = 0 ;
return 0 ;
}
2016-04-21 08:52:25 -03:00
float roll = wp_nav - > get_roll ( ) / 100.0f ;
if ( fabsf ( roll ) < weathervane . min_roll ) {
weathervane . last_output = 0 ;
return 0 ;
}
if ( roll > 0 ) {
roll - = weathervane . min_roll ;
} else {
roll + = weathervane . min_roll ;
}
float output = constrain_float ( ( roll / 45.0f ) * weathervane . gain , - 1 , 1 ) ;
2016-04-20 03:23:17 -03:00
if ( should_relax ( ) ) {
output = 0 ;
}
weathervane . last_output = 0.98f * weathervane . last_output + 0.02f * output ;
// scale over half of yaw_rate_max. This gives the pilot twice the
// authority of the weathervane controller
return weathervane . last_output * ( yaw_rate_max / 2 ) * 100 ;
}
2016-05-05 22:28:26 -03:00
/*
start guided mode control
*/
void QuadPlane : : guided_start ( void )
{
poscontrol . state = QPOS_POSITION1 ;
poscontrol . speed_scale = 0 ;
2017-09-06 04:58:08 -03:00
guided_takeoff = false ;
2016-05-05 22:28:26 -03:00
setup_target_position ( ) ;
poscontrol . slow_descent = ( plane . current_loc . alt > plane . next_WP_loc . alt ) ;
}
/*
update guided mode control
*/
void QuadPlane : : guided_update ( void )
{
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_guided & & guided_takeoff & & plane . current_loc . alt < plane . next_WP_loc . alt ) {
2017-09-06 04:58:08 -03:00
throttle_wait = false ;
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2017-09-06 04:58:08 -03:00
takeoff_controller ( ) ;
} else {
guided_takeoff = false ;
// run VTOL position controller
vtol_position_controller ( ) ;
}
2016-05-05 22:28:26 -03:00
}
2016-09-26 22:46:51 -03:00
void QuadPlane : : afs_terminate ( void )
{
if ( available ( ) ) {
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : SHUT_DOWN ) ;
2016-09-26 22:46:51 -03:00
motors - > output ( ) ;
}
}
2016-09-30 19:35:58 -03:00
/*
return true if we should do guided mode loitering using VTOL motors
*/
bool QuadPlane : : guided_mode_enabled ( void )
{
if ( ! available ( ) ) {
return false ;
}
// only use quadplane guided when in AUTO or GUIDED mode
2019-01-15 13:46:13 -04:00
if ( plane . control_mode ! = & plane . mode_guided & & plane . control_mode ! = & plane . mode_auto ) {
2016-09-30 19:35:58 -03:00
return false ;
}
return guided_mode ! = 0 ;
}
2017-09-05 19:22:38 -03:00
/*
set altitude target to current altitude
*/
void QuadPlane : : set_alt_target_current ( void )
{
pos_control - > set_alt_target ( inertial_nav . get_altitude ( ) ) ;
}
/*
adjust the altitude target to the given target , moving it slowly
*/
void QuadPlane : : adjust_alt_target ( float altitude_cm )
{
float current_alt = inertial_nav . get_altitude ( ) ;
// don't let it get beyond 50cm from current altitude
float target_cm = constrain_float ( altitude_cm , current_alt - 50 , current_alt + 50 ) ;
pos_control - > set_alt_target ( target_cm ) ;
}
2017-09-06 04:58:08 -03:00
// user initiated takeoff for guided mode
bool QuadPlane : : do_user_takeoff ( float takeoff_altitude )
{
2019-01-15 13:46:13 -04:00
if ( plane . control_mode ! = & plane . mode_guided ) {
2017-09-06 04:58:08 -03:00
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " User Takeoff only in GUIDED mode " ) ;
return false ;
}
if ( ! hal . util - > get_soft_armed ( ) ) {
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Must be armed for takeoff " ) ;
return false ;
}
if ( is_flying ( ) ) {
gcs ( ) . send_text ( MAV_SEVERITY_INFO , " Already flying - no takeoff " ) ;
return false ;
}
plane . auto_state . vtol_loiter = true ;
plane . prev_WP_loc = plane . current_loc ;
plane . next_WP_loc = plane . current_loc ;
plane . next_WP_loc . alt + = takeoff_altitude * 100 ;
2019-04-09 09:17:25 -03:00
motors - > set_desired_spool_state ( AP_Motors : : DesiredSpoolState : : THROTTLE_UNLIMITED ) ;
2017-09-06 04:58:08 -03:00
guided_start ( ) ;
guided_takeoff = true ;
2017-09-11 16:08:29 -03:00
return true ;
2017-09-06 04:58:08 -03:00
}
2017-10-20 01:39:44 -03:00
2018-07-25 18:32:42 -03:00
// return true if the wp_nav controller is being updated
bool QuadPlane : : using_wp_nav ( void ) const
{
2019-01-15 13:46:13 -04:00
return plane . control_mode = = & plane . mode_qloiter | | plane . control_mode = = & plane . mode_qland | | plane . control_mode = = & plane . mode_qrtl ;
2018-07-25 18:32:42 -03:00
}
2017-10-20 01:39:44 -03:00
/*
return mav_type for heartbeat
*/
2018-03-22 06:16:35 -03:00
MAV_TYPE QuadPlane : : get_mav_type ( void ) const
2017-10-20 01:39:44 -03:00
{
if ( mav_type . get ( ) = = 0 ) {
return MAV_TYPE_FIXED_WING ;
}
2018-03-22 06:16:35 -03:00
return MAV_TYPE ( mav_type . get ( ) ) ;
2017-10-20 01:39:44 -03:00
}
2017-10-29 03:31:09 -03:00
/*
return true if current mission item is a vtol takeoff
*/
bool QuadPlane : : is_vtol_takeoff ( uint16_t id ) const
{
if ( id = = MAV_CMD_NAV_VTOL_TAKEOFF ) {
return true ;
}
if ( id = = MAV_CMD_NAV_TAKEOFF & & available ( ) & & ( options & OPTION_ALLOW_FW_TAKEOFF ) = = 0 ) {
// treat fixed wing takeoff as VTOL takeoff
return true ;
}
return false ;
}
/*
return true if current mission item is a vtol land
*/
bool QuadPlane : : is_vtol_land ( uint16_t id ) const
{
if ( id = = MAV_CMD_NAV_VTOL_LAND ) {
2018-09-25 01:11:26 -03:00
if ( options & QuadPlane : : OPTION_MISSION_LAND_FW_APPROACH ) {
return plane . vtol_approach_s . approach_stage = = Plane : : Landing_ApproachStage : : VTOL_LANDING ;
} else {
return true ;
}
2017-10-29 03:31:09 -03:00
}
if ( id = = MAV_CMD_NAV_LAND & & available ( ) & & ( options & OPTION_ALLOW_FW_LAND ) = = 0 ) {
// treat fixed wing land as VTOL land
return true ;
}
return false ;
}
/*
return true if we are in a transition to fwd flight from hover
*/
bool QuadPlane : : in_transition ( void ) const
{
return available ( ) & & assisted_flight & &
( transition_state = = TRANSITION_AIRSPEED_WAIT | |
transition_state = = TRANSITION_TIMER ) ;
}
2017-11-05 05:44:42 -04:00
/*
calculate current stopping distance for a quadplane in fixed wing flight
*/
float QuadPlane : : stopping_distance ( void )
{
// use v^2/(2*accel). This is only quite approximate as the drag
// varies with pitch, but it gives something for the user to
// control the transition distance in a reasonable way
return plane . ahrs . groundspeed_vector ( ) . length_squared ( ) / ( 2 * transition_decel ) ;
}
2019-01-26 19:30:53 -04:00
# define LAND_CHECK_ANGLE_ERROR_DEG 30.0f // maximum angle error to be considered landing
# define LAND_CHECK_LARGE_ANGLE_CD 1500.0f // maximum angle target to be considered landing
# define LAND_CHECK_ACCEL_MOVING 3.0f // maximum acceleration after subtracting gravity
void QuadPlane : : update_throttle_thr_mix ( void )
{
// transition will directly manage the mix
if ( in_transition ( ) ) {
return ;
}
// if disarmed or landed prioritise throttle
if ( ! motors - > armed ( ) ) {
attitude_control - > set_throttle_mix_min ( ) ;
return ;
}
2019-01-15 13:46:13 -04:00
if ( plane . control_mode = = & plane . mode_qstabilize ) {
2019-01-26 19:30:53 -04:00
// manual throttle
if ( plane . get_throttle_input ( ) < = 0 ) {
attitude_control - > set_throttle_mix_min ( ) ;
} else {
attitude_control - > set_throttle_mix_man ( ) ;
}
} else {
// autopilot controlled throttle
// check for aggressive flight requests - requested roll or pitch angle below 15 degrees
const Vector3f angle_target = attitude_control - > get_att_target_euler_cd ( ) ;
bool large_angle_request = ( norm ( angle_target . x , angle_target . y ) > LAND_CHECK_LARGE_ANGLE_CD ) ;
// check for large external disturbance - angle error over 30 degrees
const float angle_error = attitude_control - > get_att_error_angle_deg ( ) ;
bool large_angle_error = ( angle_error > LAND_CHECK_ANGLE_ERROR_DEG ) ;
// check for large acceleration - falling or high turbulence
Vector3f accel_ef = plane . ahrs . get_accel_ef_blended ( ) ;
accel_ef . z + = GRAVITY_MSS ;
bool accel_moving = ( accel_ef . length ( ) > LAND_CHECK_ACCEL_MOVING ) ;
// check for requested decent
bool descent_not_demanded = pos_control - > get_desired_velocity ( ) . z > = 0.0f ;
if ( large_angle_request | | large_angle_error | | accel_moving | | descent_not_demanded ) {
attitude_control - > set_throttle_mix_max ( ) ;
} else {
attitude_control - > set_throttle_mix_min ( ) ;
}
}
}
2019-02-23 22:53:52 -04:00
/*
see if we are in the approach phase of a VTOL landing
*/
bool QuadPlane : : in_vtol_land_approach ( void ) const
{
if ( in_vtol_auto ( ) & & is_vtol_land ( plane . mission . get_current_nav_cmd ( ) . id ) & &
( poscontrol . state = = QPOS_POSITION1 | | poscontrol . state = = QPOS_POSITION2 ) ) {
return true ;
}
return false ;
}
/*
see if we are in the descent phase of a VTOL landing
*/
bool QuadPlane : : in_vtol_land_descent ( void ) const
{
if ( in_vtol_auto ( ) & & is_vtol_land ( plane . mission . get_current_nav_cmd ( ) . id ) & &
( poscontrol . state = = QPOS_LAND_DESCEND | | poscontrol . state = = QPOS_LAND_FINAL ) ) {
return true ;
}
return false ;
}
2019-09-11 04:20:04 -03:00
/*
see if we are in the final phase of a VTOL landing
*/
bool QuadPlane : : in_vtol_land_final ( void ) const
{
return in_vtol_land_descent ( ) & & poscontrol . state = = QPOS_LAND_FINAL ;
}
2019-12-27 17:13:27 -04:00
/*
see if we are in any of the phases of a VTOL landing
*/
bool QuadPlane : : in_vtol_land_sequence ( void ) const
{
return in_vtol_land_approach ( ) | | in_vtol_land_descent ( ) | | in_vtol_land_final ( ) ;
}