├── docs ├── _static │ └── .gitignore ├── _templates │ └── .gitignore ├── .gitignore ├── guide │ ├── win-screenshot.png │ ├── index.rst │ ├── debugging.rst │ ├── mavlink_messages.rst │ ├── quick_start.rst │ ├── connecting_vehicle.rst │ └── taking_off.rst ├── examples │ ├── drone-delivery-splash.png │ ├── drone-delivery-track.png │ ├── flight_replay_example.png │ ├── drone-delivery-command.png │ ├── GuidedModeExample_FlyByPosition.png │ ├── GuidedModeExample_FlyByVelocity.png │ ├── simple_goto_example_copter_path.png │ ├── mission_basic_example_copter_path.png │ ├── index.rst │ ├── running_examples.rst │ ├── mission_import_export.rst │ ├── drone_delivery.rst │ ├── simple_goto.rst │ ├── follow_me.rst │ ├── mission_basic.rst │ └── channel_overrides.rst ├── develop │ ├── MissionPlanner_ConnectPort.png │ ├── coding_standards.rst │ ├── index.rst │ ├── companion-computers.rst │ └── installation.rst ├── automodule.rst ├── about │ ├── license.rst │ ├── index.rst │ ├── release_notes.rst │ └── overview.rst ├── contributing │ ├── index.rst │ ├── getting_started.rst │ ├── developer_setup_linux.rst │ ├── developer_setup_windows.rst │ └── contributions_documentation.rst ├── index.rst └── Makefile ├── dronekit ├── test │ ├── unit │ │ ├── __init__.py │ │ └── test_api.py │ ├── README.md │ ├── sitl │ │ ├── test_mode_settings.py │ │ ├── test_state.py │ │ ├── test_earlyattrs.py │ │ ├── test_modeavailable.py │ │ ├── test_vehicleclass.py │ │ ├── test_mavlink.py │ │ ├── test_simpledemo.py │ │ ├── test_12.py │ │ ├── test_reboot.py │ │ ├── test_errprinter.py │ │ ├── test_timeout.py │ │ ├── test_capability_and_version.py │ │ ├── test_115.py │ │ ├── __init__.py │ │ ├── test_parameters.py │ │ ├── test_110.py │ │ ├── test_sensor_calibration.py │ │ ├── test_goto.py │ │ ├── test_locations.py │ │ ├── test_waypoints.py │ │ └── test_channels.py │ └── __init__.py ├── .gitignore ├── lib.py └── util.py ├── .style.yapf ├── scripts ├── publish-docs.sh ├── update-docs.sh ├── publish-pypi.sh └── update-releasenotes.sh ├── examples ├── drone_delivery │ ├── requirements.pip │ └── html │ │ ├── bottom-scripts.html │ │ ├── header.html │ │ ├── head.html │ │ ├── mapbox.html │ │ ├── index.html │ │ ├── assets │ │ └── styles.css │ │ ├── command.html │ │ └── track.html ├── follow_me │ ├── run-fake-gps.sh │ ├── fake-gps-data.log │ └── follow_me.py ├── gcs │ ├── screenshot.png │ └── microgcs.py ├── flight_replay │ └── flight.tlog ├── README.md ├── mission_import_export │ ├── mpmission.txt │ └── mission_import_export.py ├── reboot │ └── reboot.py ├── play_tune │ └── play_tune.py ├── create_attribute │ ├── create_attribute.py │ └── my_vehicle.py ├── simple_goto │ └── simple_goto.py ├── channel_overrides │ └── channel_overrides.py ├── performance_test │ └── performance_test.py └── set_attitude_target │ └── set_attitude_target.py ├── requirements.txt ├── CONTRIBUTING.md ├── .gitignore ├── windows ├── dronekitWinBuild.bat ├── returnVersion.py └── dronekit_installer.iss ├── .editorconfig ├── appveyor.yml ├── setup.py ├── .travis.yml ├── Vagrantfile ├── .circleci └── config.yml ├── README.md └── CHANGELOG.md /docs/_static/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_templates/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | 3 | -------------------------------------------------------------------------------- /dronekit/test/unit/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dronekit/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | 3 | -------------------------------------------------------------------------------- /.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | based_on_style = pep8 3 | column_limit = 100 4 | -------------------------------------------------------------------------------- /dronekit/lib.py: -------------------------------------------------------------------------------- 1 | # Backwards compatibility 2 | from dronekit import * 3 | -------------------------------------------------------------------------------- /scripts/publish-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ssh dronekit.io "./update_python.sh" 4 | -------------------------------------------------------------------------------- /scripts/update-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ssh dronekit.io "./update_python.sh" 4 | -------------------------------------------------------------------------------- /examples/drone_delivery/requirements.pip: -------------------------------------------------------------------------------- 1 | simplejson 2 | pid 3 | cherrypy 4 | jinja2 5 | dronekit 6 | -------------------------------------------------------------------------------- /examples/follow_me/run-fake-gps.sh: -------------------------------------------------------------------------------- 1 | echo Sending fake GPS data 2 | gpsfake -c 1 fake-gps-data.log 3 | -------------------------------------------------------------------------------- /docs/guide/win-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/docs/guide/win-screenshot.png -------------------------------------------------------------------------------- /examples/gcs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/examples/gcs/screenshot.png -------------------------------------------------------------------------------- /examples/flight_replay/flight.tlog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/examples/flight_replay/flight.tlog -------------------------------------------------------------------------------- /docs/examples/drone-delivery-splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/docs/examples/drone-delivery-splash.png -------------------------------------------------------------------------------- /docs/examples/drone-delivery-track.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/docs/examples/drone-delivery-track.png -------------------------------------------------------------------------------- /docs/examples/flight_replay_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/docs/examples/flight_replay_example.png -------------------------------------------------------------------------------- /docs/examples/drone-delivery-command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/docs/examples/drone-delivery-command.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pymavlink>=2.2.20 2 | monotonic>=1.3 3 | nose>=1.3.7 4 | mock>=2.0.0 5 | dronekit-sitl==3.2.0 6 | sphinx-3dr-theme>=0.4.3 7 | -------------------------------------------------------------------------------- /scripts/publish-pypi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd $(dirname $0) 4 | cd .. 5 | 6 | sudo python setup.py sdist bdist_egg bdist_wheel upload 7 | -------------------------------------------------------------------------------- /docs/develop/MissionPlanner_ConnectPort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/docs/develop/MissionPlanner_ConnectPort.png -------------------------------------------------------------------------------- /docs/examples/GuidedModeExample_FlyByPosition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/docs/examples/GuidedModeExample_FlyByPosition.png -------------------------------------------------------------------------------- /docs/examples/GuidedModeExample_FlyByVelocity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/docs/examples/GuidedModeExample_FlyByVelocity.png -------------------------------------------------------------------------------- /docs/examples/simple_goto_example_copter_path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/docs/examples/simple_goto_example_copter_path.png -------------------------------------------------------------------------------- /docs/examples/mission_basic_example_copter_path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dronekit/dronekit-python/HEAD/docs/examples/mission_basic_example_copter_path.png -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to DroneKit Python 2 | 3 | Please see [the Contribution Guide](http://python.dronekit.io/contributing/) on dronekit.io for details. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .pydevproject 3 | .python-version 4 | dronekit.egg-info 5 | dist 6 | build 7 | .vagrant 8 | *~ 9 | .idea 10 | *.pyc 11 | env 12 | vs/.vs/dronekit-python/v14/*.suo 13 | -------------------------------------------------------------------------------- /examples/drone_delivery/html/bottom-scripts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /dronekit/test/unit/test_api.py: -------------------------------------------------------------------------------- 1 | from dronekit import VehicleMode 2 | from nose.tools import assert_equals, assert_not_equals 3 | 4 | 5 | def test_vehicle_mode_eq(): 6 | assert_equals(VehicleMode('GUIDED'), VehicleMode('GUIDED')) 7 | 8 | 9 | def test_vehicle_mode_neq(): 10 | assert_not_equals(VehicleMode('AUTO'), VehicleMode('GUIDED')) 11 | -------------------------------------------------------------------------------- /docs/automodule.rst: -------------------------------------------------------------------------------- 1 | .. _api_reference: 2 | 3 | ============================= 4 | DroneKit-Python API Reference 5 | ============================= 6 | 7 | 8 | .. automodule:: dronekit 9 | :members: 10 | :inherited-members: 11 | 12 | 13 | 14 | .. toctree:: 15 | :hidden: 16 | 17 | Index 18 | 19 | 20 | 21 | .. todolist:: -------------------------------------------------------------------------------- /dronekit/test/README.md: -------------------------------------------------------------------------------- 1 | # dronekit-python tests 2 | 3 | Before running, make sure to export your api keys: 4 | 5 | ``` 6 | export DRONEAPI_KEY=. 7 | ``` 8 | 9 | Run tests using nose (`pip install nose`): 10 | 11 | ``` 12 | $ nosetests 13 | .............. 14 | ---------------------------------------------------------------------- 15 | Ran _ tests in _.___s 16 | 17 | OK 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/about/license.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | Open Source Licence 3 | ======================= 4 | 5 | DroneKit-Python is licensed under the *Apache License Version 2.0, January 2004* (http://www.apache.org/licenses/). This is present in the `LICENSE `_ file in the source tree, and reproduced below: 6 | 7 | .. include:: ../../LICENSE 8 | :literal: -------------------------------------------------------------------------------- /docs/guide/index.rst: -------------------------------------------------------------------------------- 1 | ===== 2 | Guide 3 | ===== 4 | 5 | The guide contains how-to documentation for using the DroneKit-Python API. 6 | Additional guide material can be found in the :ref:`example-toc`. 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | connecting_vehicle 12 | vehicle_state_and_parameters 13 | taking_off 14 | copter/guided_mode 15 | Missions 16 | debugging 17 | mavlink_messages -------------------------------------------------------------------------------- /dronekit/test/sitl/test_mode_settings.py: -------------------------------------------------------------------------------- 1 | from dronekit import connect 2 | from dronekit.test import with_sitl 3 | from nose.tools import assert_equals 4 | 5 | 6 | @with_sitl 7 | def test_modes_set(connpath): 8 | vehicle = connect(connpath) 9 | 10 | def listener(self, name, m): 11 | assert_equals('STABILIZE', self._flightmode) 12 | 13 | vehicle.add_message_listener('HEARTBEAT', listener) 14 | 15 | vehicle.close() 16 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_state.py: -------------------------------------------------------------------------------- 1 | from dronekit import connect, SystemStatus 2 | from dronekit.test import with_sitl 3 | from nose.tools import assert_equals 4 | 5 | 6 | @with_sitl 7 | def test_state(connpath): 8 | vehicle = connect(connpath, wait_ready=['system_status']) 9 | 10 | assert_equals(type(vehicle.system_status), SystemStatus) 11 | assert_equals(type(vehicle.system_status.state), str) 12 | 13 | vehicle.close() 14 | -------------------------------------------------------------------------------- /examples/drone_delivery/html/header.html: -------------------------------------------------------------------------------- 1 | {% macro list_item(name, location='#', active=False) -%} 2 |
  • 3 | {{name}} 4 |
  • 5 | {%- endmacro %} 6 | 7 | {% macro navigation(list, current_url, class='') -%} 8 |
      9 | {%- for item in list %} 10 | {{ list_item(item.name, item.location, (item.location == current_url)) }} 11 | {%- endfor %} 12 |
    13 | {%- endmacro %} 14 | -------------------------------------------------------------------------------- /docs/develop/coding_standards.rst: -------------------------------------------------------------------------------- 1 | .. _coding_standards: 2 | 3 | ========================== 4 | Coding Standards 5 | ========================== 6 | 7 | DroneKit-Python does not impose (or recommend) a particular set of coding standards 8 | for third party code. 9 | 10 | Internally we run the `YAPF formatter `_ 11 | on major releases and we expect contributors to copy the patterns used in similar 12 | code within the existing code base. 13 | 14 | -------------------------------------------------------------------------------- /examples/follow_me/fake-gps-data.log: -------------------------------------------------------------------------------- 1 | $PMGNST,05.40,2,T,961,08.3,+04583,00*4C 2 | $GPGLL,5036.9881,N,00707.9142,E,125412.480,A*3F 3 | $GPGGA,125412.48,5036.9881,N,00707.9142,E,2,04,20.5,00269,M,,,,*17 4 | $GPRMC,125412.48,A,5036.9881,N,00707.9142,E,00.0,000.0,230506,00,E*4F 5 | $GPGSA,A,2,27,04,08,24,,,,,,,,,20.5,20.5,*12 6 | $GPGSV,3,1,10,13,81,052,,04,58,240,39,23,44,064,,24,43,188,36*75 7 | $GPGSV,3,2,10,02,42,295,,27,34,177,40,20,21,113,,16,12,058,*7F 8 | $GPGSV,3,3,10,08,07,189,38,10,05,293,,131,11,117,,120,28,209,*76 9 | -------------------------------------------------------------------------------- /windows/dronekitWinBuild.bat: -------------------------------------------------------------------------------- 1 | rem build the standalone Dronekit.exe for Windows. 2 | rem This assumes Python2 is installed in C:\Python27 3 | rem This assumes Inno Setup 5 is installed in C:\Program Files (x86)\Inno Setup 5 4 | SETLOCAL enableextensions 5 | 6 | rem get the version 7 | for /f "tokens=*" %%a in ( 8 | 'python returnVersion.py' 9 | ) do ( 10 | set VERSION=%%a 11 | ) 12 | 13 | rem -----Build the Installer----- 14 | "C:\Program Files (x86)\Inno Setup 5\ISCC.exe" /dMyAppVersion=%VERSION% dronekit_installer.iss 15 | -------------------------------------------------------------------------------- /windows/returnVersion.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | # This script reads the setup.py and returns the current version number 3 | # Used as part of building the WIndows setup file (DronekitWinBuild.bat) 4 | # It assumes there is a line like this: 5 | # version = "12344" 6 | 7 | # glob supports Unix style pathname extensions 8 | with open("../setup.py") as f: 9 | searchlines = f.readlines() 10 | for i, line in enumerate(searchlines): 11 | if "version = " in line: 12 | print(line[11:len(line)-2]) 13 | break -------------------------------------------------------------------------------- /docs/about/index.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Introducing DroneKit-Python 3 | =========================== 4 | 5 | This section provides an overview of DroneKit-Python's functionality, licensing, and supported companion computers. 6 | 7 | After reading, you will understand what the kit offers and the opportunities it provides. You will also know where to go if you have further questions for suggestions for improvement. 8 | 9 | 10 | .. toctree:: 11 | :maxdepth: 4 12 | 13 | overview 14 | release_notes 15 | migrating 16 | license 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/drone_delivery/html/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Drone Delivery App 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # DroneKit Tutorials 2 | 3 | DroneKit-Python provides a number of examples/tutorials showing how to use the API. To find out what these examples do (and how they are run) see the [Examples](http://python.dronekit.io/examples/index.html) section in the official DroneKit-Python guide. 4 | 5 | Developers are welcome to [contribute](http://python.dronekit.io/about/contributing.html) new examples or improve our existing documentation. The source code for the example documentation on Github at [/dronekit-python/docs/examples](https://github.com/dronekit/dronekit-python/blob/master/docs/examples/). 6 | -------------------------------------------------------------------------------- /examples/mission_import_export/mpmission.txt: -------------------------------------------------------------------------------- 1 | QGC WPL 110 2 | 0 1 0 16 0 0 0 0 -35.363262 149.165237 584.000000 1 3 | 1 0 0 22 0.000000 0.000000 0.000000 0.000000 -35.361988 149.163753 100.000000 1 4 | 2 0 0 16 0.000000 0.000000 0.000000 0.000000 -35.361992 149.163593 100.000000 1 5 | 3 0 0 16 0.000000 0.000000 0.000000 0.000000 -35.363812 149.163609 100.000000 1 6 | 4 0 0 16 0.000000 0.000000 0.000000 0.000000 -35.363768 149.166055 100.000000 1 7 | 5 0 0 16 0.000000 0.000000 0.000000 0.000000 -35.361835 149.166012 100.000000 1 8 | 6 0 0 16 0.000000 0.000000 0.000000 0.000000 -35.362150 149.165046 100.000000 1 9 | -------------------------------------------------------------------------------- /dronekit/util.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import logging 4 | import sys 5 | 6 | 7 | def errprinter(*args): 8 | logger(*args) 9 | 10 | 11 | def logger(*args): 12 | print(*args, file=sys.stderr) 13 | sys.stderr.flush() 14 | 15 | 16 | class ErrprinterHandler(logging.Handler): 17 | """Logging handler to support the deprecated `errprinter` argument to connect()""" 18 | 19 | def __init__(self, errprinter): 20 | logging.Handler.__init__(self) 21 | self.errprinter = errprinter 22 | 23 | def emit(self, record): 24 | msg = self.format(record) 25 | self.errprinter(msg) 26 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_earlyattrs.py: -------------------------------------------------------------------------------- 1 | from dronekit import connect 2 | from dronekit.test import with_sitl 3 | from nose.tools import assert_equals, assert_not_equals 4 | 5 | 6 | @with_sitl 7 | def test_battery_none(connpath): 8 | vehicle = connect(connpath, _initialize=False) 9 | 10 | # Ensure we can get (possibly unpopulated) battery object without throwing error. 11 | assert_equals(vehicle.battery, None) 12 | 13 | vehicle.initialize() 14 | 15 | # Ensure we can get battery object without throwing error. 16 | vehicle.wait_ready('battery') 17 | assert_not_equals(vehicle.battery, None) 18 | 19 | vehicle.close() 20 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_modeavailable.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple test to trigger a bug in Vehicle class: issue #610 fixed in PR #611 3 | """ 4 | 5 | from dronekit import connect 6 | from dronekit.test import with_sitl 7 | 8 | 9 | @with_sitl 10 | def test_timeout(connpath): 11 | v = connect(connpath) 12 | 13 | # Set the vehicle and autopilot type to 'unsupported' types that MissionPlanner uses as of 17.Apr.2016 14 | v._vehicle_type = 6 15 | v._autopilot_type = 8 16 | 17 | # The above types trigger 'TypeError: argument of type 'NoneType' is not iterable' which is addressed in issue #610 18 | v._is_mode_available(0) 19 | 20 | v.close() 21 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_vehicleclass.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dronekit import connect, Vehicle 3 | from dronekit.test import with_sitl 4 | 5 | 6 | class DummyVehicle(Vehicle): 7 | def __init__(self, *args): 8 | super(DummyVehicle, self).__init__(*args) 9 | 10 | self.success = False 11 | 12 | def success_fn(self, name, m): 13 | self.success = True 14 | 15 | self.add_message_listener('HEARTBEAT', success_fn) 16 | 17 | 18 | @with_sitl 19 | def test_timeout(connpath): 20 | v = connect(connpath, vehicle_class=DummyVehicle) 21 | 22 | while not v.success: 23 | time.sleep(0.1) 24 | 25 | v.close() 26 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_mavlink.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dronekit import connect 3 | from dronekit.mavlink import MAVConnection 4 | from dronekit.test import with_sitl 5 | 6 | 7 | @with_sitl 8 | def test_mavlink(connpath): 9 | vehicle = connect(connpath, wait_ready=True) 10 | out = MAVConnection('udpin:localhost:15668') 11 | vehicle._handler.pipe(out) 12 | out.start() 13 | 14 | vehicle2 = connect('udpout:localhost:15668', wait_ready=True) 15 | 16 | result = {'success': False} 17 | 18 | @vehicle2.on_attribute('location') 19 | def callback(*args): 20 | result['success'] = True 21 | 22 | i = 20 23 | while not result['success'] and i > 0: 24 | time.sleep(1) 25 | i -= 1 26 | 27 | assert result['success'] 28 | 29 | vehicle2.close() 30 | vehicle.close() 31 | -------------------------------------------------------------------------------- /examples/drone_delivery/html/mapbox.html: -------------------------------------------------------------------------------- 1 | {% macro static_map(options, coords) -%} 2 | 3 | {%- endmacro %} 4 | 5 | {% macro static_map_with_marker(options, coords, marker_coords) -%} 6 | 7 | {%- endmacro %} 8 | 9 | {% macro marker_string(coords, label='heliport', color='f86767') -%} 10 | pin-m-{{ label }}+{{ color }}({{ coords[1] }},{{ coords[0] }}) 11 | {%- endmacro %} 12 | -------------------------------------------------------------------------------- /docs/contributing/index.rst: -------------------------------------------------------------------------------- 1 | .. _contributing: 2 | 3 | ============ 4 | Contributing 5 | ============ 6 | 7 | DroneKit is an open-source project. We welcome any contribution that will improve the API and make it easier to use. 8 | 9 | The articles below explain some of the :ref:`opportunities ` for working on the project, how to :ref:`contribute to the API ` or the :ref:`documentation `, and how to set up a :ref:`development environment on Linux ` or :ref:`Windows `. 10 | 11 | 12 | .. toctree:: 13 | :maxdepth: 1 14 | 15 | getting_started 16 | contributions_api 17 | contributions_documentation 18 | developer_setup_windows 19 | developer_setup_linux 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_simpledemo.py: -------------------------------------------------------------------------------- 1 | """ 2 | This test represents a simple demo for testing. 3 | Feel free to copy and modify at your leisure. 4 | """ 5 | 6 | from dronekit import connect, VehicleMode 7 | from dronekit.test import with_sitl 8 | from nose.tools import assert_equals 9 | 10 | 11 | # This test runs first! 12 | @with_sitl 13 | def test_parameter(connpath): 14 | v = connect(connpath, wait_ready=True) 15 | 16 | # Perform a simple parameter check 17 | assert_equals(type(v.parameters['THR_MIN']), float) 18 | 19 | v.close() 20 | 21 | 22 | # This test runs second. Add as many tests as you like 23 | @with_sitl 24 | def test_mode(connpath): 25 | v = connect(connpath, wait_ready=True) 26 | 27 | # Ensure Mode is an instance of VehicleMode 28 | assert isinstance(v.mode, VehicleMode) 29 | 30 | v.close() 31 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file provide reference settings for the APM code conventions 2 | # There are plug-ins available for nearly every text editor to automatically 3 | # respect the conventions contained within this file. 4 | # 5 | # Please see editorconfig.org for complete information. 6 | # 7 | # If you find errors in this file, please send a pull-request with a fix. 8 | # 9 | root = true 10 | 11 | [*] 12 | indent_style = space 13 | indent_size = 4 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | # insert_final_newline = true 18 | 19 | # 4 space indentation 20 | [*.py] 21 | indent_style = space 22 | indent_size = 4 23 | trim_trailing_whitespace = true 24 | end_of_line = LF 25 | 26 | [*.mk] 27 | indent_style = tab 28 | indent_size = 8 29 | 30 | [{makefile,Makefile}] 31 | indent_style = tab 32 | indent_size = 8 33 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_12.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dronekit import connect 3 | from dronekit.test import with_sitl 4 | from nose.tools import assert_equals 5 | 6 | 7 | def current_milli_time(): 8 | return int(round(time.time() * 1000)) 9 | 10 | 11 | @with_sitl 12 | def test_timeout(connpath): 13 | v = connect(connpath, wait_ready=True) 14 | 15 | value = v.parameters['THR_MIN'] 16 | assert_equals(type(value), float) 17 | 18 | start = current_milli_time() 19 | v.parameters['THR_MIN'] = value + 10 20 | end = current_milli_time() 21 | 22 | newvalue = v.parameters['THR_MIN'] 23 | assert_equals(type(newvalue), float) 24 | assert_equals(newvalue, value + 10) 25 | 26 | # Checks that time to set parameter was <1s 27 | # see https://github.com/dronekit/dronekit-python/issues/12 28 | assert end - start < 1000, 'time to set parameter was %s, over 1s' % (end - start, ) 29 | 30 | v.close() 31 | -------------------------------------------------------------------------------- /examples/drone_delivery/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include 'head.html' %} 5 | 6 | 7 |
    8 | {% import 'header.html' as header %} 9 |
    10 | 13 |

    DroneAPI Demos {{options.current_url}}

    14 |
    15 |
    16 |

    Drone Delivery App

    17 |

    18 | Track and Command a drone to delivery packages from a web app 19 |

    20 |

    21 | Track 22 | Command 23 |

    24 |
    25 |
    26 |

    © 3D Robotics Inc.

    27 |
    28 | 29 | {% include 'bottom-scripts.html' %} 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/examples/index.rst: -------------------------------------------------------------------------------- 1 | .. _example-toc: 2 | 3 | ======== 4 | Examples 5 | ======== 6 | 7 | This section contains the documentation for a number of useful DroneKit-Python examples. These demonstrate common use-cases, 8 | and show (among other things) how to use the API to query vehicle state and parameters, and how to control a vehicle 9 | during missions and outside missions using custom commands. 10 | 11 | 12 | .. toctree:: 13 | :maxdepth: 1 14 | 15 | running_examples 16 | Vehicle State 17 | Simple Goto 18 | Guided Movement and Commands 19 | Basic Mission 20 | Mission Import/Export 21 | Create Attribute in App 22 | Follow Me (Linux only) 23 | Drone Delivery 24 | Flight Replay 25 | Channel Overrides 26 | Performance Test 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_reboot.py: -------------------------------------------------------------------------------- 1 | from nose.tools import assert_equal 2 | 3 | from dronekit import connect 4 | from dronekit.test import with_sitl 5 | import time 6 | 7 | 8 | @with_sitl 9 | def test_reboot(connpath): 10 | """Tries to reboot the vehicle, and checks that the autopilot ACKs the command.""" 11 | 12 | vehicle = connect(connpath, wait_ready=True) 13 | 14 | reboot_acks = [] 15 | 16 | def on_ack(self, name, message): 17 | if message.command == 246: # reboot/shutdown 18 | reboot_acks.append(message) 19 | 20 | vehicle.add_message_listener('COMMAND_ACK', on_ack) 21 | vehicle.reboot() 22 | time.sleep(0.5) 23 | vehicle.remove_message_listener('COMMAND_ACK', on_ack) 24 | 25 | assert_equal(1, len(reboot_acks)) # one and only one ACK 26 | assert_equal(246, reboot_acks[0].command) # for the correct command 27 | assert_equal(0, reboot_acks[0].result) # the result must be successful 28 | 29 | vehicle.close() 30 | -------------------------------------------------------------------------------- /docs/develop/index.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Developing with DroneKit 3 | ======================== 4 | 5 | DroneKit-Python is primarily intended for use on Linux-based :doc:`companion-computers` that travel 6 | on a vehicle and communicate with the autopilot via a serial port. It can also be used 7 | on ground-based computers running Linux, Windows or Mac OSX (communicating using WiFi or a telemetry radio). 8 | 9 | During development you'll generally run it on a development computer, communicating with a 10 | :doc:`simulated vehicle` running on the same machine (via a UDP connection). 11 | 12 | This section contains topics explaining how to develop with DroneKit-Python, 13 | covering subjects like installation, setting up the target vehicle or simulator, best practices 14 | and coding standards. 15 | 16 | 17 | .. toctree:: 18 | :maxdepth: 1 19 | 20 | installation 21 | companion-computers 22 | Simulated Vehicle 23 | best_practice 24 | coding_standards -------------------------------------------------------------------------------- /dronekit/test/sitl/test_errprinter.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | 4 | from nose.tools import assert_true 5 | 6 | from dronekit import connect 7 | from dronekit.test import with_sitl 8 | 9 | 10 | @with_sitl 11 | def test_115(connpath): 12 | """Provide a custom status_printer function to the Vehicle and check that 13 | the autopilot messages are correctly logged. 14 | """ 15 | 16 | logging_check = {'ok': False} 17 | 18 | def errprinter_fn(msg): 19 | if isinstance(msg, str) and "APM:Copter" in msg: 20 | logging_check['ok'] = True 21 | 22 | vehicle = connect(connpath, wait_ready=False, status_printer=errprinter_fn) 23 | 24 | i = 5 25 | while not logging_check['ok'] and i > 0: 26 | time.sleep(1) 27 | i -= 1 28 | 29 | assert_true(logging_check['ok']) 30 | vehicle.close() 31 | 32 | # Cleanup the logger 33 | autopilotLogger = logging.getLogger('autopilot') 34 | autopilotLogger.removeHandler(autopilotLogger.handlers[0]) 35 | -------------------------------------------------------------------------------- /docs/contributing/getting_started.rst: -------------------------------------------------------------------------------- 1 | .. _contributing_getting_started: 2 | 3 | ======================= 4 | How you can Contribute 5 | ======================= 6 | 7 | One of the best ways you can contribute is to simply *use the API* and share your 8 | `bug reports and enhancement suggestions on Github `_. 9 | These can cover anything: from APIs that don't work properly through to needed features or documentation. 10 | 11 | If you want to take a more active role, then a good place to start is with the project's 12 | `open issues `_ on Github. In particular, 13 | :ref:`documentation issues ` can be resolved without a deep knowledge of the code, 14 | and will help you learn more about the project. 15 | 16 | If there is a feature that you want to add, then please do so! Before you start we highly recommend that 17 | you first create an issue in Github so it can be tracked and discussed! 18 | 19 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | SITL_SPEEDUP: 10 3 | SITL_RATE: 200 4 | PYTHONUNBUFFERED: 1 5 | 6 | matrix: 7 | - PYTHON: "C:\\Python27" 8 | PYTHON_VERSION: "2.7" 9 | - PYTHON: "C:\\Python37" 10 | PYTHON_VERSION: "3.7" 11 | 12 | cache: 13 | - "c:\\Users\\appveyor\\.pip-wheelhouse" 14 | 15 | init: 16 | - cmd: "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" 17 | 18 | install: 19 | - ps: | 20 | New-Item -ItemType Directory -Force -Path c:\Users\appveyor\pip\ 21 | @' 22 | [global] 23 | find-links = file://c:/Users/appveyor/.pip-wheelhouse 24 | 25 | [wheel] 26 | wheel-dir = c:/Users/appveyor/.pip-wheelhouse 27 | '@ | out-file -Encoding ascii -FilePath c:\Users\appveyor\pip\pip.ini 28 | - cmd: 'python -m pip install pip wheel -U' 29 | - cmd: 'pip install . -UI' 30 | - cmd: 'pip install -r requirements.txt -UI' 31 | 32 | build: off 33 | 34 | test_script: 35 | - cmd: 'nosetests -svx dronekit.test.unit' 36 | - cmd: 'nosetests -svx dronekit.test.sitl' 37 | 38 | clone_depth: 10 39 | -------------------------------------------------------------------------------- /examples/reboot/reboot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | from dronekit import connect 7 | import time 8 | 9 | # Set up option parsing to get connection string 10 | import argparse 11 | parser = argparse.ArgumentParser(description='Reboots vehicle') 12 | parser.add_argument('--connect', 13 | help="Vehicle connection target string. If not specified, SITL automatically started and used.") 14 | args = parser.parse_args() 15 | 16 | connection_string = args.connect 17 | sitl = None 18 | 19 | 20 | # Start SITL if no connection string specified 21 | if not connection_string: 22 | import dronekit_sitl 23 | sitl = dronekit_sitl.start_default() 24 | connection_string = sitl.connection_string() 25 | 26 | 27 | # Connect to the Vehicle 28 | print('Connecting to vehicle on: %s' % connection_string) 29 | vehicle = connect(connection_string, wait_ready=True) 30 | 31 | vehicle.reboot() 32 | time.sleep(1) 33 | 34 | # Shut down simulator if it was started. 35 | if sitl: 36 | sitl.stop() 37 | -------------------------------------------------------------------------------- /dronekit/test/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import os 3 | from dronekit_sitl import SITL 4 | from nose.tools import with_setup 5 | import time 6 | 7 | sitl = None 8 | sitl_args = ['-I0', '--model', 'quad', '--home=-35.363261,149.165230,584,353'] 9 | 10 | if 'SITL_SPEEDUP' in os.environ: 11 | sitl_args += ['--speedup', str(os.environ['SITL_SPEEDUP'])] 12 | if 'SITL_RATE' in os.environ: 13 | sitl_args += ['-r', str(os.environ['SITL_RATE'])] 14 | 15 | 16 | def setup_sitl(): 17 | global sitl 18 | sitl = SITL() 19 | sitl.download('copter', '3.3') 20 | sitl.launch(sitl_args, await_ready=True, restart=True) 21 | 22 | 23 | def teardown_sitl(): 24 | sitl.stop() 25 | 26 | 27 | def with_sitl(fn): 28 | @with_setup(setup_sitl, teardown_sitl) 29 | def test(*args, **kargs): 30 | return fn('tcp:127.0.0.1:5760', *args, **kargs) 31 | return test 32 | 33 | 34 | def wait_for(condition, time_max): 35 | time_start = time.time() 36 | while not condition(): 37 | if time.time() - time_start > time_max: 38 | break 39 | time.sleep(0.1) 40 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_timeout.py: -------------------------------------------------------------------------------- 1 | import time 2 | import socket 3 | from dronekit import connect 4 | from dronekit.test import with_sitl 5 | from nose.tools import assert_equals 6 | 7 | 8 | @with_sitl 9 | def test_timeout(connpath): 10 | # Connect with timeout of 10s. 11 | vehicle = connect(connpath, wait_ready=True, heartbeat_timeout=20) 12 | 13 | # Stall input. 14 | vehicle._handler._accept_input = False 15 | 16 | start = time.time() 17 | while vehicle._handler._alive and time.time() - start < 30: 18 | time.sleep(.1) 19 | 20 | assert_equals(vehicle._handler._alive, False) 21 | 22 | vehicle.close() 23 | 24 | 25 | def test_timeout_empty(): 26 | # Create a dummy server. 27 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 28 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 29 | s.bind(('127.0.0.1', 5760)) 30 | s.listen(1) 31 | 32 | try: 33 | # Connect with timeout of 10s. 34 | vehicle = connect('tcp:127.0.0.1:5760', wait_ready=True, heartbeat_timeout=20) 35 | 36 | vehicle.close() 37 | 38 | # Should not pass 39 | assert False 40 | except: 41 | pass 42 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | import os 3 | 4 | version = '2.9.2' 5 | 6 | with open(os.path.join(os.path.dirname(__file__), 'README.md')) as f: 7 | LongDescription = f.read() 8 | 9 | setuptools.setup( 10 | name='dronekit', 11 | zip_safe=True, 12 | version=version, 13 | description='Developer Tools for Drones.', 14 | long_description_content_type="text/markdown", 15 | long_description=LongDescription, 16 | url='https://github.com/dronekit/dronekit-python', 17 | author='3D Robotics', 18 | install_requires=[ 19 | 'pymavlink>=2.2.20', 20 | 'monotonic>=1.3', 21 | ], 22 | author_email='tim@3drobotics.com, kevinh@geeksville.com', 23 | classifiers=[ 24 | 'Development Status :: 5 - Production/Stable', 25 | 'Environment :: Console', 26 | 'Intended Audience :: Science/Research', 27 | 'License :: OSI Approved :: Apache Software License', 28 | 'Operating System :: OS Independent', 29 | 'Programming Language :: Python :: 2.7', 30 | 'Programming Language :: Python :: 3', 31 | 'Topic :: Scientific/Engineering', 32 | ], 33 | license='apache', 34 | packages=setuptools.find_packages() 35 | ) 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | env: 4 | global: 5 | - SITL_SPEEDUP=10 6 | - SITL_RATE=200 7 | 8 | python: 9 | - 2.7 10 | - 3.6 11 | 12 | before_install: 13 | - pip install --upgrade pip 14 | 15 | install: 16 | - pip install -r requirements.txt 17 | - pip install flake8 18 | 19 | before_script: 20 | # stop the build if there are Python syntax errors or undefined names 21 | - flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics 22 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 23 | - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 24 | 25 | script: 26 | - nosetests --debug=nose,nose.importer --debug-log=nose_debug -svx dronekit.test.unit 27 | - nosetests --debug=nose,nose.importer --debug-log=nose_debug -svx dronekit.test.sitl 28 | 29 | git: 30 | depth: 10 31 | 32 | notifications: 33 | email: false 34 | webhooks: 35 | urls: 36 | - https://webhooks.gitter.im/e/f3f81b74f930dac9583a 37 | on_success: change 38 | on_failure: always 39 | on_start: never 40 | 41 | #matrix: 42 | #allow_failures: 43 | #- python: 3.6 44 | 45 | branches: 46 | only: 47 | - master 48 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_capability_and_version.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from dronekit import connect 4 | from dronekit.test import with_sitl 5 | from nose.tools import assert_false, assert_true 6 | 7 | 8 | @with_sitl 9 | def test_115(connpath): 10 | v = connect(connpath, wait_ready=True) 11 | time.sleep(5) 12 | assert_false(v.capabilities.ftp) 13 | 14 | # versions of ArduCopter prior to v3.3 will send out capabilities 15 | # flags before they are initialised. Vehicle attempts to refetch 16 | # until capabilities are non-zero, but we may need to wait: 17 | start_time = time.time() 18 | slept = False 19 | while v.capabilities.mission_float == 0: 20 | if time.time() > start_time + 30: 21 | break 22 | time.sleep(0.1) 23 | slept = True 24 | if v.capabilities.mission_float: 25 | if slept: 26 | assert_true(v.version.major <= 3) 27 | assert_true(v.version_minor <= 3) 28 | else: 29 | # fail it 30 | assert_true(v.capabilities.mission_float) 31 | 32 | assert_true(v.version.major is not None) 33 | assert_true(len(v.version.release_type()) >= 2) 34 | assert_true(v.version.release_version() is not None) 35 | 36 | v.close() 37 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure(2) do |config| 5 | config.vm.box = "ubuntu/trusty64" 6 | config.vm.network "private_network", type: "dhcp" 7 | 8 | config.vm.provision "shell", inline: <<-SHELL 9 | apt-get -y update 10 | echo "[DroneKit]: Installing build essentials" 11 | apt-get -y install build-essential 12 | echo "[DroneKit]: Installing Python Devel" 13 | apt-get -y install python-dev 14 | echo "[DroneKit]: Installing pip ..." 15 | apt-get -y install python-pip 16 | easy_install -U pip 17 | echo "[DroneKit]: Installing python-cherrypy3 ..." 18 | apt-get -y install python-cherrypy3 19 | echo "[DroneKit]: Installing python-matplotlib ..." 20 | apt-get -y install python-matplotlib 21 | echo "[DroneKit]: Installing python-gps ..." 22 | apt-get -y install python-gps 23 | echo "[DroneKit]: Installing Sphinx ... " 24 | pip install sphinx 25 | cd /vagrant 26 | echo "[DroneKit]: Installing DroneKit-Python requirements.txt ... " 27 | pip install -r requirements.txt 28 | echo "[DroneKit]: Building docs " 29 | cd docs/ 30 | make html 31 | SHELL 32 | end 33 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_115.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dronekit import connect, VehicleMode 3 | from dronekit.test import with_sitl 4 | from nose.tools import assert_equals 5 | 6 | 7 | @with_sitl 8 | def test_115(connpath): 9 | v = connect(connpath, wait_ready=True) 10 | 11 | # Dummy callback 12 | def mavlink_callback(*args): 13 | mavlink_callback.count += 1 14 | 15 | mavlink_callback.count = 0 16 | 17 | # Set the callback. 18 | v.add_message_listener('*', mavlink_callback) 19 | 20 | # Change the vehicle into STABILIZE mode 21 | v.mode = VehicleMode("STABILIZE") 22 | # NOTE wait crudely for ACK on mode update 23 | time.sleep(3) 24 | 25 | # Expect the callback to have been called 26 | assert mavlink_callback.count > 0 27 | 28 | # Unset the callback. 29 | v.remove_message_listener('*', mavlink_callback) 30 | savecount = mavlink_callback.count 31 | 32 | # Disarm. A callback of None should not throw errors 33 | v.armed = False 34 | # NOTE wait crudely for ACK on mode update 35 | time.sleep(3) 36 | 37 | # Expect the callback to have been called 38 | assert_equals(savecount, mavlink_callback.count) 39 | 40 | # Re-arm should not throw errors. 41 | v.armed = True 42 | # NOTE wait crudely for ACK on mode update 43 | time.sleep(3) 44 | 45 | v.close() 46 | -------------------------------------------------------------------------------- /examples/drone_delivery/html/assets/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 20px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | .form-inline { 7 | margin-top: 20px; 8 | } 9 | 10 | #map, #staticMap { 11 | width: 670px; 12 | height: 470px; 13 | } 14 | 15 | .header, 16 | .marketing, 17 | .footer { 18 | padding-right: 15px; 19 | padding-left: 15px; 20 | } 21 | 22 | .header { 23 | border-bottom: 1px solid #e5e5e5; 24 | } 25 | 26 | .header h3 { 27 | padding-bottom: 19px; 28 | margin-top: 0; 29 | margin-bottom: 0; 30 | line-height: 40px; 31 | } 32 | 33 | .footer { 34 | padding-top: 19px; 35 | color: #777; 36 | border-top: 1px solid #e5e5e5; 37 | } 38 | 39 | @media (min-width: 768px) { 40 | .container { 41 | max-width: 730px; 42 | } 43 | } 44 | .container-narrow > hr { 45 | margin: 30px 0; 46 | } 47 | 48 | .jumbotron { 49 | text-align: center; 50 | border-bottom: 1px solid #e5e5e5; 51 | } 52 | 53 | .jumbotron .btn { 54 | padding: 14px 24px; 55 | font-size: 21px; 56 | } 57 | 58 | .marketing { 59 | margin: 40px 0; 60 | } 61 | .marketing p + h4 { 62 | margin-top: 28px; 63 | } 64 | 65 | @media screen and (min-width: 768px) { 66 | .header, 67 | .marketing, 68 | .footer { 69 | padding-right: 0; 70 | padding-left: 0; 71 | } 72 | .header { 73 | margin-bottom: 30px; 74 | } 75 | .jumbotron { 76 | border-bottom: 0; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /docs/about/release_notes.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Release Notes 3 | ============= 4 | 5 | This page contains the release notes for DroneKit-Python ``minor`` and ``major`` releases. 6 | 7 | .. note:: 8 | 9 | DroneKit-Python marks releases using the ``major.minor.patch`` release numbering convention, where ``patch`` is used to denote only bug fixes, ``minor`` is used for releases with new features, and ``major`` indicates the release contains significant API changes. 10 | 11 | 12 | 13 | Latest release 14 | ============== 15 | 16 | .. include:: github_latest_release.txt 17 | 18 | All releases 19 | ============ 20 | 21 | For information about all past releases, please see `this link on Github `_. 22 | 23 | Working with releases 24 | ======================= 25 | 26 | The following PyPI commands are useful for working with different version of DroneKit Python: 27 | 28 | .. code-block:: bash 29 | 30 | pip install dronekit # Install the latest version 31 | pip install dronekit --upgrade # Update to the latest version 32 | pip show dronekit # Find out what release you have installed 33 | pip install dronekit==2.0.0rc1 # Get a sepcific old release (in this case 2.0.0rc1) 34 | 35 | See `Release History on the package ranking page `_ for a list of all releases available on PyPI. 36 | 37 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | =========================================== 2 | Welcome to DroneKit-Python's documentation! 3 | =========================================== 4 | 5 | DroneKit-Python 2.x helps you create powerful apps for UAVs. These apps run on a UAV's :ref:`Companion Computer `, and augment the autopilot by performing tasks that are both computationally intensive and require a low-latency link (e.g. computer vision). 6 | 7 | This documentation provides everything you need to get started with DroneKit-Python, including an :doc:`overview ` of the API, quick start, guide material, a number of demos and examples, 8 | and :doc:`API Reference `. 9 | 10 | .. tip:: 11 | 12 | *DroneKit-Python version 1.5* has now been superseded (see these links for legacy `documentation `_ 13 | and `examples `_). 14 | 15 | If you're migrating from *DroneKit-Python version 1.x*, check out our comprehensive :ref:`Migration Guide `. 16 | 17 | 18 | Contents: 19 | 20 | .. toctree:: 21 | :maxdepth: 2 22 | 23 | Introduction 24 | guide/quick_start 25 | Developing 26 | guide/index 27 | examples/index 28 | contributing/index 29 | API Reference 30 | 31 | -------------------------------------------------------------------------------- /examples/play_tune/play_tune.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | © Copyright 2017, Peter Barker 6 | play_tune.py: GUIDED mode "simple goto" example (Copter Only) 7 | 8 | Demonstrates how to play a custom tune on a vehicle using the vehicle's buzzer 9 | 10 | Full documentation is provided at http://python.dronekit.io/examples/play_tune.html 11 | """ 12 | 13 | from __future__ import print_function 14 | import time 15 | from dronekit import connect 16 | 17 | 18 | # Set up option parsing to get connection string 19 | import argparse 20 | parser = argparse.ArgumentParser(description='Play tune on vehicle buzzer.') 21 | parser.add_argument('--connect', 22 | help="Vehicle connection target string. If not specified, SITL automatically started and used.") 23 | parser.add_argument('--tune', type=str, help="tune to play", default="AAAA") 24 | args = parser.parse_args() 25 | 26 | connection_string = args.connect 27 | sitl = None 28 | 29 | 30 | # Start SITL if no connection string specified 31 | if not connection_string: 32 | print("SITL doesn't do tunes?!") 33 | import dronekit_sitl 34 | sitl = dronekit_sitl.start_default() 35 | connection_string = sitl.connection_string() 36 | 37 | 38 | # Connect to the Vehicle 39 | print('Connecting to vehicle on: %s' % connection_string) 40 | vehicle = connect(connection_string, wait_ready=True) 41 | 42 | vehicle.play_tune(args.tune) 43 | -------------------------------------------------------------------------------- /docs/contributing/developer_setup_linux.rst: -------------------------------------------------------------------------------- 1 | .. _dronekit_development_linux: 2 | 3 | =================================== 4 | Building DroneKit-Python on Linux 5 | =================================== 6 | 7 | The setup for *developing* DroneKit-Python on Linux is almost the same as for *using* 8 | DroneKit-Python. We therefore recommend that you start by following the instructions in 9 | :ref:`installing_dronekit`. 10 | 11 | When you've got DroneKit and a vehicle (simulated or real) communicating, you can 12 | then build and install your own fork of DroneKit, as discussed below. 13 | 14 | 15 | Fetch and build DroneKit source 16 | =============================== 17 | 18 | #. Fork the `dronekit-python `_ project on Github. 19 | 20 | #. Run the following commands to clone and build DroneKit (in the directory of your choice): 21 | 22 | .. code:: bash 23 | 24 | git clone https://github.com//dronekit-python.git 25 | cd ./dronekit-python 26 | sudo python setup.py build 27 | sudo python setup.py install 28 | 29 | 30 | Updating DroneKit 31 | ================= 32 | 33 | Navigate to your local git fork, pull the latest version, and rebuild/install: 34 | 35 | .. code:: bash 36 | 37 | cd .//dronekit-python 38 | git pull 39 | sudo python setup.py build 40 | sudo python setup.py install 41 | 42 | 43 | -------------------------------------------------------------------------------- /docs/contributing/developer_setup_windows.rst: -------------------------------------------------------------------------------- 1 | .. _dronekit_development_windows: 2 | 3 | =================================== 4 | Building DroneKit-Python on Windows 5 | =================================== 6 | 7 | This article shows how to set up an environment for *developing* DroneKit-Python on Windows. 8 | 9 | 10 | Install DroneKit using WinPython command line 11 | ============================================= 12 | 13 | 14 | First set up a command line DroneKit-Python installation. We recommend *WinPython* or *ActivePython*, as discussed in :ref:`installing_dronekit`. 15 | 16 | 17 | 18 | Fetch and build DroneKit source 19 | =============================== 20 | 21 | #. Fork the `dronekit-python `_ project on Github. 22 | 23 | #. Open the *WinPython Command Prompt*. Run the following commands to clone and build DroneKit (in the directory of your choice): 24 | 25 | .. code:: bash 26 | 27 | git clone https://github.com//dronekit-python.git 28 | cd dronekit-python 29 | python setup.py build 30 | python setup.py install 31 | 32 | 33 | 34 | Updating DroneKit 35 | ================= 36 | 37 | Navigate to your local git fork, pull the latest version, and rebuild/install: 38 | 39 | .. code:: bash 40 | 41 | cd /dronekit-python 42 | git pull 43 | python setup.py build 44 | python setup.py install 45 | 46 | 47 | -------------------------------------------------------------------------------- /dronekit/test/sitl/__init__.py: -------------------------------------------------------------------------------- 1 | import time 2 | from contextlib import contextmanager 3 | from nose.tools import assert_equal 4 | from pymavlink import mavutil 5 | 6 | 7 | @contextmanager 8 | def assert_command_ack( 9 | vehicle, 10 | command_type, 11 | ack_result=mavutil.mavlink.MAV_RESULT_ACCEPTED, 12 | timeout=10 13 | ): 14 | """Context manager to assert that: 15 | 16 | 1) exactly one COMMAND_ACK is received from a Vehicle; 17 | 2) for a specific command type; 18 | 3) with the given result; 19 | 4) within a timeout (in seconds). 20 | 21 | For example: 22 | 23 | .. code-block:: python 24 | 25 | with assert_command_ack(vehicle, mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION, timeout=30): 26 | vehicle.calibrate_gyro() 27 | 28 | """ 29 | 30 | acks = [] 31 | 32 | def on_ack(self, name, message): 33 | if message.command == command_type: 34 | acks.append(message) 35 | 36 | vehicle.add_message_listener('COMMAND_ACK', on_ack) 37 | 38 | yield 39 | 40 | start_time = time.time() 41 | while not acks and time.time() - start_time < timeout: 42 | time.sleep(0.1) 43 | vehicle.remove_message_listener('COMMAND_ACK', on_ack) 44 | 45 | assert_equal(1, len(acks)) # one and only one ACK 46 | assert_equal(command_type, acks[0].command) # for the correct command 47 | assert_equal(ack_result, acks[0].result) # the result must be successful 48 | -------------------------------------------------------------------------------- /scripts/update-releasenotes.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | This script gets the release notes, converts it from markdown to 5 | reStructured Text format, and writes it to a text file. 6 | 7 | The text file can be auto-imported by Sphinx into the official release notes. 8 | """ 9 | 10 | import os 11 | from pypandoc import convert 12 | from optparse import OptionParser 13 | import re 14 | 15 | curpath = os.path.realpath(os.path.dirname(__file__)) 16 | 17 | #parser options 18 | parser = OptionParser(version="%prog 1.0.0", usage="Usage: %prog [options] version") 19 | parser.add_option("-i" , "--input", dest="input", default=os.path.join(curpath, "../CHANGELOG.md"), help="Input file") 20 | parser.add_option("-f" , "--file", dest="file", default=os.path.join(curpath, "../docs/about/github_latest_release.txt"), help="Output file") 21 | 22 | (options, args) = parser.parse_args() 23 | 24 | rst_content = convert(options.input, 'rst') 25 | 26 | # Hyperlink Github issues 27 | rst_content = re.sub(r'#\d+', lambda m: '`' + m.group() + ' `_', rst_content) 28 | 29 | # Prefix with some documentation 30 | rst_content = ".. This document was auto-generated by the get_release_notes.py script using latest-release information from github \n\n" + rst_content 31 | 32 | release_notes_file = open(options.file,'w') 33 | release_notes_file.write(rst_content) 34 | release_notes_file.close() 35 | 36 | print 'complete.' 37 | 38 | -------------------------------------------------------------------------------- /docs/develop/companion-computers.rst: -------------------------------------------------------------------------------- 1 | .. _supported-companion-computers: 2 | 3 | =================== 4 | Companion Computers 5 | =================== 6 | 7 | A companion computer is a device that travels on-board the vehicle and controls/communicates with the autopilot over a low-latency link. 8 | Apps running on a companion computer can perform computationally intensive or time-sensitive tasks, and add much greater intelligence 9 | than is provided by the autopilot alone. 10 | 11 | DroneKit can be used with onboard computers running variants of Linux that support both Python and the installation of Python packages from the Internet. 12 | The following computing platforms are known to work with DroneKit, and are supported by the ArduPilot developer community. 13 | 14 | 15 | RaspberryPi 16 | ----------- 17 | 18 | * `Communicating with Raspberry Pi via MAVLink `_ 19 | * `Making a Mavlink WiFi bridge using the Raspberry Pi `_ 20 | 21 | Intel Edison 22 | ------------ 23 | 24 | * `Edison for drones `_ 25 | 26 | BeagleBoneBlack 27 | --------------- 28 | 29 | * `BeaglePilot `_ 30 | 31 | Odroid 32 | ------ 33 | * `Communicating with ODroid via MAVLink `_ 34 | * `ODroid Wifi Access Point for sharing files via Samba `_ 35 | 36 | -------------------------------------------------------------------------------- /docs/develop/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installing_dronekit: 2 | 3 | =================== 4 | Installing DroneKit 5 | =================== 6 | 7 | DroneKit-Python can be installed on a Linux, Mac OSX, or Windows computer that 8 | has Python 2.7 or Python 3 installed and can install Python packages from the Internet. 9 | 10 | It is installed from **pip** on all platforms: 11 | 12 | .. code-block:: bash 13 | 14 | pip install dronekit 15 | 16 | 17 | **Installation notes:** 18 | 19 | * Install `dronekit` with `pip` inside a virtualenv: 20 | 21 | .. code-block:: bash 22 | 23 | python3 -m venv .venv 24 | . .venv/bin/activate 25 | pip install dronekit 26 | 27 | * On Linux you may need to first install **pip** and **python-dev**: 28 | 29 | .. code-block:: bash 30 | 31 | sudo apt-get install python-pip python-dev 32 | 33 | Alternatively, you can use the `ensurepip` module to install or upgrade Pip on your system: 34 | 35 | 36 | .. code-block:: bash 37 | 38 | python -m ensurepip --upgrade 39 | 40 | * :doc:`companion-computers` are likely to run on stripped down versions of Linux. Ensure 41 | you use a variant that supports Python 2.7 and can install Python packages from the Internet. 42 | * Windows does not come with Python by default, but there are 43 | `many distributions available `_. 44 | We have tested against: 45 | 46 | * `WinPython 2.7 64bit `_ (see 47 | `these instructions for installation and registration `_). This is the most tested version. 48 | * `ActiveState ActivePython 2.7 `_. 49 | * Python 3 is fully supported. 50 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_parameters.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dronekit import connect 3 | from dronekit.test import with_sitl 4 | from nose.tools import assert_equals, assert_not_equals 5 | 6 | 7 | @with_sitl 8 | def test_parameters(connpath): 9 | vehicle = connect(connpath) 10 | 11 | # When called on startup, parameter (may!) be none. 12 | # assert_equals(vehicle.parameters.get('THR_MIN', wait_ready=False), None) 13 | 14 | # With wait_ready, it should not be none. 15 | assert_not_equals(vehicle.parameters.get('THR_MIN', wait_ready=True), None) 16 | 17 | try: 18 | assert_not_equals(vehicle.parameters['THR_MIN'], None) 19 | except: 20 | assert False 21 | 22 | # Garbage value after all parameters are downloaded should be None. 23 | assert_equals(vehicle.parameters.get('xXx_extreme_garbage_value_xXx', wait_ready=True), None) 24 | 25 | vehicle.close() 26 | 27 | 28 | @with_sitl 29 | def test_iterating(connpath): 30 | vehicle = connect(connpath, wait_ready=True) 31 | 32 | # Iterate over parameters. 33 | for k, v in vehicle.parameters.items(): 34 | break 35 | for key in vehicle.parameters: 36 | break 37 | 38 | vehicle.close() 39 | 40 | 41 | @with_sitl 42 | def test_setting(connpath): 43 | vehicle = connect(connpath, wait_ready=True) 44 | 45 | assert_not_equals(vehicle.parameters['THR_MIN'], None) 46 | 47 | result = {'success': False} 48 | 49 | @vehicle.parameters.on_attribute('THR_MIN') 50 | def listener(self, name, value): 51 | result['success'] = (name == 'THR_MIN' and value == 3.000) 52 | 53 | vehicle.parameters['THR_MIN'] = 3.000 54 | 55 | # Wait a bit. 56 | i = 5 57 | while not result['success'] and i > 0: 58 | time.sleep(1) 59 | i = i - 1 60 | 61 | assert_equals(result['success'], True) 62 | 63 | vehicle.close() 64 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # CircleCI 2.0 configuration file for dronekit-python 2 | 3 | # Steps common to both Python 2 and Python 3 4 | common: &commonsteps 5 | steps: 6 | - checkout 7 | 8 | - run: 9 | name: Install dronekit with dependencies 10 | command: | 11 | virtualenv venv 12 | source venv/bin/activate 13 | pip install -UI future 14 | pip install -r requirements.txt -UI 15 | pip install . -UI 16 | 17 | - run: 18 | name: Run dronekit tests 19 | environment: 20 | SITL_SPEEDUP: 10 21 | SITL_RATE: 200 22 | command: | 23 | source venv/bin/activate 24 | nosetests -svx dronekit.test.unit 25 | nosetests -svx dronekit.test.sitl 26 | 27 | - run: 28 | name: Build documentation 29 | command: | 30 | source venv/bin/activate 31 | cd docs; make clean; make html 32 | 33 | - store_artifacts: 34 | path: test-reports 35 | destination: test-reports 36 | 37 | # Jobs definition 38 | version: 2 39 | jobs: 40 | python2: 41 | docker: 42 | - image: circleci/python:2.7-stretch 43 | working_directory: ~/dronekit-python2 44 | <<: *commonsteps 45 | 46 | python3: 47 | docker: 48 | - image: circleci/python:3.6-stretch 49 | working_directory: ~/dronekit-python3 50 | <<: *commonsteps 51 | 52 | deploy: 53 | name: Deploy to dronekit.io 54 | working_directory: ~/deploy 55 | machine: true 56 | steps: 57 | - checkout 58 | - run: 59 | name: update docs 60 | command: ./scripts/update-docs.sh 61 | 62 | # Send a notification on Gitter 63 | notify: 64 | webhooks: 65 | - url: https://webhooks.gitter.im/e/27b0e1a9fede99dbbe6c 66 | 67 | # Workflow definition 68 | # Run Python 2 and Python 3 testing in parallel 69 | # and deploy if the Python 2 build succeeds 70 | workflows: 71 | version: 2 72 | build_test_deploy: 73 | jobs: 74 | - python2 75 | - python3 76 | # disabling until we find a new docs server 77 | # - deploy: 78 | # requires: 79 | # - python2 80 | # filters: 81 | # branches: 82 | # only: 83 | # - master 84 | -------------------------------------------------------------------------------- /examples/create_attribute/create_attribute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | © Copyright 2015-2016, 3D Robotics. 6 | 7 | create_attribute.py: 8 | 9 | Demonstrates how to create attributes from MAVLink messages within your DroneKit-Python script 10 | and use them in the same way as the built-in Vehicle attributes. 11 | 12 | The code adds a new attribute to the Vehicle class, populating it with information from RAW_IMU messages 13 | intercepted using the message_listener decorator. 14 | 15 | Full documentation is provided at http://python.dronekit.io/examples/create_attribute.html 16 | """ 17 | from __future__ import print_function 18 | 19 | from dronekit import connect, Vehicle 20 | from my_vehicle import MyVehicle #Our custom vehicle class 21 | import time 22 | 23 | 24 | 25 | #Set up option parsing to get connection string 26 | import argparse 27 | parser = argparse.ArgumentParser(description='Demonstrates how to create attributes from MAVLink messages. ') 28 | parser.add_argument('--connect', 29 | help="Vehicle connection target string. If not specified, SITL automatically started and used.") 30 | args = parser.parse_args() 31 | 32 | connection_string = args.connect 33 | sitl = None 34 | 35 | 36 | #Start SITL if no connection string specified 37 | if not connection_string: 38 | import dronekit_sitl 39 | sitl = dronekit_sitl.start_default() 40 | connection_string = sitl.connection_string() 41 | 42 | 43 | # Connect to the Vehicle 44 | print('Connecting to vehicle on: %s' % connection_string) 45 | vehicle = connect(connection_string, wait_ready=True, vehicle_class=MyVehicle) 46 | 47 | # Add observer for the custom attribute 48 | 49 | def raw_imu_callback(self, attr_name, value): 50 | # attr_name == 'raw_imu' 51 | # value == vehicle.raw_imu 52 | print(value) 53 | 54 | vehicle.add_attribute_listener('raw_imu', raw_imu_callback) 55 | 56 | print('Display RAW_IMU messages for 5 seconds and then exit.') 57 | time.sleep(5) 58 | 59 | #The message listener can be unset using ``vehicle.remove_message_listener`` 60 | 61 | #Close vehicle object before exiting script 62 | print("Close vehicle object") 63 | vehicle.close() 64 | 65 | # Shut down simulator if it was started. 66 | if sitl is not None: 67 | sitl.stop() 68 | -------------------------------------------------------------------------------- /docs/examples/running_examples.rst: -------------------------------------------------------------------------------- 1 | .. _running_examples_top: 2 | 3 | ==================== 4 | Running the Examples 5 | ==================== 6 | 7 | General instructions for running the `example source code `_ are given below. More explicit instructions are provided within the documentation for each example (and within the examples themselves by passing the ``-h`` (help) command line argument). 8 | 9 | .. tip:: 10 | 11 | The examples all launch the :ref:`dronekit-sitl ` simulator and connect to it by default. The ``--connect`` argument is used to instead specify the :ref:`connection string ` for a target vehicle or an externally managed SITL instance. 12 | 13 | To run the examples: 14 | 15 | #. :ref:`Install DroneKit-Python ` if you have not already done so! Install :ref:`dronekit-sitl ` if you want to test against simulated vehicles. 16 | 17 | #. Get the DroneKit-Python example source code onto your local machine. The easiest way to do this 18 | is to clone the **dronekit-python** repository from Github. 19 | 20 | On the command prompt enter: 21 | 22 | .. code-block:: bash 23 | 24 | git clone http://github.com/dronekit/dronekit-python.git 25 | 26 | 27 | 28 | #. Navigate to the example you wish to run (or specify the full path in the next step). The examples are all stored in 29 | subdirectories of **dronekit-python/examples/**. 30 | 31 | For example, to run the :ref:`vehicle_state ` example, you would navigate as shown: 32 | 33 | .. code-block:: bash 34 | 35 | cd dronekit-python/examples/vehicle_state/ 36 | 37 | 38 | #. Start the example as shown: 39 | 40 | * To connect to a simulator started/managed by the script: 41 | 42 | .. code-block:: bash 43 | 44 | python vehicle_state.py 45 | 46 | * To connect to a specific vehicle, pass its :ref:`connection string ` via the ``connect`` argument. 47 | For example, to run the example on Solo you would use the following command: 48 | 49 | .. code-block:: bash 50 | 51 | python vehicle_state.py --connect udpin:0.0.0.0:14550 52 | 53 | 54 | .. warning:: 55 | 56 | Propellers should be removed before testing examples indoors (on real vehicles). 57 | -------------------------------------------------------------------------------- /examples/gcs/microgcs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | © Copyright 2015-2016, 3D Robotics. 6 | """ 7 | from __future__ import print_function 8 | # 9 | # This is a small example of the python drone API - an ultra minimal GCS 10 | # 11 | 12 | from dronekit import connect, VehicleMode 13 | from pymavlink import mavutil 14 | from Tkinter import * 15 | 16 | # The tkinter root object 17 | global root 18 | 19 | #Set up option parsing to get connection string 20 | import argparse 21 | parser = argparse.ArgumentParser(description='Tracks GPS position of your computer (Linux only). Connects to SITL on local PC by default.') 22 | parser.add_argument('--connect', 23 | help="vehicle connection target.") 24 | args = parser.parse_args() 25 | 26 | connection_string = args.connect 27 | sitl = None 28 | 29 | #Start SITL if no connection string specified 30 | if not connection_string: 31 | import dronekit_sitl 32 | sitl = dronekit_sitl.start_default() 33 | connection_string = sitl.connection_string() 34 | 35 | # Connect to the Vehicle 36 | print('Connecting to vehicle on: %s' % connection_string) 37 | vehicle = connect(connection_string, wait_ready=True) 38 | 39 | def setMode(mode): 40 | # Now change the vehicle into auto mode 41 | vehicle.mode = VehicleMode(mode) 42 | 43 | 44 | def updateGUI(label, value): 45 | label['text'] = value 46 | 47 | def addObserverAndInit(name, cb): 48 | """We go ahead and call our observer once at startup to get an initial value""" 49 | vehicle.add_attribute_listener(name, cb) 50 | 51 | root = Tk() 52 | root.wm_title("microGCS - the worlds crummiest GCS") 53 | frame = Frame(root) 54 | frame.pack() 55 | 56 | locationLabel = Label(frame, text = "No location", width=60) 57 | locationLabel.pack() 58 | attitudeLabel = Label(frame, text = "No Att", width=60) 59 | attitudeLabel.pack() 60 | modeLabel = Label(frame, text = "mode") 61 | modeLabel.pack() 62 | 63 | addObserverAndInit('attitude', lambda vehicle, name, attitude: updateGUI(attitudeLabel, vehicle.attitude)) 64 | addObserverAndInit('location', lambda vehicle, name, location: updateGUI(locationLabel, str(location.global_frame))) 65 | addObserverAndInit('mode', lambda vehicle,name,mode: updateGUI(modeLabel, mode)) 66 | 67 | Button(frame, text = "Auto", command = lambda : setMode("AUTO")).pack() 68 | Button(frame, text = "RTL", command = lambda : setMode("RTL")).pack() 69 | 70 | root.mainloop() 71 | 72 | # Shut down simulator if it was started. 73 | if sitl is not None: 74 | sitl.stop() 75 | -------------------------------------------------------------------------------- /examples/drone_delivery/html/command.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include 'head.html' %} 5 | 6 | 7 |
    8 | {% import 'header.html' as header %} 9 |
    10 | 13 |

    DroneAPI Demos {{options.current_url}}

    14 |
    15 |
    16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 | 23 |
    24 |
    25 | 26 |
    27 | 28 |
    29 |
    30 |
    31 |
    32 |

    © 3D Robotics Inc.

    33 |
    34 | 35 | {% include 'bottom-scripts.html' %} 36 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_110.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dronekit import connect, VehicleMode 3 | from dronekit.test import with_sitl, wait_for 4 | from nose.tools import assert_equals 5 | 6 | 7 | @with_sitl 8 | def test_110(connpath): 9 | vehicle = connect(connpath, wait_ready=True) 10 | 11 | # NOTE these are *very inappropriate settings* 12 | # to make on a real vehicle. They are leveraged 13 | # exclusively for simulation. Take heed!!! 14 | vehicle.parameters['FS_GCS_ENABLE'] = 0 15 | vehicle.parameters['FS_EKF_THRESH'] = 100 16 | 17 | # Await armability. 18 | wait_for(lambda: vehicle.is_armable, 60) 19 | 20 | # Change the vehicle into STABILIZE mode 21 | vehicle.mode = VehicleMode("GUIDED") 22 | 23 | # NOTE wait crudely for ACK on mode update 24 | time.sleep(3) 25 | 26 | # Define example callback for mode 27 | def armed_callback(vehicle, attribute, value): 28 | armed_callback.called += 1 29 | 30 | armed_callback.called = 0 31 | 32 | # When the same (event, callback) pair is passed to add_attribute_listener, 33 | # only one instance of the observer callback should be added. 34 | vehicle.add_attribute_listener('armed', armed_callback) 35 | vehicle.add_attribute_listener('armed', armed_callback) 36 | vehicle.add_attribute_listener('armed', armed_callback) 37 | vehicle.add_attribute_listener('armed', armed_callback) 38 | vehicle.add_attribute_listener('armed', armed_callback) 39 | 40 | # arm and see update. 41 | vehicle.armed = True 42 | 43 | # Wait for ACK. 44 | time_max = 10 45 | wait_for(lambda: armed_callback.called, time_max) 46 | 47 | # Ensure the callback was called. 48 | assert armed_callback.called > 0, "Callback should have been called within %d seconds" % (time_max,) 49 | 50 | # Rmove all listeners. The first call should remove all listeners 51 | # we've added; the second call should be ignored and not throw. 52 | # NOTE: We test if armed_callback were treating adding each additional callback 53 | # and remove_attribute_listener were removing them one at a time; in this 54 | # case, there would be three callbacks still attached. 55 | vehicle.remove_attribute_listener('armed', armed_callback) 56 | vehicle.remove_attribute_listener('armed', armed_callback) 57 | callcount = armed_callback.called 58 | 59 | # Disarm and see update. 60 | vehicle.armed = False 61 | 62 | # Wait for ack 63 | time.sleep(3) 64 | 65 | # Ensure the callback was called zero times. 66 | assert_equals(armed_callback.called, callcount, 67 | "Callback should not have been called once removed.") 68 | 69 | vehicle.close() 70 | -------------------------------------------------------------------------------- /docs/guide/debugging.rst: -------------------------------------------------------------------------------- 1 | .. _debugging: 2 | 3 | ========= 4 | Debugging 5 | ========= 6 | 7 | DroneKit-Python apps can be debugged in the same way as any other standalone Python scripts. 8 | The sections below outline a few methods. 9 | 10 | 11 | 12 | pdb - The Python Debugger 13 | ========================= 14 | 15 | The `Python Debugger - pdb `_ can be used to debug *DroneKit-Python* apps. 16 | 17 | The command below can be used to run a script in debug mode: 18 | 19 | .. code-block:: bash 20 | 21 | python -m pdb my_dronekit_script.py 22 | 23 | You can also instrument your code to invoke the debugger at a certain point. To do this 24 | add ``set-trace()`` at the point where you want to break execution: 25 | 26 | .. code-block:: python 27 | :emphasize-lines: 4 28 | 29 | # Connect to the Vehicle on udp at 127.0.0.1:14550 30 | vehicle = connect('127.0.0.1:14550', wait_ready=True) 31 | 32 | import pdb; pdb.set_trace() 33 | print "Global Location: %s" % vehicle.location.global_frame 34 | 35 | 36 | The available `debugger commands are listed here `_. 37 | 38 | pudb - A full-screen, console-based Python debugger 39 | =================================================== 40 | 41 | If you prefer a IDE like debug you can use `pudb - A full-screen, console-based Python debugger `_. 42 | 43 | .. code-block:: python 44 | :emphasize-lines: 4 45 | 46 | pip install pudb 47 | 48 | 49 | To start debugging, simply insert: 50 | 51 | .. code-block:: python 52 | :emphasize-lines: 4 53 | 54 | from pudb import set_trace; set_trace() 55 | 56 | Insert either of these snippets into the piece of code you want to debug, or run the entire script with: 57 | 58 | .. code-block:: python 59 | :emphasize-lines: 4 60 | 61 | pudb my-script.py 62 | 63 | 64 | Print/log statements 65 | ==================== 66 | 67 | The simplest and most common method of debugging is to manually add debug/print statements to the source. 68 | 69 | .. code-block:: python 70 | :emphasize-lines: 4 71 | 72 | # Connect to the Vehicle on udp at 127.0.0.1:14550 73 | vehicle = connect('127.0.0.1:14550', wait_ready=True) 74 | 75 | # print out debug information 76 | print "Global Location: %s" % vehicle.location.global_frame 77 | 78 | In addition to printing DroneKit variables, Python provides numerous inbuilt and add-on modules/methods 79 | for inspecting code (e.g. `dir() `_, `traceback `_, etc.) 80 | 81 | 82 | Other IDEs/debuggers 83 | ==================== 84 | 85 | There is no reason you should not be able to straightforwardly use other popular Python IDEs including IDLE and Eclipse. 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /examples/drone_delivery/html/track.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include 'head.html' %} 5 | 6 | 7 |
    8 | {% import 'header.html' as header %} 9 |
    10 | 13 |

    DroneAPI Demos {{options.current_url}}

    14 |
    15 |
    16 | {% import 'mapbox.html' as mapbox%} 17 |
    18 | {{ mapbox.static_map_with_marker(options, options.current_coords, options.current_coords) }} 19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 | 26 |
    27 |
    28 | 29 |
    30 | 31 |
    32 |
    33 |
    34 |
    35 |

    © 3D Robotics Inc.

    36 |
    37 |
    38 | {% include 'bottom-scripts.html' %} 39 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_sensor_calibration.py: -------------------------------------------------------------------------------- 1 | from pymavlink import mavutil 2 | 3 | from dronekit import connect 4 | from dronekit.test import with_sitl 5 | 6 | from dronekit.test.sitl import assert_command_ack 7 | 8 | 9 | @with_sitl 10 | def test_gyro_calibration(connpath): 11 | """Request gyroscope calibration, and check for the COMMAND_ACK.""" 12 | 13 | vehicle = connect(connpath, wait_ready=True) 14 | 15 | with assert_command_ack(vehicle, mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION, timeout=30): 16 | vehicle.send_calibrate_gyro() 17 | 18 | vehicle.close() 19 | 20 | 21 | @with_sitl 22 | def test_magnetometer_calibration(connpath): 23 | """Request magnetometer calibration, and check for the COMMAND_ACK.""" 24 | 25 | vehicle = connect(connpath, wait_ready=True) 26 | 27 | with assert_command_ack( 28 | vehicle, 29 | mavutil.mavlink.MAV_CMD_DO_START_MAG_CAL, 30 | timeout=30, 31 | ack_result=mavutil.mavlink.MAV_RESULT_UNSUPPORTED, # TODO: change when APM is upgraded 32 | ): 33 | vehicle.send_calibrate_magnetometer() 34 | 35 | vehicle.close() 36 | 37 | 38 | @with_sitl 39 | def test_simple_accelerometer_calibration(connpath): 40 | """Request simple accelerometer calibration, and check for the COMMAND_ACK.""" 41 | 42 | vehicle = connect(connpath, wait_ready=True) 43 | 44 | with assert_command_ack( 45 | vehicle, 46 | mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION, 47 | timeout=30, 48 | ack_result=mavutil.mavlink.MAV_RESULT_FAILED, 49 | ): 50 | vehicle.send_calibrate_accelerometer(simple=True) 51 | 52 | vehicle.close() 53 | 54 | 55 | @with_sitl 56 | def test_accelerometer_calibration(connpath): 57 | """Request accelerometer calibration, and check for the COMMAND_ACK.""" 58 | 59 | vehicle = connect(connpath, wait_ready=True) 60 | 61 | # The calibration is expected to fail because in the SITL we don't tilt the Vehicle. 62 | # We just check that the command isn't denied or unsupported. 63 | with assert_command_ack( 64 | vehicle, 65 | mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION, 66 | timeout=30, 67 | ack_result=mavutil.mavlink.MAV_RESULT_FAILED, 68 | ): 69 | vehicle.send_calibrate_accelerometer(simple=False) 70 | 71 | vehicle.close() 72 | 73 | 74 | @with_sitl 75 | def test_board_level_calibration(connpath): 76 | """Request board level calibration, and check for the COMMAND_ACK.""" 77 | 78 | vehicle = connect(connpath, wait_ready=True) 79 | 80 | with assert_command_ack(vehicle, mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION, timeout=30): 81 | vehicle.send_calibrate_vehicle_level() 82 | 83 | vehicle.close() 84 | 85 | 86 | @with_sitl 87 | def test_barometer_calibration(connpath): 88 | """Request barometer calibration, and check for the COMMAND_ACK.""" 89 | 90 | vehicle = connect(connpath, wait_ready=True) 91 | 92 | with assert_command_ack(vehicle, mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION, timeout=30): 93 | vehicle.send_calibrate_barometer() 94 | 95 | vehicle.close() 96 | -------------------------------------------------------------------------------- /docs/about/overview.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | About DroneKit 3 | ============== 4 | 5 | DroneKit-Python allows developers to create apps that run on an onboard :ref:`companion computer ` and communicate with the `ArduPilot `_ flight controller using a low-latency link. Onboard apps can significantly enhance the autopilot, adding greater intelligence to vehicle behaviour, and performing tasks that are computationally intensive or time-sensitive (for example, computer vision, path planning, or 3D modelling). DroneKit-Python can also be used for ground station apps, communicating with vehicles over a higher latency RF-link. 6 | 7 | The API communicates with vehicles over MAVLink. It provides programmatic access to a connected vehicle's telemetry, state and parameter information, and enables both mission management and direct control over vehicle movement and operations. 8 | 9 | 10 | 11 | Open source community 12 | ===================== 13 | 14 | DroneKit-Python is an open source and community-driven project. 15 | 16 | You can find all the source code on `Github here `_ and check out our permissive :doc:`Apache v2 Licence `. 17 | If you want to join the community, then see our :doc:`contributing section <../contributing/index>` for lots of ideas on how you can help. 18 | 19 | 20 | Compatibility 21 | ============= 22 | DroneKit-Python is compatible with vehicles that communicate using the `MAVLink protocol `_ (including most vehicles made by `3DR `_ and other members of the `DroneCode foundation `_). It runs on Linux, Mac OS X, or Windows. 23 | 24 | .. note:: 25 | 26 | DroneKit-Python is validated against, and hence *most compatible* with, the `ArduPilot UAV Platform `_. 27 | Vehicles running other autopilots may be be less compatible due to differences in adhererence/interpretation of the MAVLink specification. 28 | Please report any autopilot-specific issues `on Github here `_. 29 | 30 | 31 | 32 | API features 33 | ============ 34 | 35 | 36 | The API provides classes and methods to: 37 | 38 | - Connect to a vehicle (or multiple vehicles) from a script 39 | - Get and set vehicle state/telemetry and parameter information. 40 | - Receive asynchronous notification of state changes. 41 | - Guide a UAV to specified position (GUIDED mode). 42 | - Send arbitrary custom messages to control UAV movement and other hardware (GUIDED mode). 43 | - Create and manage waypoint missions (AUTO mode). 44 | - Override RC channel settings. 45 | 46 | A complete API reference is available :ref:`here `. 47 | 48 | 49 | Technical support 50 | ================= 51 | 52 | This documentation is a great place to get started with developing DroneKit Python APIs. 53 | 54 | If you run into problems, the best place to ask questions is the `DroneKit-Python Forum `_. 55 | If your problem turns out to be a bug, then it should be `posted on Github `_. 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_goto.py: -------------------------------------------------------------------------------- 1 | """ 2 | simple_goto.py: GUIDED mode "simple goto" example (Copter Only) 3 | 4 | The example demonstrates how to arm and takeoff in Copter and how to navigate to 5 | points using Vehicle.simple_goto. 6 | 7 | Full documentation is provided at http://python.dronekit.io/examples/simple_goto.html 8 | """ 9 | 10 | import time 11 | from dronekit import connect, VehicleMode, LocationGlobalRelative 12 | from dronekit.test import with_sitl 13 | from nose.tools import assert_equals 14 | 15 | 16 | @with_sitl 17 | def test_goto(connpath): 18 | vehicle = connect(connpath, wait_ready=True) 19 | 20 | # NOTE these are *very inappropriate settings* 21 | # to make on a real vehicle. They are leveraged 22 | # exclusively for simulation. Take heed!!! 23 | vehicle.parameters['FS_GCS_ENABLE'] = 0 24 | vehicle.parameters['FS_EKF_THRESH'] = 100 25 | 26 | def arm_and_takeoff(aTargetAltitude): 27 | """ 28 | Arms vehicle and fly to aTargetAltitude. 29 | """ 30 | 31 | # Don't let the user try to fly autopilot is booting 32 | i = 60 33 | while not vehicle.is_armable and i > 0: 34 | time.sleep(1) 35 | i = i - 1 36 | assert_equals(vehicle.is_armable, True) 37 | 38 | # Copter should arm in GUIDED mode 39 | vehicle.mode = VehicleMode("GUIDED") 40 | i = 60 41 | while vehicle.mode.name != 'GUIDED' and i > 0: 42 | # print " Waiting for guided %s seconds..." % (i,) 43 | time.sleep(1) 44 | i = i - 1 45 | assert_equals(vehicle.mode.name, 'GUIDED') 46 | 47 | # Arm copter. 48 | vehicle.armed = True 49 | i = 60 50 | while not vehicle.armed and vehicle.mode.name == 'GUIDED' and i > 0: 51 | # print " Waiting for arming %s seconds..." % (i,) 52 | time.sleep(1) 53 | i = i - 1 54 | assert_equals(vehicle.armed, True) 55 | 56 | # Take off to target altitude 57 | vehicle.simple_takeoff(aTargetAltitude) 58 | 59 | # Wait until the vehicle reaches a safe height before 60 | # processing the goto (otherwise the command after 61 | # Vehicle.simple_takeoff will execute immediately). 62 | while True: 63 | # print " Altitude: ", vehicle.location.global_relative_frame.alt 64 | # Test for altitude just below target, in case of undershoot. 65 | if vehicle.location.global_relative_frame.alt >= aTargetAltitude * 0.95: 66 | # print "Reached target altitude" 67 | break 68 | 69 | assert_equals(vehicle.mode.name, 'GUIDED') 70 | time.sleep(1) 71 | 72 | arm_and_takeoff(10) 73 | 74 | # print "Going to first point..." 75 | point1 = LocationGlobalRelative(-35.361354, 149.165218, 20) 76 | vehicle.simple_goto(point1) 77 | 78 | # sleep so we can see the change in map 79 | time.sleep(3) 80 | 81 | # print "Going to second point..." 82 | point2 = LocationGlobalRelative(-35.363244, 149.168801, 20) 83 | vehicle.simple_goto(point2) 84 | 85 | # sleep so we can see the change in map 86 | time.sleep(3) 87 | 88 | # print "Returning to Launch" 89 | vehicle.mode = VehicleMode("RTL") 90 | 91 | vehicle.close() 92 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_locations.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dronekit import connect, VehicleMode 3 | from dronekit.test import with_sitl, wait_for 4 | from nose.tools import assert_equals, assert_not_equals 5 | 6 | 7 | @with_sitl 8 | def test_timeout(connpath): 9 | vehicle = connect(connpath, wait_ready=True) 10 | 11 | # NOTE these are *very inappropriate settings* 12 | # to make on a real vehicle. They are leveraged 13 | # exclusively for simulation. Take heed!!! 14 | vehicle.parameters['FS_GCS_ENABLE'] = 0 15 | vehicle.parameters['FS_EKF_THRESH'] = 100 16 | 17 | def arm_and_takeoff(aTargetAltitude): 18 | """ 19 | Arms vehicle and fly to aTargetAltitude. 20 | """ 21 | 22 | # Don't let the user try to fly autopilot is booting 23 | wait_for(lambda: vehicle.is_armable, 60) 24 | assert_equals(vehicle.is_armable, True) 25 | 26 | # Copter should arm in GUIDED mode 27 | vehicle.mode = VehicleMode("GUIDED") 28 | wait_for(lambda: vehicle.mode.name == 'GUIDED', 60) 29 | assert_equals(vehicle.mode.name, 'GUIDED') 30 | 31 | # Arm copter. 32 | vehicle.armed = True 33 | wait_for(lambda: vehicle.armed, 60) 34 | assert_equals(vehicle.armed, True) 35 | 36 | # Take off to target altitude 37 | vehicle.simple_takeoff(aTargetAltitude) 38 | 39 | # Wait until the vehicle reaches a safe height before 40 | # processing the goto (otherwise the command after 41 | # Vehicle.simple_takeoff will execute immediately). 42 | while True: 43 | # print " Altitude: ", vehicle.location.alt 44 | # Test for altitude just below target, in case of undershoot. 45 | if vehicle.location.global_frame.alt >= aTargetAltitude * 0.95: 46 | # print "Reached target altitude" 47 | break 48 | 49 | assert_equals(vehicle.mode.name, 'GUIDED') 50 | time.sleep(1) 51 | 52 | arm_and_takeoff(10) 53 | vehicle.wait_ready('location.local_frame', timeout=60) 54 | 55 | # .north, .east, and .down are initialized to None. 56 | # Any other value suggests that a LOCAL_POSITION_NED was received and parsed. 57 | assert_not_equals(vehicle.location.local_frame.north, None) 58 | assert_not_equals(vehicle.location.local_frame.east, None) 59 | assert_not_equals(vehicle.location.local_frame.down, None) 60 | 61 | # global_frame 62 | assert_not_equals(vehicle.location.global_frame.lat, None) 63 | assert_not_equals(vehicle.location.global_frame.lon, None) 64 | assert_not_equals(vehicle.location.global_frame.alt, None) 65 | assert_equals(type(vehicle.location.global_frame.lat), float) 66 | assert_equals(type(vehicle.location.global_frame.lon), float) 67 | assert_equals(type(vehicle.location.global_frame.alt), float) 68 | 69 | vehicle.close() 70 | 71 | 72 | @with_sitl 73 | def test_location_notify(connpath): 74 | vehicle = connect(connpath) 75 | 76 | ret = {'success': False} 77 | 78 | @vehicle.location.on_attribute('global_frame') 79 | def callback(*args): 80 | assert_not_equals(args[2].alt, 0) 81 | ret['success'] = True 82 | 83 | wait_for(lambda: ret['success'], 30) 84 | 85 | assert ret['success'], 'Expected location object to emit notifications.' 86 | 87 | vehicle.close() 88 | -------------------------------------------------------------------------------- /docs/guide/mavlink_messages.rst: -------------------------------------------------------------------------------- 1 | .. _mavlink_messages: 2 | 3 | ================ 4 | MAVLink Messages 5 | ================ 6 | 7 | Some useful MAVLink messages sent by the autopilot are not (yet) directly available to DroneKit-Python scripts 8 | through the :ref:`observable attributes ` in :py:class:`Vehicle `. 9 | 10 | This topic shows how you can intercept specific MAVLink messages by defining a listener callback function 11 | using the :py:func:`Vehicle.on_message() ` decorator. 12 | 13 | .. tip:: 14 | 15 | :ref:`example_create_attribute` shows how you can extend this approach to create a new :py:class:`Vehicle ` 16 | attribute in your client code. 17 | 18 | 19 | .. _mavlink_messages_message_listener: 20 | .. _mavlink_messages_set_mavlink_callback: 21 | 22 | Creating a message listener 23 | =========================== 24 | 25 | The :py:func:`Vehicle.on_message() ` decorator can be used to 26 | set a particular function as the callback handler for a particular message type. You can create listeners 27 | for as many messages as you like, or even multiple listeners for the same message. 28 | 29 | For example, the code snippet below shows how to set a listener for the ``RANGEFINDER`` message: 30 | 31 | .. code:: python 32 | 33 | #Create a message listener using the decorator. 34 | @vehicle.on_message('RANGEFINDER') 35 | def listener(self, name, message): 36 | print message 37 | 38 | .. tip:: 39 | 40 | Every single listener can have the same name/prototpye as above ("``listener``") because 41 | Python does not notice the decorated functions are in the same scope. 42 | 43 | Unfortunately this also means that you can't unregister a callback created using this method. 44 | 45 | The messages are `classes `_ from the 46 | `pymavlink `_ library. 47 | The output of the code above looks something like: 48 | 49 | .. code:: bash 50 | 51 | RANGEFINDER {distance : 0.0899999961257, voltage : 0.00900000054389} 52 | ... 53 | 54 | You can access the message fields directly. For example, to access the ``RANGEFINDER`` message your listener 55 | function might look like this: 56 | 57 | .. code:: python 58 | 59 | #Create a message listener using the decorator. 60 | @vehicle.on_message('RANGEFINDER') 61 | def listener(self, name, message): 62 | print 'distance: %s' % message.distance 63 | print 'voltage: %s' % message.voltage 64 | 65 | 66 | Watching all messages 67 | ===================== 68 | 69 | You can register a callback for *all messages* by setting the message name as the wildcard string ('``*``'): 70 | 71 | .. code:: python 72 | 73 | #Create a message listener for all messages. 74 | @vehicle.on_message('*') 75 | def listener(self, name, message): 76 | print 'message: %s' % message 77 | 78 | 79 | Removing an observer 80 | ==================== 81 | 82 | Callbacks registered using the :py:func:`Vehicle.on_message() ` decorator *cannot be removed*. 83 | This is generally not a problem, because in most cases you're interested in messages for the lifetime of a session. 84 | 85 | If you do need to be able to remove messages you can instead add the callback using 86 | :py:func:`Vehicle.add_message_listener `, and then remove it by calling 87 | :py:func:`Vehicle.remove_message_listener `. 88 | -------------------------------------------------------------------------------- /windows/dronekit_installer.iss: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 3 | 4 | #define MyAppName "DroneKit" 5 | ; #define MyAppVersion "1" 6 | #define MyAppPublisher "3D Robotics, Inc" 7 | #define MyAppURL "https://github.com/dronekit/dronekit-python" 8 | #define MyAppExeName "" 9 | 10 | [Setup] 11 | ; NOTE: The value of AppId uniquely identifies this application. 12 | ; Do not use the same AppId value in installers for other applications. 13 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 14 | AppId={{35EE5962-C212-4874-90EC-50863DD1537D} 15 | AppName={#MyAppName} 16 | AppVersion={#MyAppVersion} 17 | ;AppVerName={#MyAppName} {#MyAppVersion} 18 | AppPublisher={#MyAppPublisher} 19 | AppPublisherURL={#MyAppURL} 20 | AppSupportURL={#MyAppURL} 21 | AppUpdatesURL={#MyAppURL} 22 | CreateAppDir=no 23 | OutputBaseFilename=DroneKitsetup-{#MyAppVersion} 24 | Compression=lzma 25 | SolidCompression=yes 26 | LicenseFile=..\LICENSE 27 | DisableDirPage=yes 28 | 29 | [Languages] 30 | Name: "english"; MessagesFile: "compiler:Default.isl" 31 | 32 | [Files] 33 | Source: "..\dronekit\*"; DestDir: "{code:GetMAVProxyPath}\dronekit"; Flags: ignoreversion recursesubdirs createallsubdirs 34 | Source: "..\examples\*"; DestDir: "{code:GetMAVProxyPath}\examples"; Flags: ignoreversion recursesubdirs createallsubdirs 35 | 36 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 37 | 38 | ; Check if MAVProxy is installed (if so, get the install path) 39 | [Code] 40 | function IsMAVProxyInstalled: boolean; 41 | begin 42 | result := RegKeyExists(HKEY_LOCAL_MACHINE, 43 | 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{D81B9EDA-1357-462E-96E4-B47372709F7C}_is1'); 44 | end; 45 | 46 | function GetMAVProxyPath(Dummy: string): string; 47 | var 48 | sInstallPath: string; 49 | MAVProxyPath: string; 50 | begin 51 | MAVProxyPath := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{D81B9EDA-1357-462E-96E4-B47372709F7C}_is1' 52 | sInstallPath := ''; 53 | RegQueryStringValue(HKLM, MAVProxyPath, 'InstallLocation', sInstallPath); 54 | Result := sInstallPath; 55 | end; 56 | 57 | function InitializeSetup: boolean; 58 | begin 59 | result := IsMAVProxyInstalled; 60 | if not result then 61 | MsgBox('You need to install MAVProxy before you install DroneKit. Install MAVProxy and then run this installer again.', mbError, MB_OK); 62 | end; 63 | 64 | function MAVDir_CreatePage(PreviousPageId: Integer): Integer; 65 | var 66 | Page: TWizardPage; 67 | Label1: TLabel; 68 | MAVDir: TEdit; 69 | begin 70 | Page := CreateCustomPage( 71 | PreviousPageId, 72 | 'Installation Directory', 73 | '' 74 | ); 75 | 76 | Label1 := TLabel.Create(Page); 77 | with Label1 do 78 | begin 79 | Parent := Page.Surface; 80 | Caption := 'DroneKit will be installed in the MAVProxy directory:' 81 | Left := ScaleX(16); 82 | Top := ScaleY(0); 83 | Width := ScaleX(300); 84 | Height := ScaleY(17); 85 | end; 86 | 87 | MAVDir := TEdit.Create(Page); 88 | with MAVDir do 89 | begin 90 | Parent := Page.Surface; 91 | Left := ScaleX(16); 92 | Top := ScaleY(24); 93 | Width := ScaleX(300); 94 | Height := ScaleY(25); 95 | TabOrder := 0; 96 | Text := GetMAVProxyPath(''); 97 | Enabled := False; 98 | end 99 | 100 | end; 101 | 102 | procedure InitializeWizard(); 103 | begin 104 | MAVDir_CreatePage(wpLicense); 105 | end; -------------------------------------------------------------------------------- /examples/simple_goto/simple_goto.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | © Copyright 2015-2016, 3D Robotics. 6 | simple_goto.py: GUIDED mode "simple goto" example (Copter Only) 7 | 8 | Demonstrates how to arm and takeoff in Copter and how to navigate to points using Vehicle.simple_goto. 9 | 10 | Full documentation is provided at http://python.dronekit.io/examples/simple_goto.html 11 | """ 12 | 13 | from __future__ import print_function 14 | import time 15 | from dronekit import connect, VehicleMode, LocationGlobalRelative 16 | 17 | 18 | # Set up option parsing to get connection string 19 | import argparse 20 | parser = argparse.ArgumentParser(description='Commands vehicle using vehicle.simple_goto.') 21 | parser.add_argument('--connect', 22 | help="Vehicle connection target string. If not specified, SITL automatically started and used.") 23 | args = parser.parse_args() 24 | 25 | connection_string = args.connect 26 | sitl = None 27 | 28 | 29 | # Start SITL if no connection string specified 30 | if not connection_string: 31 | import dronekit_sitl 32 | sitl = dronekit_sitl.start_default() 33 | connection_string = sitl.connection_string() 34 | 35 | 36 | # Connect to the Vehicle 37 | print('Connecting to vehicle on: %s' % connection_string) 38 | vehicle = connect(connection_string, wait_ready=True) 39 | 40 | 41 | def arm_and_takeoff(aTargetAltitude): 42 | """ 43 | Arms vehicle and fly to aTargetAltitude. 44 | """ 45 | 46 | print("Basic pre-arm checks") 47 | # Don't try to arm until autopilot is ready 48 | while not vehicle.is_armable: 49 | print(" Waiting for vehicle to initialise...") 50 | time.sleep(1) 51 | 52 | print("Arming motors") 53 | # Copter should arm in GUIDED mode 54 | vehicle.mode = VehicleMode("GUIDED") 55 | vehicle.armed = True 56 | 57 | # Confirm vehicle armed before attempting to take off 58 | while not vehicle.armed: 59 | print(" Waiting for arming...") 60 | time.sleep(1) 61 | 62 | print("Taking off!") 63 | vehicle.simple_takeoff(aTargetAltitude) # Take off to target altitude 64 | 65 | # Wait until the vehicle reaches a safe height before processing the goto 66 | # (otherwise the command after Vehicle.simple_takeoff will execute 67 | # immediately). 68 | while True: 69 | print(" Altitude: ", vehicle.location.global_relative_frame.alt) 70 | # Break and return from function just below target altitude. 71 | if vehicle.location.global_relative_frame.alt >= aTargetAltitude * 0.95: 72 | print("Reached target altitude") 73 | break 74 | time.sleep(1) 75 | 76 | 77 | arm_and_takeoff(10) 78 | 79 | print("Set default/target airspeed to 3") 80 | vehicle.airspeed = 3 81 | 82 | print("Going towards first point for 30 seconds ...") 83 | point1 = LocationGlobalRelative(-35.361354, 149.165218, 20) 84 | vehicle.simple_goto(point1) 85 | 86 | # sleep so we can see the change in map 87 | time.sleep(30) 88 | 89 | print("Going towards second point for 30 seconds (groundspeed set to 10 m/s) ...") 90 | point2 = LocationGlobalRelative(-35.363244, 149.168801, 20) 91 | vehicle.simple_goto(point2, groundspeed=10) 92 | 93 | # sleep so we can see the change in map 94 | time.sleep(30) 95 | 96 | print("Returning to Launch") 97 | vehicle.mode = VehicleMode("RTL") 98 | 99 | # Close vehicle object before exiting script 100 | print("Close vehicle object") 101 | vehicle.close() 102 | 103 | # Shut down simulator if it was started. 104 | if sitl: 105 | sitl.stop() 106 | -------------------------------------------------------------------------------- /examples/create_attribute/my_vehicle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | © Copyright 2015-2016, 3D Robotics. 6 | 7 | my_vehicle.py: 8 | 9 | Custom Vehicle subclass to add IMU data. 10 | """ 11 | 12 | from dronekit import Vehicle 13 | 14 | 15 | class RawIMU(object): 16 | """ 17 | The RAW IMU readings for the usual 9DOF sensor setup. 18 | This contains the true raw values without any scaling to allow data capture and system debugging. 19 | 20 | The message definition is here: https://mavlink.io/en/messages/common.html#RAW_IMU 21 | 22 | :param time_boot_us: Timestamp (microseconds since system boot). #Note, not milliseconds as per spec 23 | :param xacc: X acceleration (mg) 24 | :param yacc: Y acceleration (mg) 25 | :param zacc: Z acceleration (mg) 26 | :param xgyro: Angular speed around X axis (millirad /sec) 27 | :param ygyro: Angular speed around Y axis (millirad /sec) 28 | :param zgyro: Angular speed around Z axis (millirad /sec) 29 | :param xmag: X Magnetic field (milli tesla) 30 | :param ymag: Y Magnetic field (milli tesla) 31 | :param zmag: Z Magnetic field (milli tesla) 32 | """ 33 | def __init__(self, time_boot_us=None, xacc=None, yacc=None, zacc=None, xygro=None, ygyro=None, zgyro=None, xmag=None, ymag=None, zmag=None): 34 | """ 35 | RawIMU object constructor. 36 | """ 37 | self.time_boot_us = time_boot_us 38 | self.xacc = xacc 39 | self.yacc = yacc 40 | self.zacc = zacc 41 | self.xgyro = zgyro 42 | self.ygyro = ygyro 43 | self.zgyro = zgyro 44 | self.xmag = xmag 45 | self.ymag = ymag 46 | self.zmag = zmag 47 | 48 | def __str__(self): 49 | """ 50 | String representation used to print the RawIMU object. 51 | """ 52 | return "RAW_IMU: time_boot_us={},xacc={},yacc={},zacc={},xgyro={},ygyro={},zgyro={},xmag={},ymag={},zmag={}".format(self.time_boot_us, self.xacc, self.yacc,self.zacc,self.xgyro,self.ygyro,self.zgyro,self.xmag,self.ymag,self.zmag) 53 | 54 | 55 | class MyVehicle(Vehicle): 56 | def __init__(self, *args): 57 | super(MyVehicle, self).__init__(*args) 58 | 59 | # Create an Vehicle.raw_imu object with initial values set to None. 60 | self._raw_imu = RawIMU() 61 | 62 | # Create a message listener using the decorator. 63 | @self.on_message('RAW_IMU') 64 | def listener(self, name, message): 65 | """ 66 | The listener is called for messages that contain the string specified in the decorator, 67 | passing the vehicle, message name, and the message. 68 | 69 | The listener writes the message to the (newly attached) ``vehicle.raw_imu`` object 70 | and notifies observers. 71 | """ 72 | self._raw_imu.time_boot_us=message.time_usec 73 | self._raw_imu.xacc=message.xacc 74 | self._raw_imu.yacc=message.yacc 75 | self._raw_imu.zacc=message.zacc 76 | self._raw_imu.xgyro=message.xgyro 77 | self._raw_imu.ygyro=message.ygyro 78 | self._raw_imu.zgyro=message.zgyro 79 | self._raw_imu.xmag=message.xmag 80 | self._raw_imu.ymag=message.ymag 81 | self._raw_imu.zmag=message.zmag 82 | 83 | # Notify all observers of new message (with new value) 84 | # Note that argument `cache=False` by default so listeners 85 | # are updated with every new message 86 | self.notify_attribute_listeners('raw_imu', self._raw_imu) 87 | 88 | @property 89 | def raw_imu(self): 90 | return self._raw_imu 91 | -------------------------------------------------------------------------------- /examples/channel_overrides/channel_overrides.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | © Copyright 2015-2016, 3D Robotics. 6 | 7 | channel_overrides.py: 8 | 9 | Demonstrates how set and clear channel-override information. 10 | 11 | # NOTE: 12 | Channel overrides (a.k.a "RC overrides") are highly discommended (they are primarily implemented 13 | for simulating user input and when implementing certain types of joystick control). 14 | 15 | They are provided for development purposes. Please raise an issue explaining why you need them 16 | and we will try to find a better alternative: https://github.com/dronekit/dronekit-python/issues 17 | 18 | Full documentation is provided at http://python.dronekit.io/examples/channel_overrides.html 19 | """ 20 | from __future__ import print_function 21 | from dronekit import connect 22 | 23 | 24 | #Set up option parsing to get connection string 25 | import argparse 26 | parser = argparse.ArgumentParser(description='Example showing how to set and clear vehicle channel-override information.') 27 | parser.add_argument('--connect', 28 | help="vehicle connection target string. If not specified, SITL automatically started and used.") 29 | args = parser.parse_args() 30 | 31 | connection_string = args.connect 32 | sitl = None 33 | 34 | 35 | #Start SITL if no connection string specified 36 | if not connection_string: 37 | import dronekit_sitl 38 | sitl = dronekit_sitl.start_default() 39 | connection_string = sitl.connection_string() 40 | 41 | 42 | # Connect to the Vehicle 43 | print('Connecting to vehicle on: %s' % connection_string) 44 | vehicle = connect(connection_string, wait_ready=True) 45 | 46 | # Get all original channel values (before override) 47 | print("Channel values from RC Tx:", vehicle.channels) 48 | 49 | # Access channels individually 50 | print("Read channels individually:") 51 | print(" Ch1: %s" % vehicle.channels['1']) 52 | print(" Ch2: %s" % vehicle.channels['2']) 53 | print(" Ch3: %s" % vehicle.channels['3']) 54 | print(" Ch4: %s" % vehicle.channels['4']) 55 | print(" Ch5: %s" % vehicle.channels['5']) 56 | print(" Ch6: %s" % vehicle.channels['6']) 57 | print(" Ch7: %s" % vehicle.channels['7']) 58 | print(" Ch8: %s" % vehicle.channels['8']) 59 | print("Number of channels: %s" % len(vehicle.channels)) 60 | 61 | 62 | # Override channels 63 | print("\nChannel overrides: %s" % vehicle.channels.overrides) 64 | 65 | print("Set Ch2 override to 200 (indexing syntax)") 66 | vehicle.channels.overrides['2'] = 200 67 | print(" Channel overrides: %s" % vehicle.channels.overrides) 68 | print(" Ch2 override: %s" % vehicle.channels.overrides['2']) 69 | 70 | print("Set Ch3 override to 300 (dictionary syntax)") 71 | vehicle.channels.overrides = {'3':300} 72 | print(" Channel overrides: %s" % vehicle.channels.overrides) 73 | 74 | print("Set Ch1-Ch8 overrides to 110-810 respectively") 75 | vehicle.channels.overrides = {'1': 110, '2': 210,'3': 310,'4':4100, '5':510,'6':610,'7':710,'8':810} 76 | print(" Channel overrides: %s" % vehicle.channels.overrides) 77 | 78 | 79 | # Clear override by setting channels to None 80 | print("\nCancel Ch2 override (indexing syntax)") 81 | vehicle.channels.overrides['2'] = None 82 | print(" Channel overrides: %s" % vehicle.channels.overrides) 83 | 84 | print("Clear Ch3 override (del syntax)") 85 | del vehicle.channels.overrides['3'] 86 | print(" Channel overrides: %s" % vehicle.channels.overrides) 87 | 88 | print("Clear Ch5, Ch6 override and set channel 3 to 500 (dictionary syntax)") 89 | vehicle.channels.overrides = {'5':None, '6':None,'3':500} 90 | print(" Channel overrides: %s" % vehicle.channels.overrides) 91 | 92 | print("Clear all overrides") 93 | vehicle.channels.overrides = {} 94 | print(" Channel overrides: %s" % vehicle.channels.overrides) 95 | 96 | #Close vehicle object before exiting script 97 | print("\nClose vehicle object") 98 | vehicle.close() 99 | 100 | # Shut down simulator if it was started. 101 | if sitl is not None: 102 | sitl.stop() 103 | 104 | print("Completed") 105 | -------------------------------------------------------------------------------- /docs/guide/quick_start.rst: -------------------------------------------------------------------------------- 1 | .. _quick_start_top: 2 | 3 | =========== 4 | Quick Start 5 | =========== 6 | 7 | This topic shows how to quickly install a DroneKit-Python 8 | *development environment* and run a simple example to get 9 | vehicle attributes from a *simulated* Copter. 10 | 11 | 12 | Installation 13 | ============ 14 | 15 | DroneKit-Python and the *dronekit-sitl simulator* are installed 16 | from **pip** on all platforms. 17 | 18 | On Linux you will first need to install **pip** and **python-dev**: 19 | 20 | .. code-block:: bash 21 | 22 | sudo apt-get install python-pip python-dev 23 | 24 | 25 | **pip** is then used to install *dronekit* and *dronekit-sitl*. 26 | Mac and Linux may require you to prefix these commands with ``sudo``: 27 | 28 | .. code-block:: bash 29 | 30 | pip install dronekit 31 | pip install dronekit-sitl 32 | 33 | See :doc:`../develop/installation` and `dronekit-sitl `_ 34 | for more detailed installation instructions. 35 | 36 | 37 | Basic "Hello Drone" 38 | =================== 39 | 40 | The script below first launches the simulator. It then 41 | imports and calls the :py:func:`connect() ` method, 42 | specifying the simulator's connection string (``tcp:127.0.0.1:5760``). 43 | The method returns a :py:class:`Vehicle ` object that 44 | we then use to query the attributes. 45 | 46 | .. code:: python 47 | 48 | print "Start simulator (SITL)" 49 | import dronekit_sitl 50 | sitl = dronekit_sitl.start_default() 51 | connection_string = sitl.connection_string() 52 | 53 | # Import DroneKit-Python 54 | from dronekit import connect, VehicleMode 55 | 56 | # Connect to the Vehicle. 57 | print("Connecting to vehicle on: %s" % (connection_string,)) 58 | vehicle = connect(connection_string, wait_ready=True) 59 | 60 | # Get some vehicle attributes (state) 61 | print "Get some vehicle attribute values:" 62 | print " GPS: %s" % vehicle.gps_0 63 | print " Battery: %s" % vehicle.battery 64 | print " Last Heartbeat: %s" % vehicle.last_heartbeat 65 | print " Is Armable?: %s" % vehicle.is_armable 66 | print " System status: %s" % vehicle.system_status.state 67 | print " Mode: %s" % vehicle.mode.name # settable 68 | 69 | # Close vehicle object before exiting script 70 | vehicle.close() 71 | 72 | # Shut down simulator 73 | sitl.stop() 74 | print("Completed") 75 | 76 | 77 | Copy the text above into a new text file (**hello.py**) and run it in the same way 78 | as you would any other standalone Python script. 79 | 80 | .. code-block:: bash 81 | 82 | python hello.py 83 | 84 | You should see the following output from the simulated vehicle: 85 | 86 | .. code-block:: bash 87 | 88 | Start simulator (SITL) 89 | Downloading SITL from http://dronekit-assets.s3.amazonaws.com/sitl/copter/sitl-win-copter-3.3.tar.gz 90 | Extracted. 91 | Connecting to vehicle on: 'tcp:127.0.0.1:5760' 92 | >>> APM:Copter V3.3 (d6053245) 93 | >>> Frame: QUAD 94 | >>> Calibrating barometer 95 | >>> Initialising APM... 96 | >>> barometer calibration complete 97 | >>> GROUND START 98 | Get some vehicle attribute values: 99 | GPS: GPSInfo:fix=3,num_sat=10 100 | Battery: Battery:voltage=12.587,current=0.0,level=100 101 | Last Heartbeat: 0.713999986649 102 | Is Armable?: False 103 | System status: STANDBY 104 | Mode: STABILIZE 105 | Completed 106 | 107 | That's it- you've run your first DroneKit-Python script. 108 | 109 | Next Steps 110 | ========== 111 | 112 | * Learn more about :doc:`../develop/index`. 113 | This covers development best practices and coding standards, 114 | and has more information about installation, working with a simulator 115 | and setting up a companion computer. 116 | * Read through our step by step :doc:`index` to learn how to connect to your 117 | vehicle, takeoff, fly, and much more. 118 | * Check out our :doc:`../examples/index`. 119 | -------------------------------------------------------------------------------- /docs/guide/connecting_vehicle.rst: -------------------------------------------------------------------------------- 1 | .. _connecting_vehicle: 2 | .. _get_started_connecting: 3 | 4 | ======================= 5 | Connecting to a Vehicle 6 | ======================= 7 | 8 | The connection to the vehicle (or multiple vehicles) is set up within the 9 | DroneKit script. Scripts import and call the :py:func:`connect() ` 10 | method. After connecting this returns a :py:class:`Vehicle ` 11 | object from which you can get/set parameters and attributes, and control vehicle movement. 12 | 13 | The most common way to call :py:func:`connect() ` is shown below: 14 | 15 | .. code-block:: python 16 | 17 | from dronekit import connect 18 | 19 | # Connect to the Vehicle (in this case a UDP endpoint) 20 | vehicle = connect('127.0.0.1:14550', wait_ready=True) 21 | 22 | The first parameter specifies the target address (in this case the loopback 23 | address for UDP port 14550). See :ref:`get_started_connect_string` for the strings to use for 24 | other common vehicles. 25 | 26 | The second parameter (``wait_ready``) is used to determine whether ``connect()`` returns immediately 27 | on connection or if it waits until *some* vehicle parameters and attributes are populated. In most cases you 28 | should use ``wait_ready=True`` to wait on the default set of parameters. 29 | 30 | Connecting over a serial device will look something like this: 31 | 32 | .. code-block:: python 33 | 34 | from dronekit import connect 35 | 36 | # Connect to the Vehicle (in this case a serial endpoint) 37 | vehicle = connect('/dev/ttyAMA0', wait_ready=True, baud=57600) 38 | 39 | .. tip:: 40 | 41 | If the baud rate is not set correctly, ``connect`` may fail with a 42 | timeout error. It is best to set the baud rate explicitly. 43 | 44 | :py:func:`connect() ` also has arguments for setting the baud rate, 45 | the length of the connection timeout, and/or to use 46 | a :ref:`custom vehicle class `. 47 | 48 | There is more documentation on all of the parameters in the :py:func:`API Reference `. 49 | 50 | 51 | .. _connection_string_options: 52 | .. _get_started_connect_string: 53 | 54 | Connection string options 55 | ========================= 56 | 57 | The table below shows *connection strings* you can use for some of the more common connection types: 58 | 59 | .. list-table:: 60 | :widths: 10 10 61 | :header-rows: 1 62 | 63 | * - Connection type 64 | - Connection string 65 | * - Linux computer connected to the vehicle via USB 66 | - ``/dev/ttyUSB0`` 67 | * - Linux computer connected to the vehicle via Serial port (RaspberryPi example) 68 | - ``/dev/ttyAMA0`` (also set ``baud=57600``) 69 | * - SITL connected to the vehicle via UDP 70 | - ``127.0.0.1:14550`` 71 | * - SITL connected to the vehicle via TCP 72 | - ``tcp:127.0.0.1:5760`` 73 | * - OSX computer connected to the vehicle via USB 74 | - ``dev/cu.usbmodem1`` 75 | * - Windows computer connected to the vehicle via USB (in this case on COM14) 76 | - ``com14`` 77 | * - Windows computer connected to the vehicle using a 3DR Telemetry Radio on COM14 78 | - ``com14`` (also set ``baud=57600``) 79 | 80 | .. tip:: 81 | 82 | The strings above are the same as are used when connecting the MAVProxy GCS. For other options see the 83 | `MAVProxy documentation `_. 84 | 85 | .. note:: 86 | 87 | The default baud rate may not be appropriate for all connection types (this may be the cause 88 | if you can connect via a GCS but not DroneKit). 89 | 90 | 91 | Connecting to multiple vehicles 92 | =============================== 93 | 94 | You can control multiple vehicles from within a single script by calling 95 | :py:func:`connect() ` for each vehicle 96 | with the appropriate :ref:`connection strings `. 97 | 98 | The returned :py:class:`Vehicle ` objects are independent of 99 | each other and can be separately used to control their respective 100 | vehicle. 101 | 102 | -------------------------------------------------------------------------------- /examples/performance_test/performance_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | © Copyright 2015-2016, 3D Robotics. 6 | performance_test.py: 7 | 8 | This performance test logs the interval between messages being 9 | sent by Dronekit-Python and an acknowledgment being received 10 | from the autopilot. It provides a running report of the maximum, 11 | minimum, and most recent interval for 30 seconds. 12 | 13 | Full documentation is provided at http://python.dronekit.io/examples/performance_test.html 14 | """ 15 | from __future__ import print_function 16 | from dronekit import connect 17 | from pymavlink import mavutil 18 | import time 19 | import sys 20 | from datetime import datetime 21 | 22 | 23 | #Set up option parsing to get connection string 24 | import argparse 25 | parser = argparse.ArgumentParser(description='Generates max, min and current interval between message sent and ack recieved. Will start and connect to SITL if no connection string specified.') 26 | parser.add_argument('--connect', 27 | help="vehicle connection target string. If not specified, SITL automatically started and used.") 28 | args = parser.parse_args() 29 | 30 | connection_string=args.connect 31 | 32 | #Start SITL if no connection string specified 33 | if not connection_string: 34 | import dronekit_sitl 35 | sitl = dronekit_sitl.start_default() 36 | connection_string = sitl.connection_string() 37 | 38 | 39 | # Connect to the Vehicle 40 | print('Connecting to vehicle on: %s' % connection_string) 41 | vehicle = connect(connection_string, wait_ready=True) 42 | 43 | #global vehicle 44 | 45 | 46 | def cur_usec(): 47 | """Return current time in usecs""" 48 | # t = time.time() 49 | dt = datetime.now() 50 | t = dt.minute * 60 + dt.second + dt.microsecond / (1e6) 51 | return t 52 | 53 | class MeasureTime(object): 54 | def __init__(self): 55 | self.prevtime = cur_usec() 56 | self.previnterval = 0 57 | self.numcount = 0 58 | self.reset() 59 | 60 | def reset(self): 61 | self.maxinterval = 0 62 | self.mininterval = 10000 63 | 64 | def log(self): 65 | #print "Interval", self.previnterval 66 | #print "MaxInterval", self.maxinterval 67 | #print "MinInterval", self.mininterval 68 | sys.stdout.write('MaxInterval: %s\tMinInterval: %s\tInterval: %s\r' % (self.maxinterval,self.mininterval, self.previnterval) ) 69 | sys.stdout.flush() 70 | 71 | 72 | def update(self): 73 | now = cur_usec() 74 | self.numcount = self.numcount + 1 75 | self.previnterval = now - self.prevtime 76 | self.prevtime = now 77 | if self.numcount>1: #ignore first value where self.prevtime not reliable. 78 | self.maxinterval = max(self.previnterval, self.maxinterval) 79 | self.mininterval = min(self.mininterval, self.previnterval) 80 | self.log() 81 | 82 | 83 | acktime = MeasureTime() 84 | 85 | 86 | #Create COMMAND_ACK message listener. 87 | @vehicle.on_message('COMMAND_ACK') 88 | def listener(self, name, message): 89 | acktime.update() 90 | send_testpackets() 91 | 92 | 93 | def send_testpackets(): 94 | #Send message using `command_long_encode` (returns an ACK) 95 | msg = vehicle.message_factory.command_long_encode( 96 | 1, 1, # target system, target component 97 | #mavutil.mavlink.MAV_CMD_DO_SET_RELAY, #command 98 | mavutil.mavlink.MAV_CMD_DO_SET_ROI, #command 99 | 0, #confirmation 100 | 0, 0, 0, 0, #params 1-4 101 | 0, 102 | 0, 103 | 0 104 | ) 105 | 106 | vehicle.send_mavlink(msg) 107 | 108 | #Start logging by sending a test packet 109 | send_testpackets() 110 | 111 | print("Logging for 30 seconds") 112 | for x in range(1,30): 113 | time.sleep(1) 114 | 115 | # Close vehicle object before exiting script 116 | vehicle.close() 117 | 118 | if not args.connect: 119 | # Shut down simulator if it was started. 120 | sitl.stop() 121 | -------------------------------------------------------------------------------- /examples/follow_me/follow_me.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | © Copyright 2015-2016, 3D Robotics. 6 | followme - Tracks GPS position of your computer (Linux only). 7 | 8 | This example uses the python gps package to read positions from a GPS attached to your 9 | laptop and sends a new vehicle.simple_goto command every two seconds to move the 10 | vehicle to the current point. 11 | 12 | When you want to stop follow-me, either change vehicle modes or type Ctrl+C to exit the script. 13 | 14 | Example documentation: http://python.dronekit.io/examples/follow_me.html 15 | """ 16 | from __future__ import print_function 17 | 18 | from dronekit import connect, VehicleMode, LocationGlobalRelative 19 | import gps 20 | import socket 21 | import time 22 | import sys 23 | 24 | #Set up option parsing to get connection string 25 | import argparse 26 | parser = argparse.ArgumentParser(description='Tracks GPS position of your computer (Linux only).') 27 | parser.add_argument('--connect', 28 | help="vehicle connection target string. If not specified, SITL automatically started and used.") 29 | args = parser.parse_args() 30 | 31 | connection_string = args.connect 32 | sitl = None 33 | 34 | 35 | #Start SITL if no connection string specified 36 | if not connection_string: 37 | import dronekit_sitl 38 | sitl = dronekit_sitl.start_default() 39 | connection_string = sitl.connection_string() 40 | 41 | # Connect to the Vehicle 42 | print('Connecting to vehicle on: %s' % connection_string) 43 | vehicle = connect(connection_string, wait_ready=True, timeout=300) 44 | 45 | 46 | 47 | def arm_and_takeoff(aTargetAltitude): 48 | """ 49 | Arms vehicle and fly to aTargetAltitude. 50 | """ 51 | 52 | print("Basic pre-arm checks") 53 | # Don't let the user try to arm until autopilot is ready 54 | while not vehicle.is_armable: 55 | print(" Waiting for vehicle to initialise...") 56 | time.sleep(1) 57 | 58 | 59 | print("Arming motors") 60 | # Copter should arm in GUIDED mode 61 | vehicle.mode = VehicleMode("GUIDED") 62 | vehicle.armed = True 63 | 64 | while not vehicle.armed: 65 | print(" Waiting for arming...") 66 | time.sleep(1) 67 | 68 | print("Taking off!") 69 | vehicle.simple_takeoff(aTargetAltitude) # Take off to target altitude 70 | 71 | # Wait until the vehicle reaches a safe height before processing the goto (otherwise the command 72 | # after Vehicle.simple_takeoff will execute immediately). 73 | while True: 74 | print(" Altitude: ", vehicle.location.global_relative_frame.alt) 75 | if vehicle.location.global_relative_frame.alt>=aTargetAltitude*0.95: #Trigger just below target alt. 76 | print("Reached target altitude") 77 | break 78 | time.sleep(1) 79 | 80 | 81 | 82 | try: 83 | # Use the python gps package to access the laptop GPS 84 | gpsd = gps.gps(mode=gps.WATCH_ENABLE) 85 | 86 | #Arm and take off to altitude of 5 meters 87 | arm_and_takeoff(5) 88 | 89 | while True: 90 | 91 | if vehicle.mode.name != "GUIDED": 92 | print("User has changed flight modes - aborting follow-me") 93 | break 94 | 95 | # Read the GPS state from the laptop 96 | next(gpsd) 97 | 98 | # Once we have a valid location (see gpsd documentation) we can start moving our vehicle around 99 | if (gpsd.valid & gps.LATLON_SET) != 0: 100 | altitude = 30 # in meters 101 | dest = LocationGlobalRelative(gpsd.fix.latitude, gpsd.fix.longitude, altitude) 102 | print("Going to: %s" % dest) 103 | 104 | # A better implementation would only send new waypoints if the position had changed significantly 105 | vehicle.simple_goto(dest) 106 | 107 | # Send a new target every two seconds 108 | # For a complete implementation of follow me you'd want adjust this delay 109 | time.sleep(2) 110 | 111 | except socket.error: 112 | print("Error: gpsd service does not seem to be running, plug in USB GPS or run run-fake-gps.sh") 113 | sys.exit(1) 114 | 115 | #Close vehicle object before exiting script 116 | print("Close vehicle object") 117 | vehicle.close() 118 | 119 | # Shut down simulator if it was started. 120 | if sitl is not None: 121 | sitl.stop() 122 | 123 | print("Completed") 124 | -------------------------------------------------------------------------------- /docs/contributing/contributions_documentation.rst: -------------------------------------------------------------------------------- 1 | .. _contributing-to-documentation: 2 | 3 | ================================= 4 | Contributing to the Documentation 5 | ================================= 6 | 7 | One of the best ways that you can help is by improving this documentation. Here we explain 8 | the documentation system, how to build the documents locally, and how to submit your changes. 9 | 10 | 11 | Documentation system overview 12 | ============================= 13 | 14 | The documentation source files are `stored in Github `_. 15 | The content is written in plain-text files (file-extension :file:`.rst`) using 16 | `reStructuredText `_ markup, and is compiled into HTML using the 17 | `Sphinx Documentation Generator `_. 18 | 19 | Submitting changes 20 | ================== 21 | 22 | The process and requirements for submitting changes to the documentation are **the same** as when 23 | :ref:`contributing to the source code `. 24 | 25 | As when submitting source code you should fork the main project Github repository and 26 | contribute changes back to the project using pull requests. The changes should be tested 27 | locally (by :ref:`building the docs `) before being submitted. 28 | 29 | See :ref:`contributing_api` for more information. 30 | 31 | .. _contributing_building_docs: 32 | 33 | Building the docs 34 | ================= 35 | 36 | We've made it very easy to get started by providing a `Vagrant `_ based setup for :program:`Sphinx`. Using :program:`Vagrant` you can work with source files on your host machine using a familiar :program:`git` client and text editor, and then invoke :program:`Sphinx` in the :program:`Vagrant` VM to compile the source to HTML. 37 | 38 | The instructions below explain how to get the documentation source, and build it using our Vagrant VM: 39 | 40 | * Install the Vagrant pre-conditions: 41 | 42 | * `Download and install VirtualBox `_. 43 | * `Download and install Vagrant for your platform `_ (Windows, OS-X and Linux are supported). 44 | 45 | * `Fork the official dronekit-python repo `_ 46 | * Clone your fork of the Github repository anywhere on the host PC: :: 47 | 48 | git clone https://github.com/YOUR-REPOSITORY/dronekit-python.git 49 | 50 | * Navigate to the root of *dronekit-python* and start the Vagrant VM: 51 | :: 52 | 53 | cd /your-path-to-clone/dronekit-python/ 54 | vagrant up 55 | 56 | .. note:: This may take a long time to complete the first time it is run — Vagrant needs to download the virtual machine and then set up Sphinx. 57 | 58 | * When the VM is running, you can build the source by entering the following command in the prompt: 59 | :: 60 | 61 | vagrant ssh -c "cd /vagrant/docs && make html" 62 | 63 | The files will be built by :program:`Sphinx`, and will appear on the host system in :file:`/dronekit-python/docs/\_build/html/`. To preview, simply open them in a Web browser. 64 | 65 | .. note:: 66 | 67 | The ``vagrant ssh -c "cd /vagrant/docs && make html"`` command starts (and closes) an SSH session with the VM. If you plan on building the source a number of times it is much faster to keep the session open: 68 | 69 | :: 70 | 71 | vagrant ssh # Open an SSH session with the Vagrant VM 72 | cd /vagrant/docs # Navigate to the docs root (contains Sphinx configuration files) 73 | make html # Build the HTML 74 | ... # Repeat "make html" as many time as needed 75 | make html 76 | exit # Close the SSH session. 77 | 78 | 79 | 80 | * When you are finished you can suspend the VM. Next time you need to build more HTML simply restart it (this is a fast operation): 81 | :: 82 | 83 | vagrant suspend #Suspend the VM 84 | vagrant resume #Restart the VM 85 | vagrant ssh -c "cd /vagrant/docs && make html" #Build files when needed. 86 | 87 | 88 | Style guide 89 | =========== 90 | 91 | .. tip:: 92 | 93 | This guide is evolving. The most important guidance we can give is 94 | to *copy the existing style of reference, guide and example material*! 95 | 96 | 97 | #. Use US English for spelling. 98 | 99 | #. Use emphasis sparingly (italic, bold, underline). 100 | 101 | #. Use `Sphinx semantic markup `_ to mark up *types* of text (key-presses, file names etc.) 102 | 103 | #. Use double backticks (``) around ``inline code`` items. -------------------------------------------------------------------------------- /dronekit/test/sitl/test_waypoints.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dronekit import connect, LocationGlobal, Command 3 | from pymavlink import mavutil 4 | from dronekit.test import with_sitl 5 | from nose.tools import assert_not_equals, assert_equals 6 | 7 | 8 | @with_sitl 9 | def test_empty_clear(connpath): 10 | vehicle = connect(connpath) 11 | 12 | # Calling clear() on an empty object should not crash. 13 | vehicle.commands.clear() 14 | vehicle.commands.upload() 15 | 16 | assert_equals(len(vehicle.commands), 0) 17 | 18 | vehicle.close() 19 | 20 | 21 | @with_sitl 22 | def test_set_home(connpath): 23 | vehicle = connect(connpath, wait_ready=True) 24 | 25 | # Wait for home position to be real and not 0, 0, 0 26 | # once we request it via cmds.download() 27 | time.sleep(10) 28 | vehicle.commands.download() 29 | vehicle.commands.wait_ready() 30 | assert_not_equals(vehicle.home_location, None) 31 | 32 | # Note: If the GPS values differ heavily from EKF values, this command 33 | # will basically fail silently. This GPS coordinate is tailored for that 34 | # the with_sitl initializer uses to not fail. 35 | vehicle.home_location = LocationGlobal(-35, 149, 600) 36 | vehicle.commands.download() 37 | vehicle.commands.wait_ready() 38 | 39 | assert_equals(vehicle.home_location.lat, -35) 40 | assert_equals(vehicle.home_location.lon, 149) 41 | assert_equals(vehicle.home_location.alt, 600) 42 | 43 | vehicle.close() 44 | 45 | 46 | @with_sitl 47 | def test_parameter(connpath): 48 | vehicle = connect(connpath, wait_ready=True) 49 | 50 | # Home should be None at first. 51 | assert_equals(vehicle.home_location, None) 52 | 53 | # Wait for home position to be real and not 0, 0, 0 54 | # once we request it via cmds.download() 55 | time.sleep(10) 56 | 57 | # Initial 58 | vehicle.commands.download() 59 | vehicle.commands.wait_ready() 60 | assert_equals(len(vehicle.commands), 0) 61 | assert_not_equals(vehicle.home_location, None) 62 | 63 | # Save home for comparison. 64 | home = vehicle.home_location 65 | 66 | # After clearing 67 | vehicle.commands.clear() 68 | vehicle.commands.upload() 69 | vehicle.commands.download() 70 | vehicle.commands.wait_ready() 71 | assert_equals(len(vehicle.commands), 0) 72 | 73 | # Upload 74 | for command in [ 75 | Command(0, 0, 0, 0, 16, 1, 1, 0.0, 0.0, 0.0, 0.0, -35.3605, 149.172363, 747.0), 76 | Command(0, 0, 0, 3, 22, 0, 1, 0.0, 0.0, 0.0, 0.0, -35.359831, 149.166334, 100.0), 77 | Command(0, 0, 0, 3, 16, 0, 1, 0.0, 0.0, 0.0, 0.0, -35.363489, 149.167213, 100.0), 78 | Command(0, 0, 0, 3, 16, 0, 1, 0.0, 0.0, 0.0, 0.0, -35.355491, 149.169595, 100.0), 79 | Command(0, 0, 0, 3, 16, 0, 1, 0.0, 0.0, 0.0, 0.0, -35.355071, 149.175839, 100.0), 80 | Command(0, 0, 0, 3, 113, 0, 1, 0.0, 0.0, 0.0, 0.0, -35.362666, 149.178715, 22222.0), 81 | Command(0, 0, 0, 3, 115, 0, 1, 2.0, 22.0, 1.0, 3.0, 0.0, 0.0, 0.0), 82 | Command(0, 0, 0, 3, 16, 0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), 83 | ]: 84 | vehicle.commands.add(command) 85 | vehicle.commands.upload() 86 | 87 | # After upload 88 | vehicle.commands.download() 89 | vehicle.commands.wait_ready() 90 | assert_equals(len(vehicle.commands), 8) 91 | 92 | # Test iteration. 93 | count = 0 94 | for cmd in vehicle.commands: 95 | assert_not_equals(cmd, None) 96 | count += 1 97 | assert_equals(count, 8) 98 | 99 | # Test slicing 100 | count = 3 101 | for cmd in vehicle.commands[2:5]: 102 | assert_not_equals(cmd, None) 103 | assert_equals(cmd.seq, count) 104 | count += 1 105 | assert_equals(count, 6) 106 | 107 | # Test next property 108 | assert_equals(vehicle.commands.next, 0) 109 | vehicle.commands.next = 3 110 | while vehicle.commands.next != 3: 111 | time.sleep(0.1) 112 | assert_equals(vehicle.commands.next, 3) 113 | 114 | # Home should be preserved 115 | assert_equals(home.lat, vehicle.home_location.lat) 116 | assert_equals(home.lon, vehicle.home_location.lon) 117 | assert_equals(home.alt, vehicle.home_location.alt) 118 | 119 | vehicle.close() 120 | 121 | 122 | @with_sitl 123 | def test_227(connpath): 124 | """ 125 | Tests race condition when downloading items 126 | """ 127 | 128 | vehicle = connect(connpath, wait_ready=True) 129 | 130 | def assert_commands(count): 131 | vehicle.commands.download() 132 | vehicle.commands.wait_ready() 133 | assert_equals(len(vehicle.commands), count) 134 | 135 | assert_commands(0) 136 | 137 | vehicle.commands.add(Command(0, 0, 0, mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT, 138 | mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 0, 0, 0, 0, 0, 0, 10, 10, 139 | 10)) 140 | vehicle.flush() # Send commands 141 | 142 | assert_commands(1) 143 | 144 | vehicle.close() 145 | -------------------------------------------------------------------------------- /dronekit/test/sitl/test_channels.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dronekit import connect 3 | from dronekit.test import with_sitl 4 | from nose.tools import assert_equals 5 | 6 | 7 | def assert_readback(vehicle, values): 8 | i = 10 9 | while i > 0: 10 | time.sleep(.1) 11 | i -= .1 12 | for k, v in values.items(): 13 | if vehicle.channels[k] != v: 14 | continue 15 | break 16 | if i <= 0: 17 | raise Exception('Did not match in channels readback %s' % values) 18 | 19 | 20 | @with_sitl 21 | def test_timeout(connpath): 22 | vehicle = connect(connpath, wait_ready=True) 23 | 24 | assert_equals(len(vehicle.channels), 8) 25 | assert_equals(len(vehicle.channels.overrides), 8) 26 | 27 | assert_equals(sorted(vehicle.channels.keys()), [str(x) for x in range(1, 9)]) 28 | assert_equals(sorted(vehicle.channels.overrides.keys()), []) 29 | 30 | assert_equals(type(vehicle.channels['1']), int) 31 | assert_equals(type(vehicle.channels['2']), int) 32 | assert_equals(type(vehicle.channels['3']), int) 33 | assert_equals(type(vehicle.channels['4']), int) 34 | assert_equals(type(vehicle.channels['5']), int) 35 | assert_equals(type(vehicle.channels['6']), int) 36 | assert_equals(type(vehicle.channels['7']), int) 37 | assert_equals(type(vehicle.channels['8']), int) 38 | assert_equals(type(vehicle.channels[1]), int) 39 | assert_equals(type(vehicle.channels[2]), int) 40 | assert_equals(type(vehicle.channels[3]), int) 41 | assert_equals(type(vehicle.channels[4]), int) 42 | assert_equals(type(vehicle.channels[5]), int) 43 | assert_equals(type(vehicle.channels[6]), int) 44 | assert_equals(type(vehicle.channels[7]), int) 45 | assert_equals(type(vehicle.channels[8]), int) 46 | 47 | vehicle.channels.overrides = {'1': 1010} 48 | assert_readback(vehicle, {'1': 1010}) 49 | 50 | vehicle.channels.overrides = {'2': 1020} 51 | assert_readback(vehicle, {'1': 1500, '2': 1010}) 52 | 53 | vehicle.channels.overrides['1'] = 1010 54 | assert_readback(vehicle, {'1': 1010, '2': 1020}) 55 | 56 | del vehicle.channels.overrides['1'] 57 | assert_readback(vehicle, {'1': 1500, '2': 1020}) 58 | 59 | vehicle.channels.overrides = {'1': 1010, '2': None} 60 | assert_readback(vehicle, {'1': 1010, '2': 1500}) 61 | 62 | vehicle.channels.overrides['1'] = None 63 | assert_readback(vehicle, {'1': 1500, '2': 1500}) 64 | 65 | # test 66 | try: 67 | vehicle.channels['9'] 68 | assert False, "Can read over end of channels" 69 | except: 70 | pass 71 | 72 | try: 73 | vehicle.channels['0'] 74 | assert False, "Can read over start of channels" 75 | except: 76 | pass 77 | 78 | try: 79 | vehicle.channels['1'] = 200 80 | assert False, "can write a channel value" 81 | except: 82 | pass 83 | 84 | # Set Ch1 to 100 using braces syntax 85 | vehicle.channels.overrides = {'1': 1000} 86 | assert_readback(vehicle, {'1': 1000}) 87 | 88 | # Set Ch2 to 200 using bracket 89 | vehicle.channels.overrides['2'] = 200 90 | assert_readback(vehicle, {'1': 200, '2': 200}) 91 | 92 | # Set Ch2 to 1010 93 | vehicle.channels.overrides = {'2': 1010} 94 | assert_readback(vehicle, {'1': 1500, '2': 1010}) 95 | 96 | # Set Ch3,4,5,6,7 to 300,400-700 respectively 97 | vehicle.channels.overrides = {'3': 300, '4': 400, '5': 500, '6': 600, '7': 700} 98 | assert_readback(vehicle, {'3': 300, '4': 400, '5': 500, '6': 600, '7': 700}) 99 | 100 | # Set Ch8 to 800 using braces 101 | vehicle.channels.overrides = {'8': 800} 102 | assert_readback(vehicle, {'8': 800}) 103 | 104 | # Set Ch8 to 800 using brackets 105 | vehicle.channels.overrides['8'] = 810 106 | assert_readback(vehicle, {'8': 810}) 107 | 108 | try: 109 | # Try to write channel 9 override to a value with brackets 110 | vehicle.channels.overrides['9'] = 900 111 | assert False, "can write channels.overrides 9" 112 | except: 113 | pass 114 | 115 | try: 116 | # Try to write channel 9 override to a value with braces 117 | vehicle.channels.overrides = {'9': 900} 118 | assert False, "can write channels.overrides 9 with braces" 119 | except: 120 | pass 121 | 122 | # Clear channel 3 using brackets 123 | vehicle.channels.overrides['3'] = None 124 | assert '3' not in vehicle.channels.overrides, 'overrides hould not contain None' 125 | 126 | # Clear channel 2 using braces 127 | vehicle.channels.overrides = {'2': None} 128 | assert '2' not in vehicle.channels.overrides, 'overrides hould not contain None' 129 | 130 | # Clear all channels 131 | vehicle.channels.overrides = {} 132 | assert_equals(len(vehicle.channels.overrides.keys()), 0) 133 | 134 | # Set Ch2 to 33, clear channel 6 135 | vehicle.channels.overrides = {'2': 33, '6': None} 136 | assert_readback(vehicle, {'2': 33, '6': 1500}) 137 | assert_equals(list(vehicle.channels.overrides.keys()), ['2']) 138 | 139 | # Callbacks 140 | result = {'success': False} 141 | vehicle.channels.overrides = {} 142 | 143 | def channels_callback(vehicle, name, channels): 144 | if channels['3'] == 55: 145 | result['success'] = True 146 | 147 | vehicle.add_attribute_listener('channels', channels_callback) 148 | vehicle.channels.overrides = {'3': 55} 149 | 150 | i = 5 151 | while not result['success'] and i > 0: 152 | time.sleep(.1) 153 | i -= 1 154 | assert result['success'], 'channels callback should be invoked.' 155 | 156 | vehicle.close() 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DroneKit Python 2 | 3 | ![dronekit_python_logo](https://cloud.githubusercontent.com/assets/5368500/10805537/90dd4b14-7e22-11e5-9592-5925348a7df9.png) 4 | 5 | [![PyPi published version](https://img.shields.io/pypi/v/dronekit.svg)](https://pypi.org/project/dronekit/) 6 | [![Windows Build status](https://img.shields.io/appveyor/ci/3drobotics/dronekit-python/master.svg?label=windows)](https://ci.appveyor.com/project/3drobotics/dronekit-python/branch/master) 7 | [![OS X Build Status](https://img.shields.io/travis/dronekit/dronekit-python/master.svg?label=os%20x)](https://travis-ci.org/dronekit/dronekit-python) 8 | [![Linux Build Status](https://img.shields.io/circleci/project/dronekit/dronekit-python/master.svg?label=linux)](https://circleci.com/gh/dronekit/dronekit-python) 9 | 10 | DroneKit-Python helps you create powerful apps for UAVs. 11 | 12 | # ⚠️ ATTENTION: MAINTAINERS NEEDED ⚠️ 13 | 14 | Hey it's true this project is not very active, but it could be with your help. We are looking for maintainers interested in keeping the project alive by keep up with CI and PRs. If you are interested in helping please apply by [creating an issue]([url](https://github.com/dronekit/dronekit-python/issues/new)) and listing the reasons why you would like to help, in return we will be granting committer access to folks who are truly interested in helping. 15 | 16 | 17 | ## Overview 18 | 19 | DroneKit-Python (formerly DroneAPI-Python) contains the python language implementation of DroneKit. 20 | 21 | The API allows developers to create Python apps that communicate with vehicles over MAVLink. It provides programmatic access to a connected vehicle's telemetry, state and parameter information, and enables both mission management and direct control over vehicle movement and operations. 22 | 23 | The API is primarily intended for use in onboard companion computers (to support advanced use cases including computer vision, path planning, 3D modelling etc). It can also be used for ground station apps, communicating with vehicles over a higher latency RF-link. 24 | 25 | ## Getting Started 26 | 27 | The [Quick Start](https://dronekit-python.readthedocs.io/en/latest/guide/quick_start.html) guide explains how to set up DroneKit on each of the supported platforms (Linux, Mac OSX, Windows) and how to write a script to connect to a vehicle (real or simulated). 28 | 29 | A basic script looks like this: 30 | 31 | ```python 32 | from dronekit import connect 33 | 34 | # Connect to UDP endpoint. 35 | vehicle = connect('127.0.0.1:14550', wait_ready=True) 36 | # Use returned Vehicle object to query device state - e.g. to get the mode: 37 | print("Mode: %s" % vehicle.mode.name) 38 | ``` 39 | 40 | Once you've got DroneKit set up, the [guide](https://dronekit-python.readthedocs.io/en/latest/guide/index.html) explains how to perform operations like taking off and flying the vehicle. You can also try out most of the tasks by running the [examples](https://dronekit-python.readthedocs.io/en/latest/examples/index.html). 41 | 42 | ## Resources 43 | 44 | The project documentation is available at [https://readthedocs.org/projects/dronekit-python/](https://readthedocs.org/projects/dronekit-python/). This includes [guide](https://dronekit-python.readthedocs.io/en/latest/guide/index.html), [example](https://dronekit-python.readthedocs.io/en/latest/examples/index.html) and [API Reference](https://dronekit-python.readthedocs.io/en/latest/automodule.html) material. 45 | 46 | The example source code is hosted here on Github as sub-folders of [/dronekit-python/examples](https://github.com/dronekit/dronekit-python/tree/master/examples). 47 | 48 | The [DroneKit Forums](http://discuss.dronekit.io) are the best place to ask for technical support on how to use the library. You can also check out our [Gitter channel](https://gitter.im/dronekit/dronekit-python?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) though we prefer posts on the forums where possible. 49 | 50 | * **Documentation:** [https://dronekit-python.readthedocs.io/en/latest/about/index.html](https://dronekit-python.readthedocs.io/en/latest/about/index.html) 51 | * **Guides:** [https://dronekit-python.readthedocs.io/en/latest/guide/index.html) 52 | * **API Reference:** [https://dronekit-python.readthedocs.io/en/latest/automodule.html) 53 | * **Examples:** [/dronekit-python/examples](https://github.com/dronekit/dronekit-python/tree/master/examples) 54 | * **Forums:** [http://discuss.dronekit.io/](http://discuss.dronekit.io) 55 | * **Gitter:** [https://gitter.im/dronekit/dronekit-python](https://gitter.im/dronekit/dronekit-python?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) though we prefer posts on the forums where possible. 56 | 57 | 58 | ## Users and contributors wanted! 59 | 60 | We'd love your [feedback and suggestions](https://github.com/dronekit/dronekit-python/issues) about this API and are eager to evolve it to meet your needs, please feel free to create an issue to report bugs or feature requests. 61 | 62 | If you've created some awesome software that uses this project, [let us know on the forums here](https://discuss.dronekit.io/t/notable-projects-using-dronekit/230)! 63 | 64 | If you want to contribute, see our [Contributing](https://dronekit-python.readthedocs.io/en/latest/contributing/index.html) guidelines, we welcome all types of contributions but mostly contributions that would help us shrink our [issues list](https://github.com/dronekit/dronekit-python/issues). 65 | 66 | 67 | ## Licence 68 | 69 | DroneKit-Python is made available under the permissive open source [Apache 2.0 License](http://python.dronekit.io/about/license.html). 70 | 71 | 72 | *** 73 | 74 | Copyright 2015 3D Robotics, Inc. 75 | -------------------------------------------------------------------------------- /docs/examples/mission_import_export.rst: -------------------------------------------------------------------------------- 1 | .. _example_mission_import_export: 2 | 3 | ============================== 4 | Example: Mission Import/Export 5 | ============================== 6 | 7 | This example shows how to import and export files in the 8 | `Waypoint file format `_. 9 | 10 | The commands are first imported from a file into a list and then uploaded to the vehicle. 11 | Then the current mission is downloaded from the vehicle and put into a list, which is then 12 | saved into (another file). Finally, we print out both the original and new files 13 | for comparison 14 | 15 | The example does not show how missions can be modified, but once the mission is in a list, 16 | changing the order and content of commands is straightforward. 17 | 18 | The guide topic :ref:`auto_mode_vehicle_control` provides information about 19 | missions and AUTO mode. 20 | 21 | 22 | Running the example 23 | =================== 24 | 25 | The example can be run as described in :doc:`running_examples` (which in turn assumes that the vehicle 26 | and DroneKit have been set up as described in :ref:`installing_dronekit`). 27 | 28 | In summary, after cloning the repository: 29 | 30 | #. Navigate to the example folder as shown: 31 | 32 | .. code-block:: bash 33 | 34 | cd dronekit-python/examples/mission_import_export/ 35 | 36 | #. You can run the example against a simulator (DroneKit-SITL) by specifying the Python script without any arguments. 37 | The example will download SITL binaries (if needed), start the simulator, and then connect to it: 38 | 39 | .. code-block:: bash 40 | 41 | python mission_import_export.py 42 | 43 | On the command prompt you should see (something like): 44 | 45 | .. code:: bash 46 | 47 | Starting copter simulator (SITL) 48 | SITL already Downloaded. 49 | Connecting to vehicle on: tcp:127.0.0.1:5760 50 | >>> APM:Copter V3.3 (d6053245) 51 | >>> Frame: QUAD 52 | >>> Calibrating barometer 53 | >>> Initialising APM... 54 | >>> barometer calibration complete 55 | >>> GROUND START 56 | Waiting for vehicle to initialise... 57 | Waiting for vehicle to initialise... 58 | Waiting for vehicle to initialise... 59 | Waiting for vehicle to initialise... 60 | Waiting for vehicle to initialise... 61 | Reading mission from file: mpmission.txt 62 | Upload mission from a file: mpmission.txt 63 | Clear mission 64 | Upload mission 65 | Save mission from Vehicle to file: exportedmission.txt 66 | Download mission from vehicle 67 | >>> flight plan received 68 | Write mission to file 69 | Close vehicle object 70 | Show original and uploaded/downloaded files: 71 | 72 | Mission file: mpmission.txt 73 | QGC WPL 110 74 | 0 1 0 16 0 0 0 0 -35.363262 149.165237 584.000000 1 75 | 1 0 0 22 0.000000 0.000000 0.000000 0.000000 -35.361988 149.163753 00.000000 1 76 | 2 0 0 16 0.000000 0.000000 0.000000 0.000000 -35.361992 149.163593 00.000000 1 77 | 3 0 0 16 0.000000 0.000000 0.000000 0.000000 -35.363812 149.163609 00.000000 1 78 | 4 0 0 16 0.000000 0.000000 0.000000 0.000000 -35.363768 149.166055 00.000000 1 79 | 5 0 0 16 0.000000 0.000000 0.000000 0.000000 -35.361835 149.166012 00.000000 1 80 | 6 0 0 16 0.000000 0.000000 0.000000 0.000000 -35.362150 149.165046 00.000000 1 81 | 82 | Mission file: exportedmission.txt 83 | QGC WPL 110 84 | 0 1 0 16 0 0 0 0 -35.3632621765 149.165237427 583.989990234 1 85 | 1 0 0 22 0.0 0.0 0.0 0.0 -35.3619880676 149.163757324 100.0 1 86 | 2 0 0 16 0.0 0.0 0.0 0.0 -35.3619918823 149.163589478 100.0 1 87 | 3 0 0 16 0.0 0.0 0.0 0.0 -35.3638114929 149.163604736 100.0 1 88 | 4 0 0 16 0.0 0.0 0.0 0.0 -35.3637695312 149.166061401 100.0 1 89 | 5 0 0 16 0.0 0.0 0.0 0.0 -35.3618354797 149.166015625 100.0 1 90 | 6 0 0 16 0.0 0.0 0.0 0.0 -35.3621482849 149.165039062 100.0 1 91 | 92 | 93 | .. note:: 94 | 95 | The position values uploaded and then downloaded above do not match exactly. This rounding error can be ignored 96 | because the difference is much smaller than the precision provided by GPS. 97 | 98 | The error occurs because all the params are encoded as 32-bit floats rather than 64-bit doubles (Python's native datatype). 99 | 100 | #. You can run the example against a specific connection (simulated or otherwise) by passing the :ref:`connection string ` for your vehicle in the ``--connect`` parameter. 101 | 102 | For example, to connect to SITL running on UDP port 14550 on your local computer: 103 | 104 | .. code-block:: bash 105 | 106 | python mission_import_export.py --connect 127.0.0.1:14550 107 | 108 | 109 | How does it work? 110 | ================= 111 | 112 | The :ref:`source code ` is largely self-documenting. 113 | 114 | More information about the functions can be found in the guide at 115 | :ref:`auto_mode_load_mission_file` and :ref:`auto_mode_save_mission_file`. 116 | 117 | 118 | 119 | Known issues 120 | ============ 121 | 122 | There are no known issues with this example. 123 | 124 | 125 | 126 | .. _example_mission_import_export_source_code: 127 | 128 | Source code 129 | =========== 130 | 131 | The full source code at documentation build-time is listed below (`current version on github `_): 132 | 133 | .. literalinclude:: ../../examples/mission_import_export/mission_import_export.py 134 | :language: python 135 | 136 | -------------------------------------------------------------------------------- /docs/examples/drone_delivery.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Example: Drone Delivery 3 | =========================== 4 | 5 | This example shows how to create a `CherryPy `_ based web application that 6 | displays a mapbox map to let you view the current vehicle position and send the vehicle commands 7 | to fly to a particular latitude and longitude. 8 | 9 | New functionality demonstrated by this example includes: 10 | 11 | * Using attribute observers to be notified of vehicle state changes. 12 | * Starting *CherryPy* from a DroneKit application. 13 | 14 | 15 | Running the example 16 | =================== 17 | 18 | The example can be run much as described in :doc:`running_examples` (which in turn assumes that the vehicle 19 | and DroneKit have been set up as described in :ref:`installing_dronekit`). The main exception is that you need to 20 | install the CherryPy dependencies and view the behaviour in a web browser. 21 | 22 | In summary, after cloning the repository: 23 | 24 | #. Navigate to the example folder as shown: 25 | 26 | .. code-block:: bash 27 | 28 | cd dronekit-python\examples\drone_delivery\ 29 | 30 | 31 | #. Install *CherryPy* and any other dependencies from **requirements.pip** in that directory: 32 | 33 | .. code-block:: bash 34 | 35 | pip install -r requirements.pip 36 | 37 | #. You can run the example against the simulator by specifying the Python script without any arguments. 38 | The example will download and start DroneKit-SITL, and then connect to it: 39 | 40 | .. code-block:: bash 41 | 42 | python drone_delivery.py 43 | 44 | On the command prompt you should see (something like): 45 | 46 | .. code:: bash 47 | 48 | >python drone_delivery.py 49 | 50 | D:\Github\dronekit-python\examples\drone_delivery>drone_delivery.py 51 | Starting copter simulator (SITL) 52 | SITL already Downloaded. 53 | local path: D:\Github\dronekit-python\examples\drone_delivery 54 | Connecting to vehicle on: tcp:127.0.0.1:5760 55 | >>> APM:Copter V3.3 (d6053245) 56 | >>> Frame: QUAD 57 | >>> Calibrating barometer 58 | >>> Initialising APM... 59 | >>> barometer calibration complete 60 | >>> GROUND START 61 | Launching Drone... 62 | [DEBUG]: Connected to vehicle. 63 | [DEBUG]: DroneDelivery Start 64 | [DEBUG]: Waiting for location... 65 | [DEBUG]: Waiting for ability to arm... 66 | [DEBUG]: Running initial boot sequence 67 | [DEBUG]: Changing to mode: GUIDED 68 | [DEBUG]: ... polled mode: GUIDED 69 | [DEBUG]: Waiting for arming... 70 | >>> ARMING MOTORS 71 | >>> GROUND START 72 | >>> Initialising APM... 73 | [DEBUG]: Taking off 74 | http://localhost:8080/ 75 | Waiting for cherrypy engine... 76 | 77 | #. You can run the example against a specific connection (simulated or otherwise) by passing the :ref:`connection string ` for your vehicle in the ``--connect`` parameter. 78 | For example, to connect to Solo: 79 | 80 | .. code-block:: bash 81 | 82 | python drone_delivery.py --connect udpin:0.0.0.0:14550 83 | 84 | 85 | #. After a short while you should be able to reach your new webserver at http://localhost:8080. 86 | Navigate to the **Command** screen, select a target on the map, then select **Go**. 87 | The command prompt will show something like the message below. 88 | 89 | .. code-block:: bash 90 | 91 | [DEBUG]: Goto: [u'-35.4', u'149.2'], 29.98 92 | 93 | The web server will switch you to the **Track** screen. You can view the vehicle progress by pressing the 94 | **Update** button. 95 | 96 | 97 | Screenshots 98 | =========== 99 | 100 | The webserver (http://localhost:8080) will look like the following: 101 | 102 | .. image:: drone-delivery-splash.png 103 | 104 | .. image:: drone-delivery-track.png 105 | 106 | .. image:: drone-delivery-command.png 107 | 108 | 109 | How it works 110 | ============ 111 | 112 | Using attribute observers 113 | ------------------------- 114 | 115 | All attributes in DroneKit can have observers - this is the primary mechanism you should use to be notified of changes in vehicle state. 116 | For instance, `drone_delivery.py `_ calls: 117 | 118 | .. code-block:: python 119 | 120 | self.vehicle.add_attribute_listener('location', self.location_callback) 121 | 122 | ... 123 | 124 | def location_callback(self, vehicle, name, location): 125 | if location.global_relative_frame.alt is not None: 126 | self.altitude = location.global_relative_frame.alt 127 | 128 | self.current_location = location.global_relative_frame 129 | 130 | 131 | This results in DroneKit calling our ``location_callback`` method any time the location attribute gets changed. 132 | 133 | .. tip:: 134 | 135 | It is also possible (and often more elegant) to add listeners using a decorator 136 | - see :py:func:`Vehicle.on_attribute `. 137 | 138 | 139 | 140 | Starting CherryPy from a DroneKit application 141 | --------------------------------------------- 142 | 143 | We start running a web server by calling ``cherrypy.engine.start()``. 144 | 145 | *CherryPy* is a very small and simple webserver. It is probably best to refer to their eight line `tutorial `_ for more information. 146 | 147 | 148 | 149 | Known issues 150 | ============ 151 | 152 | This example has the following issues: 153 | 154 | * `#537: Dronekit delivery tracking needs to zoom and also ideally auto update `_ 155 | * `#538: Dronekit delivery example does not exit `_ 156 | 157 | Source code 158 | =========== 159 | 160 | The full source code at documentation build-time is listed below (`current version on github `_): 161 | 162 | .. include:: ../../examples/drone_delivery/drone_delivery.py 163 | :literal: 164 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/DroneApi.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/DroneApi.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/DroneApi" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/DroneApi" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /examples/mission_import_export/mission_import_export.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | © Copyright 2015-2016, 3D Robotics. 6 | mission_import_export.py: 7 | 8 | This example demonstrates how to import and export files in the Waypoint file format 9 | (http://qgroundcontrol.org/mavlink/waypoint_protocol#waypoint_file_format). The commands are imported 10 | into a list, and can be modified before saving and/or uploading. 11 | 12 | Documentation is provided at http://python.dronekit.io/examples/mission_import_export.html 13 | """ 14 | from __future__ import print_function 15 | 16 | 17 | from dronekit import connect, Command 18 | import time 19 | 20 | 21 | #Set up option parsing to get connection string 22 | import argparse 23 | parser = argparse.ArgumentParser(description='Demonstrates mission import/export from a file.') 24 | parser.add_argument('--connect', 25 | help="Vehicle connection target string. If not specified, SITL automatically started and used.") 26 | args = parser.parse_args() 27 | 28 | connection_string = args.connect 29 | sitl = None 30 | 31 | 32 | #Start SITL if no connection string specified 33 | if not connection_string: 34 | import dronekit_sitl 35 | sitl = dronekit_sitl.start_default() 36 | connection_string = sitl.connection_string() 37 | 38 | 39 | # Connect to the Vehicle 40 | print('Connecting to vehicle on: %s' % connection_string) 41 | vehicle = connect(connection_string, wait_ready=True) 42 | 43 | # Check that vehicle is armable. 44 | # This ensures home_location is set (needed when saving WP file) 45 | 46 | while not vehicle.is_armable: 47 | print(" Waiting for vehicle to initialise...") 48 | time.sleep(1) 49 | 50 | 51 | def readmission(aFileName): 52 | """ 53 | Load a mission from a file into a list. The mission definition is in the Waypoint file 54 | format (http://qgroundcontrol.org/mavlink/waypoint_protocol#waypoint_file_format). 55 | 56 | This function is used by upload_mission(). 57 | """ 58 | print("\nReading mission from file: %s" % aFileName) 59 | cmds = vehicle.commands 60 | missionlist=[] 61 | with open(aFileName) as f: 62 | for i, line in enumerate(f): 63 | if i==0: 64 | if not line.startswith('QGC WPL 110'): 65 | raise Exception('File is not supported WP version') 66 | else: 67 | linearray=line.split('\t') 68 | ln_index=int(linearray[0]) 69 | ln_currentwp=int(linearray[1]) 70 | ln_frame=int(linearray[2]) 71 | ln_command=int(linearray[3]) 72 | ln_param1=float(linearray[4]) 73 | ln_param2=float(linearray[5]) 74 | ln_param3=float(linearray[6]) 75 | ln_param4=float(linearray[7]) 76 | ln_param5=float(linearray[8]) 77 | ln_param6=float(linearray[9]) 78 | ln_param7=float(linearray[10]) 79 | ln_autocontinue=int(linearray[11].strip()) 80 | cmd = Command( 0, 0, 0, ln_frame, ln_command, ln_currentwp, ln_autocontinue, ln_param1, ln_param2, ln_param3, ln_param4, ln_param5, ln_param6, ln_param7) 81 | missionlist.append(cmd) 82 | return missionlist 83 | 84 | 85 | def upload_mission(aFileName): 86 | """ 87 | Upload a mission from a file. 88 | """ 89 | #Read mission from file 90 | missionlist = readmission(aFileName) 91 | 92 | print("\nUpload mission from a file: %s" % aFileName) 93 | #Clear existing mission from vehicle 94 | print(' Clear mission') 95 | cmds = vehicle.commands 96 | cmds.clear() 97 | #Add new mission to vehicle 98 | for command in missionlist: 99 | cmds.add(command) 100 | print(' Upload mission') 101 | vehicle.commands.upload() 102 | 103 | 104 | def download_mission(): 105 | """ 106 | Downloads the current mission and returns it in a list. 107 | It is used in save_mission() to get the file information to save. 108 | """ 109 | print(" Download mission from vehicle") 110 | missionlist=[] 111 | cmds = vehicle.commands 112 | cmds.download() 113 | cmds.wait_ready() 114 | for cmd in cmds: 115 | missionlist.append(cmd) 116 | return missionlist 117 | 118 | def save_mission(aFileName): 119 | """ 120 | Save a mission in the Waypoint file format 121 | (http://qgroundcontrol.org/mavlink/waypoint_protocol#waypoint_file_format). 122 | """ 123 | print("\nSave mission from Vehicle to file: %s" % aFileName) 124 | #Download mission from vehicle 125 | missionlist = download_mission() 126 | #Add file-format information 127 | output='QGC WPL 110\n' 128 | #Add home location as 0th waypoint 129 | home = vehicle.home_location 130 | output+="%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" % (0,1,0,16,0,0,0,0,home.lat,home.lon,home.alt,1) 131 | #Add commands 132 | for cmd in missionlist: 133 | commandline="%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" % (cmd.seq,cmd.current,cmd.frame,cmd.command,cmd.param1,cmd.param2,cmd.param3,cmd.param4,cmd.x,cmd.y,cmd.z,cmd.autocontinue) 134 | output+=commandline 135 | with open(aFileName, 'w') as file_: 136 | print(" Write mission to file") 137 | file_.write(output) 138 | 139 | 140 | def printfile(aFileName): 141 | """ 142 | Print a mission file to demonstrate "round trip" 143 | """ 144 | print("\nMission file: %s" % aFileName) 145 | with open(aFileName) as f: 146 | for line in f: 147 | print(' %s' % line.strip()) 148 | 149 | 150 | import_mission_filename = 'mpmission.txt' 151 | export_mission_filename = 'exportedmission.txt' 152 | 153 | 154 | #Upload mission from file 155 | upload_mission(import_mission_filename) 156 | 157 | #Download mission we just uploaded and save to a file 158 | save_mission(export_mission_filename) 159 | 160 | #Close vehicle object before exiting script 161 | print("Close vehicle object") 162 | vehicle.close() 163 | 164 | # Shut down simulator if it was started. 165 | if sitl is not None: 166 | sitl.stop() 167 | 168 | 169 | print("\nShow original and uploaded/downloaded files:") 170 | #Print original file (for demo purposes only) 171 | printfile(import_mission_filename) 172 | #Print exported file (for demo purposes only) 173 | printfile(export_mission_filename) 174 | -------------------------------------------------------------------------------- /docs/examples/simple_goto.rst: -------------------------------------------------------------------------------- 1 | .. _example_simple_goto: 2 | 3 | ============================== 4 | Example: Simple Go To (Copter) 5 | ============================== 6 | 7 | This example demonstrates how to arm and launch a Copter in GUIDED mode, travel towards a number of target points, and then return 8 | to the home location. It uses :py:func:`Vehicle.simple_takeoff() `, 9 | :py:func:`Vehicle.simple_goto() ` and :py:attr:`Vehicle.mode `. 10 | 11 | The target locations are centered around the home location when the :ref:`Simulated Vehicle ` is booted; 12 | you can edit the latitude and longitude to use more appropriate positions for your own vehicle. 13 | 14 | .. note:: 15 | 16 | This example will only run on *Copter*: 17 | 18 | * *Plane* does not support ``takeoff`` in GUIDED mode. 19 | * *Rover* will ignore the ``takeoff`` command and will then stick at the altitude check. 20 | 21 | 22 | .. figure:: simple_goto_example_copter_path.png 23 | :width: 75 % 24 | :alt: Setting destination using position and changing speed and ROI 25 | 26 | Simple Goto Example: Flight path 27 | 28 | 29 | 30 | Running the example 31 | =================== 32 | 33 | The example can be run as described in :doc:`running_examples` (which in turn assumes that the vehicle 34 | and DroneKit have been set up as described in :ref:`installing_dronekit`). 35 | 36 | In summary, after cloning the repository: 37 | 38 | #. Navigate to the example folder as shown: 39 | 40 | .. code-block:: bash 41 | 42 | cd dronekit-python/examples/simple_goto/ 43 | 44 | #. You can run the example against a simulator (DroneKit-SITL) by specifying the Python script without any arguments. 45 | The example will download SITL binaries if needed, start the simulator, and then connect to it: 46 | 47 | .. code-block:: bash 48 | 49 | python simple_goto.py 50 | 51 | On the command prompt you should see (something like): 52 | 53 | .. code:: bash 54 | 55 | Starting copter simulator (SITL) 56 | SITL already Downloaded. 57 | Connecting to vehicle on: tcp:127.0.0.1:5760 58 | >>> APM:Copter V3.3 (d6053245) 59 | >>> Frame: QUAD 60 | >>> Calibrating barometer 61 | >>> Initialising APM... 62 | >>> barometer calibration complete 63 | >>> GROUND START 64 | Basic pre-arm checks 65 | Waiting for vehicle to initialise... 66 | ... 67 | Waiting for vehicle to initialise... 68 | Arming motors 69 | Waiting for arming... 70 | ... 71 | Waiting for arming... 72 | >>> ARMING MOTORS 73 | >>> GROUND START 74 | Waiting for arming... 75 | >>> Initialising APM... 76 | Taking off! 77 | Altitude: 0.0 78 | ... 79 | Altitude: 7.4 80 | Altitude: 9.0 81 | Altitude: 9.65 82 | Reached target altitude 83 | Set default/target airspeed to 3 84 | Going towards first point for 30 seconds ... 85 | Going towards second point for 30 seconds (groundspeed set to 10 m/s) ... 86 | Returning to Launch 87 | Close vehicle object 88 | 89 | .. tip:: 90 | 91 | It is more interesting to watch the example run on a map than the console. The topic :ref:`viewing_uav_on_map` 92 | explains how to set up *Mission Planner* to view a vehicle running on the simulator (SITL). 93 | 94 | #. You can run the example against a specific connection (simulated or otherwise) by passing the :ref:`connection string ` for your vehicle in the ``--connect`` parameter. 95 | 96 | For example, to connect to SITL running on UDP port 14550 on your local computer: 97 | 98 | .. code-block:: bash 99 | 100 | python simple_goto.py --connect 127.0.0.1:14550 101 | 102 | 103 | 104 | How does it work? 105 | ================= 106 | 107 | The code has three distinct sections: arming and takeoff, flight to two locations, and return-to-home. 108 | 109 | Takeoff 110 | ------- 111 | 112 | To launch *Copter* you need to first check that the vehicle :py:func:`Vehicle.is_armable `. 113 | Then set the mode to ``GUIDED``, arm the vehicle, and call 114 | :py:func:`Vehicle.simple_takeoff() `. The takeoff code in this example 115 | is explained in the guide topic :ref:`taking-off`. 116 | 117 | 118 | Flying to a point - simple_goto 119 | ------------------------------- 120 | 121 | The vehicle is already in ``GUIDED`` mode, so to send it to a certain point we just need to 122 | call :py:func:`Vehicle.simple_goto() ` with the target 123 | :py:class:`dronekit.LocationGlobalRelative`: 124 | 125 | .. code-block:: python 126 | 127 | # set the default travel speed 128 | vehicle.airspeed=3 129 | 130 | point1 = LocationGlobalRelative(-35.361354, 149.165218, 20) 131 | vehicle.simple_goto(point1) 132 | 133 | # sleep so we can see the change in map 134 | time.sleep(30) 135 | 136 | .. tip:: 137 | 138 | Without some sort of "wait" the next command would be executed immediately. In this example we just 139 | sleep for 30 seconds before executing the next command. 140 | 141 | When moving towards the first point we set the airspeed using the :py:attr:`Vehicle.airspeed ` 142 | attribute. For the second point the example specifies the target groundspeed when calling 143 | :py:func:`Vehicle.simple_goto() ` 144 | 145 | .. code-block:: python 146 | 147 | vehicle.simple_goto(point2, groundspeed=10) 148 | 149 | .. tip:: 150 | 151 | The script doesn't report anything during the sleep periods, 152 | but you can observe the vehicle's movement on a ground station map. 153 | 154 | 155 | 156 | 157 | RTL - Return to launch 158 | ---------------------- 159 | 160 | To return to the home position and land, we set the mode to ``RTL``. 161 | The vehicle travels at the previously set default speed: 162 | 163 | .. code-block:: python 164 | 165 | vehicle.mode = VehicleMode("RTL") 166 | 167 | 168 | Source code 169 | =========== 170 | 171 | The full source code at documentation build-time is listed below 172 | (`current version on Github `_): 173 | 174 | .. literalinclude:: ../../examples/simple_goto/simple_goto.py 175 | :language: python -------------------------------------------------------------------------------- /docs/guide/taking_off.rst: -------------------------------------------------------------------------------- 1 | .. _taking-off: 2 | 3 | ========== 4 | Taking Off 5 | ========== 6 | 7 | This article explains how to get your *Copter* to take off. 8 | 9 | At high level, the steps are: check that the vehicle is *able* to arm, set the mode to ``GUIDED``, 10 | command the vehicle to arm, takeoff and block until we reach the desired altitude. 11 | 12 | .. todo:: 13 | 14 | Plane apps take off using the ``MAV_CMD_NAV_TAKEOFF`` command in a mission. The plane should first arm and then change to 15 | ``AUTO`` mode to start the mission. The action here is to add a link when we have an example we can point to. 16 | 17 | 18 | .. tip:: 19 | 20 | Copter is usually started in ``GUIDED`` mode. 21 | 22 | * For Copter 3.2.1 and earlier you cannot take off in ``AUTO`` mode (if you need to run a mission you take off 23 | in ``GUIDED`` mode and then switch to ``AUTO`` mode once you're in the air). 24 | * Starting from Copter 3.3 you can takeoff in ``AUTO`` mode (provided the mission has a 25 | `MAV_CMD_NAV_TAKEOFF `_ command) 26 | but the mission will not start until you explicitly send the 27 | `MAV_CMD_MISSION_START `_ 28 | message. 29 | 30 | By contrast, Plane apps take off using the ``MAV_CMD_NAV_TAKEOFF`` command in a mission. 31 | Plane should first arm and then change to ``AUTO`` mode to start the mission. 32 | 33 | The code below shows a function to arm a Copter, take off, and fly to a specified altitude. This is taken from :ref:`example_simple_goto`. 34 | 35 | .. code-block:: python 36 | 37 | # Connect to the Vehicle (in this case a simulator running the same computer) 38 | vehicle = connect('tcp:127.0.0.1:5760', wait_ready=True) 39 | 40 | def arm_and_takeoff(aTargetAltitude): 41 | """ 42 | Arms vehicle and fly to aTargetAltitude. 43 | """ 44 | 45 | print "Basic pre-arm checks" 46 | # Don't try to arm until autopilot is ready 47 | while not vehicle.is_armable: 48 | print " Waiting for vehicle to initialise..." 49 | time.sleep(1) 50 | 51 | print "Arming motors" 52 | # Copter should arm in GUIDED mode 53 | vehicle.mode = VehicleMode("GUIDED") 54 | vehicle.armed = True 55 | 56 | # Confirm vehicle armed before attempting to take off 57 | while not vehicle.armed: 58 | print " Waiting for arming..." 59 | time.sleep(1) 60 | 61 | print "Taking off!" 62 | vehicle.simple_takeoff(aTargetAltitude) # Take off to target altitude 63 | 64 | # Wait until the vehicle reaches a safe height before processing the goto (otherwise the command 65 | # after Vehicle.simple_takeoff will execute immediately). 66 | while True: 67 | print " Altitude: ", vehicle.location.global_relative_frame.alt 68 | #Break and return from function just below target altitude. 69 | if vehicle.location.global_relative_frame.alt>=aTargetAltitude*0.95: 70 | print "Reached target altitude" 71 | break 72 | time.sleep(1) 73 | 74 | arm_and_takeoff(20) 75 | 76 | 77 | The function first performs some pre-arm checks. 78 | 79 | .. note:: 80 | 81 | Arming turns on the vehicle's motors in preparation for flight. The flight controller will not arm 82 | until the vehicle has passed a series of pre-arm checks to ensure that it is safe to fly. 83 | 84 | These checks are encapsulated by the :py:func:`Vehicle.is_armable ` 85 | attribute, which is ``true`` when the vehicle has booted, EKF is ready, and the vehicle has GPS lock. 86 | 87 | .. code-block:: python 88 | 89 | print "Basic pre-arm checks" 90 | # Don't let the user try to arm until autopilot is ready 91 | while not vehicle.is_armable: 92 | print " Waiting for vehicle to initialise..." 93 | time.sleep(1) 94 | 95 | .. note:: 96 | 97 | If you need more status information you can perform the following sorts of checks: 98 | 99 | .. code-block:: python 100 | 101 | if v.mode.name == "INITIALISING": 102 | print "Waiting for vehicle to initialise" 103 | time.sleep(1) 104 | while vehicle.gps_0.fix_type < 2: 105 | print "Waiting for GPS...:", vehicle.gps_0.fix_type 106 | time.sleep(1) 107 | 108 | You should always do a final check on :py:func:`Vehicle.is_armable `! 109 | 110 | 111 | Once the vehicle is ready we set the mode to ``GUIDED`` and arm it. We then wait until arming is confirmed 112 | before sending the :py:func:`takeoff ` command. 113 | 114 | .. code-block:: python 115 | 116 | print "Arming motors" 117 | # Copter should arm in GUIDED mode 118 | vehicle.mode = VehicleMode("GUIDED") 119 | vehicle.armed = True 120 | 121 | while not vehicle.armed: 122 | print " Waiting for arming..." 123 | time.sleep(1) 124 | 125 | print "Taking off!" 126 | vehicle.simple_takeoff(aTargetAltitude) # Take off to target altitude 127 | 128 | The ``takeoff`` command is asynchronous and can be interrupted if another command arrives before it reaches 129 | the target altitude. This could have potentially serious consequences if the vehicle is commanded to move 130 | horizontally before it reaches a safe height. In addition, there is no message sent back from the vehicle 131 | to inform the client code that the target altitude has been reached. 132 | 133 | To address these issues, the function waits until the vehicle reaches a specified height before returning. If you're not 134 | concerned about reaching a particular height, a simpler implementation might just "wait" for a few seconds. 135 | 136 | .. code-block:: python 137 | 138 | while True: 139 | print " Altitude: ", vehicle.location.global_relative_frame.alt 140 | #Break and return from function just below target altitude. 141 | if vehicle.location.global_relative_frame.alt>=aTargetAltitude*0.95: 142 | print "Reached target altitude" 143 | break 144 | time.sleep(1) 145 | 146 | When the function returns the app can continue in ``GUIDED`` mode or switch to ``AUTO`` mode to start a mission. 147 | -------------------------------------------------------------------------------- /docs/examples/follow_me.rst: -------------------------------------------------------------------------------- 1 | .. _example_follow_me: 2 | 3 | ================== 4 | Example: Follow Me 5 | ================== 6 | 7 | The *Follow Me* example moves a vehicle to track your position, using location information from a USB GPS attached to your (Linux) laptop. 8 | 9 | The source code is a good *starting point* for your own applications. It can be extended to use other 10 | python language features and libraries (OpenCV, classes, lots of packages etc...) 11 | 12 | 13 | .. note:: This example can only run on a Linux computer, because it depends on the Linux-only *gpsd* service. 14 | 15 | .. warning:: Run this example with caution - be ready to exit follow-me mode by switching the flight mode switch on your RC radio. 16 | 17 | 18 | Running the example 19 | =================== 20 | 21 | DroneKit (for Linux) and the vehicle should be set up as described in :ref:`installing_dronekit`. 22 | 23 | Once you've done that: 24 | 25 | #. Install the *gpsd* service (as shown for Ubuntu Linux below): 26 | 27 | .. code-block:: bash 28 | 29 | sudo apt-get install gpsd gpsd-clients 30 | 31 | You can then plug in a USB GPS and run the "xgps" client to confirm that it is working. 32 | 33 | .. note:: 34 | 35 | If you do not have a USB GPS you can use simulated data by running *dronekit-python/examples/follow_me/run-fake-gps.sh* 36 | (in a separate terminal from where you're running DroneKit-Python). This approach simulates a single location, and so 37 | is really only useful for verifying that the script is working correctly. 38 | 39 | 40 | #. Get the DroneKit-Python example source code onto your local machine. The easiest way to do this 41 | is to clone the **dronekit-python** repository from Github. On the command prompt enter: 42 | 43 | .. code-block:: bash 44 | 45 | git clone http://github.com/dronekit/dronekit-python.git 46 | 47 | #. Navigate to the example folder as shown: 48 | 49 | .. code-block:: bash 50 | 51 | cd dronekit-python/examples/follow_me/ 52 | 53 | 54 | #. You can run the example against a simulator (DroneKit-SITL) by specifying the Python script without any arguments. 55 | The example will download SITL binaries (if needed), start the simulator, and then connect to it: 56 | 57 | .. code-block:: bash 58 | 59 | python follow_me.py 60 | 61 | On the command prompt you should see (something like): 62 | 63 | .. code:: bash 64 | 65 | Starting copter simulator (SITL) 66 | SITL already Downloaded. 67 | Connecting to vehicle on: tcp:127.0.0.1:5760 68 | >>> APM:Copter V3.4-dev (e0810c2e) 69 | >>> Frame: QUAD 70 | Link timeout, no heartbeat in last 5 seconds 71 | Basic pre-arm checks 72 | Waiting for GPS...: None 73 | ... 74 | Waiting for GPS...: None 75 | Taking off! 76 | Altitude: 0.019999999553 77 | ... 78 | Altitude: 4.76000022888 79 | Reached target altitude 80 | Going to: Location:lat=50.616468333,lon=7.131903333,alt=30,is_relative=True 81 | ... 82 | Going to: Location:lat=50.616468333,lon=7.131903333,alt=30,is_relative=True 83 | Going to: Location:lat=50.616468333,lon=7.131903333,alt=30,is_relative=True 84 | User has changed flight modes - aborting follow-me 85 | Close vehicle object 86 | Completed 87 | 88 | .. note:: 89 | 90 | The terminal output above was created using simulated GPS data 91 | (which is why the same target location is returned every time). 92 | 93 | To stop follow-me you can change the vehicle mode or do Ctrl+C 94 | (on a real flight you can just change the mode switch on your 95 | RC transmitter). 96 | 97 | 98 | #. You can run the example against a specific connection (simulated or otherwise) by passing the :ref:`connection string ` for your vehicle in the ``--connect`` parameter. 99 | 100 | For example, to connect to SITL running on UDP port 14550 on your local computer: 101 | 102 | .. code-block:: bash 103 | 104 | python follow_me.py --connect 127.0.0.1:14550 105 | 106 | 107 | 108 | How does it work? 109 | ================= 110 | 111 | Most of the example should be fairly familiar as it uses the same code as other examples for connecting to the vehicle, 112 | :ref:`taking off `, and closing the vehicle object. 113 | 114 | The example-specific code is shown below. All this does is attempt to get a gps socket and read the location in a two second loop. If it is successful it 115 | reports the value and uses :py:func:`Vehicle.simple_goto ` to move to the new position. The loop exits when 116 | the mode is changed. 117 | 118 | .. code-block:: python 119 | 120 | import gps 121 | import socket 122 | 123 | ... 124 | 125 | try: 126 | # Use the python gps package to access the laptop GPS 127 | gpsd = gps.gps(mode=gps.WATCH_ENABLE) 128 | 129 | #Arm and take off to an altitude of 5 meters 130 | arm_and_takeoff(5) 131 | 132 | while True: 133 | 134 | if vehicle.mode.name != "GUIDED": 135 | print "User has changed flight modes - aborting follow-me" 136 | break 137 | 138 | # Read the GPS state from the laptop 139 | gpsd.next() 140 | 141 | # Once we have a valid location (see gpsd documentation) we can start moving our vehicle around 142 | if (gpsd.valid & gps.LATLON_SET) != 0: 143 | altitude = 30 # in meters 144 | dest = LocationGlobalRelative(gpsd.fix.latitude, gpsd.fix.longitude, altitude) 145 | print "Going to: %s" % dest 146 | 147 | # A better implementation would only send new waypoints if the position had changed significantly 148 | vehicle.simple_goto(dest) 149 | 150 | # Send a new target every two seconds 151 | # For a complete implementation of follow me you'd want adjust this delay 152 | time.sleep(2) 153 | 154 | except socket.error: 155 | print "Error: gpsd service does not seem to be running, plug in USB GPS or run run-fake-gps.sh" 156 | sys.exit(1) 157 | 158 | 159 | 160 | Source code 161 | =========== 162 | 163 | The full source code at documentation build-time is listed below (`current version on github `_): 164 | 165 | .. include:: ../../examples/follow_me/follow_me.py 166 | :literal: 167 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## Version 2.9.2 (2019-03-18) 5 | 6 | ### Improvements 7 | * CI integration improvements 8 | * Python3 compatability 9 | * use logging module 10 | * log statustexts 11 | * documentation improvements 12 | * convenience functions added: wait_for, wait_for_armable, arm, disarm, wait_for_mode, wait_for_alt, wait_simple_takeoff 13 | * play_tune method added 14 | * reboot method added 15 | * send_calibrate_gyro, send_calibrate_magnetometer, send_calibrate_magnetometer, send_calibrate_vehicle_level, send_calibrate_barometer all added 16 | * update gimbal orientation from MOUNT_ORIENTATION 17 | * add a still-waiting callback for connect() to provide debug on where the connection is up to 18 | * several new tests added (including, play_tune, reboot and set_attitude_target) 19 | 20 | ### Cleanup 21 | * flake8 compliance improvements 22 | * test includes pruned 23 | * examples cleaned up 24 | 25 | ### Bug Fixes 26 | * ignore GCS heartbeats for the purposes of link up 27 | * many! 28 | 29 | ## Version 2.9.1 (2017-04-21) 30 | 31 | ### Improvements 32 | * home locatin notifications 33 | * notify ci status to gitter 34 | * basic python 3 support 35 | * isolated logger function so implementers can override 36 | * rename windows installer 37 | 38 | ### Cleanup 39 | * removed legacy cloud integrations 40 | 41 | ### Bug Fixes 42 | * fix missing ** operator for pymavlink compatibility 43 | 44 | ## Version 2.9.0 (2016-08-29) 45 | 46 | ### Bug Fixes 47 | * MAVConnection stops threads on exit and close 48 | * PX4 Pro flight modes are now properly supported 49 | * go to test now uses correct `global_relative_frame` alt 50 | 51 | ### Improvements 52 | * Updated pymavlink dependency to v2 from v1 hoping we don't fall behind 53 | again. 54 | 55 | ## Version 2.8.0 (2016-07-15) 56 | 57 | ### Bug Fixes 58 | * Makes sure we are listening to `HOME_LOCATION` message, befor we 59 | would only set home location if received by waypoints. 60 | 61 | ## Version 2.7.0 (2016-06-21) 62 | 63 | ### Improvements 64 | * Adds udpin-multi support 65 | 66 | ## Version 2.6.0 (2016-06-17) 67 | 68 | ### Bug Fixes 69 | * Fixes patched mavutil sendfn 70 | 71 | ## Version 2.5.0 (2016-05-04) 72 | 73 | ### Improvements 74 | * Catch and display message and attribute errors, then continue 75 | * Improved takeoff example docs 76 | * Deploy docs on successful merge into master (from CircleCI) 77 | * Drone delivery example, explain port to connect 78 | * MicroCGS example now uses SITL 79 | * Make running examples possible on Vagrant 80 | 81 | ### Bug Fixes 82 | * Mav type for rover was incorrect 83 | * `_is_mode_available` can now handle unrecognized mode codes 84 | * Fix broken links on companion computer page 85 | * Fix infinite loop on channel test 86 | 87 | 88 | 89 | ## Version 2.4.0 (2016-02-29) 90 | 91 | ### Bug Fixes 92 | 93 | * Use monotonic clock for all of the internal timeouts and time 94 | measurements 95 | * Docs fixes 96 | 97 | 98 | ## Version 2.3.0 (2016-02-26) 99 | 100 | ### New Features 101 | 102 | * PX4 compatibility improvements 103 | 104 | ### Updated Features 105 | 106 | * Documentation fixes 107 | * PIP repository improvements 108 | * Mode-setting API improvements 109 | * ardupilot-solo compatibility fixes 110 | 111 | 112 | 113 | ## Version 2.2.0 (2016-02-19) 114 | 115 | ### Bug Fixes 116 | 117 | * Splits outbound messages into its own thread. 118 | * Remove of capabilities request on HEARTBEAT listener 119 | * Check if mode_mapping has items before iteration 120 | 121 | 122 | 123 | ## Version 2.1.0 (2016-02-16) 124 | 125 | 126 | ### New Features 127 | 128 | 129 | * Gimbal control attribute 130 | * Autopilot version attribute 131 | * Autopilot capabilities attribute 132 | * Best Practice guide documentation. 133 | * Performance test example (restructured and docs added) 134 | 135 | ### Updated Features: 136 | 137 | Many documentation fixes: 138 | 139 | * Restructured documentation with Develop (Concepts) and Guide (HowTo) sections 140 | * Docs separated out "Connection Strings" section. 141 | * Improved test and contribution sections. 142 | * Updated examples and documentation to use DroneKit-Sitl for simulation ("zero setup examples") 143 | * Debugging docs updated with additional libraries. 144 | * Flight Replay example fetches data from TLOG rather than droneshare 145 | * Drone Delivery example now uses strart location for home address. 146 | * Disabled web tests (not currently supported/used) 147 | * Updated copyright range to include changes in 2016 148 | 149 | ### Bug Fixes 150 | 151 | * Numerous minor docs fixes. 152 | * Harmonise nosetest options across each of the integration platforms 153 | * Fix incorrect property marker for airspeed attribute 154 | 155 | 156 | 157 | ## Version 2.0.2 (2015-11-30) 158 | 159 | ### Bug Fixes: 160 | 161 | * Updates `requests` dependency to work >=2.5.0 162 | 163 | 164 | ## Version 2.0.0 (2015-11-23) 165 | 166 | ### New Features: 167 | 168 | * Renamed library and package from DroneAPI to DroneKit on pip 169 | * DroneKit Python is now a standalone library and no longer requires use of MAVProxy 170 | * Connect multiple vehicles in one script by creating separate vehicle instances 171 | * Removed NumPy, ProtoBuf as dependencies 172 | * Add MAVLink message listeners using `add_message_listener` methods 173 | * Added `on_attribute` and `on_message` function decorator shorthands 174 | * Added `mount_status`, `system_status`, `ekf_ok`, `is_armable`, `heading` 175 | * Made settable `groundspeed`, `airspeed` 176 | * Moved `dronekit.lib` entries to root package `dronekit` 177 | * Added `parameters.set` and `parameters.get` for fine-tuned parameter access 178 | * `parameters` now observable and iterable (#442) 179 | * Added `last_heartbeat` attribute, updated every event loop with time since last heartbeat (#451) 180 | * Await attributes through `wait_ready` method and `connect` method parameter 181 | * Adds subclassable Vehicle class, used by `vehicle_class` parameter in `connect` 182 | 183 | ### Updated Features: 184 | 185 | * local_connect renamed to connect(), accepting a connection path, link configuration, and timeout settings 186 | * Removed `.set_mavrx_callback`. Use `vehicle.on_message('*', obj)` methods 187 | * Renamed `add_attribute_observer` methods to `add_attribute_listener`, etc. (#420) 188 | * Renamed `wait_init` and `wait_valid` to `wait_ready` 189 | * Split `home_location` is a separate attribute from `commands` waypoint array 190 | * Moved RC channels into `.channels` object (#427) 191 | * Split location information into `local_frame`, `global_frame`, and `global_relative_frame` (and removed `is_relative`) (#445) 192 | * Renamed `flush` to `commands.upload`, as it only impacts waypoints (#276) 193 | * `commands.goto` and `commands.takeoff` renamed to `simple_goto` and `simple_takeoff` 194 | 195 | ### Bug Fixes: 196 | 197 | * `armed` and `mode` attributes updated constantly (#60, #446) 198 | * Parameter setting times out (#12) 199 | * `battery` access can throw exception (#298) 200 | * Vehicle.location reports incorrect is_relative value for Copter (#130) 201 | * Excess arming message when already armed 202 | -------------------------------------------------------------------------------- /docs/examples/mission_basic.rst: -------------------------------------------------------------------------------- 1 | .. _example_mission_basic: 2 | 3 | ====================== 4 | Example: Basic Mission 5 | ====================== 6 | 7 | This example demonstrates the basic mission operations provided by DroneKit-Python, including: 8 | downloading missions from the vehicle, clearing missions, creating mission commands 9 | and uploading them to the vehicle, monitoring the current active command, and changing the active 10 | command. 11 | 12 | The guide topic :ref:`auto_mode_vehicle_control` provides more detailed explanation of how the API 13 | should be used. 14 | 15 | .. figure:: mission_basic_example_copter_path.png 16 | :width: 50 % 17 | :alt: Basic Mission Path 18 | 19 | Basic Mission Example: Flight path 20 | 21 | 22 | Running the example 23 | =================== 24 | 25 | The example can be run as described in :doc:`running_examples` (which in turn assumes that the vehicle 26 | and DroneKit have been set up as described in :ref:`installing_dronekit`). 27 | 28 | In summary, after cloning the repository: 29 | 30 | #. Navigate to the example folder as shown: 31 | 32 | .. code-block:: bash 33 | 34 | cd dronekit-python/examples/mission_basic/ 35 | 36 | #. You can run the example against a simulator (DroneKit-SITL) by specifying the Python script without any arguments. 37 | The example will download SITL binaries (if needed), start the simulator, and then connect to it: 38 | 39 | .. code-block:: bash 40 | 41 | python mission_basic.py 42 | 43 | On the command prompt you should see (something like): 44 | 45 | .. code:: bash 46 | 47 | Starting copter simulator (SITL) 48 | SITL already Downloaded. 49 | Connecting to vehicle on: tcp:127.0.0.1:5760 50 | >>> APM:Copter V3.3 (d6053245) 51 | >>> Frame: QUAD 52 | >>> Calibrating barometer 53 | >>> Initialising APM... 54 | >>> barometer calibration complete 55 | >>> GROUND START 56 | >>> Mission Planner 1.3.35 57 | Create a new mission (for current location) 58 | Clear any existing commands 59 | Define/add new commands. 60 | Upload new commands to vehicle 61 | Basic pre-arm checks 62 | Waiting for vehicle to initialise... 63 | >>> flight plan received 64 | Waiting for vehicle to initialise... 65 | ... 66 | Waiting for vehicle to initialise... 67 | Arming motors 68 | Waiting for arming... 69 | ... 70 | Waiting for arming... 71 | >>> ARMING MOTORS 72 | >>> GROUND START 73 | Waiting for arming... 74 | >>> Initialising APM... 75 | Taking off! 76 | Altitude: 0.0 77 | Altitude: 0.11 78 | ... 79 | Altitude: 8.9 80 | Altitude: 9.52 81 | Reached target altitude 82 | Starting mission 83 | Distance to waypoint (0): None 84 | Distance to waypoint (1): 78.8000191616 85 | Distance to waypoint (1): 78.3723704927 86 | ... 87 | Distance to waypoint (1): 20.7131390269 88 | Distance to waypoint (1): 15.4196151863 89 | >>> Reached Command #1 90 | Distance to waypoint (2): 115.043560356 91 | Distance to waypoint (2): 117.463458185 92 | ... 93 | Distance to waypoint (2): 25.7122243168 94 | Distance to waypoint (2): 16.8624794106 95 | >>> Reached Command #2 96 | Distance to waypoint (3): 100.45231832 97 | Skipping to Waypoint 5 when reach waypoint 3 98 | Distance to waypoint (5): 154.645144788 99 | Exit 'standard' mission when start heading to final waypoint (5) 100 | Return to launch 101 | Close vehicle object 102 | 103 | 104 | .. tip:: 105 | 106 | It is more interesting to watch the example run on a map than the console. The topic :ref:`viewing_uav_on_map` 107 | explains how to set up *Mission Planner* to view a vehicle running on the simulator (SITL). 108 | 109 | #. You can run the example against a specific connection (simulated or otherwise) by passing the :ref:`connection string ` for your vehicle in the ``--connect`` parameter. 110 | 111 | For example, to connect to SITL running on UDP port 14550 on your local computer: 112 | 113 | .. code-block:: bash 114 | 115 | python mission_basic.py --connect 127.0.0.1:14550 116 | 117 | 118 | 119 | How does it work? 120 | ================= 121 | 122 | The :ref:`source code ` is relatively self-documenting, and most of its main 123 | operations are explained in the guide topic :ref:`auto_mode_vehicle_control` . 124 | 125 | In overview, the example calls ``adds_square_mission(vehicle.location.global_frame,50)`` to first 126 | clear the current mission and then define a new mission with a takeoff command and four waypoints arranged 127 | in a square around the central position (two waypoints are added in the last position - 128 | we use :py:func:`next ` to determine when we've reached the final point). 129 | The clear command and new mission items are then uploaded to the vehicle. 130 | 131 | After taking off (in guided mode using the ``takeoff()`` function) the example starts the mission by setting the mode to AUTO: 132 | 133 | .. code:: python 134 | 135 | print "Starting mission" 136 | # Set mode to AUTO to start mission 137 | vehicle.mode = VehicleMode("AUTO") 138 | 139 | The progress of the mission is monitored in a loop. The convenience function 140 | :ref:`distance_to_current_waypoint() ` 141 | gets the distance to the next waypoint and 142 | :py:func:`Vehicle.commands.next ` gets the value of 143 | the next command. 144 | 145 | We also show how to jump to a specified command using 146 | :py:func:`Vehicle.commands.next ` (note how we skip the third command below): 147 | 148 | .. code:: python 149 | 150 | while True: 151 | nextwaypoint=vehicle.commands.next 152 | print 'Distance to waypoint (%s): %s' % (nextwaypoint, distance_to_current_waypoint()) 153 | 154 | if nextwaypoint==3: #Skip to next waypoint 155 | print 'Skipping to Waypoint 5 when reach waypoint 3' 156 | vehicle.commands.next=5 157 | vehicle.commands.upload() 158 | if nextwaypoint==5: #Dummy waypoint - as soon as we reach waypoint 4 this is true and we exit. 159 | print "Exit 'standard' mission when start heading to final waypoint (5)" 160 | break; 161 | time.sleep(1) 162 | 163 | When the vehicle starts the 5th command (a dummy waypoint) the loop breaks and the mode is set to RTL (return to launch). 164 | 165 | 166 | .. _example_mission_basic_known_issues: 167 | 168 | Known issues 169 | ============ 170 | 171 | This example has no known issues. 172 | 173 | 174 | .. _example_mission_basic_source_code: 175 | 176 | Source code 177 | =========== 178 | 179 | The full source code at documentation build-time is listed below 180 | (`current version on Github `_): 181 | 182 | .. literalinclude:: ../../examples/mission_basic/mission_basic.py 183 | :language: python 184 | 185 | -------------------------------------------------------------------------------- /examples/set_attitude_target/set_attitude_target.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | 5 | set_attitude_target.py: (Copter Only) 6 | 7 | This example shows how to move/direct Copter and send commands 8 | in GUIDED_NOGPS mode using DroneKit Python. 9 | 10 | Caution: A lot of unexpected behaviors may occur in GUIDED_NOGPS mode. 11 | Always watch the drone movement, and make sure that you are in dangerless environment. 12 | Land the drone as soon as possible when it shows any unexpected behavior. 13 | 14 | Tested in Python 2.7.10 15 | 16 | """ 17 | 18 | from dronekit import connect, VehicleMode, LocationGlobal, LocationGlobalRelative 19 | from pymavlink import mavutil # Needed for command message definitions 20 | import time 21 | import math 22 | 23 | # Set up option parsing to get connection string 24 | import argparse 25 | parser = argparse.ArgumentParser(description='Control Copter and send commands in GUIDED mode ') 26 | parser.add_argument('--connect', 27 | help="Vehicle connection target string. If not specified, SITL automatically started and used.") 28 | args = parser.parse_args() 29 | 30 | connection_string = args.connect 31 | sitl = None 32 | 33 | # Start SITL if no connection string specified 34 | if not connection_string: 35 | import dronekit_sitl 36 | sitl = dronekit_sitl.start_default() 37 | connection_string = sitl.connection_string() 38 | 39 | 40 | # Connect to the Vehicle 41 | print('Connecting to vehicle on: %s' % connection_string) 42 | vehicle = connect(connection_string, wait_ready=True) 43 | 44 | def arm_and_takeoff_nogps(aTargetAltitude): 45 | """ 46 | Arms vehicle and fly to aTargetAltitude without GPS data. 47 | """ 48 | 49 | ##### CONSTANTS ##### 50 | DEFAULT_TAKEOFF_THRUST = 0.7 51 | SMOOTH_TAKEOFF_THRUST = 0.6 52 | 53 | print("Basic pre-arm checks") 54 | # Don't let the user try to arm until autopilot is ready 55 | # If you need to disable the arming check, 56 | # just comment it with your own responsibility. 57 | while not vehicle.is_armable: 58 | print(" Waiting for vehicle to initialise...") 59 | time.sleep(1) 60 | 61 | 62 | print("Arming motors") 63 | # Copter should arm in GUIDED_NOGPS mode 64 | vehicle.mode = VehicleMode("GUIDED_NOGPS") 65 | vehicle.armed = True 66 | 67 | while not vehicle.armed: 68 | print(" Waiting for arming...") 69 | vehicle.armed = True 70 | time.sleep(1) 71 | 72 | print("Taking off!") 73 | 74 | thrust = DEFAULT_TAKEOFF_THRUST 75 | while True: 76 | current_altitude = vehicle.location.global_relative_frame.alt 77 | print(" Altitude: %f Desired: %f" % 78 | (current_altitude, aTargetAltitude)) 79 | if current_altitude >= aTargetAltitude*0.95: # Trigger just below target alt. 80 | print("Reached target altitude") 81 | break 82 | elif current_altitude >= aTargetAltitude*0.6: 83 | thrust = SMOOTH_TAKEOFF_THRUST 84 | set_attitude(thrust = thrust) 85 | time.sleep(0.2) 86 | 87 | def send_attitude_target(roll_angle = 0.0, pitch_angle = 0.0, 88 | yaw_angle = None, yaw_rate = 0.0, use_yaw_rate = False, 89 | thrust = 0.5): 90 | """ 91 | use_yaw_rate: the yaw can be controlled using yaw_angle OR yaw_rate. 92 | When one is used, the other is ignored by Ardupilot. 93 | thrust: 0 <= thrust <= 1, as a fraction of maximum vertical thrust. 94 | Note that as of Copter 3.5, thrust = 0.5 triggers a special case in 95 | the code for maintaining current altitude. 96 | """ 97 | if yaw_angle is None: 98 | # this value may be unused by the vehicle, depending on use_yaw_rate 99 | yaw_angle = vehicle.attitude.yaw 100 | # Thrust > 0.5: Ascend 101 | # Thrust == 0.5: Hold the altitude 102 | # Thrust < 0.5: Descend 103 | msg = vehicle.message_factory.set_attitude_target_encode( 104 | 0, # time_boot_ms 105 | 1, # Target system 106 | 1, # Target component 107 | 0b00000000 if use_yaw_rate else 0b00000100, 108 | to_quaternion(roll_angle, pitch_angle, yaw_angle), # Quaternion 109 | 0, # Body roll rate in radian 110 | 0, # Body pitch rate in radian 111 | math.radians(yaw_rate), # Body yaw rate in radian/second 112 | thrust # Thrust 113 | ) 114 | vehicle.send_mavlink(msg) 115 | 116 | def set_attitude(roll_angle = 0.0, pitch_angle = 0.0, 117 | yaw_angle = None, yaw_rate = 0.0, use_yaw_rate = False, 118 | thrust = 0.5, duration = 0): 119 | """ 120 | Note that from AC3.3 the message should be re-sent more often than every 121 | second, as an ATTITUDE_TARGET order has a timeout of 1s. 122 | In AC3.2.1 and earlier the specified attitude persists until it is canceled. 123 | The code below should work on either version. 124 | Sending the message multiple times is the recommended way. 125 | """ 126 | send_attitude_target(roll_angle, pitch_angle, 127 | yaw_angle, yaw_rate, False, 128 | thrust) 129 | start = time.time() 130 | while time.time() - start < duration: 131 | send_attitude_target(roll_angle, pitch_angle, 132 | yaw_angle, yaw_rate, False, 133 | thrust) 134 | time.sleep(0.1) 135 | # Reset attitude, or it will persist for 1s more due to the timeout 136 | send_attitude_target(0, 0, 137 | 0, 0, True, 138 | thrust) 139 | 140 | def to_quaternion(roll = 0.0, pitch = 0.0, yaw = 0.0): 141 | """ 142 | Convert degrees to quaternions 143 | """ 144 | t0 = math.cos(math.radians(yaw * 0.5)) 145 | t1 = math.sin(math.radians(yaw * 0.5)) 146 | t2 = math.cos(math.radians(roll * 0.5)) 147 | t3 = math.sin(math.radians(roll * 0.5)) 148 | t4 = math.cos(math.radians(pitch * 0.5)) 149 | t5 = math.sin(math.radians(pitch * 0.5)) 150 | 151 | w = t0 * t2 * t4 + t1 * t3 * t5 152 | x = t0 * t3 * t4 - t1 * t2 * t5 153 | y = t0 * t2 * t5 + t1 * t3 * t4 154 | z = t1 * t2 * t4 - t0 * t3 * t5 155 | 156 | return [w, x, y, z] 157 | 158 | # Take off 2.5m in GUIDED_NOGPS mode. 159 | arm_and_takeoff_nogps(2.5) 160 | 161 | # Hold the position for 3 seconds. 162 | print("Hold position for 3 seconds") 163 | set_attitude(duration = 3) 164 | 165 | # Uncomment the lines below for testing roll angle and yaw rate. 166 | # Make sure that there is enough space for testing this. 167 | 168 | # set_attitude(roll_angle = 1, thrust = 0.5, duration = 3) 169 | # set_attitude(yaw_rate = 30, thrust = 0.5, duration = 3) 170 | 171 | # Move the drone forward and backward. 172 | # Note that it will be in front of original position due to inertia. 173 | print("Move forward") 174 | set_attitude(pitch_angle = -5, thrust = 0.5, duration = 3.21) 175 | 176 | print("Move backward") 177 | set_attitude(pitch_angle = 5, thrust = 0.5, duration = 3) 178 | 179 | 180 | print("Setting LAND mode...") 181 | vehicle.mode = VehicleMode("LAND") 182 | time.sleep(1) 183 | 184 | # Close vehicle object before exiting script 185 | print("Close vehicle object") 186 | vehicle.close() 187 | 188 | # Shut down simulator if it was started. 189 | if sitl is not None: 190 | sitl.stop() 191 | 192 | print("Completed") 193 | -------------------------------------------------------------------------------- /docs/examples/channel_overrides.rst: -------------------------------------------------------------------------------- 1 | .. _example_channel_overrides: 2 | .. _vehicle_state_channel_override: 3 | 4 | ======================================= 5 | Example: Channels and Channel Overrides 6 | ======================================= 7 | 8 | This example shows how to get channel information and to get/set channel-override information. 9 | 10 | .. warning:: 11 | 12 | Channel overrides (a.k.a. "RC overrides") are highly dis-commended (they are primarily intended 13 | for simulating user input and when implementing certain types of joystick control). 14 | 15 | Instead use the appropriate MAVLink commands like DO_SET_SERVO/DO_SET_RELAY, or more generally set 16 | the desired position or direction/speed. 17 | 18 | If you have no choice but to use a channel-override please explain why in a 19 | `Github issue `_ and we will attempt to find a 20 | better alternative. 21 | 22 | 23 | Running the example 24 | =================== 25 | 26 | The example can be run as described in :doc:`running_examples` (which in turn assumes that the vehicle 27 | and DroneKit have been set up as described in :ref:`installing_dronekit`). 28 | 29 | In summary, after cloning the repository: 30 | 31 | #. Navigate to the example folder as shown: 32 | 33 | .. code-block:: bash 34 | 35 | cd dronekit-python/examples/channel_overrides/ 36 | 37 | 38 | #. You can run the example against a simulator (DroneKit-SITL) by specifying the Python script without any arguments. 39 | The example will download SITL binaries (if needed), start the simulator, and then connect to it: 40 | 41 | .. code-block:: bash 42 | 43 | python channel_overrides.py 44 | 45 | On the command prompt you should see (something like): 46 | 47 | .. code:: bash 48 | 49 | Starting copter simulator (SITL) 50 | SITL already Downloaded. 51 | Connecting to vehicle on: tcp:127.0.0.1:5760 52 | >>> APM:Copter V3.3 (d6053245) 53 | >>> Frame: QUAD 54 | >>> Calibrating barometer 55 | >>> Initialising APM... 56 | >>> barometer calibration complete 57 | >>> GROUND START 58 | Channel values from RC Tx: {'1': 1500, '3': 1000, '2': 1500, '5': 1800, '4': 1500, '7': 1000, '6': 1000, '8': 1800} 59 | Read channels individually: 60 | Ch1: 1500 61 | Ch2: 1500 62 | Ch3: 1000 63 | Ch4: 1500 64 | Ch5: 1800 65 | Ch6: 1000 66 | Ch7: 1000 67 | Ch8: 1800 68 | Number of channels: 8 69 | Channel overrides: {} 70 | Set Ch2 override to 200 (indexing syntax) 71 | Channel overrides: {'2': 200} 72 | Ch2 override: 200 73 | Set Ch3 override to 300 (dictionary syntax) 74 | Channel overrides: {'3': 300} 75 | Set Ch1-Ch8 overrides to 110-810 respectively 76 | Channel overrides: {'1': 110, '3': 310, '2': 210, '5': 510, '4': 4100, '7': 710, '6': 610, '8': 810} 77 | Cancel Ch2 override (indexing syntax) 78 | Channel overrides: {'1': 110, '3': 310, '5': 510, '4': 4100, '7': 710, '6': 610, '8': 810} 79 | Clear Ch3 override (del syntax) 80 | Channel overrides: {'1': 110, '5': 510, '4': 4100, '7': 710, '6': 610, '8': 810} 81 | Clear Ch5, Ch6 override and set channel 3 to 500 (dictionary syntax) 82 | Channel overrides: {'3': 500} 83 | Clear all overrides 84 | Channel overrides: {} 85 | Close vehicle object 86 | Completed 87 | 88 | #. You can run the example against a specific connection (simulated or otherwise) by passing the :ref:`connection string ` for your vehicle in the ``--connect`` parameter. 89 | 90 | For example, to connect to SITL running on UDP port 14550 on your local computer: 91 | 92 | .. code-block:: bash 93 | 94 | python channel_overrides.py --connect 127.0.0.1:14550 95 | 96 | 97 | How does it work? 98 | ================= 99 | 100 | The RC transmitter channels are connected to the autopilot and control the vehicle. 101 | 102 | The values of the first four channels map to the main flight controls: 1=Roll, 2=Pitch, 3=Throttle, 4=Yaw (the mapping is defined in ``RCMAP_`` parameters in 103 | `Plane `_, 104 | `Copter `_ , 105 | `Rover `_). 106 | 107 | The remaining channel values are configurable, and their purpose can be determined using the 108 | `RCn_FUNCTION parameters `_. 109 | In general a value of 0 set for a specific ``RCn_FUNCTION`` indicates that the channel can be 110 | `mission controlled `_ (i.e. it will not directly be 111 | controlled by normal autopilot code). 112 | 113 | You can read the values of the channels using the :py:attr:`Vehicle.channels ` attribute. The values are regularly updated, 114 | from the UAV, based on the RC inputs from the transmitter. These can be read either as a set or individually: 115 | 116 | .. code:: python 117 | 118 | # Get all channel values from RC transmitter 119 | print "Channel values from RC Tx:", vehicle.channels 120 | 121 | # Access channels individually 122 | print "Read channels individually:" 123 | print " Ch1: %s" % vehicle.channels['1'] 124 | print " Ch2: %s" % vehicle.channels['2'] 125 | 126 | You can override the values sent to the vehicle by the autopilot using :py:attr:`Vehicle.channels.overrides `. 127 | The overrides can be written individually using an indexing syntax or as a set using a dictionary syntax. 128 | 129 | .. code:: python 130 | 131 | # Set Ch2 override to 200 using indexing syntax 132 | vehicle.channels.overrides['2'] = 200 133 | # Set Ch3, Ch4 override to 300,400 using dictionary syntax" 134 | vehicle.channels.overrides = {'3':300, '4':400} 135 | 136 | To clear all overrides, set the attribute to an empty dictionary. 137 | To clear an individual override you can set its value to ``None`` (or call ``del`` on it): 138 | 139 | .. code:: python 140 | 141 | # Clear override by setting channels to None 142 | # Clear using index syntax 143 | vehicle.channels.overrides['2'] = None 144 | 145 | # Clear using 'del' syntax 146 | del vehicle.channels.overrides['3'] 147 | 148 | # Clear using dictionary syntax (and set override at same time!) 149 | vehicle.channels.overrides = {'5':None, '6':None,'3':500} 150 | 151 | # Clear all overrides by setting an empty dictionary 152 | vehicle.channels.overrides = {} 153 | 154 | Read the channel overrides either as a dictionary or by index. 155 | 156 | .. code:: python 157 | 158 | # Get all channel overrides 159 | print " Channel overrides: %s" % vehicle.channels.overrides 160 | # Print just one channel override 161 | print " Ch2 override: %s" % vehicle.channels.overrides['2'] 162 | 163 | .. note:: 164 | 165 | You'll get a ``KeyError`` exception if you read a channel override that has 166 | not been set. 167 | 168 | 169 | Source code 170 | =========== 171 | 172 | The full source code at documentation build-time is listed below (`current version on github `_): 173 | 174 | .. literalinclude:: ../../examples/channel_overrides/channel_overrides.py 175 | :language: python 176 | 177 | --------------------------------------------------------------------------------