Crowflap LUA-Mixer With Differential Spoiler - Flying Wing Glider

Background for this LUA script:

Our “Flebatron”, a high speed flying wing glider designed and built by @Matzito (Matthias Möller) to explore dynamic sailing, had its successful maiden flight last week. On the one hand, we were pleased with the good glide range, on the other hand, we were concerned about the long distance in ground effect required for landing:

So we decided to use Crowflaps as a kind of airbrake. Crow flaps usually work by adjusting ailerons and flaps in opposite directions and not ailerons and elevators as installed in our gilder:

The Arduplane Crow mixer therefore cannot work. That’s why this LUA script was created. Also differential spoiler were impemented and the gain values for crow and spoiler function (separately for aileron and elevator) can be comfortably set by 4 new parameters between 0 and 100%.

--[[ Crowflaps with differential spoilers for delta-wing glider with elevator and aileron on each wing
     Separate gain settings for elevator and aileron each for crow and spoiler function.   
     Many thanks to Peter Hall for pointing me out the SRV_Channels:get/set_output_scaled lua-commands 

The following servooutputs should be set:
SERVO1_FUNCTION = 94 (Script 1)  left aileron
SERVO1_FUNCTION = 95 (Script 2) right aileron
SERVO3_FUNCTION = 96 (Script 3)  left elevator
SERVO3_FUNCTION = 97 (Script 4) right elevator

The following new parameters should be set via groundcontrolstation
(values between 0 and 100 %) 
GAIN_AIL_CROW
GAIN_AIL_SPOIL
GAIN_ELE_CROW
GAIN_ELE_SPOIL

--]] 

local UPDATE_RATE_HZ = 25
local MESSAGE = 6

local PARAM_TABLE_KEY = 99 -- unique index value between 0 and 200

-- create custom parameter set
local function add_params(key, prefix, tbl)
    assert(param:add_table(key, prefix, #tbl), string.format('Could not add %s param table.', prefix))
    for num = 1, #tbl do
        assert(param:add_param(key, num,  tbl[num][1], tbl[num][2]), string.format('Could not add %s%s.', prefix, tbl[num][1]))
    end
end

-- arming:arm()  -- only for glider if throttlestick is used as flapinput

-- Create parameters 
add_params(PARAM_TABLE_KEY, 'GAIN_', {
    --  { name, default value },
        { 'AIL_CROW', 80 },
		{ 'ELE_CROW', 80 },
        { 'AIL_SPOIL', 40 },
        { 'ELE_SPOIL', 40 }
    })



-- Bind parameters 
function bind_param(name)
   local p = Parameter()
   assert(p:init(name), string.format('could not find %s parameter', name))
   return p
end

local GAIN_Aileron_Crow = bind_param("GAIN_AIL_CROW") 
local GAIN_Elevator_Crow = bind_param("GAIN_ELE_CROW")
local GAIN_Aileron_Spoil = bind_param("GAIN_AIL_SPOIL")
local GAIN_Elevator_Spoil = bind_param("GAIN_ELE_SPOIL")

-- servo outputs
local K_AILERON = 4
local K_ELEVATOR = 19
local K_RUDDER = 21
local K_THROTTLE = 70
local K_FLAP = 2

local K_SCRIPTING1 = 94 -- for left aileron servo 
local K_SCRIPTING2 = 95 -- for right aileron servo
local K_SCRIPTING3 = 96 -- for left elevator servo
local K_SCRIPTING4 = 97 -- for right elevator servo

local vmin = -4500
local vmax = 4500 

-- get time in seconds since boot
-- only used for debugging
function get_time()
   return millis():tofloat() * 0.001
end

local last_warning = get_time()

-- constrain a value between limits
function constrain(v) 
   if v < vmin then
      v = vmin
   end
   if v > vmax then
      v = vmax
   end
   return v
end

function update()
 
	-- local cflaps = SRV_Channels:get_output_scaled(K_THROTTLE)  -- 0 - 100
	local cflaps = SRV_Channels:get_output_scaled(K_FLAP)  -- -0 - 100

	cflaps = (cflaps-100) * 45 -- scale throttel to 4500
	
	local aileron = SRV_Channels:get_output_scaled(K_AILERON)   -- -4500 to +4500
	local elevator = SRV_Channels:get_output_scaled(K_ELEVATOR) -- -4500 to +4500
	local rudder = SRV_Channels:get_output_scaled(K_RUDDER) -- -4500 to +4500
	
	local GAC = GAIN_Aileron_Crow:get()/100
	local GAS = GAIN_Aileron_Spoil:get()/100
	local GES = GAIN_Elevator_Spoil:get()/100
	local GEC = GAIN_Elevator_Crow:get()/100

	
	-- Crow Mixer:
	elevator = constrain(elevator - (cflaps * GEC ))
	local aileron_l = constrain(aileron - (cflaps * GAC ))
	local aileron_r = constrain(aileron + (cflaps * GAC ))
	local elevator_l = elevator 
	local elevator_r = elevator 
	
	-- Spoiler Mixer 
	if rudder > 0 then
		elevator_l = constrain(elevator_l - (rudder * GES))
		aileron_l = constrain(aileron_l + (rudder * GAS ))
	else
		elevator_r = constrain(elevator_r - (rudder  * GES ))
		aileron_r = constrain(aileron_r + (rudder * GAS))
	end
	
	--servooutput:
    SRV_Channels:set_output_scaled(K_SCRIPTING1, aileron_l)
	SRV_Channels:set_output_scaled(K_SCRIPTING2, aileron_r)
	SRV_Channels:set_output_scaled(K_SCRIPTING3, elevator_l)
	SRV_Channels:set_output_scaled(K_SCRIPTING4, elevator_r)

	-- only used for debugging:
	--[[if get_time() > last_warning + 4 then
      gcs:send_text(MESSAGE, cflaps)
      last_warning = get_time()
    end
	--]]
    return update, 1000/UPDATE_RATE_HZ 
end

gcs:send_text(MESSAGE, "Crow lua is running")
return update()

Thanks to everyone who contributed to the implementation of Ardupilot’s LUA script capability and to those who created the LUA script examples, and especially to @iampete Peter Hall for
pointing me to the crucial LUA script command (SRV_Channels:get/set_output_scaled) for this script.

I hope that the script is self-explanatory for those interested. Servos are connected according to the picture above. Also servo functions for aileron, elevator, rudder and flaps have to be connected to free servo outputs, due to their values have to be read in by SRV_Channels:get_output_scaled().

Rolf

4 Likes