Check if goto_location has reached target position

This surely has been asked before and I just cannot find it. I can’t be the first one.

How to check if a drone has reached a target position after issuing a goto_location command?

Right now, my solution is to regularly check the drone’s position and consider the waypoint done when the difference between actual and target position lies within a certain tolerance. It then waits a few seconds to let the drone settle itself at the new position and moves on.

    import math
    from mavsdk import System

    TOLERANCE_DEG = 0.00005
    TOLERANCE_M = 0.3
    WAIT_FOR_TARGET = 2
    uav = System()
    lat = ...
    lon = ...
    alt = ...
    yaw = ...
    ...
    await uav.action.goto_location(lat, lon, alt, yaw)
    
    last = 0
    async for position in uav.telemetry.position():
        curr = time.time()
        if (curr - last >= 1):
            last = curr
            diff_lat = math.fabs(position.latitude_deg - lat) - TOLERANCE_DEG
            diff_lon = math.fabs(position.longitude_deg - lon) - TOLERANCE_DEG
            diff_alt = math.fabs(position.relative_altitude_m - alt) - TOLERANCE_M
            if (diff_lat <= 0 and diff_lon <= 0 and diff_alt <= 0):
                print("Reached target vincinity, wait another " + str(WAIT_FOR_TARGET) + " s")
                asyncio.sleep(WAIT_FOR_TARGET)
                print("Reached target position")
                break

However, this feels a little hacky. This approach is based on a lot of estimates that may change with drone model or situation.

Is there a better solution to do this?

Thanks and best regards.

Hello, I had the same problem. I needed to execute some custom code when I reached a checkpoint of a mission. I implemented something similar to your approach because I don’t think there is a possibility to get a callback when the point is reached. For missions, you can get a callback when the drone leaves, but that is also not super helpful.

Here is my solution. It checks whether the distance in meter is lower than the acceptance distance:

val missionPlan = MissionPlan(flightPlan.checkpoints?.map {
    Mission.MissionItem(
        it.latitude,
        it.longitude,
        10f,
        5f,
        false,
        0f,
        0f,
        Mission.MissionItem.CameraAction.TAKE_PHOTO,
        5f,
        0.0,
        1f,
        0f,
        0f,
        Mission.MissionItem.VehicleAction.NONE
    )
} ?: emptyList())
drone?.mission?.uploadMission(missionPlan)?.blockingAwait()
drone?.mission?.setReturnToLaunchAfterMission(true)?.blockingAwait()
drone?.mission?.setCurrentMissionItem(0)?.blockingAwait()
drone?.mission?.startMission()?.blockingAwait()

var currentCheckpoint = missionPlan.missionItems[0]
var checkpointReached = false
drone?.mission?.missionProgress?.subscribe {
    currentCheckpoint = missionPlan.missionItems[it.current]
    checkpointReached = false
}
drone?.telemetry?.position?.subscribe {
    if (checkpointReached) {
        return@subscribe
    }
    val distanceM = haversine(
        currentCheckpoint.latitudeDeg,
        currentCheckpoint.longitudeDeg,
        it.latitudeDeg,
        it.longitudeDeg
    )
    if (distanceM < 1) {
        checkpointReached = true
        println("Checkpoint reached!")
    }
}

Distance function:

private fun haversine(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
    val radiusEarth = 6371000
    val dLat = Math.toRadians(lat2 - lat1)
    val dLon = Math.toRadians(lon2 - lon1)
    val originLat = Math.toRadians(lat1)
    val destinationLat = Math.toRadians(lat2)

    val a = sin(dLat / 2).pow(2) + sin(dLon / 2).pow(2) * cos(originLat) * cos(destinationLat)
    val c = 2 * atan2(sqrt(a), sqrt(1 - a))
    return radiusEarth * c
}

The same approach can be used for goto_location. Maybe this helps someone in the future.