Yuri
3 years ago
committed by
Randy Mackay
3 changed files with 215 additions and 0 deletions
@ -0,0 +1,209 @@
@@ -0,0 +1,209 @@
|
||||
--[[---------------------------------------------------------------------------- |
||||
|
||||
TerrainDetector ArduPilot Lua script |
||||
|
||||
Uses gyro and accelerometer data to detect rough terrain and slow the |
||||
vehicle's speed accordingly during auto missions. |
||||
|
||||
Provides a set of custom tuning parameters: |
||||
ROUGH_SPEED : (m/s) rough terrain desired speed, set to -1 to disable detection |
||||
ROUGH_TIMEOUT_MS: (ms) min time to remain slowed down for rough terrain |
||||
ROUGH_GZ_MAX : (G) slow down on Gz impulses greater than this value |
||||
ROUGH_RATE_MAX : (deg/s) slow down on rate transients greater than this value |
||||
|
||||
These gain values are used for smooth terrain detection. |
||||
Smaller values (less than 1.0) are more restrictive about resuming normal speed. |
||||
ROUGH_GZ_GAIN : Gz impulse gain |
||||
ROUGH_RATE_GAIN : gyro transient rate gain |
||||
|
||||
CAUTION: This script is capable of engaging and disengaging autonomous control |
||||
of a vehicle. Use this script AT YOUR OWN RISK. |
||||
|
||||
-- Yuri -- Jul 2022 |
||||
|
||||
LICENSE - GNU GPLv3 https://www.gnu.org/licenses/gpl-3.0.en.html |
||||
|
||||
Concept first presented during this live stream: |
||||
https://www.youtube.com/watch?v=UdXGXjigxAo&t=7155s |
||||
Credit to @ktrussell for the idea and discussion! |
||||
------------------------------------------------------------------------------]] |
||||
|
||||
local SCRIPT_NAME = 'TerrainDetector' |
||||
local RUN_INTERVAL_MS = 25 -- needs to be pretty fast for good detection |
||||
local SBY_INTERVAL_MS = 500 -- slower interval when detection is disabled |
||||
local PARAM_TABLE_KEY = 117 -- unique index value between 0 and 200 |
||||
|
||||
-- GCS messages |
||||
local VERBOSE_MODE = 1 -- 0 to suppress all GCS messages, |
||||
-- 1 for normal status messages |
||||
-- 2 for additional debug messages |
||||
local MSG_NORMAL = 1 |
||||
local MSG_DEBUG = 2 |
||||
|
||||
-- MAVLink values |
||||
local MAV_SEVERITY_WARNING = 4 |
||||
local MAV_SEVERITY_INFO = 6 |
||||
|
||||
-- mathematical/physical constants |
||||
local G = -9.81 -- m/s/s |
||||
|
||||
-- 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 |
||||
|
||||
add_params(PARAM_TABLE_KEY, 'ROUGH_', { |
||||
-- { name, default value }, |
||||
{ 'SPEED', 0.7 }, |
||||
{ 'GZ_MAX', 1.33 }, |
||||
{ 'RATE_MAX', 28 }, |
||||
{ 'GZ_GAIN', 0.9 }, |
||||
{ 'RATE_GAIN', 0.8 }, |
||||
{ 'TIMEOUT_MS', 7500 } |
||||
}) |
||||
|
||||
-- wrapper for gcs:send_text() |
||||
local function gcs_msg(msg_type, severity, txt) |
||||
if type(msg_type) == 'string' then |
||||
-- allow just a string to be passed for simple/routine messages |
||||
txt = msg_type |
||||
msg_type = MSG_NORMAL |
||||
severity = MAV_SEVERITY_INFO |
||||
end |
||||
if type(severity) == 'string' then |
||||
-- allow just severity and string to be passed for normal messages |
||||
txt = severity |
||||
severity = msg_type |
||||
msg_type = MSG_NORMAL |
||||
end |
||||
if msg_type <= VERBOSE_MODE then |
||||
gcs:send_text(severity, string.format('%s: %s', SCRIPT_NAME, txt)) |
||||
end |
||||
end |
||||
|
||||
local last_g_z = 0 -- to calculate impulse Gz |
||||
local timeout_ms = 0 |
||||
|
||||
-- debug values |
||||
local max_g_z = 0 |
||||
local max_gyro_rate = 0 |
||||
|
||||
local wp_speed_normal = nil |
||||
local wp_speed_rough = nil |
||||
local impulse_gz_threshold = nil |
||||
local gyro_rate_threshold = nil |
||||
local rough_terrain_timeout_ms = nil |
||||
local impulse_gain = nil |
||||
local gyro_gain = nil |
||||
|
||||
function standby() |
||||
wp_speed_rough = param:get('ROUGH_SPEED') |
||||
if wp_speed_rough < 0 then return standby, SBY_INTERVAL_MS end |
||||
|
||||
if mission:state() == mission.MISSION_RUNNING then |
||||
-- only poll remaining parameters at start of mission |
||||
wp_speed_normal = param:get('WP_SPEED') |
||||
impulse_gz_threshold = param:get('ROUGH_GZ_MAX') |
||||
gyro_rate_threshold = param:get('ROUGH_RATE_MAX') |
||||
rough_terrain_timeout_ms = param:get('ROUGH_TIMEOUT_MS') |
||||
impulse_gain = param:get('ROUGH_GZ_GAIN') |
||||
gyro_gain = param:get('ROUGH_RATE_GAIN') |
||||
-- if ROUGH_SPEED param not set or invalid, use half of WP_SPEED |
||||
if wp_speed_rough == 0 or wp_speed_rough > wp_speed_normal then |
||||
wp_speed_rough = wp_speed_normal / 2 |
||||
gcs_msg(MAV_SEVERITY_WARNING, 'ROUGH_SPEED invalid, using half WP_SPEED') |
||||
end |
||||
|
||||
last_g_z = 0 |
||||
return do_normal_speed, RUN_INTERVAL_MS |
||||
end |
||||
return standby, SBY_INTERVAL_MS |
||||
end |
||||
|
||||
function initiate_rough_speed() |
||||
gcs_msg(MSG_NORMAL, MAV_SEVERITY_WARNING, 'Slowing for rough terrain') |
||||
vehicle:set_desired_speed(wp_speed_rough) |
||||
last_g_z = 0 |
||||
timeout_ms = millis() + rough_terrain_timeout_ms |
||||
max_g_z = 0 |
||||
max_gyro_rate = 0 |
||||
return do_rough_speed, RUN_INTERVAL_MS |
||||
end |
||||
|
||||
function initiate_normal_speed() |
||||
gcs_msg(MSG_NORMAL, MAV_SEVERITY_WARNING, 'Resuming normal speed') |
||||
vehicle:set_desired_speed(wp_speed_normal) |
||||
last_g_z = 0 |
||||
return do_normal_speed, RUN_INTERVAL_MS |
||||
end |
||||
|
||||
function do_normal_speed() |
||||
if mission:state() ~= mission.MISSION_RUNNING then |
||||
return standby, SBY_INTERVAL_MS |
||||
end |
||||
|
||||
local g_z = ahrs:get_accel():z() / G -- convert to G from m/s/s |
||||
local gyro = ahrs:get_gyro() |
||||
-- sample max of roll/pitch rates |
||||
local gyro_rate = math.deg(math.max(math.abs(gyro:x()), math.abs(gyro:y()))) |
||||
local impulse_g_z = math.abs(g_z - last_g_z) -- sample delta is the measured impulse |
||||
last_g_z = g_z |
||||
|
||||
if impulse_g_z > impulse_gz_threshold then -- slow down |
||||
gcs_msg(MSG_DEBUG, MAV_SEVERITY_INFO, string.format('Impulse - %.2f Gz', impulse_g_z)) |
||||
return initiate_rough_speed, RUN_INTERVAL_MS |
||||
end |
||||
|
||||
if gyro_rate > gyro_rate_threshold then -- slow down |
||||
gcs_msg(MSG_DEBUG, MAV_SEVERITY_INFO, string.format('Transient - %.2f deg/s', gyro_rate)) |
||||
return initiate_rough_speed, RUN_INTERVAL_MS |
||||
end |
||||
|
||||
return do_normal_speed, RUN_INTERVAL_MS |
||||
end |
||||
|
||||
function do_rough_speed() |
||||
if mission:state() ~= mission.MISSION_RUNNING then |
||||
return standby, SBY_INTERVAL_MS |
||||
end |
||||
|
||||
local g_z = ahrs:get_accel():z() / G -- convert to G from m/s/s |
||||
local gyro = ahrs:get_gyro() |
||||
-- sample max of roll/pitch rates |
||||
local gyro_rate = math.deg(math.max(math.abs(gyro:x()), math.abs(gyro:y()))) |
||||
local impulse_g_z = math.abs(g_z - last_g_z) -- sample delta is the measured impulse |
||||
last_g_z = g_z |
||||
|
||||
max_g_z = math.max(g_z, max_g_z) |
||||
max_gyro_rate = math.max(gyro_rate, max_gyro_rate) |
||||
|
||||
local now = millis() |
||||
|
||||
if impulse_g_z > impulse_gz_threshold * impulse_gain then -- stay slow |
||||
gcs_msg(MSG_DEBUG, MAV_SEVERITY_INFO, string.format('Timer reset - %.2fGz', impulse_g_z)) |
||||
max_g_z = 0 |
||||
timeout_ms = now + rough_terrain_timeout_ms |
||||
return do_rough_speed, RUN_INTERVAL_MS |
||||
end |
||||
|
||||
if gyro_rate > gyro_rate_threshold * gyro_gain then -- stay slow |
||||
gcs_msg(MSG_DEBUG, MAV_SEVERITY_INFO, string.format('Timer reset - %.2f deg/s', gyro_rate)) |
||||
max_gyro_rate = 0 |
||||
timeout_ms = now + rough_terrain_timeout_ms |
||||
return do_rough_speed, RUN_INTERVAL_MS |
||||
end |
||||
|
||||
if now > timeout_ms then -- no longer in rough terrain |
||||
gcs_msg(MSG_DEBUG, MAV_SEVERITY_INFO, string.format('Max - %.2fGz ... %.2f deg/s', max_g_z, max_gyro_rate)) |
||||
return initiate_normal_speed, RUN_INTERVAL_MS |
||||
end |
||||
|
||||
return do_rough_speed, RUN_INTERVAL_MS |
||||
end |
||||
|
||||
gcs_msg('Script active') |
||||
|
||||
return standby, SBY_INTERVAL_MS |
Loading…
Reference in new issue