forked from ArduPilot/ardupilot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
quadplane.cpp
3026 lines (2651 loc) · 108 KB
/
quadplane.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "Plane.h"
const AP_Param::GroupInfo QuadPlane::var_info[] = {
// @Param: ENABLE
// @DisplayName: Enable QuadPlane
// @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
// @User: Standard
AP_GROUPINFO_FLAGS("ENABLE", 1, QuadPlane, enable, 0, AP_PARAM_FLAG_ENABLE),
// @Group: M_
// @Path: ../libraries/AP_Motors/AP_MotorsMulticopter.cpp
AP_SUBGROUPVARPTR(motors, "M_", 2, QuadPlane, plane.quadplane.motors_var_info),
// 3 ~ 8 were used by quadplane attitude control PIDs
// @Param: ANGLE_MAX
// @DisplayName: Angle Max
// @Description: Maximum lean angle in all VTOL flight modes
// @Units: cdeg
// @Range: 1000 8000
// @User: Advanced
AP_GROUPINFO("ANGLE_MAX", 10, QuadPlane, aparm.angle_max, 3000),
// @Param: TRANSITION_MS
// @DisplayName: Transition time
// @Description: Transition time in milliseconds after minimum airspeed is reached
// @Units: ms
// @Range: 0 30000
// @User: Advanced
AP_GROUPINFO("TRANSITION_MS", 11, QuadPlane, transition_time_ms, 5000),
// 12 ~ 16 were used by position, velocity and acceleration PIDs
// @Group: P
// @Path: ../libraries/AC_AttitudeControl/AC_PosControl.cpp
AP_SUBGROUPPTR(pos_control, "P", 17, QuadPlane, AC_PosControl),
// @Param: VELZ_MAX
// @DisplayName: Pilot maximum vertical speed
// @Description: The maximum vertical velocity the pilot may request in cm/s
// @Units: cm/s
// @Range: 50 500
// @Increment: 10
// @User: Standard
AP_GROUPINFO("VELZ_MAX", 18, QuadPlane, pilot_velocity_z_max, 250),
// @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
AP_GROUPINFO("ACCEL_Z", 19, QuadPlane, pilot_accel_z, 250),
// @Group: WP_
// @Path: ../libraries/AC_WPNav/AC_WPNav.cpp
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
// @Units: PWM
// @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
// @Units: PWM
// @Range: 800 2200
// @Increment: 1
// @User: Standard
AP_GROUPINFO("THR_MAX_PWM", 23, QuadPlane, thr_max_pwm, 2000),
// @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),
// @Param: YAW_RATE_MAX
// @DisplayName: Maximum yaw rate
// @Description: This is the maximum yaw rate for pilot input on rudder stick in degrees/second
// @Units: deg/s
// @Range: 50 500
// @Increment: 1
// @User: Standard
AP_GROUPINFO("YAW_RATE_MAX", 25, QuadPlane, yaw_rate_max, 100),
// @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),
// 28 was used by THR_MID
// @Param: TRAN_PIT_MAX
// @DisplayName: Transition max pitch
// @Description: Maximum pitch during transition to auto fixed wing flight
// @User: Standard
// @Range: 0 30
// @Units: deg
// @Increment: 1
AP_GROUPINFO("TRAN_PIT_MAX", 29, QuadPlane, transition_pitch_max, 3),
// frame class was moved from 30 when consolidating AP_Motors classes
#define FRAME_CLASS_OLD_IDX 30
// @Param: FRAME_CLASS
// @DisplayName: Frame Class
// @Description: Controls major frame class for multicopter component
// @Values: 0:Undefined, 1:Quad, 2:Hexa, 3:Octa, 4:OctaQuad, 5:Y6, 7:Tri, 10: TailSitter
// @User: Standard
AP_GROUPINFO("FRAME_CLASS", 46, QuadPlane, frame_class, 1),
// @Param: FRAME_TYPE
// @DisplayName: Frame Type (+, X or V)
// @Description: Controls motor mixing for multicopter component
// @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
// @User: Standard
AP_GROUPINFO("FRAME_TYPE", 31, QuadPlane, frame_type, 1),
// @Param: VFWD_GAIN
// @DisplayName: Forward velocity hold gain
// @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.
// @Range: 0 0.5
// @Increment: 0.01
// @User: Standard
AP_GROUPINFO("VFWD_GAIN", 32, QuadPlane, vel_forward.gain, 0),
// @Param: WVANE_GAIN
// @DisplayName: Weathervaning gain
// @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.
// @Range: 0 1
// @Increment: 0.01
// @User: Standard
AP_GROUPINFO("WVANE_GAIN", 33, QuadPlane, weathervane.gain, 0),
// @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),
// @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),
// @Param: RTL_MODE
// @DisplayName: VTOL RTL mode
// @Description: If this is set to 1 then an RTL will change to QRTL when within RTL_RADIUS meters of the RTL destination
// @Values: 0:Disabled,1:Enabled
// @User: Standard
AP_GROUPINFO("RTL_MODE", 36, QuadPlane, rtl_mode, 0),
// @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),
// @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
// @Units: deg/s
// @Increment: 1
// @Range: 10 300
// @User: Standard
AP_GROUPINFO("TILT_RATE_UP", 38, QuadPlane, tilt.max_rate_up_dps, 40),
// @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
// @Units: deg
// @Increment: 1
// @Range: 20 80
// @User: Standard
AP_GROUPINFO("TILT_MAX", 39, QuadPlane, tilt.max_angle_deg, 45),
// @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),
// 41 was used by THR_MIN
// @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),
// @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.
// @Units: m
// @Range: 0 10
// @Increment: 0.25
// @User: Standard
AP_GROUPINFO("VFWD_ALT", 43, QuadPlane, vel_forward_alt_cutoff, 0),
// @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),
// @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.
// @Units: deg
// @Range: 0 90
// @Increment: 1
// @User: Standard
AP_GROUPINFO("ASSIST_ANGLE", 45, QuadPlane, assist_angle, 30),
// @Param: TILT_TYPE
// @DisplayName: Tiltrotor type
// @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
// @Values: 0:Continuous,1:Binary,2:VectoredYaw
AP_GROUPINFO("TILT_TYPE", 47, QuadPlane, tilt.tilt_type, TILT_TYPE_CONTINUOUS),
// @Param: TAILSIT_ANGLE
// @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
AP_GROUPINFO("TAILSIT_ANGLE", 48, QuadPlane, tailsitter.transition_angle, 45),
// @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.
// @Units: deg/s
// @Increment: 1
// @Range: 10 300
// @User: Standard
AP_GROUPINFO("TILT_RATE_DN", 49, QuadPlane, tilt.max_rate_down_dps, 0),
// @Param: TAILSIT_INPUT
// @DisplayName: Tailsitter input type
// @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.
// @Values: 0:MultiCopterInput,1:FixedWingInput,2:BodyFrameRoll_M,3:BodyFrameRoll_P
AP_GROUPINFO("TAILSIT_INPUT", 50, QuadPlane, tailsitter.input_type, TAILSITTER_INPUT_MULTICOPTER),
// @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),
// @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),
// @Param: TILT_YAW_ANGLE
// @DisplayName: Tilt minimum angle for vectored yaw
// @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.
// @Range: 0 30
AP_GROUPINFO("TILT_YAW_ANGLE", 55, QuadPlane, tilt.tilt_yaw_angle, 0),
// @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),
// @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),
// @Param: OPTIONS
// @DisplayName: quadplane options
// @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.
// @Bitmask: 0:LevelTransition,1:AllowFWTakeoff,2:AllowFWLand,3:Respect takeoff frame types,4:Use a fixed wing approach for VTOL landings
AP_GROUPINFO("OPTIONS", 58, QuadPlane, options, 0),
AP_SUBGROUPEXTENSION("",59, QuadPlane, var_info2),
AP_GROUPEND
};
// 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),
// @Group: LOIT_
// @Path: ../libraries/AC_WPNav/AC_Loiter.cpp
AP_SUBGROUPPTR(loiter_nav, "LOIT_", 2, QuadPlane, AC_Loiter),
// @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),
// @Param: TRIM_PITCH
// @DisplayName: Quadplane AHRS trim pitch
// @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.
// @Units: deg
// @Range: -10 +10
// @Increment: 0.1
// @User: Advanced
// @RebootRequired: True
AP_GROUPINFO("TRIM_PITCH", 4, QuadPlane, ahrs_trim_pitch, 0),
// @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),
#if QAUTOTUNE_ENABLED
// @Group: AUTOTUNE_
// @Path: qautotune.cpp
AP_SUBGROUPINFO(qautotune, "AUTOTUNE_", 6, QuadPlane, QAutoTune),
#endif
// @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),
// @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),
// @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),
// @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),
// @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),
AP_GROUPEND
};
/*
defaults for all quadplanes
*/
static const struct AP_Param::defaults_table_struct defaults_table[] = {
{ "Q_A_RAT_RLL_P", 0.25 },
{ "Q_A_RAT_RLL_I", 0.25 },
{ "Q_A_RAT_RLL_FILT", 10.0 },
{ "Q_A_RAT_PIT_P", 0.25 },
{ "Q_A_RAT_PIT_I", 0.25 },
{ "Q_A_RAT_PIT_FILT", 10.0 },
{ "Q_M_SPOOL_TIME", 0.25 },
{ "Q_M_HOVER_LEARN", 0 },
{ "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 },
};
/*
extra defaults for tailsitters
*/
static const struct AP_Param::defaults_table_struct defaults_table_tailsitter[] = {
{ "KFF_RDDRMIX", 0.02 },
{ "Q_A_RAT_PIT_FF", 0.2 },
{ "Q_A_RAT_YAW_FF", 0.2 },
{ "Q_A_RAT_YAW_I", 0.18 },
{ "LIM_PITCH_MAX", 3000 },
{ "LIM_PITCH_MIN", -3000 },
{ "MIXING_GAIN", 1.0 },
{ "RUDD_DT_GAIN", 10 },
{ "Q_TRANSITION_MS", 2000 },
};
/*
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
{ 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
{ Parameters::k_param_quadplane, 400, AP_PARAM_FLOAT, "Q_P_ACCZ_FILT"}, // Q_AZ_FILT
{ Parameters::k_param_quadplane, 464, AP_PARAM_FLOAT, "Q_P_ACCZ_FF"}, // Q_AZ_FF
{ 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
};
QuadPlane::QuadPlane(AP_AHRS_NavEKF &_ahrs) :
ahrs(_ahrs)
{
AP_Param::setup_object_defaults(this, var_info);
AP_Param::setup_object_defaults(this, var_info2);
}
// 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++) {
SRV_Channels::set_aux_channel_default(SRV_Channels::get_motor_function(i), CH_5+i);
}
}
bool QuadPlane::setup(void)
{
if (initialised) {
return true;
}
if (!enable || hal.util->get_soft_armed()) {
return false;
}
float loop_delta_t = 1.0 / plane.scheduler.get_loop_rate_hz();
enum AP_Motors::motor_frame_class motor_class;
enum Rotation rotation = ROTATION_NONE;
/*
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);
}
if (hal.util->available_memory() <
4096 + sizeof(*motors) + sizeof(*attitude_control) + sizeof(*pos_control) + sizeof(*wp_nav) + sizeof(*ahrs_view) + sizeof(*loiter_nav)) {
gcs().send_text(MAV_SEVERITY_INFO, "Not enough memory for quadplane");
goto failed;
}
/*
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
*/
motor_class = (enum AP_Motors::motor_frame_class)frame_class.get();
switch (motor_class) {
case AP_Motors::MOTOR_FRAME_QUAD:
setup_default_channels(4);
break;
case AP_Motors::MOTOR_FRAME_HEXA:
setup_default_channels(6);
break;
case AP_Motors::MOTOR_FRAME_OCTA:
case AP_Motors::MOTOR_FRAME_OCTAQUAD:
setup_default_channels(8);
break;
case AP_Motors::MOTOR_FRAME_Y6:
setup_default_channels(7);
break;
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);
AP_Param::set_frame_type_flags(AP_PARAM_FRAME_TRICOPTER);
break;
case AP_Motors::MOTOR_FRAME_TAILSITTER:
break;
default:
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;
}
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;
rotation = ROTATION_PITCH_90;
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);
}
rotation = ROTATION_PITCH_90;
motors = new AP_MotorsMatrixTS(plane.scheduler.get_loop_rate_hz(), rc_speed);
motors_var_info = AP_MotorsMatrixTS::var_info;
}
const static char *strUnableToAllocate = "Unable to allocate";
if (!motors) {
hal.console->printf("%s motors\n", strUnableToAllocate);
goto failed;
}
AP_Param::load_object_from_eeprom(motors, motors_var_info);
// create the attitude view used by the VTOL code
ahrs_view = ahrs.create_view(rotation, ahrs_trim_pitch);
if (ahrs_view == nullptr) {
goto failed;
}
attitude_control = new AC_AttitudeControl_Multi(*ahrs_view, aparm, *motors, loop_delta_t);
if (!attitude_control) {
hal.console->printf("%s attitude_control\n", strUnableToAllocate);
goto failed;
}
AP_Param::load_object_from_eeprom(attitude_control, attitude_control->var_info);
pos_control = new AC_PosControl(*ahrs_view, inertial_nav, *motors, *attitude_control);
if (!pos_control) {
hal.console->printf("%s pos_control\n", strUnableToAllocate);
goto failed;
}
AP_Param::load_object_from_eeprom(pos_control, pos_control->var_info);
wp_nav = new AC_WPNav(inertial_nav, *ahrs_view, *pos_control, *attitude_control);
if (!wp_nav) {
hal.console->printf("%s wp_nav\n", strUnableToAllocate);
goto failed;
}
AP_Param::load_object_from_eeprom(wp_nav, wp_nav->var_info);
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);
motors->init((AP_Motors::motor_frame_class)frame_class.get(), (AP_Motors::motor_frame_type)frame_type.get());
motors->set_throttle_range(thr_min_pwm, thr_max_pwm);
motors->set_update_rate(rc_speed);
motors->set_interlock(true);
pos_control->set_dt(loop_delta_t);
attitude_control->parameter_sanity_check();
// setup the trim of any motors used by AP_Motors so px4io
// failsafe will disable motors
for (uint8_t i=0; i<8; i++) {
SRV_Channel::Aux_servo_function_t func = SRV_Channels::get_motor_function(i);
SRV_Channels::set_failsafe_pwm(func, thr_min_pwm);
}
transition_state = TRANSITION_DONE;
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);
}
}
setup_defaults();
AP_Param::convert_old_parameters(&q_conversion_table[0], ARRAY_SIZE(q_conversion_table));
gcs().send_text(MAV_SEVERITY_INFO, "QuadPlane initialised");
initialised = true;
return true;
failed:
initialised = false;
enable.set(0);
gcs().send_text(MAV_SEVERITY_INFO, "QuadPlane setup failed");
return false;
}
/*
setup default parameters from defaults_table
*/
void QuadPlane::setup_defaults(void)
{
AP_Param::set_defaults_from_table(defaults_table, ARRAY_SIZE(defaults_table));
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) {
AP_Param::set_defaults_from_table(defaults_table_tailsitter, ARRAY_SIZE(defaults_table_tailsitter));
}
// reset ESC calibration
if (esc_calibration != 0) {
esc_calibration.set_and_save(0);
}
}
// 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) {
gcs().send_text(MAV_SEVERITY_INFO, "Starting ESC calibration");
}
AP_Notify::flags.esc_calibration = true;
switch (esc_calibration) {
case 1:
// throttle based calibration
motors->set_throttle_passthrough_for_esc_calibration(plane.get_throttle_input() * 0.01f);
break;
case 2:
// full range calibration
motors->set_throttle_passthrough_for_esc_calibration(1);
break;
}
}
// init quadplane stabilize mode
void QuadPlane::init_stabilize(void)
{
throttle_wait = false;
}
/*
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
*/
void QuadPlane::multicopter_attitude_rate_update(float yaw_rate_cds)
{
check_attitude_relax();
// tailsitter-only bodyframe roll control options
if (is_tailsitter()) {
if (tailsitter.input_type == TAILSITTER_INPUT_BF_ROLL_M) {
// 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)
attitude_control->input_euler_rate_yaw_euler_angle_pitch_bf_roll_m(plane.nav_roll_cd,
plane.nav_pitch_cd,
yaw_rate_cds);
return;
} 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);
return;
}
}
// 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);
} else {
// use the fixed wing desired rates
float roll_rate = plane.rollController.get_pid_info().desired;
float pitch_rate = plane.pitchController.get_pid_info().desired;
attitude_control->input_rate_bf_roll_pitch_yaw_2(roll_rate*100.0f, pitch_rate*100.0f, yaw_rate_cds);
}
}
// hold in stabilize with given throttle
void QuadPlane::hold_stabilize(float throttle_in)
{
// call attitude controller
multicopter_attitude_rate_update(get_desired_yaw_rate_cds());
if (throttle_in <= 0) {
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::GROUND_IDLE);
attitude_control->set_throttle_out(0, true, 0);
if (!is_tailsitter()) {
// always stabilize with tailsitters so we can do belly takeoffs
attitude_control->relax_attitude_controllers();
}
} else {
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
attitude_control->set_throttle_out(throttle_in, true, 0);
}
}
// quadplane stabilize mode
void QuadPlane::control_stabilize(void)
{
// special check for ESC calibration in QSTABILIZE
if (esc_calibration != 0) {
run_esc_calibration();
return;
}
// normal QSTABILIZE mode
float pilot_throttle_scaled = get_pilot_throttle();
hold_stabilize(pilot_throttle_scaled);
}
// run the multicopter Z controller
void QuadPlane::run_z_controller(void)
{
const uint32_t now = AP_HAL::millis();
if (now - last_pidz_active_ms > 2000) {
// set alt target to current height on transition. This
// starts the Z controller off with the right values
gcs().send_text(MAV_SEVERITY_INFO, "Reset alt target to %.1f", (double)inertial_nav.get_altitude() / 100);
set_alt_target_current();
pos_control->set_desired_velocity_z(inertial_nav.get_velocity_z());
// initialize vertical speeds and leash lengths
pos_control->set_max_speed_z(-pilot_velocity_z_max, pilot_velocity_z_max);
pos_control->set_max_accel_z(pilot_accel_z);
// it has been two seconds since we last ran the Z
// controller. We need to assume the integrator may be way off
// the base throttle we start at is the current throttle we are using
// note that AC_PosControl::run_z_controller() adds the Z pid (_pid_accel_z) output to _motors.get_throttle_hover()
float base_throttle = constrain_float(motors->get_throttle() - motors->get_throttle_hover(), -1, 1) * 1000;
pos_control->get_accel_z_pid().set_integrator(base_throttle);
last_pidz_init_ms = now;
}
last_pidz_active_ms = now;
pos_control->update_z_controller();
}
/*
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();
if (now - last_att_control_ms > 100) {
attitude_control->relax_attitude_controllers();
}
last_att_control_ms = now;
}
/*
init QACRO mode
*/
void QuadPlane::init_qacro(void)
{
throttle_wait = false;
transition_state = TRANSITION_DONE;
attitude_control->relax_attitude_controllers();
}
// init quadplane hover mode
void QuadPlane::init_hover(void)
{
// initialize vertical speeds and leash lengths
pos_control->set_max_speed_z(-pilot_velocity_z_max, pilot_velocity_z_max);
pos_control->set_max_accel_z(pilot_accel_z);
// initialise position and desired velocity
set_alt_target_current();
pos_control->set_desired_velocity_z(inertial_nav.get_velocity_z());
init_throttle_wait();
}
/*
check for an EKF yaw reset
*/
void QuadPlane::check_yaw_reset(void)
{
if (!initialised) {
return;
}
float yaw_angle_change_rad = 0.0f;
uint32_t new_ekfYawReset_ms = ahrs.getLastYawResetAngle(yaw_angle_change_rad);
if (new_ekfYawReset_ms != ekfYawReset_ms) {
attitude_control->inertial_frame_reset();
ekfYawReset_ms = new_ekfYawReset_ms;
gcs().send_text(MAV_SEVERITY_INFO, "EKF yaw reset %.2f", (double)degrees(yaw_angle_change_rad));
}
}
/*
hold hover with target climb rate
*/
void QuadPlane::hold_hover(float target_climb_rate)
{
// motors use full range
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
// initialize vertical speeds and acceleration
pos_control->set_max_speed_z(-pilot_velocity_z_max, pilot_velocity_z_max);
pos_control->set_max_accel_z(pilot_accel_z);
// call attitude controller
multicopter_attitude_rate_update(get_desired_yaw_rate_cds());
// call position controller
pos_control->set_alt_target_from_climb_rate_ff(target_climb_rate, plane.G_Dt, false);
run_z_controller();
}
float QuadPlane::get_pilot_throttle()
{
// get scaled throttle input
float throttle_in = plane.channel_throttle->get_control_in();
// normalize to [0,1]
throttle_in /= plane.channel_throttle->get_range();
// 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);
// this puts mid stick at hover throttle
return throttle_curve(thr_mid, thrust_curve_expo, throttle_in);;
}
/*
control QACRO mode
*/
void QuadPlane::control_qacro(void)
{
if (throttle_wait) {
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::GROUND_IDLE);
attitude_control->set_throttle_out(0, true, 0);
attitude_control->relax_attitude_controllers();
} else {
check_attitude_relax();
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
// convert the input to the desired body frame rate
float target_roll = 0;
float target_pitch = plane.channel_pitch->norm_input() * acro_pitch_rate * 100.0f;
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
target_roll = plane.channel_rudder->norm_input() * acro_roll_rate * 100.0f;
target_yaw = -plane.channel_roll->norm_input() * acro_yaw_rate * 100.0f;
} else {
target_roll = plane.channel_roll->norm_input() * acro_roll_rate * 100.0f;
target_yaw = plane.channel_rudder->norm_input() * acro_yaw_rate * 100.0;
}
float throttle_out = get_pilot_throttle();
// run attitude controller
if (plane.g.acro_locking) {
attitude_control->input_rate_bf_roll_pitch_yaw_3(target_roll, target_pitch, target_yaw);
} else {