mavlink / MAVSDK-Python

MAVSDK client for Python.
https://mavsdk.mavlink.io
BSD 3-Clause "New" or "Revised" License
324 stars 220 forks source link

Return to launch position once landed and drone is on the ground #466

Closed huytd-pi closed 2 years ago

huytd-pi commented 2 years ago

I am testing my flight according to the scenario when i land on the ground, after a few seconds the drone makes a return to the launch position. but I encountered that when I landed on the ground, the launch position is now the landing position, so how can the launch position not change? Thank you for your answers.

julianoes commented 2 years ago

You could manually set up a mission that lands at the previous home position. Not sure how else this could be done.

huytd-pi commented 2 years ago

I do download mission which was uploaded from QGC, purpose to do mission edit so drone can return to original position, but i have problem with download_mission(). i don't understand how it works so i can edit the downloaded quest here is my test code:

import asyncio
from mavsdk import System
from mavsdk.mission import (MissionItem, MissionPlan)

async def run():
    # Init the drone
    drone = System()
    await drone.connect(system_address="udp://:14540")

    print("Waiting for drone to connect...")
    async for state in drone.core.connection_state():
        if state.is_connected:
            print(f"Drone discovered!")
            break

    await drone.mission.download_mission()

    print("-- Arming")
    await drone.action.arm()

    print("-- Starting mission")
    await drone.mission.start_mission()

if __name__ == "__main__":
    # Start the main function
    asyncio.ensure_future(run())

    # Runs the event loop until the program is canceled with e.g. CTRL-C
    asyncio.get_event_loop().run_forever()

and when run, an error occurs

huytd@huytd-HP:~/Project/Drone/MAVSDK/MAVSDK-Python/examples$ python3 test_mission.py 
Waiting for drone to connect...
Drone discovered!
test_mission.py:17: RuntimeWarning: coroutine 'Mission.download_mission' was never awaited
  drone.mission.download_mission()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
^CTraceback (most recent call last):
  File "test_mission.py", line 30, in <module>
    asyncio.get_event_loop().run_forever()
  File "/usr/lib/python3.7/asyncio/base_events.py", line 541, in run_forever
    self._run_once()
  File "/usr/lib/python3.7/asyncio/base_events.py", line 1750, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.7/selectors.py", line 468, in select
    fd_event_list = self._selector.poll(timeout, max_ev)
KeyboardInterrupt
julianoes commented 2 years ago

I wonder if you need to actually to get the result, so the downloaded mission using:

    result = await drone.mission.download_mission()
JonasVautherin commented 2 years ago

I wonder if you need to actually to get the result, so the downloaded mission using:

You don't. It's weird because await is there. Usually that message comes when you forget it and just call drone.mission.download_mission() instead of await drone.mission.download_mission() :thinking:.

Which version of MAVSDK is that? I just tried with latest develop (in the python REPL) and it works for me...

julianoes commented 2 years ago

@JonasVautherin huh? But where is the result stored/returned?

JonasVautherin commented 2 years ago

Well it's lost if you don't save it :slightly_smiling_face:. Just like a normal function. Say in C++:

action->get_takeoff_altitude();

vs

auto takeoff_altitude = action->get_takeoff_altitude();

It's just that in python, drone.mission.download_mission() returns a coroutine, but does not run it. In order to run that coroutine, you need to await it. So you could do something like this if you wanted to:

my_coroutine = drone.mission.download_mission()
downloaded_mission = await my_coroutine

Look at this, in the apython REPL (from package aioconsole):

>>> async def foo():
...     print("hello")
... 
>>> foo()
<coroutine object __corofn.<locals>.foo at 0x7fa61d7fc240>
>>> await foo()
hello

foo() returns a coroutine, await foo() executes it.

Finally, if the coroutine returns something:

>>> async def get_foo():
...     return 2
... 
>>> await get_foo()
2
>>> result = await get_foo()
>>> print(result)
2

Back to the error that says "test_mission.py:17: RuntimeWarning: coroutine 'Mission.download_mission' was never awaited drone.mission.download_mission()", it means that it tries to garbage collect a coroutine object that was never awaited. It's a bit like a "warning: unused". But that's really weird here because line 17 has the await...

JonasVautherin commented 2 years ago

Here are two examples to illustrate it.

This one works and waits for 2 seconds before terminating:

#!/usr/bin/env python3

import asyncio
from mavsdk import System

async def foo():
    await asyncio.sleep(2)

async def run():
    await foo()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())

This one says: "example.py:11: RuntimeWarning: coroutine 'foo' was never awaited"

#!/usr/bin/env python3

import asyncio
from mavsdk import System

async def foo():
    await asyncio.sleep(2)

async def run():
    foo()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())
julianoes commented 2 years ago

Ok, I was confused because you seemed to say that my "solution":

from:

await drone.mission.download_mission()

to:

result = await drone.mission.download_mission()

was not correct but it seems to actually fix the problem here, doesn't it?

So await doesn't run the coroutine unless you also get the result? It's a bit odd, right?

JonasVautherin commented 2 years ago

it seems to actually fix the problem here, doesn't it?

Does it? I would not understand why, it would be very weird to me :sweat_smile:. Is that fixing the problem?

await executes the coroutine, the result is completely independent. Just like in C++ you can call mission->download_mission(); and not take the result if you are not interested in it, in python you can run await drone.mission.download_mission(). It could be that you only want the side effects of the function.

But calling drone.mission.download_mission() (without awaiting it) never, ever makes sense in a program. So the interpreter warns you about it.

Or did I miss something? :smile:

julianoes commented 2 years ago

That would make sense yes.

I'll try it out tomorrow.