User guide

This guide will walk you through Olympe API using a series of examples that increasingly demonstrate more advanced usage.

If you haven’t followed Olympe installation procedure, you should do it now.

For your own safety and the safety of others, the following examples will use a simuated ANAFI drone but remember that you can also connect to a physical drone.

At the end of each example, remember to reset the simulation before getting into the next example because each example assume that the drone is landed with a fully charged battery. Just hit Ctrl+R inside the Sphinx GUI to reset the simulation.

Create a simulated drone

First things first, you need a drone to connect to. For this example we will use (sphinx) to create a simulated drone and then connect to it using Olympe before sending our first commands.

If you haven’t installed (sphinx) yet, now is a good time to install it.

$ sudo systemctl start firmwared
$ sphinx /opt/parrot-sphinx/usr/share/sphinx/drones/anafi4k.drone::stolen_interface=::simple_front_cam=true

The above commands start a simulation of an ANAFI drone with a simplified front camera and without a wifi interface. In the following examples, we will be using the virtual ethernet interface, and reach for the simulated drone at “10.202.0.1”.

Setup your shell environment

Don’t forget to set up your Python environment using the shell.

$ source ~/code/parrot-groundsdk/./products/olympe/linux/env/shell
(olympe-python3) $

ARSDK messages explained

At its core, Olympe basically just send and receive ARSDK messages to control a drone. For example, the following sequence diagram shows what is happening when an Olympe scripts sends a TakeOff() command to a drone.

blockdiag Olympe Drone TakeOff() FlyingStateChanged(state='motor_ramping') FlyingStateChanged(state='takingoff')

Take off command sequence

When Olympe sends a command message like the TakeOff() above, it then waits for a response from the drone, the FlyingStateChanged(state="takingoff") event message in this case.

Sometimes, the drone can also spontaneously notify its controller (Olympe) of a particular event. Olympe provides a way to monitor such events (or a combination of such events). The following sequence diagram illustrates this scenario with a GPSFixStateChanged(0) that informs Olympe that the GPS fix has been lost.

blockdiag Olympe Drone GPSFixStateChanged(0)

Losing the GPS fix

As a user of Olympe, you might also be punctually interested in the current state of the drone without monitoring every received message from the drone. To do this, Olympe just remembers the last received event that provides this state information and expose this information through the olympe.Drone.get_state() method.

blockdiag Olympe Drone GPSFixStateChanged(0) get_state

Getting current GPS fix status

As demonstrated in the following usage examples, Olympe provides a relatively simple API to perform the above actions (and much more) using the following olympe.Drone class methods:

ARSDK message Python types are available in the olympe.messages.<feature_name>[.<class_name>] modules. Likewise, ARSDK enum Python types are available in the olympe.enums.<feature_name>[.<class_name>] modules.

See the Messages Reference Documentation for more information.

Olympe basics

Taking off - “Hello world” example

The first thing you might want to do with Olympe is making your drone to take off. In this example we’ll write a simple python script that will connect to the simulated drone we’ve just created and then send it a TakeOff() command.

Create the following python takeoff.py script somewhere in your home directory:

1
2
3
4
5
6
7
8
9
# -*- coding: UTF-8 -*-

import olympe
from olympe.messages.ardrone3.Piloting import TakeOff

drone = olympe.Drone("10.202.0.1")
drone.connection()
drone(TakeOff()).wait()
drone.disconnection()

First, this script imports the olympe module and then the TakeOff() command message from the arsdk Ardrone3 features. A “feature” is just a collection of related command and event messages that the drone exchanges with the controller (FreeFlight, Skycontroller, Olympe, …).

Next, this script creates the drone interface object with the olympe.Drone class. For anafi this class constructor requires only one argument: the drone IP address. For a simulated drone, we can use “10.202.0.1” which is the default drone IP address over the virtual Ethernet interface.

olympe.Drone.connection() actually performs the connection to the drone. This would fail if the drone is unreachable (or non-existent) for some reason.

Then, drone(TakeOff()).wait() sends the TakeOff() command to the drone and then waits for the drone to acknowledge the command. When the wait() function returns, our simulated drone should be taking off. For now, we will always use the drone(....).wait() construct to send command message and will explain later what the wait() function does and what we could do differently with or without it.

Finally, olympe.Drone.disconnection() disconnect Olympe from the drone properly.

To execute this script and see your drone taking off, from the same shell/terminal you’ve just source’d the shell script:

(olympe-python3) $ python ./takeoff.py

Getting the current value of a drone state or setting

In this example, we will be using the get_state() method to query the current “maximum tilt” setting value.

When the maximum tilt drone setting is changed by the controller (Olympe) with the MaxTilt() command message the drone sends the MaxTiltChanged() event message in response. Changing a drone setting will be demonstrated in the following example. Here, we’re just interested in getting the current drone setting.

When Olympe connects to a drone it also asks the drone to send back all its event messages in order to initialize Olympe drone state informations as returned by the olympe.Drone.get_state() method. So if Olympe is connected to a drone olympe.Drone.get_state() always returns the current drone state associated to an event message.

In this case, we will be passing the MaxTiltChanged() message to the olympe.Drone.get_state() method. This will return a dictionary of the MaxTiltChanged() event message which provide the following parameters:

MaxTiltChanged Parameters:
 
  • current (float) – Current max tilt
  • min (float) – Range min of tilt
  • max (float) – Range max of tilt

Note: Don’t be confused here, the “min” and “max” parameters are actually the minimum and the maximum values for the “maximum tilt” setting. Here, we are only interested in the “current” value of this setting.

Let’s practice! First, reset the simulation (Ctrl+R inside the Sphinx GUI).

Create the following python maxtiltget.py script somewhere in your home directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# -*- coding: UTF-8 -*-

from __future__ import print_function  # python2/3 compatibility for the print function
import olympe
from olympe.messages.ardrone3.PilotingSettingsState import MaxTiltChanged

drone = olympe.Drone("10.202.0.1")
drone.connection()
print("Drone MaxTilt = ", drone.get_state(MaxTiltChanged)["current"])
drone.disconnection()

To execute this script and see your drone taking off, from the same shell/terminal you’ve just source’d the shell script:

(olympe-python3) $ python ./maxtiltget.py

This should print the current maximum tilt value in your console. The following sequence diagram illustrate what is happening in this simple example.

blockdiag Olympe Drone also send all event messages get_state(MaxTiltChanged) connect connected disconnect disconnected

Getting the drone MaxTilt

Changing a drone setting - Understand the “expectation” mechanism

In this example, we will change the “maximum tilt” drone setting. This setting indirectly controls the maximum drone acceleration and speed. The more the drone can tilt, the more the drone gain speed.

The maximum tilt setting itself must be within a minimum and a maximum value. A drone with a max tilt value of 0° is not particularly useful while a maximum tilt of 180° might only be useful for a racer drone. For ANAFI the maximum tilt setting must be within 5° and 40°.

You might be wondering:

  • What is happening when you send an invalid setting value (ex: 180°)?
  • How does the drone respond to that?
  • How do we catch this kind of error with Olympe?

Let’s see how it’s done in the following example. Some important explanations will follow.

First, reset the simulation (Ctrl+R inside the Sphinx GUI).

Create the following python maxtilt.py script somewhere in your home directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# -*- coding: UTF-8 -*-

from __future__ import print_function  # python2/3 compatibility for the print function
import olympe
from olympe.messages.ardrone3.PilotingSettings import MaxTilt

drone = olympe.Drone("10.202.0.1")
drone.connection()
maxTiltAction = drone(MaxTilt(10)).wait()
if maxTiltAction.success():
    print("MaxTilt(10) success")
elif maxTiltAction.timedout():
    print("MaxTilt(10) timedout")
else:
    # If ".wait()" is called on the ``maxTiltAction`` this shouldn't happen
    print("MaxTilt(10) is still in progress")
maxTiltAction = drone(MaxTilt(1)).wait()
if maxTiltAction.success():
    print("MaxTilt(1) success")
elif maxTiltAction.timedout():
    print("MaxTilt(1) timedout")
else:
    # If ".wait()" is called on the ``maxTiltAction`` this shouldn't happen
    print("MaxTilt(1) is still in progress")
drone.disconnection()

This time, the script starts by importing the MaxTilt() command from the ardrone3 feature. Then, it connects to the drone and sends two MaxTilt commands. The first one with a 10° tilt value, the second with a 1° tilt value.

Note that this time, we are assigning into the maxTiltAction variable the object returned by the .wait() method. For now, all you have to know is that you can call .success() on an action object if you want to know if your command succeeded or not. The success() function just returns True in case of success and False otherwise. You can also call .timedout() on an action to know if the your command message timed out. This .timedout() method is not particularly useful in this example because we always call .wait() on the action object so the action is either successful or has timed out.

To execute this script, from the same shell/terminal you have source’d the shell script in:

(olympe-python3) $ python ./maxtilt.py

If all goes well, you should see the following output in your console:

MaxTilt(10) success
MaxTilt(1) timedout

Obviously, the 10° maximum tilt value is correct so the first command succeeded while the second command failed to set an incorrect 1° maximum tilt value.

It is important to understand how Olympe knows if a particular command succeeded or not. When olympe sends a command message, it usually implicitly expects an event message in return.

Up until now, we have only explicitly used command messages. Command messages and event messages are somewhat similar. They are both associated with an internal unique ID and eventually with some arguments (ex: the maximum tilt value) and they both travel from one source to a destination.

A command message travel from the controller (Olympe) to the drone while an event message travel the other way around.

Here, when Olympe sends the MaxTilt(10) command message it implicitly expects a MaxTiltChanged(10) event message in return. If the event is received in time, everything is fine: maxTiltAction.success() is True and maxTiltAction.timedout() is False. Otherwise, the maxTiltAction times out (maxTiltAction.success() is False and maxTiltAction.timedout() is True).

The following sequence diagram illustrates what is happening here. For the second maximum tilt command, when Olympe sends the MaxTilt(1) command message it receives a MaxTiltChanged(5) event message because 1° is an invalid setting value so the drone just informs the controller that it has set the minimum setting value instead (5°). Olympe does not assume that this response means “No, I won’t do what you are asking”. Instead, it still waits for a MaxTiltChanged(1) event that will never come and the command message times out: (maxTiltAction.success() is False and maxTiltAction.timedout() is True). This behavior is identical for every command message: when Olympe sends a command message to a drone, it either result in a success or a timeout.

blockdiag Olympe Drone maxTiltAction pending maxTiltAction successful maxTiltAction pending maxTiltAction still pending maxTiltAction timedout connect connected MaxTilt(10) MaxTiltChanged(current=10., min=5., max=40.) MaxTilt(1) MaxTiltChanged(current=5., min=5., max=40.) disconnect disconnected

Setting the drone MaxTilt

The arsdk protocol defined in arsdk-xml does not provide a way to report errors uniformly. This is why Olympe cannot detect errors like this one and just time out instead. Olympe associates to each command a default timeout that can be overridden with the _timeout message parameter. For example:

maxTiltAction = drone(MaxTilt(10, _timeout=1)).wait()

Moving around - Waiting for a ‘hovering’ flying state

In this example, we will move our drone around using the moveBy() command.

First, reset the simulation (Ctrl+R inside the Sphinx GUI).

Create the following python moveby.py script somewhere in your home directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# -*- coding: UTF-8 -*-

import olympe
from olympe.messages.ardrone3.Piloting import TakeOff, moveBy, Landing

drone = olympe.Drone("10.202.0.1")
drone.connection()
drone(TakeOff()).wait()
drone(moveBy(10, 0, 0, 0)).wait()
drone(Landing()).wait()
drone.disconnection()

First, this script imports the olympe module and then the TakeOff(), moveBy() and Landing() command messages from the ardrone3 feature. It then connects to the drone and send the TakeOff(), moveBy() and Landing() commands.

This script should work as-is, right? Let’s see.

To execute this script, from the same shell/terminal you’ve source’d the shell script:

(olympe-python3) $ python ./moveby.py

The output of this script should be:

moveBy timedout

Wait! The drone takes off and then eventually lands without performing the moveBy?! What happened?

When olympe sends a command message to the drone it expects an acknowledgement event message from the drone in return. In this script, drone(TakeOff()).wait() sends the TakeOff() command to the drone and then waits for the drone taking off event message as an acknowledgment from the drone. Olympe knows that after a TakeOff() command it should expect a FlyingStateChanged(state='takingoff') and automatically waits for that event for you.

The problem with the moveBy() command is that it is rejected by the drone as long as the drone is not in the “hovering” flying state. In this case it is rejected because after the TakeOff() command the drone is in takingoff flying state. So, to correct this script we will have to wait for the hovering state before sending the moveBy() command. The following sequence diagram illustrates what is happening with this first attempt to use the moveBy() command.

blockdiag Olympe Drone takeOffAction pending takeOffAction successful takeOffAction successful moveByAction pending silently rejected, moveBy is unavailable moveBy available moveByAction timedout connect connected TakeOff() FlyingStateChanged(state='motor_ramping') FlyingStateChanged(state='takingoff') moveBy() FlyingStateChanged(state='hovering') disconnect disconnected

Attempt to use the moveBy command

Edit moveby.py with the following corrected script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# -*- coding: UTF-8 -*-

import olympe
from olympe.messages.ardrone3.Piloting import TakeOff, moveBy, Landing
from olympe.messages.ardrone3.PilotingState import FlyingStateChanged

drone = olympe.Drone("10.202.0.1")
drone.connection()
drone(
    TakeOff()
    >> FlyingStateChanged(state="hovering", _timeout=5)
).wait()
drone(
    moveBy(10, 0, 0, 0)
    >> FlyingStateChanged(state="hovering", _timeout=5)
).wait()
drone(Landing()).wait()
drone.disconnection()

This new script will wait for the hovering state after each command sent to the drone. To do that, it imports the FlyingStateChanged() event message from the same ardrone3 feature.

Note: The expectations for each command messages are defined in arsdk-xml along with the command and event messages themselves.

1
2
3
4
drone(
    TakeOff()
    >> FlyingStateChanged(state="hovering", _timeout=5)
).wait()

In this new example after the drone connection, the above code tells Olympe to:

  1. Send the TakeOff() command
  2. Then, implicitly wait for the expectations of the TakeOff() command: FlyingStateChanged(state='takingoff')
  3. Then, explicitly wait for the drone hovering flying state event: FlyingStateChanged(state='hovering')

Here, the drone() functor accepts more than just a command message. The drone() takes an expression that may be a combination of command and event messages to process. The “>>” operator is used to combine two expressions with an “and then” semantic. This example could be read as “Take off and then wait a maximum of 5 seconds for the “hovering” flying state”).

The rest of the example should be easy to understand now. After the drone has taken off, this script waits for the drone “hovering” state and then sends the moveBy command, waits for the “hovering” state again and then sends the landing command. The following sequence diagram illustrates this second (successful) attempt to use the moveBy() command.

blockdiag Olympe Drone takeOffAction pending takeOffAction pending takeOffAction successful waiting for FlyingStateChanged(state='hovering') moveBy available moveByAction pending moveByAction successful connect connected TakeOff() FlyingStateChanged(state='motor_ramping') FlyingStateChanged(state='takingoff') FlyingStateChanged(state='hovering') moveBy() moveByEnd() disconnect disconnected

Using the moveBy command

Let’s check everything works! Reset the simulation (Ctrl+R inside the Sphinx GUI) and execute this script, from the same shell/terminal you have source’d the shell script:

(olympe-python3) $ python ./moveby.py

And it should work now! The drone should take off, perform a forward move by 10 meters and then land.

Explore available commands

If you followed this guide so far, you might want to explore the Messages Reference Documentation. If you are looking for a specific message or feature, you can also use the search box within the sidebar on the left.

Alternatively, you can also use Olympe in an interactive Python console with ipython and leverage the autocompletion and the help functions to browse the ARSDK messages.

(olympe-python3) $ ipython
In [1]: import olympe
In [2]: from olympe.messages.<TAB>
olympe.messages.animation       olympe.messages.camera          olympe.messages.debug           olympe.messages.generic         olympe.messages.mapper          olympe.messages.precise_home    olympe.messages.thermal
olympe.messages.ardrone3        olympe.messages.common          olympe.messages.drone_manager   olympe.messages.gimbal          olympe.messages.mediastore      olympe.messages.rth             olympe.messages.user_storage
olympe.messages.battery         olympe.messages.controller_info olympe.messages.follow_me       olympe.messages.leds            olympe.messages.powerup         olympe.messages.skyctrl         olympe.messages.wifi

In [3]: from olympe.messages.ardrone3.Piloting import <TAB>
AutoTakeOffMode  CancelMoveTo     Emergency        Landing          PCMD             StopPilotedPOI   UserTakeOff      moveTo
CancelMoveBy     Circle           FlatTrim         NavigateHome     StartPilotedPOI  TakeOff          moveBy

In [4]: from olympe.messages.ardrone3.Piloting import TakeOff
In [5]: help(TakeOff)
Help on ardrone3.Piloting.TakeOff object:

class ardrone3.Piloting.TakeOff(ArsdkMessage)
 |  Ardrone3.Piloting.TakeOff
 |
 |
 |
 |  Ask the drone to take off. On the fixed wings (such as Disco): not
 |  used except to cancel a land.
 |
 |  **Supported by**:
 |
 |      :Bebop: since an unspecified version
 |      :Bebop 2: since an unspecified version
 |      :Disco: since an unspecified version
 |
 |
 |
 |  Result: On the quadcopters: the drone takes off if its :py:func:`~olympe.messages.ardrone3.PilotingState.FlyingStateChanged`
 |  was landed. On the fixed wings, the landing process is aborted if the
 |  :py:func:`~olympe.messages.ardrone3.PilotingState.FlyingStateChanged` was landing. Then, event :py:func:`~olympe.messages.ardrone3.PilotingState.FlyingStateChanged`
 |  is triggered.

You should now understand the basics of Olympe and should be able to write your own scripts. The rest of this guide will walk you through the most advanced (nevertheless important) features of Olympe.

Advanced usage examples

Using Olympe asynchronously

In the basic examples above we’ve always performed actions synchronously because we were always immediately waiting for an action to complete with the .wait() method.

In this example, we will send some flying commands to the drone asynchronously. While the drone executes those commands, we will start a video recording and change the gimbal velocity target along the pitch axis. After sending those camera-related commands, we will call the .wait() method on the “flying action” and then stop the recording.

Create the following python asyncaction.py script somewhere in your home directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# -*- coding: UTF-8 -*-

import olympe
from olympe.messages.ardrone3.Piloting import TakeOff, moveBy, Landing
from olympe.messages.ardrone3.PilotingState import FlyingStateChanged
from olympe.messages.camera import start_recording, stop_recording
from olympe.messages import gimbal

with olympe.Drone("10.202.0.1") as drone:
    drone.connection()

    # Start a flying action asynchronously
    flyingAction = drone(
        TakeOff()
        >> FlyingStateChanged(state="hovering", _timeout=5)
        >> moveBy(10, 0, 0, 0)
        >> FlyingStateChanged(state="hovering", _timeout=5)
        >> Landing()
    )

    # Start video recording while the drone is flying
    if not drone(start_recording(cam_id=0)).wait().success():
        raise RuntimeError("Cannot start video recording")

    # Send a gimbal pitch velocity target while the drone is flying
    cameraAction = drone(gimbal.set_target(
        gimbal_id=0,
        control_mode="velocity",
        yaw_frame_of_reference="none",
        yaw=0.0,
        pitch_frame_of_reference="none",
        pitch=0.1,
        roll_frame_of_reference="none",
        roll=0.0,
    )).wait()

    if not cameraAction.success():
        raise RuntimeError("Cannot set gimbal velocity target")

    # Wait for the end of the flying action
    if not flyingAction.wait().success():
        raise RuntimeError("Cannot complete the flying action")

    # Stop video recording while the drone is flying
    if not drone(stop_recording(cam_id=0)).wait().success():
        raise RuntimeError("Cannot stop video recording")

    # Leaving the with statement scope: implicit drone.disconnection()

Reset the simulation (Ctrl+R inside the Sphinx GUI) and execute this script, from the same shell/terminal you have source’d the shell script:

In this example, the olympe.Drone.__call__() functor process commands and events asynchronously so that multiple commands can be sent to the drone and processed concurrently. The events associated to asynchronous actions are interleaved in an undefined order. The following sequence diagram illustrates a possible sequence of event for this script.

blockdiag Olympe Drone flyingAction pending start record pending start record successful flyingAction pending cameraAction successful flyingAction pending waiting for FlyingStateChanged(state='hovering') flyingAction pending flyingAction pending flyingAction pending flyingAction pending waiting for FlyingStateChanged(state='hovering') flyingAction pending flyingAction pending flyingAction pending flyingAction successful stop record pending stop record successful connect connected TakeOff() VideoV2(record='start') VideoStateChangedV2(state='started') FlyingStateChanged(state='motor_ramping') set_target(control_mode='velocity', ...) FlyingStateChanged(state='takingoff') FlyingStateChanged(state='hovering') moveBy() FlyingStateChanged(state='flying') moveByEnd() FlyingStateChanged(state='hovering') Landing() FlyingStateChanged(state='landing') FlyingStateChanged(state='landed') VideoV2(record='stop') VideoStateChangedV2(state='stopped') disconnect disconnected

Asynchronous command examples

Using Olympe enums types

In addition to the ARSDK messages, Olympe also provides Python Enum and Bitfield types in the olympe.enums.<feature_name>[.<class_name>] modules.

Most of the time, you shouldn’t really need to import an enum type into your script because enum types are implicitly constructed from a string when you create a message object so the following examples are roughly equivalent:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# -*- coding: UTF-8 -*-

import olympe
from olympe.messages.ardrone3.PilotingState import FlyingStateChanged
from olympe.enums.ardrone3.PilotingState import FlyingStateChanged_State as FlyingState

drone = olympe.Drone("10.202.0.1", loglevel=2)
drone.connection()

drone(FlyingStateChanged(state="hovering")).wait(5)
# is equivalent to
drone(FlyingStateChanged(state=FlyingState.hovering)).wait(5)

Olympe enum types should behave very much like Python 3 enum.Enum.

Bitfields types are associated to each ARSDK enums types and are occasionally used by ARSDK messages. A Bitfield type can hold any combination of its associated Enum type values.

Bitfield example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# -*- coding: UTF-8 -*-

import olympe
from olympe.messages.ardrone3.PilotingState import FlyingStateChanged
from olympe.enums.ardrone3.PilotingState import FlyingStateChanged_State as FlyingState

drone = olympe.Drone("10.202.0.1", loglevel=2)
drone.connection()

flying_states = FlyingState._bitfield_type_("takingoff|hovering|flying")

if drone.get_state(FlyingStateChanged)["state"] in flying_states:
    print("The drone is in flight")
else:
    print("The drone is not in flight")

Additional usage examples are available in the unit tests of olympe.arsdkng.enums.

Using Olympe exptectation expressions

Sometimes it can be useful to send a command to the drone only if it is in a specific state. For example, if the drone is already hovering when you start an Olympe script, you might want to skip the usual taking off command. This can be useful if a previous execution of your script left your drone in a hovering state.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# -*- coding: UTF-8 -*-

import olympe
from olympe.messages.ardrone3.Piloting import TakeOff
from olympe.messages.ardrone3.PilotingState import FlyingStateChanged
from olympe.enums.ardrone3.PilotingState import FlyingStateChanged_State
from olympe.messages.ardrone3.GPSSettingsState import GPSFixStateChanged

with olympe.Drone("10.202.0.1") as drone:
    drone.connection()
    print("Takeoff if necessary...")
    if (drone.get_state(FlyingStateChanged)["state"] is not
            FlyingStateChanged_State.hovering):
        drone(GPSFixStateChanged(fixed=1, _timeout=10, _policy="check_wait")).wait()
        drone(
            TakeOff(_no_expect=True)
            & FlyingStateChanged(state="hovering", _policy="wait", _timeout=5)
        ).wait()

Here olympe.Drone.get_state() is used to check the current flying state of the drone. If the drone is not in hovering, we check and eventually wait for a GPS fix. Note that “check_wait” is the default value for the _policy parameter. The possible values for the _policy parameter are:

  • “check”, to check the current state of the drone (i.e. match the last event message of this kind received from the drone).
  • “wait”, to wait for a new event message from the drone (even if the last event message of this kind that has been received would have matched).
  • “check_wait” (the default), to “check” the current state of the drone and if neccessary “wait” for a matching event message.

In the above example we are using a compound expectation expression to send a taking off command when the drone has a GPS fix and when it is not already in the hovering state.

The default expectations for the TakeOff command are: FlyingStateChanged(state='motor_ramping', _policy='wait') & FlyingStateChanged(state='takingoff', _policy='wait') (see the TakeOff() command documentation). When the controller receives the “takingoff” flying state a few milliseconds after the TakeOff command has been sent, the drone has just climbed a few centimeters. Here, we don’t really care for this “takingoff” flying state and this is why we are disabling the default expectations of the TakeOff command. TakeOff(_no_expect=True) sends the take off command and does not wait for the default expectations for this command. Instead of the default expectations, we are directly expecting the “hovering” flying state. We are using the ‘&’ (“AND”) operator instead of ‘>>’ (“THEN”) to wait for this event while Olympe sends the TakeOff command concurrently. If the “>>” (“THEN”) operator were to be used instead, we might (theoretically) miss the FlyingStateChanged event drone response while Olympe sends the ‘TakeOff’ message.

As demonstrated bellow, this problem can also be solved without using any control flow statements:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# -*- coding: UTF-8 -*-

import olympe
from olympe.messages.ardrone3.Piloting import TakeOff
from olympe.messages.ardrone3.PilotingState import FlyingStateChanged
from olympe.messages.ardrone3.GPSSettingsState import GPSFixStateChanged

with olympe.Drone("10.202.0.1") as drone:
    drone.connection()
    print("Takeoff if necessary...")
    drone(
        FlyingStateChanged(state="hovering", _policy="check")
        | (
            GPSFixStateChanged(fixed=1, _timeout=10)
            >> (
                TakeOff(_no_expect=True)
                & FlyingStateChanged(state="hovering", _policy="wait", _timeout=5)
            )
        )
    ).wait()

Here, the ‘|’ (“OR”) operator is used to “check” if the current flying state is “hovering”. If not, we wait for a GPS fix if necessary with the implicit “check_wait” policy. Then (“>>”) we send the taking off command and override the default expectations to wait for the “hovering” flying state as before.

Capture the video streaming and its metadata

Once you are connected to a drone with Olympe, to start the video streaming just call the olympe.Drone.start_video_streaming() function and the drone will start sending its video stream to Olympe. Call olympe.Drone.stop_video_streaming() the video streaming.

Realtime capture

Before you start the video streaming, you can register some callback functions that will be called whenever Olympe receive/decode a new video frame. See set_streaming_callbacks().

Record the video stream for a post-processing

Before you start the video streaming, you can specify some output files that will be used by Olympe to record the video stream and its metadata. set_streaming_output_files().

Connect to a physical drone or to a SkyController

Warning

DISCLAIMER You should really carefully validate your code before trying to control a physical drone through Olympe. Use at your own risk.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT COMPANY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Connect to a physical drone

to connect olympe to a physical drone, you first need to connect to your linux box to a drone wifi access point. once you are connected to your drone over wifi, you just need to specify the drone ip address on its WiFi interface (“192.168.42.1”).

# -*- coding: UTF-8 -*-

import olympe

drone = olympe.Drone("192.168.42.1")
drone.connection()

Connect to a SkyController

To connect Olympe to a physical SkyController, you first need to connect to your Linux box to the SkyController 3 USB-C port. The you should be able to connect to your SkyController with its RNDIS IP address (“192.168.53.1”).

# -*- coding: UTF-8 -*-

import olympe
from olympe.messages.skyctrl.CoPiloting import setPilotingSource

drone = olympe.Drone("192.168.53.1", mpp=True)
drone.connection()
drone(setPilotingSource(source="Controller")).wait()

TODO

Todo

Document the expectation “explain” method. Maybe insert it into a “How to debug” section.

Todo

How to connect to a physical drone with or without a SkyController.