mirror of
https://github.com/ArduPilot/ardupilot.git
synced 2026-10-06 19:00:27 +08:00
199 lines
6.9 KiB
Lua
199 lines
6.9 KiB
Lua
--[[
|
|
crash-actions.lua augments FS_CRASH_CHECK (Copter/Rover) and CRASH_DETECT (Plane).
|
|
|
|
If crash detection is enabled, this script enables additional, configurable
|
|
post-crash actions. See crash-actions.md for configuration details.
|
|
|
|
-- Yuri Rage, May 2026
|
|
]]
|
|
|
|
local SCRIPT_NAME = "Crash Actions"
|
|
local UPDATE_MS = 500
|
|
local MAV_SEVERITY = { EMERGENCY = 0, ALERT = 1, CRITICAL = 2, ERROR = 3, WARNING = 4, NOTICE = 5, INFO = 6, DEBUG = 7 }
|
|
local MAV_CMD_DO_SET_SERVO = 183
|
|
local ACTION = {
|
|
DISARM = 1,
|
|
SET_RELAY_OFF = 2,
|
|
SET_RELAY_ON = 3,
|
|
SET_SERVO_MIN = 4,
|
|
SET_SERVO_TRIM = 5,
|
|
SET_SERVO_MAX = 6,
|
|
}
|
|
local SERVO_PARAM = {
|
|
[ACTION.SET_SERVO_MIN] = "MIN",
|
|
[ACTION.SET_SERVO_TRIM] = "TRIM",
|
|
[ACTION.SET_SERVO_MAX] = "MAX",
|
|
}
|
|
local MAX_INSTANCES = 32 -- max number of relay/servo instances
|
|
|
|
-- parameter table variables
|
|
local NUM_CRASH_ACTIONS = 4
|
|
local PARAM_TABLE_PREFIX = "CRASH_"
|
|
local PARAM_TABLE_SIZE = NUM_CRASH_ACTIONS * 3
|
|
local PARAM_TABLE_KEY = 188
|
|
|
|
-- parameter variables bound at runtime
|
|
local CRASH_ACTIONS = {}
|
|
|
|
-- forward declarations for state machine functions
|
|
local standby = nil
|
|
local do_crash_actions = nil
|
|
local protected_wrapper = nil
|
|
|
|
local valid_relays = {}
|
|
local valid_servos = {}
|
|
|
|
local crash_time_ms = uint32_t(0)
|
|
|
|
-- determine valid (configured) servo/relay instances
|
|
for i = 1, MAX_INSTANCES do
|
|
local val = param:get(string.format("RELAY%d_FUNCTION", i)) or 0
|
|
if val > 0 then
|
|
valid_relays[i] = true
|
|
end
|
|
|
|
val = param:get(string.format("SERVO%d_FUNCTION", i)) or 0
|
|
if val > 0 then
|
|
valid_servos[i] = true
|
|
end
|
|
end
|
|
|
|
-- add a parameter and bind it to a variable
|
|
local function bind_add_param(name, idx, default_value)
|
|
assert(param:add_param(PARAM_TABLE_KEY, idx, name, default_value),
|
|
string.format("%s: could not add param %s", SCRIPT_NAME, name))
|
|
return Parameter(PARAM_TABLE_PREFIX .. name)
|
|
end
|
|
|
|
-- add CRASH_ param table
|
|
assert(param:add_table(PARAM_TABLE_KEY, PARAM_TABLE_PREFIX, PARAM_TABLE_SIZE),
|
|
string.format("%s: could not add param table", SCRIPT_NAME))
|
|
|
|
-- bind CRASH_ params
|
|
for i = 1, PARAM_TABLE_SIZE - 1, 3 do
|
|
local idx = (i + 2) // 3
|
|
CRASH_ACTIONS[idx] = {
|
|
action = bind_add_param(string.format("ACT%d", idx), i, 0),
|
|
instance = bind_add_param(string.format("ACT%d_INST", idx), i + 1, 0),
|
|
timeout = bind_add_param(string.format("ACT%d_TIME", idx), i + 2, 0),
|
|
complete = false,
|
|
}
|
|
end
|
|
|
|
-- handle a crash action
|
|
-- sends GCS message on failure
|
|
local function handle_crash_action(action_type, instance)
|
|
if action_type == ACTION.DISARM then
|
|
gcs:send_text(MAV_SEVERITY.NOTICE, string.format("%s: Disarming", SCRIPT_NAME))
|
|
arming:disarm()
|
|
return
|
|
end
|
|
|
|
if action_type == ACTION.SET_RELAY_OFF or action_type == ACTION.SET_RELAY_ON then
|
|
if not valid_relays[instance] then
|
|
gcs:send_text(MAV_SEVERITY.ERROR, string.format("%s: RELAY%d not configured", SCRIPT_NAME, instance))
|
|
return
|
|
end
|
|
local state = (action_type == ACTION.SET_RELAY_OFF) and "OFF" or "ON"
|
|
gcs:send_text(MAV_SEVERITY.NOTICE, string.format("%s: Setting RELAY%d %s", SCRIPT_NAME, instance, state))
|
|
if action_type == ACTION.SET_RELAY_OFF then
|
|
relay:off(instance - 1)
|
|
else
|
|
relay:on(instance - 1)
|
|
end
|
|
return
|
|
end
|
|
|
|
if action_type == ACTION.SET_SERVO_MIN or action_type == ACTION.SET_SERVO_TRIM or action_type == ACTION.SET_SERVO_MAX then
|
|
if not valid_servos[instance] then
|
|
gcs:send_text(MAV_SEVERITY.ERROR, string.format("%s: SERVO%d not configured", SCRIPT_NAME, instance))
|
|
return
|
|
end
|
|
|
|
local suffix = SERVO_PARAM[action_type]
|
|
local val = param:get(string.format("SERVO%d_%s", instance, suffix))
|
|
if not val then
|
|
gcs:send_text(MAV_SEVERITY.ERROR,
|
|
string.format("%s: Failed to get SERVO%d_%s", SCRIPT_NAME, instance, suffix))
|
|
return
|
|
end
|
|
gcs:send_text(MAV_SEVERITY.NOTICE, string.format("%s: Setting SERVO%d %s", SCRIPT_NAME, instance, suffix))
|
|
-- uses MAV_CMD_DO_SET_SERVO rather than SRV_Channels:set_() bindings
|
|
-- gets ignore_small_rcin_changes() behavior used by AP_ServoRelayEvents
|
|
gcs:run_command_int(MAV_CMD_DO_SET_SERVO, { p1 = instance, p2 = val })
|
|
return
|
|
end
|
|
|
|
gcs:send_text(MAV_SEVERITY.ERROR, string.format("%s: Unknown action %d", SCRIPT_NAME, action_type))
|
|
end
|
|
|
|
-- state machine function to iterate over crash actions
|
|
-- returns next state function
|
|
do_crash_actions = function()
|
|
if not vehicle:is_crashed() then
|
|
gcs:send_text(MAV_SEVERITY.NOTICE, string.format("%s: Recovered", SCRIPT_NAME))
|
|
return standby
|
|
end
|
|
|
|
for _, crash_action in ipairs(CRASH_ACTIONS) do
|
|
local action = crash_action.action:get()
|
|
|
|
if not crash_action.complete and
|
|
action > 0 and
|
|
millis() - crash_time_ms > (crash_action.timeout:get() * 1000) then
|
|
handle_crash_action(action, crash_action.instance:get())
|
|
crash_action.complete = true
|
|
end
|
|
end
|
|
|
|
return do_crash_actions
|
|
end
|
|
|
|
-- state machine function active until crash detection
|
|
-- returns next state function
|
|
standby = function()
|
|
if vehicle:is_crashed() then
|
|
crash_time_ms = millis()
|
|
for _, crash_action in ipairs(CRASH_ACTIONS) do
|
|
crash_action.complete = (crash_action.action:get() == 0)
|
|
end
|
|
gcs:send_text(MAV_SEVERITY.EMERGENCY, string.format("%s: Executing...", SCRIPT_NAME))
|
|
return do_crash_actions
|
|
end
|
|
return standby
|
|
end
|
|
|
|
-- wrapper around the state machine to handle errors
|
|
-- if a state machine function fails, an error message is sent and the function is retried after 1s
|
|
local result = standby
|
|
protected_wrapper = function()
|
|
local last_fn = result
|
|
local success
|
|
success, result = pcall(result)
|
|
if not success then
|
|
gcs:send_text(MAV_SEVERITY.ERROR, "Internal Error: " .. result)
|
|
result = standby -- reset the state machine
|
|
return protected_wrapper, 1000
|
|
end
|
|
|
|
if last_fn == standby and result == do_crash_actions then
|
|
-- execute crash actions ASAP if changing state from standby
|
|
return protected_wrapper, 0
|
|
end
|
|
return protected_wrapper, UPDATE_MS
|
|
end
|
|
|
|
-- Copter/Rover firmware crash detection check/warning
|
|
if param:get("FS_CRASH_CHECK") == 0 then
|
|
gcs:send_text(MAV_SEVERITY.WARNING, string.format("%s: FS_CRASH_CHECK is disabled", SCRIPT_NAME))
|
|
end
|
|
|
|
-- Plane firmware crash detection check/warning
|
|
if param:get("CRASH_DETECT") == 0 then
|
|
gcs:send_text(MAV_SEVERITY.WARNING, string.format("%s: CRASH_DETECT is disabled", SCRIPT_NAME))
|
|
end
|
|
|
|
gcs:send_text(MAV_SEVERITY.INFO, string.format("%s: Script loaded", SCRIPT_NAME))
|
|
|
|
return protected_wrapper()
|