Skip to content

Commit

Permalink
AP_Scripting: ahrs-source example script
Browse files Browse the repository at this point in the history
switches automatically between gps, external nav and optical flow
  • Loading branch information
rmackay9 committed Oct 21, 2020
1 parent 79b5ca8 commit 5fe2573
Showing 1 changed file with 177 additions and 0 deletions.
177 changes: 177 additions & 0 deletions libraries/AP_Scripting/examples/ahrs-source.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
-- switches between AHRS/EKF sources if the rangefinder distance is less than 10m and the pilot's source selection switch is in the middle
-- this script is intended to help vehicles from between GPS and Non-GPS environments
--
-- setup RCx_OPTION = 90 (EKF Pos Source) to select the source (low=primary, middle=secondary, high=tertiary)
-- setup RCx_OPTION = 83 (ZigZag Auto). When this switch is pulled high, the source will be automatically selected
-- setup EK3_SRC_ parameters so that GPS is the primary source, Non-GPS (i.e. T265) is secondary and optical flow tertiary
-- configure a forward or downward facing lidar with a range of more than 5m
--
-- When the auxiliary switch is in middle position, automatic source selection uses these thresholds:
-- SCR_USER1 holds the threshold for rangefinder altitude:
-- if rangefinder distance >= SCR_USER1, source1 will be used
-- if rangefinder distance < SCR_USER1, source2 will be used
-- SCR_USER2 holds the threshold for GPS speed accuracy (around 0.3 is a good choice)
-- SCR_USER3 holds the threshold for Non-GPS vertical speed innovation (about 0.5 may be a good choice)
-- if both GPS speed accuracy <= SCR_USER2 and ExternalNav speed variance >= SCR_USER3, source1 will be used
-- otherwise source2 will be used
--
-- GPS will be used when RangeFinder returns distance of >=8m for at least 3 seconds
-- NonGPS will be used when RangeFinder returns distance of <8m for at least 3 seconds

local rangefinder_rotation = 25 -- check downward (25) facing lidar
local source_prev = 0 -- previous source, defaults to primary source
local sw_source_prev = -1 -- previous source switch position
local sw_auto_pos_prev = -1 -- previous auto source switch position
local auto_switch = false -- true when auto switching between sources is active
local gps_usable_accuracy = 1.0 -- GPS is usable if speed accuracy is at or below this value
local vote_counter_max = 20 -- when a vote counter reaches this number (i.e. 2sec) source may be switched
local gps_vs_nongps_vote = 0 -- vote counter for GPS vs NonGPS (-20 = GPS, +20 = NonGPS)
local extnav_vs_opticalflow_vote = 0 -- vote counter for extnav vs optical flow (-20 = extnav, +20 = opticalflow)

local debug_counter = 0

-- the main update function that uses the takeoff and velocity controllers to fly a rough square pattern
function update()

-- check switches are configured
-- source selection from RCx_FUNCTION = 90 (EKF Source Select)
-- auto source from RCx_FUNCTION = 83 (ZigZag_Auto)
local rc_function_source = rc:find_channel_for_option(90)
local rc_function_auto = rc:find_channel_for_option(83)
if (rc_function_source == nil) or (rc_function_auto == nil) then
gcs:send_text(0, "ahrs-source.lua: RCx_FUNCTION=90 or 83 not set!")
return update, 1000
end

-- check rangefinder distance threshold has been set
local rangefinder_thresh_dist = param:get('SCR_USER1') -- SCR_USER1 holds rangefinder threshold
if (rangefinder_thresh_dist == nil) or (rangefinder_thresh_dist <= 0) then
gcs:send_text(0, "ahrs-source.lua: set SCR_USER1 to rangefinder threshold")
return update, 1000
end

-- check GPS speed accuracy threshold has been set
local gps_speedaccuracy_thresh = param:get('SCR_USER2') -- SCR_USER2 holds GPS speed accuracy threshold
if (gps_speedaccuracy_thresh == nil) or (gps_speedaccuracy_thresh <= 0) then
gcs:send_text(0, "ahrs-source.lua: set SCR_USER2 to GPS speed accuracy threshold")
return update, 1000
end

-- check external nav innovation threshold has been set
local extnav_innov_thresh = param:get('SCR_USER3') -- SCR_USER3 holds Non-GPS vertical velocity innovation
if (extnav_innov_thresh == nil) or (extnav_innov_thresh <= 0) then
gcs:send_text(0, "ahrs-source.lua: set SCR_USER3 to ExtNav innovation threshold")
return update, 1000
end

-- check if GPS speed accuracy is over threshold
local gps_speed_accuracy = gps:speed_accuracy(0)
local gps_over_threshold = (gps_speed_accuracy == nil) or (gps:speed_accuracy(0) > gps_speedaccuracy_thresh)
local gps_usable = (gps_speed_accuracy ~= nil) and (gps_speed_accuracy <= gps_usable_accuracy)

-- get external nav innovations from ahrs
local extnav_innov = Vector3f()
local extnav_var = Vector3f()
extnav_innov, extnav_var = ahrs:get_vel_innovations_and_variances_by_source(6)
local extnav_over_threshold = (extnav_innov == nil) or (extnav_innov:z() == 0.0) or (math.abs(extnav_innov:z()) > extnav_innov_thresh)

-- get rangefinder distance
local rngfnd_distance_m = 0
if rangefinder:has_data_orient(rangefinder_rotation) then
rngfnd_distance_m = rangefinder:distance_cm_orient(rangefinder_rotation) * 0.01
end
local rngfnd_over_threshold = (rngfnd_distance_m == 0) or (rngfnd_distance_m > rangefinder_thresh_dist)

-- NpnGPS is usable if extnav innovations are good or rangefinder distance is short (for optical flow)
local nongps_usable = (not extnav_over_threshold) or (not rngfnd_over_threshold)

-- debug.debug
debug_counter = debug_counter + 1
if (debug_counter > 30) then
debug_counter = 0
--gcs:send_text(0, string.format("gpsu:%s ot:%s extot:%s rngot:%s nongpsu:%s", tostring(gps_usable), tostring(gps_over_threshold), tostring(extnav_over_threshold), tostring(rngfnd_over_threshold), tostring(nongps_usable)))
end

-- automatic selection logic --

-- GPS vs NonGPS vote. "-1" to move towards GPS, "+1" to move to Non-GPS
if (not gps_over_threshold) or (gps_usable and not nongps_usable) then
-- vote for GPS if GPS accuracy good OR usable GPS and NonGPS unusable
gps_vs_nongps_vote = math.max(gps_vs_nongps_vote - 1, -vote_counter_max)
elseif nongps_usable then
-- vote for NonGPS if extnav or opticalflow is usable
gps_vs_nongps_vote = math.min(gps_vs_nongps_vote + 1, vote_counter_max)
end

-- extnav vs optical flow vote. "-1" to move towards extnav, "+1" to move to opticalflow
if (not extnav_over_threshold) then
-- vote for extnav is innovations under threshold
extnav_vs_opticalflow_vote = math.max(extnav_vs_opticalflow_vote - 1, -vote_counter_max)
elseif (not rngfnd_over_threshold) then
-- vote for optical flow if rangefinder is not over threshold
extnav_vs_opticalflow_vote = math.min(extnav_vs_opticalflow_vote + 1, vote_counter_max)
end

-- auto source vote collation
local auto_source = -1 -- auto source undecided if -1
if gps_vs_nongps_vote <= -vote_counter_max then
auto_source = 0 -- GPS
elseif gps_vs_nongps_vote >= vote_counter_max then
if extnav_vs_opticalflow_vote <= -vote_counter_max then
auto_source = 1 -- extnav
elseif extnav_vs_opticalflow_vote >= vote_counter_max then
auto_source = 2 -- opticalflow
end
end

-- read source switch position from RCx_FUNCTION = 90 (EKF Source Select)
local sw_source_pos = rc_function_source:get_aux_switch_pos()
if sw_source_pos ~= sw_source_pos_prev then -- check for changes in source switch position
sw_source_pos_prev = sw_source_pos -- record new switch position so we can detect changes
auto_switch = false -- disable auto switching of source
if source_prev ~= sw_source_pos then -- check if switch position does not match source (there is a one-to-one mapping of switch to source)
source_prev = sw_source_pos -- record what source should now be (changed by ArduPilot vehicle code)
gcs:send_text(0, "Pilot switched to Source " .. string.format("%d", source_prev+1))
else
gcs:send_text(0, "Pilot switched but already Source " .. string.format("%d", source_prev+1))
end
end

-- read auto source switch position from RCx_FUNCTION = 83 (ZigZag_Auto)
local sw_auto_pos = rc_function_auto:get_aux_switch_pos()
if sw_auto_pos ~= sw_auto_pos_prev then -- check for changes in source auto switch position
sw_auto_pos_prev = sw_auto_pos -- record new switch position so we can detect changes
if sw_auto_pos == 0 then -- pilot has pulled switch low
auto_switch = false -- disable auto switching of source
if sw_source_pos ~= source_prev then -- check if source will change
source_prev = sw_source_pos -- record pilot's selected source
ahrs:set_position_source(source_prev) -- switch to pilot's selected source
gcs:send_text(0, "Auto source disabled, switched to Source " .. string.format("%d", source_prev+1))
else
gcs:send_text(0, "Auto source disabled, already Source " .. string.format("%d", source_prev+1))
end
elseif sw_auto_pos == 2 then -- pilot has pulled switch high
auto_switch = true -- enable auto switching of source
if auto_source < 0 then
gcs:send_text(0, "Auto source enabled, undecided, Source " .. string.format("%d", source_prev+1))
elseif auto_source ~= source_prev then -- check if source will change
source_prev = auto_source -- record pilot's selected source
ahrs:set_position_source(source_prev) -- switch to pilot's selected source
gcs:send_text(0, "Auto source enabled, switched to Source " .. string.format("%d", source_prev+1))
else
gcs:send_text(0, "Auto source enabled, already Source " .. string.format("%d", source_prev+1))
end
end
end

-- auto switching
if auto_switch and (auto_source >= 0) and (auto_source ~= source_prev) then
source_prev = auto_source -- record selected source
ahrs:set_position_source(source_prev) -- switch to pilot's selected source
gcs:send_text(0, "Auto switched to Source " .. string.format("%d", source_prev+1))
end

return update, 100
end

return update()

0 comments on commit 5fe2573

Please sign in to comment.