In this post I will explore the model, the API and the snippets of code needed to command, control and monitor a vehicle running Ardupilot with DroneKit-Python.
The DroneKit project and DroneKit-Python
DroneKit is (was?) an open development kit. It offers a SDK and web API to implement applications for drones. It also contains an integrated flight simulator to test your code, documentation and some examples to illustrate the API. DroneKit was announced by 3DR in 2015.
DroneKit was designed to support commercial cases in one broad ecosystem. This ecosystem considers applications on the air and ground domains together with cloud computing technology.
In our case, we are interested in the DroneKit-Python component and onboard computing support. This support will be useful to communicate directly with the flight controller from a companion computer. It will provide the path planning, autonomous flight and live telemetry interfaces over the flight code per vehicle.
As you might imagine, DroneKit-Python is useful to avoid the overhead with the MAVLink protocol (parsing, encoding/decoding, marshalling, etc) with different vehicles/configurations. The library provides an extensible and unified API to work with all these vehicles.
In detail, we will be interested on the following features and support:
- Vehicle state and settings
- Monitor and react to state change
- Vehicle guide and control (GUIDED mode)
- Missions and waypoints (AUTO mode)
The DroneKit-Python API
The DroneKit-Python programming model and API is very simple and direct. Its major responsibility is handling the MAVLink messages coming in quickly and tracking state changes in the vehicle under the hood.
The main abstraction is the Vehicle class. It exposes most state information through attributes. The vehicle parameters and settings are available through named elements in Vehicle.parameters
The way to monitor and react on state changes is through the well-known observer pattern.
So the application code will be able to observe any of the vehicle attributes and monitor for changes without the need for polling.
The observer callback functions (listeners) are registered/unregistered with the Vehicle.add_attribute_listener() or the Vehicle.on_attribute() decorator method.
Related to fligh modes (GUIDED and AUTO), you can configure it through VehicleMode(). Some code...
from dronekit import connect, VehicleMode # connect to the Vehicle vehicle = connect(...) # set the vehicle into guided mode vehicle.mode = VehicleMode("GUIDED") ... # set the vehicle into auto mode vehicle.mode = VehicleMode("AUTO")
In GUIDED mode, the vehicle movement can be controlled either by explicitly setting a target position:
# set the target location in global-relative frame a_location = LocationGlobalRelative(-34.364114, 149.166022, 30) vehicle.simple_goto(a_location)
or by specifying the vehicle's velocity components to guide it in a preferred direction:
# set up velocity mappings # velocity_x > 0 => fly North # velocity_x < 0 => fly South # velocity_y > 0 => fly East # velocity_y < 0 => fly West # velocity_z < 0 => ascend # velocity_z > 0 => descend SOUTH = -2 UP = -0.5 #NOTE: up is negative! # fly south and up send_ned_velocity(SOUTH,0,UP,DURATION)
The vehicle's latitude and longitude are relative to the WGS84 coordinate system. The altitude is relative to mean sea-level (MSL) in the case of LocationGlobal(). In the case of LocationGlobalRelative() the altitude is relative to the home position.
Finally we are interested in the API support for autonomous missions (AUTO mode). The API provides the required abstractions to run pre-defined waypoint missions.
Under the hood the DroneKitPython's mission commands correspond with the navigation commands (MAV_CMD_NAV_*), 'do' commands (MAV_CMD_DO_*) and condition commands (MAV_CMD_CONDITION_*) in the MAVLink protocol.
The main abstraction with autonomous missions is the CommandSequence class. It encapsulates a sequence of vehicle waypoints, a "mission". The current mission/commands for a vehicle are accessed using the Vehicle.commands attribute.
With this abstraction the application is able to 'load' and 'download' the current mission in order to download/upload the changes to the vehicle. It is also possible 'clear' the mission, 'add/modify' mission commands, etc. Some code...
# connect to the vehicle vehicle = connect(...) # get the set of commands from the vehicle cmds = vehicle.commands cmds.download() cmds.wait_ready() lat = -34.364114, lon = 149.166022 altitude = 30.0 # add command cmd = Command(..., mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, ..., lat, lon, altitude) cmds.add(cmd) # send commands cmds.upload()
Some useful DroneKit-Python examples
Among the DroneKit-Python examples available here I would highlight those four snippets of code:
- Mission Import/Export. This example contains a pointer to the the Waypoint file format. Although not part of MAVLink, this format is used by the usual ground control stations.
- Drone Delivery. This example is a CherryPy based web application that displays a map to let you view the current vehicle position and send the vehicle commands to fly to a particular latitude and longitude. It shows basic integration with a web server.
- Flight Replay. This example loads a telemetry log and use position data to generate waypoints for a vehicle. The approach behind of this example could be interesting to 'replay' flight crashes or adding a 'backward/forward' feature in vehicles simulations based on past flights.
- Ultra minimal ground control station. This example implements a minimal GCS. It uses Tkinter, a Python's de-facto standard GUI on top of Tcl/Tk.
Geocoding with Geopy
Right now, and with the previous content in mind, it is possible flying the vehicle, although any code will always need the target relative/absolute position.
It is useful hardcoding or asking the position in some use cases although it would be great automating the way to locate the coordinates via geocoding web services. This kind of automating is possible via Geopy, a Python client for several popular geocoding web services.
Geopy includes various geocoder classes. They are available here.
The usual use cases with Geopy are geolocating a query to an address and coordinates:
>>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim() >>> location = geolocator.geocode("Maria Pita Square") >>> print(location.address) María Pita, Praza de María Pita, Cidade Vella, A Coruña, Galicia, 15001, España >>> print((location.latitude, location.longitude)) (43.3706752, -8.3959388)
and finding the address corresponding to a set of coordinates:
>>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim() >>> location = geolocator.reverse("43.38593855, -8.40656065") >>> print(location.address) Torre de Hércules, Paseo Marítimo, As Lagoas, A Coruña, Galicia, 15003, España >>> print((location.latitude, location.longitude)) (43.38593855, -8.40656065)
Geopy can also calculate the geodesic distance between two points using the Vincenty distance or great-circle distance formulas, with a default of Vincenty available as the class geopy.distance.distance, and the computed distance available as attributes (e.g., miles, meters, etc)
>>> from geopy.distance import vincenty, great_circle >>> a_corunha_spain = (43.3002595, -8.37960854218971) >>> melbourne_australia = (-37.66722, 144.842671663258) >>> print(vincenty(a_corunha_spain, melbourne_australia).kilometers) 17677.8310831 >>> print(great_circle(a_corunha_spain, melbourne_australia).kilometers) 17681.6340284
This type of measurements are the right way to calculate distances in waypoints tracking, mission path planning, power optimization algorithms, etc. when long distances are involved.
As expected, DroneKit-Python and Pymavlink codebases are Open Source libraries exposing primitives and abstractions (vehicle state and settings, state tracking, C&C, missions and waypoints, etc) over MAVLink.
These libraries are useful to make available in the companion computer a well-tested and supported subset of 'flight primitives'. With this support onboard developers will be able to script the vehicle behaviour on real-time or develop low-coupled applications easily.
On the other side the Geopy library may be used to include geocoding features in the solution. Geopy supports geocoding web services and geodesic distance calculations (Vincenty distance and great-circle formulas) between two points.
Those three projects provide simple and good abstractions to pilot UAV in absolute or relative reference frames.