1
+ -- This script will preform a control surface doublet
2
+ -- The desired axis to perform a doublet on is selected and started with a momentary switch
3
+ -- The plane maintains trim conditions while the switch is activated using servo overrides
4
+ -- If the momentary switch is released before "DOUBLET FINISHED" is seen on the GCS,
5
+ -- the aircraft recovers to FBWA mode to assist the pilot.
6
+ -- Magnitude and duration of the doublet can also be controlled.
7
+ -- It is suggested to allow the aircraft to trim for straight, level, unaccelerated flight (SLUF) in FBWB mode before
8
+ -- starting a doublet
9
+ -- Charlie Johnson, Oklahoma State University 2020
10
+
11
+ local DOUBLET_ACTION_CHANNEL = 6 -- RCIN channel to start a doublet when high (>1700)
12
+ local DOUBLET_CHOICE_CHANNEL = 7 -- RCIN channel to choose elevator (low) or rudder (high)
13
+ local DOUBLET_FUCNTION = 1 -- which control surface (SERVOx_FUNCTION) number will have a doublet happen
14
+ -- A (Servo 1, Function 4), E (Servo 2, Function 19), and R (Servo 4, Function 21)
15
+ local DOUBLET_MAGNITUDE = 6 -- defined out of 45 deg used for set_output_scaled
16
+ local DOUBLET_TIME = 2000 -- period of doublet signal in ms
17
+ local start_time = - 1
18
+ local end_time = - 1
19
+ -- flight mode numbers for plane https://mavlink.io/en/messages/ardupilotmega.html
20
+ local MODE_MANUAL = 0
21
+ local MODE_STABILIZE = 2
22
+ local MODE_FBWA = 5
23
+ local K_AILERON = 4
24
+ local K_ELEVATOR = 19
25
+ local K_THROTTLE = 70
26
+ local K_RUDDER = 21
27
+
28
+ -- store timing information during doublet
29
+ local start_time = - 1
30
+ local end_time = - 1
31
+ local now = - 1
32
+
33
+ -- store information about the doublet channel
34
+ local pre_doublet_mode = vehicle :get_mode ()
35
+
36
+ function retry_set_mode (mode )
37
+ if vehicle :set_mode (mode ) then
38
+ -- if the mode was set successfully, carry on as normal
39
+ desired_mode = - 1
40
+ return doublet , 1
41
+ else
42
+ -- if the mode was not set successfully, try again ASAP
43
+ desired_mode = mode
44
+ return retry_set_mode , 1
45
+ end
46
+ end
47
+
48
+ function doublet ()
49
+ local now = millis ()
50
+ local pre_doublet_mode = vehicle :get_mode ()
51
+ local pre_doublet_elevator = SRV_Channels :get_output_pwm (K_ELEVATOR )
52
+ local pre_doublet_aileron = SRV_Channels :get_output_pwm (K_AILERON )
53
+ local pre_doublet_rudder = SRV_Channels :get_output_pwm (K_RUDDER )
54
+ local pre_doublet_throttle = 1200
55
+ local doublet_choice_pwm = rc :get_pwm (DOUBLET_CHOICE_CHANNEL )
56
+ local callback_time = 100
57
+ if arming :is_armed () == true and rc :get_pwm (DOUBLET_ACTION_CHANNEL ) > 1700 then
58
+ gcs :send_text (6 , " in Doublet function" )
59
+ local trim_funcs
60
+ local doublet_srv_trim = 1500
61
+ -- choosing control actuator, 900 for elevator, 1100 for aileron, 1300 for rudder, 1500 for throttle
62
+ if doublet_choice_pwm < 1000 then
63
+ DOUBLET_FUCNTION = K_ELEVATOR
64
+ doublet_srv_trim = pre_doublet_elevator
65
+ trim_funcs = {K_AILERON , K_RUDDER , K_THROTTLE }
66
+ DOUBLET_MAGNITUDE = 12
67
+ elseif doublet_choice_pwm > 1000 and doublet_choice_pwm < 1200 then
68
+ DOUBLET_FUCNTION = K_AILERON
69
+ doublet_srv_trim = pre_doublet_aileron
70
+ trim_funcs = {K_ELEVATOR , K_RUDDER , K_THROTTLE }
71
+ DOUBLET_MAGNITUDE = 15
72
+ elseif doublet_choice_pwm > 1200 and doublet_choice_pwm < 1400 then
73
+ DOUBLET_FUCNTION = K_RUDDER
74
+ doublet_srv_trim = pre_doublet_rudder
75
+ trim_funcs = {K_ELEVATOR , K_AILERON , K_THROTTLE }
76
+ DOUBLET_MAGNITUDE = 15
77
+ elseif doublet_choice_pwm > 1400 and doublet_choice_pwm < 1600 then
78
+ DOUBLET_FUCNTION = K_THROTTLE
79
+ doublet_srv_trim = pre_doublet_throttle
80
+ trim_funcs = {K_ELEVATOR , K_AILERON , K_RUDDER }
81
+ DOUBLET_MAGNITUDE = 12
82
+ else
83
+ DOUBLET_FUCNTION = K_ELEVATOR
84
+ doublet_srv_trim = pre_doublet_elevator
85
+ trim_funcs = {K_THROTTLE , K_AILERON , K_RUDDER }
86
+ DOUBLET_MAGNITUDE = 12
87
+ end
88
+ local doublet_srv_chan = SRV_Channels :find_channel (DOUBLET_FUCNTION )
89
+ local doublet_srv_min = param :get (" SERVO" .. doublet_srv_chan + 1 .. " _MIN" )
90
+ local doublet_srv_max = param :get (" SERVO" .. doublet_srv_chan + 1 .. " _MAX" )
91
+ doublet_srv_trim = param :get (" SERVO" .. doublet_srv_chan + 1 .. " _TRIM" )
92
+ if start_time == - 1 then
93
+ start_time = now
94
+ gcs :send_text (6 , " STARTING DOUBLET " .. doublet_srv_chan )
95
+ retry_set_mode (MODE_MANUAL )
96
+ end
97
+ if now < (start_time + (DOUBLET_TIME / 2 )) then
98
+ gcs :send_text (6 , " DOUBLET up" .. doublet_srv_chan )
99
+ up = doublet_srv_trim + math.floor ((doublet_srv_max - doublet_srv_trim ) * (DOUBLET_MAGNITUDE / 45 ))
100
+ SRV_Channels :set_output_pwm_chan_timeout (doublet_srv_chan , up , DOUBLET_TIME / 2 + 100 )
101
+ gcs :send_text (6 , " pwm" .. tostring (SRV_Channels :get_output_pwm (DOUBLET_FUCNTION )))
102
+ elseif now < (start_time + DOUBLET_TIME ) and now > (start_time + (DOUBLET_TIME / 2 ))then
103
+ gcs :send_text (6 , " DOUBLET down" .. doublet_srv_chan )
104
+ down = doublet_srv_trim - math.floor ((doublet_srv_trim - doublet_srv_min ) * (DOUBLET_MAGNITUDE / 45 ))
105
+ SRV_Channels :set_output_pwm_chan_timeout (doublet_srv_chan , down , DOUBLET_TIME / 2 + 100 )
106
+ gcs :send_text (6 , " pwm" .. tostring (SRV_Channels :get_output_pwm (DOUBLET_FUCNTION )))
107
+ elseif now > start_time + (DOUBLET_TIME * 2 ) then
108
+ -- stick fixed response
109
+ -- hold at pre doublet trim position for any damping effects
110
+ gcs :send_text (6 , " DOUBLET finished" )
111
+
112
+ if vehicle :get_mode () == MODE_MANUAL then
113
+ retry_set_mode (MODE_FBWA )
114
+ gcs :send_text (6 , " DOUBLET FINISHED, in mode" .. vehicle :get_mode ())
115
+ SRV_Channels :set_output_pwm_chan_timeout (DOUBLET_ACTION_CHANNEL , 1000 , DOUBLET_TIME )
116
+ end
117
+ end
118
+ end
119
+ return doublet , callback_time
120
+ end
121
+
122
+ gcs :send_text (6 , " plane-doublets.lua is running" )
123
+ return doublet (), 500
0 commit comments