--[[---------------------------------------------------------------------------- SaveTurns ArduPilot Lua script Saves the locations of vehicle turns as waypoints in the current nav mission. CAUTION: This script is capable of engaging and disengaging autonomous control of a vehicle. Use this script AT YOUR OWN RISK. -- Yuri -- Nov 2021, revised Apr 2022 LICENSE - GNU GPLv3 https://www.gnu.org/licenses/gpl-3.0.en.html ------------------------------------------------------------------------------]] ---@diagnostic disable: cast-local-type ---@diagnostic disable: need-check-nil local SCRIPT_NAME = 'SaveTurns' -------- USER EDITABLE GLOBALS -------- local RC_OPTION = 300 -- RC option number for switched control (300, 301, etc) local MIN_DIST = 2.0 -- (m) min distance between waypoints local HDG_DELTA = 8.0 -- (deg) save wp after heading change of this magnitude local RUN_INTERVAL_MS = 200 -- (ms) how often to run this script local VERBOSE_MODE = 2 -- 0 to suppress all GCS messages, -- 1 for normal status messages -- 2 for additional GPS/debug messages -------- MAVLINK/AUTOPILOT 'CONSTANTS' -------- local ROVER_MODE_AUTO = 10 local STANDBY = 0 local SAVE_WPS = 1 local CLEAR_WPS = 2 local WAYPOINT = 16 -- waypoint command local MAV_SEVERITY_WARNING = 4 local MAV_SEVERITY_INFO = 6 local MSG_NORMAL = 1 local RC_CHAN = rc:find_channel_for_option(RC_OPTION) local last_wp = Location() local last_yaw = 999.0 -- 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 msg_type <= VERBOSE_MODE then gcs:send_text(severity, string.format('%s: %s', SCRIPT_NAME, txt)) end end local function yaw_diff(yaw1, yaw2) --https://stackoverflow.com/questions/5024375/getting-the-difference-between-two-headings return math.abs((yaw2 - yaw1 + 540.0) % 360.0 - 180.0) end local function new_mission() local home = ahrs:get_home() local item = mavlink_mission_item_int_t() mission:clear() item:command(WAYPOINT) item:x(home:lat()) item:y(home:lng()) item:z(home:alt()) if not mission:set_item(0, item) then gcs_msg(MSG_NORMAL, MAV_SEVERITY_WARNING, 'Failed to create new mission') return false end return true end local function save_wp(position, index) local item = mavlink_mission_item_int_t() if (not position) then gcs_msg(MSG_NORMAL, MAV_SEVERITY_WARNING, string.format('WP %d - invalid position', index)) return false end item:command(WAYPOINT) item:x(position:lat()) item:y(position:lng()) item:z(0) if not mission:set_item(index, item) then gcs_msg(MSG_NORMAL, MAV_SEVERITY_WARNING, string.format('WP %d - failed to save', index)) return false end return true end function collect_breadcrumbs() if vehicle:get_mode() == ROVER_MODE_AUTO then gcs_msg('AUTO mode - saving canceled') return standby, RUN_INTERVAL_MS end local sw_pos = RC_CHAN:get_aux_switch_pos() if sw_pos ~= SAVE_WPS then return standby, RUN_INTERVAL_MS end if not ahrs:healthy() then return collect_breadcrumbs, RUN_INTERVAL_MS end local cur_pos = ahrs:get_location() local cur_yaw = ahrs:get_yaw() * 180.0 / math.pi if cur_pos:get_distance(last_wp) < MIN_DIST then last_yaw = cur_yaw return collect_breadcrumbs, RUN_INTERVAL_MS end if yaw_diff(cur_yaw, last_yaw) > HDG_DELTA then local idx = mission:num_commands() if save_wp(cur_pos, idx) then gcs_msg(string.format('WP %d - saved', idx)) last_wp = cur_pos last_yaw = cur_yaw end end return collect_breadcrumbs, RUN_INTERVAL_MS end function await_switch_change() local sw_pos = RC_CHAN:get_aux_switch_pos() if sw_pos == STANDBY then return standby, RUN_INTERVAL_MS end if sw_pos == SAVE_WPS then return collect_breadcrumbs, RUN_INTERVAL_MS end return await_switch_change, RUN_INTERVAL_MS end function standby() if vehicle:get_mode() == ROVER_MODE_AUTO then return standby, RUN_INTERVAL_MS end local sw_pos = RC_CHAN:get_aux_switch_pos() if sw_pos == SAVE_WPS then last_wp = Location() last_yaw = 999.0 return collect_breadcrumbs, RUN_INTERVAL_MS end if sw_pos == CLEAR_WPS then if new_mission() then gcs_msg(MSG_NORMAL, MAV_SEVERITY_WARNING, 'Mission cleared') end return await_switch_change, RUN_INTERVAL_MS end return standby, RUN_INTERVAL_MS end function initialize() if not ahrs:healthy() then return initialize, RUN_INTERVAL_MS end gcs_msg('Ready') return standby, RUN_INTERVAL_MS end gcs_msg('Awaiting AHRS initialization...') return initialize()