This guide helps users understand Onboard SDK programming paradigms and correct usage of the APIs provided by the DJI Onboard SDK library. The guide walks through the two asynchronous programming paradigms that are supported by the SDK, and provides information about DJI Onboard SDK workflow.
All structures and functions are declared in DJI_API.h
, DJI_Type.h
and DJICommonType.h
. For more details, please refer to the source code - Doxygen documentation for the API is also provided.
For good example usage of the APIs and the onboard SDK in general, we encourage you to take a look at the sample programs (Linux, ROS, STM32F4 and Windows (on Qt)).
Since the Onboard SDK communicates with the Flight Controller on the aircraft entirely via serial communication, it is necessary to have an efficient serial device driver that implements I/O, memory locking/unlocking and ways for the serial channel to talk to or interrupt other threads in the program.
We provide implementations of serial drivers in our Qt sample, in the ROS sample and a generic Linux platform device driver in the platform
folder, used by the new (3.1.8) Linux sample.
The serial communication described above must always run - and we usually want to execute other processes in the foreground while I/O goes on. To make this happen, the preferred method is to spawn a read thread, and let the main flow of the program continue in the main thread. In order to allow inter-thread communication, a method of signaling also needs to be implemented, or the main thread must continuously poll for changes from the read thread.
We provide threading implementations for all our x86 and ARM-based samples. For embedded systems where threading is not an option, an interrupt-based mechanism must be implemented. We provide one such mechanism as part of the STM32F4 sample.
This is the part of the system where functionality for controlling the drone is implemented. In the core Onboard SDK library, we provide a number of APIs that provide interfaces for all functionality made available through the Open Protocol. The sample programs provided execute combinations and compositions of the library API calls together with some logic.
The core of the Onboard API is a command (CMD)-acknowledgement(ACK) communication for executing actions; the onboard computer sends CMD packets to the Flight Controller containing information about the action it is requesting. The flight controller, in return, replies with an ACK packet confirming or rejecting the CMD. SDK-side code must therefore implement some mechanism of relaying this ACK information back to the user or the CMD-sending function, so that the caller can take further action based on ths ACK response.
The Onboard SDK supports two ways of doing this, described below.
For true asynchronous programming, the SDK needs to allow for a mechanism for the user to execute code upon receiving ACKs from the aircraft indepdendent of the main flow of execution. The Onboard SDK provides this through callback functions.
Upon receiving the ACK from the aircraft, the SDK's read
function calls the user-provided callback function. The user is also allowed to supply some data (UserData
, implemented as a void*
), which is passed as an argument to the user callback. The user callback has access to the ACK and can implement logic based on the ACK. UserData
is meant for two purposes: (1) the user can also pass any additional information to the callback that the callback may act on depending on the ACK parsing logic (2) it is a mechanism for the callback to pass data back to user, if the user passes a reference to a variable that can be populated in the callback based on the ACK.
The Linux sample now incorporates Asychronous programming through the Non-Blocking sample. A separate callback thread runs all the callback functions.
Example: Non-Blocking Take Control Linux function.
takeControlNonBlocking(api); |
void takeControlCallback(CoreAPI* api, Header *protHeader, DJI::UserData data) { |
Note: Callbacks, in this implementation is executed in a separate callback thread, but blocks the 'read' thread with mutexes until the callback is complete. Do not execute large pieces of code in callbacks; reading the serial port is prevented until the callback finishes.
Example: Activation function in QT sample
Declare and define the callback function in your user code.
static void activationCallback(CoreAPI* api, Header* protocolHeader, UserData userData); |
Callback functions must have the same function prototype as above. Use UserData
to send void*
data and then cast it to appropriate datatypes in your callback function.
Pass the callback function when you call activate.
api->activate(&activateData, DJIonboardSDK::activationCallback, this); |
Here, we call the overloaded activate
function that takes in a user-specified callback and some user-provided data as the second and third arguments. The prototype for all API functions can be found in DJI_API.h.
The meaning of ACK return values(result) is explained for each command in the Flight ACK Codes document.
Note: Callbacks, in the current implementation, are executed on the read thread. Do not execute large pieces of code in callbacks; reading the serial port is prevented until the callback finishes.
The SDK provides a clean way to implement blocking API calls through its signaling-based thread synchronization. Blocking calls are very convenient because a blocking call only returns after the CMD-ACK round trip is done. When the blocking call returns, users are assured that the command has executed, and the ACK is available in user code as the blocking call's return value. As a result, users do not need to implement callbacks to take decisions or execute code based on ACKs.
Blocking calls are great for maintaining a linear flow of execution. They are also free from the issues arising executing heavy code in callbacks.
Example: Take Control function in new(3.1.8) Linux sample
Call the blocking overload for activate.
unsigned short takeControlAck = api->setControl(true, 1); |
This function prototype takes in a timeout as the second parameter - this is to prevent the blocking funtion from blocking execution indefinitely if the ACK is not received - when there is a packet corruption, for example.
Parse the returned ACK according to the Flight ACK Codes document.
switch (takeControlAck) |
The enums for the cases above are defined in the SDK.
When calling APIs, developers should make sure that the sequence of events follows this chart:
There are some exceptions to this workflow - Virtual RC and arming/disarming are not part of this flow, for example - but for most cases this workflow suffices.
The above workflow talks about the sending side of user code; another important part is reading the aircraft's flight data. A full list of all the data that can be received is available on the SDK tab of DJI Assistant 2.
The aircraft will always send this data at the frequencies set here (or set through the Onboard SDK's setBroadcastFreq()
API call) over the serial port.
To save this flight data, developers need to declare correct structs. The DJI_Type.h file contains sample structs for correctly accepting all kinds of broadcast data.
This is the BroadcastData struct definition in DJI_Type.h:
typedef struct BroadcastData |
Example: Get quaternion data:
Declare quaternion struction
typedef struct QuaternionData |
Get the quaternion
q = flight->getQuaternion() |
Movement control is a mechanism through which users can command the drone to execute varied, complex maneouvers and custom missions.
We recommend developers send their movement control commands at 50Hz frequency. Developers can implement that by calling usleep(20000)
, ros::Duration(1/50)
or other ways which depend on the development environment.
For Movement Control, specific meanings of arguments are decided by the control mode byte. For more info about Movement Control, please refer to the Control mode byte section in the Appendix.
To execute horizontal movement, developers can use the HORI_POS
mode (0x91
) for horizontal movement. More details are shown in Position Control(HORI_POS) in this document. In this mode, speed and attitude are controlled by autopilot, thus developers do not need to worry about that.
We provide higher-level APIs for common control modes - position control, attitude control and velocity control - through simple functions in the new Linux example. We recommend you try these functions out and view their implementations before calling API-level movement control functions.
Please note that certain conditions are required for some control modes to be functional:
Example: Direct API call to setFlightControl()
FlightData data; |
Example: Linux example call to move the aircraft by 10 m in the forward (x) direction:
int positionControlStatus = moveByPositionOffset(api, flight, 10, 0, 0, 0); |
The HORI_POS control mode is a relative offset control mode; the input horizontal arguments are the offset between current position and target position. The unit of offset is meters.
For example, in ground frame, target
is target position and current
is UAV's current position. The coordinates of these positions are caculated by GPS, Guidance or other sensors. In most cases, GPS is the correct way to make this work.
Because, for the autopilot, the maximum frequency of receiving data is 50Hz, the frequency of calculating offset should be over 50Hz to ensure the closed-loop control is valid; you always want to have a new offset calculated before you send it to the flight controller to execute.
Example": Writing position control procedure:
void update_offset() |
As mentioned before, we have implemented this checking procedure in the moveByPositionOffset
Linux example call.
This information will help you go back and forth between the various coordinate-systems and methods of angle representation used in the code.
Convert GPS to North-East-Down Coordinate. (GPS in radian,North-East Coordinate in meter)
For example, origin_longti
and origin_lati
, as the longitude and latitude of original position, are decided by developers. The position of UAV takeoff is recommended to be set as the original position. longti
and lati
are longitude and latitude of UAV's current posistion. x
and y
are offsets to the original position in the local frame North and the East directions. The unit of offset is meter.
#define C_EARTH (double) 6378137.0 |
Convert quaternion to roll, pitch and yaw in radian in body coordinate. A fairly good introduction to quaternions can be found here.
api_quaternion_data_t q; |