Let the boat drift until [SCRIPT_TIME continues]

I’m looking for a way to do a mission that let the boat drift with the sea current until it reach a fence and/or during X seconds time.
I need this for my kind of survey and for energy saving.
I thought I can do that with simply the delay command, but in the sim the boat stay in the last waypoint, tacking (it’s a sailboat too).

Then I discovered pythons scripts based here Using Python Scripts in Mission Planner — Mission Planner documentation but there is no variables about fences?
Maybe the Script.Sleep() do a different behavior compared to the DELAY event?

Do you have ideas, how to do this mission?

You can use a LUA script to do it.

You’ll want to use Lua. There aren’t any direct access bindings for fences, but what kind of fence is it? If it’s a simple circular inclusion fence, the script probably wouldn’t be hard to write. A polygonal fence would be slightly more challenging (but not impossible).

A time based script would be quite simple, though you may want to access GPS time rather than using a millisecond based algorithm, since your vehicle sounds like it’s built to operate for long durations. Getting actual time of day may prove a slight challenge, but not too bad.

But can we launch a script after a waypoint reached, then the script lock the safran and motor for example, and start the payload, and if a fence polygon is reached the native firmware start the FS action?

There are a handful of ways to approach the problem, but all of them require Lua scripting. It’s all possible.

What I mean is, can we use both mission planner interface and add a script? because the boat will be used by a low computer skills guy.

So He can use the mission planner interface, add the WPs, launch the script by a plan event, add the fences and the rally points, and that’s it?

Is there a way to achieve that?

The script will constantly run in the background onboard the autopilot and monitor conditions. If you want to trigger it to drift at a certain waypoint, use a NAV_SCRIPT_TIME command in the mission. The script can then take over and monitor for an appropriate trigger to resume. Little to no user interface required.


I got curious as to how this might work, so I worked up a quick script that might meet your needs (or at least serve as an example for you).

Follow the linked wiki instructions for enabling onboard scripting. If you want to use this exact script, save the example below as a file with a .lua extension.
Lua Scripts — Rover documentation (ardupilot.org)

To use the script, your mission must include SCRIPT_TIME commands that look like this:

In the command column, enter 86 (a number I picked arbitrarily as the trigger for the script). In the arg1 column, enter the allowable distance to drift after reaching the preceding waypoint.

For example, the screenshot shows that the craft will be allowed to drift 20 meters after reaching waypoint 2 and 8 meters after waypoint 4.

Drift distance can be monitored on the Quick tab of Mission Planner as MAV_DRIFT:

I tested this in simulation (SITL), and it seemed to work very well. Please do your own carefully monitored testing before deploying this (or any) script on a working vehicle. You may want to include a timeout, as well, since this script as written will require user intervention to select the subsequent waypoint if the drift distance is never achieved (on a very calm day, for example).

local DRIFT_CMD = 86
local DEFAULT_DRIFT_M = 10  -- (m) allowable drift distance if not specified
local ROVER_MODE_AUTO = 10
local RUN_INTERVAL_MS = 200

local last_id = -1
local drift_start_loc
local script_time_msn_index
local allowable_drift_dist = DEFAULT_DRIFT_M

function do_script_time()
    -- exit script time on waypoint change, mode change, or disarm
    if not arming:is_armed() or
       vehicle:get_mode() ~= ROVER_MODE_AUTO or
       mission:get_current_nav_index() ~= script_time_msn_index then

        allowable_drift_dist = DEFAULT_DRIFT_M
        gcs:send_text(MAV_SEVERITY_NOTICE, 'Drift canceled!')
        return await_script_time, RUN_INTERVAL_MS

    -- zero steering/throttle using guided mode binding
    vehicle:set_desired_turn_rate_and_speed(0, 0)

    -- compute drift distance and send to GCS
    local current_loc = assert(ahrs:get_location(), 'AHRS location error!')
    local drift_dist = current_loc:get_distance(drift_start_loc)
    gcs:send_named_float(DRIFT_MAV_NAME, drift_dist)

    -- resume mission if drift distance exceeds allowable
    if drift_dist > allowable_drift_dist then
        allowable_drift_dist = DEFAULT_DRIFT_M
        gcs:send_text(MAV_SEVERITY_NOTICE, ('Drifted %.1fm. Resuming nav!'):format(drift_dist))
        return await_script_time, RUN_INTERVAL_MS

    return do_script_time, RUN_INTERVAL_MS

function await_script_time()
    -- enter script time routine if DRIFT_CMD is received
    -- first argument is allowable drift distance in meters
    local id, cmd, arg1, arg2, arg3, arg4 = vehicle:nav_script_time()
    if id and cmd == DRIFT_CMD then
        drift_start_loc = assert(ahrs:get_location(), 'AHRS location error!')
        if arg1 and arg1 > 0 then allowable_drift_dist = arg1 end
        last_id = id
        script_time_msn_index = mission:get_current_nav_index()
        gcs:send_text(MAV_SEVERITY_NOTICE, ('Allowing %.1fm drift...'):format(allowable_drift_dist))
        return do_script_time, RUN_INTERVAL_MS

    -- report -1 for drift distance if script time is inactive
    gcs:send_named_float(DRIFT_MAV_NAME, -1)
    return await_script_time, RUN_INTERVAL_MS

gcs:send_text(MAV_SEVERITY_INFO, 'Allow drift script loaded')

return await_script_time()


Amazing, thank you very much for taking the time to do this.

If I understand well (I haven’t used lua since middle school) you are using the guided mode to drift, why not just the servo.set_output () method?

“SCRIPT_TIME” is not in the list in mission planner, even if I enable SCR_ENABLE parameter :confused:

I guess I’m missing something.

What this line is doing ? :

There is no description on the api for this method.

I updated mission planner from 1.3.77 to 1.3.80 and now I can select “SCRIPT_TIME”
I paste my file “drift.lua” in the folder sitl/rover/scripts
but when the script_time command is launched I have just the message " Mission: 2 NavScriptTime" and nothing happen until the time out :confused:

edit : Ok finally on the boat the script is launching.
But… guided mode will not be compatible with the mission because the steering servo make noise continuously… so I tried to control servo output instead :

servo.set_output_pwm(70, 1500) --motor stop
servo.set_output_pwm(26, 1900) --rudder
servo.set_output_pwm(137, 1700) --mast

but I don’t know why I got this error :

nil value (global ‘servo’)
Lua: /APM/scripts/drift.lua:29: attempt to index a

(line 29 is the first servo output method)

edit 2 : tried with SRV_Channels:set_output_pwm, no errors but the boat isn’t drifting

1 Like

You can’t use the servo output bindings in auto/guided modes. But the guided command that I used just zeros steering and throttle. It’s the equivalent.

1 Like

ok, but in that case the steering servo is still moving a little and it’s making noise which is not good for the payload, do you have an idea to avoid this? I’m still searching too

Give it a steering command of something other than zero. Or use a better quality servo…

Alternatively, you could employ a relay to simply cut all power to the steering servo during the drift.

I can’t let you say that, in manual the servo isn’t making noise, it’s the guided mode which produces these small movements, maybe because it’s correcting little vehicle movements? It’s a 120 $ hitec servo :smiley:

So you suggest to try something like this? : vehicle:set_desired_turn_rate_and_speed(3, 0)

Value needs to be between -1 and 1. In my testing, the steering command stayed locked at its trim position. Does the servo output page show that it’s moving?

yes it’s moving a little we can see it on the servo outputs tab.
A turn rate of 1 change nothing

I think I will have to intercept the pwm signals with an arduino…

No you don’t. There is a workaround I was trying to avoid, but I’ve used it in the past to great effect, and there’s nothing wrong with it. I just thought we could do better.

We can “intercept” the signals onboard by setting unused outputs to throttle and steering, and then setting the actual steering and throttle outputs to “Scripting1” and “Scripting2,” respectively.

In the SITL vehicle, it looks like this:

Then we use this modified script that will directly manipulate the servo outputs.

drift.lua (2.8 KB)

Alternatively, you could just cut power to non-essential components using relays and a very similar script. This may actually be preferable. Connect the power to a servo through the NC contact of a relay and then open the relay during SCRIPT_TIME. If the relay consumes less power than servo actuation, it’s a win.

In all cases, you don’t need some janky Arduino in the mix.

others modes like manual or follow are still working with this setup?

All modes work fine - they just now depend on scripting.

I used this method in a critical application in a commercial environment, so I’m confident that it does work and has few drawbacks.