mirror of https://github.com/ArduPilot/ardupilot
235 lines
9.7 KiB
Lua
235 lines
9.7 KiB
Lua
--[[
|
|
|
|
example script to show reseting guided mode to terrain height.
|
|
It's intended to be used when selecting "fly to altitude" in a ground station
|
|
for example by right clicking on the map in QGC.
|
|
|
|
This is actually a workaround to QGC and Mission Planner not having a
|
|
way to set guided altitude above terrain.
|
|
|
|
Depending on ZGP_MODE
|
|
When the GCS requests a guided altitude X above home
|
|
1: reset to current terrain height ignore X
|
|
2: reset to X above terrain
|
|
3: reset to current alt + X
|
|
|
|
this functionality is only available in Plane and
|
|
requires TERRAIN_ENABLE = 1 and TERRAIN_FOLLOW =1
|
|
--]]
|
|
|
|
SCRIPT_NAME = "OverheadIntel Guided Terrain"
|
|
SCRIPT_NAME_SHORT = "TerrGuided"
|
|
SCRIPT_VERSION = "4.6.0-005"
|
|
|
|
REFRESH_RATE = 0.2 -- in seconds, so 5Hz
|
|
ALTITUDE_MIN = 50
|
|
ALTITUDE_MAX = 120
|
|
|
|
MAV_SEVERITY = {EMERGENCY=0, ALERT=1, CRITICAL=2, ERROR=3, WARNING=4, NOTICE=5, INFO=6, DEBUG=7}
|
|
MAV_FRAME = { GLOBAL = 0, GLOBAL_RELATIVE_ALT = 3}
|
|
ALT_FRAME = { ABSOLUTE = 0, ABOVE_HOME = 1, ABOVE_ORIGIN = 2, ABOVE_TERRAIN = 3 }
|
|
FLIGHT_MODE = {AUTO=10, RTL=11, LOITER=12, GUIDED=15, QHOVER=18, QLOITER=19, QRTL=21}
|
|
|
|
PARAM_TABLE_KEY = 101
|
|
PARAM_TABLE_PREFIX = "ZGT_"
|
|
|
|
local now = millis():tofloat() * 0.001
|
|
|
|
-- bind a parameter to a variable
|
|
function bind_param(name)
|
|
return Parameter(name)
|
|
end
|
|
|
|
-- add a parameter and bind it to a variable
|
|
function bind_add_param(name, idx, default_value)
|
|
assert(param:add_param(PARAM_TABLE_KEY, idx, name, default_value), SCRIPT_NAME_SHORT .. string.format('could not add param %s', name))
|
|
return bind_param(PARAM_TABLE_PREFIX .. name)
|
|
end
|
|
|
|
-- setup follow mode specific parameters
|
|
assert(param:add_table(PARAM_TABLE_KEY, PARAM_TABLE_PREFIX, 10), SCRIPT_NAME_SHORT .. 'could not add param table: ' .. PARAM_TABLE_PREFIX)
|
|
|
|
--[[
|
|
// @Param: ZGT_MODE
|
|
// @DisplayName: Guided Terrain Mode
|
|
// @Description: When the GCS requests a guided altitude X above home 1: reset to current terrain height ignore X 2: reset to X above terrain 3: reset to current alt + X
|
|
// @Range: 1,2,3
|
|
--]]
|
|
ZGT_MODE = bind_add_param("MODE", 1, 1)
|
|
TERRAIN_ENABLE = bind_param("TERRAIN_ENABLE")
|
|
TERRAIN_FOLLOW = bind_param("TERRAIN_FOLLOW")
|
|
|
|
local zgt_mode = ZGT_MODE:get()
|
|
local terrain_enable = TERRAIN_ENABLE:get()
|
|
local terrain_follow = TERRAIN_FOLLOW:get()
|
|
|
|
MAV_CMD_INT = { DO_SET_MODE = 176, DO_CHANGE_SPEED = 178, DO_REPOSITION = 192,
|
|
GUIDED_CHANGE_SPEED = 43000, GUIDED_CHANGE_ALTITUDE = 43001, GUIDED_CHANGE_HEADING = 43002 }
|
|
MAV_FRAME = { GLOBAL = 0, GLOBAL_RELATIVE_ALT = 3, GLOBAL_TERRAIN_ALT = 10}
|
|
|
|
-- constrain a value between limits
|
|
local function constrain(v, vmin, vmax)
|
|
if v < vmin then
|
|
v = vmin
|
|
end
|
|
if v > vmax then
|
|
v = vmax
|
|
end
|
|
return v
|
|
end
|
|
|
|
local function follow_frame_to_mavlink(follow_frame)
|
|
local mavlink_frame = MAV_FRAME.GLOBAL
|
|
if (follow_frame == ALT_FRAME.ABOVE_TERRAIN) then
|
|
mavlink_frame = MAV_FRAME.GLOBAL_TERRAIN_ALT
|
|
elseif (follow_frame == ALT_FRAME.ABOVE_HOME) then
|
|
mavlink_frame = MAV_FRAME.GLOBAL_RELATIVE_ALT
|
|
end
|
|
return mavlink_frame
|
|
end
|
|
|
|
local now_altitude = millis():tofloat() * 0.001
|
|
-- target.alt = new target altitude in meters
|
|
-- set_vehicle_target_altitude() Parameters
|
|
-- target.frame = Altitude frame MAV_FRAME, it's very important to get this right!
|
|
-- target.alt = altitude in meters to acheive
|
|
-- target.accel = z acceleration to altitude (1000.0 = max)
|
|
local function set_vehicle_target_altitude(target)
|
|
local acceleration = target.accel or 1000.0 -- default to maximum z acceleration
|
|
if math.floor(now) ~= math.floor(now_altitude) then
|
|
now_altitude = millis():tofloat() * 0.001
|
|
end
|
|
if target.alt == nil then
|
|
gcs:send_text(MAV_SEVERITY.ERROR, SCRIPT_NAME_SHORT .. ": set_vehicle_target_altitude no altiude")
|
|
return
|
|
end
|
|
-- GUIDED_CHANGE_ALTITUDE takes altitude in meters
|
|
local mavlink_result = gcs:run_command_int(MAV_CMD_INT.GUIDED_CHANGE_ALTITUDE, {
|
|
frame = follow_frame_to_mavlink(target.frame),
|
|
p3 = acceleration,
|
|
z = target.alt })
|
|
if mavlink_result > 0 then
|
|
gcs:send_text(MAV_SEVERITY.ERROR, SCRIPT_NAME_SHORT .. string.format(": MAVLink GUIDED_CHANGE_ALTITUDE returned %d", mavlink_result))
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
|
|
local vehicle_mode = vehicle:get_mode()
|
|
local save_target_altitude = -1
|
|
local save_old_target_altitude = -1
|
|
local save_zgt_mode = -1
|
|
local save_vehicle_mode = -1
|
|
|
|
local function update()
|
|
|
|
vehicle_mode = vehicle:get_mode()
|
|
|
|
terrain_enable = TERRAIN_ENABLE:get()
|
|
terrain_follow = TERRAIN_FOLLOW:get()
|
|
zgt_mode = ZGT_MODE:get()
|
|
if zgt_mode ~= save_zgt_mode then
|
|
-- user changed modes, reset everything
|
|
save_target_altitude = -1
|
|
save_old_target_altitude = -1
|
|
end
|
|
save_zgt_mode = zgt_mode
|
|
|
|
-- We should only reset the altitude if newly switched to guided mode
|
|
if save_vehicle_mode ~= vehicle_mode and vehicle_mode == FLIGHT_MODE.GUIDED and terrain_enable == 1 and
|
|
((terrain_follow & 1) == 1 or (terrain_follow & (1 << 6)) == 64) then
|
|
local target_location = vehicle:get_target_location()
|
|
if target_location ~= nil then
|
|
local target_location_frame = target_location:get_alt_frame()
|
|
-- need to convert the target_location to ABOVE_TERRAIN so we have apples and apples
|
|
local new_target_location = target_location:copy()
|
|
local new_target_altitude = save_target_altitude
|
|
local old_target_altitude = new_target_location:alt()/100
|
|
|
|
if save_old_target_altitude ~= old_target_altitude then
|
|
if target_location_frame ~= ALT_FRAME.ABOVE_TERRAIN then
|
|
if new_target_location:change_alt_frame(ALT_FRAME.ABOVE_TERRAIN) then
|
|
old_target_altitude = new_target_location:alt()/100
|
|
end
|
|
end
|
|
-- adjust target_location for home altitude
|
|
local home_location = ahrs:get_home()
|
|
if home_location ~= nil then
|
|
local home_amsl = terrain:height_amsl(home_location, true)
|
|
local above_home = (target_location:alt() * 0.01 - home_amsl)
|
|
local location = ahrs:get_location()
|
|
if location ~= nil then
|
|
if location:get_alt_frame() ~= ALT_FRAME.ABOVE_TERRAIN then
|
|
location:change_alt_frame(ALT_FRAME.ABOVE_TERRAIN)
|
|
end
|
|
local current_altitude = location:alt() * 0.01
|
|
-- @Description: When the GCS requests a guided altitude X above home
|
|
-- 1: reset to current terrain height ignore X
|
|
-- 2: reset to X above terrain
|
|
-- 3: reset to current alt + X
|
|
if zgt_mode == 1 then
|
|
new_target_altitude = current_altitude
|
|
elseif zgt_mode == 2 then
|
|
new_target_altitude = above_home
|
|
elseif zgt_mode == 3 then
|
|
new_target_altitude = current_altitude + above_home
|
|
end
|
|
new_target_altitude = constrain(new_target_altitude, ALTITUDE_MIN, ALTITUDE_MAX)
|
|
end
|
|
else
|
|
gcs:send_text(MAV_SEVERITY.ERROR, string.format("%s: FAILED to get HOME terrain height", SCRIPT_NAME_SHORT))
|
|
end
|
|
|
|
if new_target_altitude > 0 and
|
|
set_vehicle_target_altitude({alt = new_target_altitude, frame = ALT_FRAME.ABOVE_TERRAIN}) then -- pass altitude in meters (location has it in cm)
|
|
if new_target_altitude ~= save_target_altitude then
|
|
gcs:send_text(MAV_SEVERITY.NOTICE, string.format("%s: Reset to alt %.0fm above terrain", SCRIPT_NAME_SHORT,
|
|
new_target_altitude
|
|
))
|
|
save_target_altitude = new_target_altitude
|
|
end
|
|
else
|
|
gcs:send_text(MAV_SEVERITY.ERROR, string.format("%s: FAILED to set altitude ABOVE_TERRAIN ait %.0f", SCRIPT_NAME_SHORT,
|
|
new_target_altitude
|
|
))
|
|
end
|
|
end
|
|
save_old_target_altitude = old_target_altitude
|
|
else
|
|
gcs:send_text(MAV_SEVERITY.ERROR, string.format("%s: altitude not available", SCRIPT_NAME_SHORT))
|
|
end
|
|
else
|
|
-- we switched out of guided, so forget what we thought we knew
|
|
save_target_altitude = -1
|
|
save_old_target_altitude = -1
|
|
end
|
|
save_vehicle_mode = vehicle_mode
|
|
|
|
return update, 1000 * REFRESH_RATE
|
|
end
|
|
|
|
-- wrapper around update(). This calls update() at 1/REFRESHRATE Hz,
|
|
-- and if update faults then an error is displayed, but the script is not
|
|
-- stopped
|
|
local function protected_wrapper()
|
|
local success, err = pcall(update)
|
|
if not success then
|
|
gcs:send_text(0, SCRIPT_NAME_SHORT .. ": Error: " .. err)
|
|
-- when we fault we run the update function again after 1s, slowing it
|
|
-- down a bit so we don't flood the console with errors
|
|
return protected_wrapper, 1000
|
|
end
|
|
return protected_wrapper, 1000 * REFRESH_RATE
|
|
end
|
|
|
|
gcs:send_text(MAV_SEVERITY.NOTICE, string.format("%s %s script loaded", SCRIPT_NAME, SCRIPT_VERSION) )
|
|
|
|
-- start running update loop
|
|
if FWVersion:type() == 3 and terrain_enable then
|
|
gcs:send_text(MAV_SEVERITY.NOTICE, string.format("%s %s script loaded", SCRIPT_NAME, SCRIPT_VERSION) )
|
|
return protected_wrapper()
|
|
else
|
|
gcs:send_text(MAV_SEVERITY.NOTICE,string.format("%s: Must run on Plane with terrain follow", SCRIPT_NAME_SHORT))
|
|
end
|