注意事项
- 在实际测试或执行飞行任务前,先在DJI Assistant 2 中模拟使用DJI OSDK 开发的应用程序。
- 在使用OSDK 开发应用程序或测试基于OSDK 开发的应用程序时,取下无人机上的桨叶。
- 使用USB 连接无人机和用户电脑时,无人机将会进入安全保护模式,在该模式下,无法使用遥控器或OSDK 控制无人机解锁或启动无人机的电机。
- 确保无人机和遥控器电池电量超过50%。
- 确认DJI Assistant 2 、应用程序和终端工具的波特率一致。更改波特率要重新启动飞行平台、遥控器和计算平台。
- 使用OSDK 开发应用程序时,需先使用DJI Assistant2 升级无人机的固件。
- 为使基于OSDK 开发的程序能够与飞行平台间正常通信,在DJI Assistant2 中启用OSDK API 控制功能,Enable API Control 打勾。
控制权限
无人机控制权限的等级由高至低依次为遥控器、基于MSDK 开发的移动端APP 和基于OSDK 开发的应用程序。
断连控制
- 当仅使用遥控器控制DJI 的无人机执行飞行任务时,若DJI 的无人机和遥控器信号中断,DJI 的无人机会按照开发者在DJI Pilot 或基于MSDK 开发的移动端APP 上设置的断连控制策略执行相应的控制动作。
- 当使用遥控器并接入机载计算机控制无人机飞行时,无人机将按照机载计算机中的逻辑自动执行飞行任务,如需使用遥控器控制无人机,先切换档位(随机切换即可)再控制无人机;若机载计算机与DJI 的无人机信号中断,则需开发者指定对应的控制策略,如悬停、降落或返航,务必启用安全返航功能,确保机载计算机与DJI 的无人机在连接中断时能按照指定的返航策略安全返航,
设备连接
在Matrice 300 RTK 无人机上安装Manifold 2 或第三方开发平台时,使用OSDK 转接板。该转接板提供了XT30 24V 供电接口、OSDK串口以及OSDK USB 2.0 接口。
购买链接:
https://store.dji.com/cn/product/matrice-300-rtk-osdk-expansion-module?from=autocomplete&position=3
机载计算机转接板使用说明书:
https://dl.djicdn.com/downloads/matrice-300/20200617/OSDK_Expansion_Module_Product_Information.pdf
转接板其他连接详解:
https://developer.dji.com/cn/document/f1b72093-0a02-460c-ae2d-dce9bf37a60e
配置Linux 开发环境
- C++ 编译器:GCC 5.4.0/5.5.0 版本
- CMake:2.8 及以上版本
- Linux:Ubuntu 16.04 (如需要使用高级传感功能,请使用Libusb库)
添加UART 读写权限
- 使用
sudo usermod -a -G dialout $USER
命令将用户添加至dialout
组中。 - 重新登录所添加的账户后,该账户即可获取UART 读写权限。
添加DJI USB 设备节点
- 在
/etc/udev/rules.d/
目录下创建文件DJIDevice.rules
。 - 在
DJIDevice.rules
文件中添加SUBSYSTEM=="usb", ATTRS{idVendor}=="2ca3", MODE="0666"
。 - 重新启动电脑后,系统即可识别DJI USB 设备。
配置ROS开发环境 / 配置STM32开发环境
https://developer.dji.com/cn/document/32bc1c7e-6c55-421f-afc9-fc99437b5392
参数设置
- 在ROS 平台开发的应用程序通过UART 接口与无人机通信时,波特率应设为921600,否则可能会出现丢包现象。
- 使用Manifold 2-G 上的ttyTHS2与无人机通信时,将波特率设置为1000000;若使用USB 转TTL 串口模块与无人机通信,建议选用芯片为FT232 的模块。
运行示例程序
https://developer.dji.com/cn/document/dc0b69f6-e234-4390-87a5-97df7d96612d
使用OSDK
引入OSDK 开发包
应用程序通过DJI Onboard SDK 中的Vehicle
类调用DJI OSDK 的功能,因此使用OSDK 开发应用程序时,先引入OSDK 开发包。
- 引入OSDK 头文件
引入OSDK 头文件后,即可使用OSDK 中的接口,开发应用程序。
#include <dji_vehicle.hpp>
- 引入OSDK 帮助文件
引入OSDK 帮助文件后,在Linux 平台上开发的应用程序即可读取用户的配置文件,激活DJI 的无人机。
#include <dji_linux_helpers.hpp>
配置依赖项
在Linux上使用CMake 开发基于OSDK 的应用程序时,请配置相应的依赖项:
- 指定CMake 最小版本: version 2.8
- 指定工程名称
- 配置编译选项(可选)
set(CMAKE_CXX_FLAGS"${CMAKE_CXX_FLAGS} -std=C++11 -pthread -g -O0")
- 包含所需头文件的路径
- 生成并链接可执行文件
OSDK 接口调用
-
同步调用接口
同步接口调用,开发者在调用接口时,该接口会根据应用程序实际的情况获得对应的返回值,调用者需要等待调用的接口发送返回值。如下以同步调用的方式设置相机的模式:
ErrorCode::ErrorCodeType retCode;
CameraManager *pm = vehicle->cameraManager;
/*set camera work mode as SHOOT_PHOTO*/
DSTATUS("set camera work mode as SHOOT_PHOTO");
retCode == pm->setModeSync(index, camreamodule::WorkMode::SHOOT_PHOTO, 3);
if (retCode != ErrorCode::SysCommonErr::Success){
DERROR("Set camera as SHOOT_PHOTO fail. Error code : 0x%lX", retCode);
ErrorCode::printErrorCodeMsg(retCode);
return retCode;
}
-
异步接口调用
异步接口调用,开发者在调用接口时,该接口会根据应用程序实际的情况获得对应的返回值,但开发者可能无法立刻得到对应的结果,当调用的接口获得结果后,该接口会通过状态或通知向开发者告知该结果,开发这可通过回调函数处理该调用结果。如异步调用的方式设置相机的模式:
- 构造回调函数
/*set camera work mode as RECORD_VIDEO*/
DSTATUS("set camera mode to RECORD_VIDEO");
pm->setModeAsync(index, camreamodule::WorkMode::RECORD_VIDEO,
setCameraModeForRecordVideoCb, &udata);
void CameraManagerAsyncSample::setCameraModeForRecordVideoCb(
ErrorCode ::ErrCodeType retCode, UserData userData){
AsyncSampleData *uData = (AsyncSampleData *)userData;
DSTATUS("retCode : 0x%lX", retCode);
if (!uData) {
DERROR("User data is a null value.");
return;
}
if (retCode == ErrorCode::SysCommonErr::Success) {
DSTATUS("set camera work mode successfully");
if (uData->pm) {
DSTATUS("start to RECORD_VIDEO");
uData->pm->startRecordVideoAsync) {
uData->index,
(void(*)ErrprCode::ErrorCodeType, UserData))uData->userCallBack,
uData-> userData);
}
} else {
DERROR("start to record video error. Error code : 0x%lX", retCode);
ErrorCode::printErrorCodeMsg(retCode);
if (uData->userCallBack) {
void (*cb)(ErrorCode::ErrorCodeType, UserData);
cb = (void (*)(ErrorCode::ErrorCodeType, UserData))uData->userCallBack;
cb(retCode, uData->userData);
}
}
}
-
注册回调函数函数
开发者调用OSDK 中的异步接口后,将会接收到相应的数据,开发者需要注册回调函数处理所接收的数据。
void NMEA Callback(Vehicle* vehiclePtr, RecvContainer recvFrame, UserData userdata)
{
int length =recvFrame.recvinfo.len-OpenProtocol::PackageMin-4;
uint8_t rawBuf[length];
memcpy(rawBuf, recvFrame.recvData.raw_ack_array, length);
DSTATUS("&s/n",std string((char*)rawBuf, length).c_str())
}
使用 Vehicle
无人机对象实例化
使用OSDK 开发应用程序时,应用程序需要先读取环境配置参数、初始化无人机并创建实例对象vehicle
。
- 读取环境配置参数(Userconfig.txt)编译OSDK 提供的示例代码时,基于OSDK 开发的应用程序需要读取如第三方库、波特率以及驱动权限等环境配置参数。
LinuxSetup linuxEnvironment(argc, argv);
- 创建实例对象
vehicle,
并完成初始化。
Vehicle *vehicle = linuxEnvironment.getVehicle();
if (vehicle == NULL) {
std::cout << "Vehicle not initialized, exiting. \n";
return -1;
}
std::string sampleCase = linuxEnvironment.getEnvironment()->getSampleCase();
功能模块实例化
无人机对象实例化后,开发者可根据实际的使用需求,实例化所需使用的功能模块。
如下代码以实例化相机模块,并初始化PAYLOAD_INDEX_0
和PAYLOAD_INDEX_1
为例。
ErrorCode::ErrorCodeType
ret = vehicle->cameraManager->initCameraModule(PAYLOAD_INDEX_0,"Sample_camera_1");
if (ret != ErrorCode::SysCommonErr::Success) {
DERROR("Init Camera module Sample_camera_1 failed.");
ErrorCode::printErrorCodeMsg(ret);
}
ret = vehicle->cameraManager->initCameraModule(PAYLOAD_INDEX_1,"Sample_camera_2");
if (ret != ErrorCode::SysCommonErr::Success) {
DERROR("Init Camera module Sample_camera_2 failed.");
ErrorCode::printErrorCodeMsg(ret);
}
跨平台移植
https://developer.dji.com/cn/document/4f57e7f7-b946-4f43-bb7b-2c7ff077c31a
基于OSDK 开发的应用程序能够运行在Linux 和ROS 系统上,为满足开发者在不同的操作系统和开发平台上使用OSDK 开发应用程序,OSDK 提供了Hal 层和Osal 层库,通过Hal(Hardware Abstraction Layer,硬件接口层)适配不同的硬件平台,通过Osal(Operating System Abstraction Layer,操作系统抽象层)实现与不同操作系统的兼容。
- 使用OSDK 4.0 及以上版本,在STM32上开发的应用程序必须运行在FreeRTOS上;
- 使用OSDK 4.0 以下版本,在STM32上开发的应用程序仅能运行在裸机上。
基础控制
无人机基础信息
- 开启或关闭RTK功能和避障功能(水平避障和顶部避障)
- 设置无人机的返航点和返航高度
- 获取RTK功能和避障功能的状态(水平避障和顶部避障)
- 获取无人机返航点和返航高度
无人机基础飞行动作
- 不同型号的无人机在执行起飞、降落和返航动作时的高度可能会有差异,详情参见选购机型的说明书。
- 无人机解锁后,若未接收到起飞指令,无人机将自动上锁,螺旋桨会停止旋转
- 无人机锁定
-
解锁:无人机解锁后,无人机的螺旋桨会怠速旋转,但不会飞离地面
-
上锁:无人机上锁后,无人机的螺旋桨由怠速旋转状态变为静止状态
- 无人机起飞降落
-
自动起飞:无人机会自动起飞1.2 米(该高度不可调整)
-
自动降落:无人机会自动降落
-
取消自动降落:无人机在下降多过程中会停止降落,并悬停在空中
-
降落确认:当无人机已降落到离地面一定距离时,用户使用该功能可确认无人机降落到地面
-
强制降落:无视降停面的状态,强制无人机降落(降落速度较快)
- 无人机返航
-
返航:无人机会自动返航
-
取消返航:无人机会悬停在空中
Joystick 功能
Joystick 是一个无人机综合控制功能,使用Joystick 功能时,开发者根据实际的应用需要,通过调用Joystick 中的接口,同时设置无人机使用的坐标系、水平控制的模式、垂直控制的模式、yaw角度控制的模式和悬停模式,才能设计出满足使用需求的无人机飞行控制逻辑。
设置坐标系
-
机体坐标系
机体坐标系以无人机的重心为原点,无人机机头前进的方向为X轴,机头前进方向的右侧为Y轴,Z轴与X轴、Y轴相互垂直交于重心且指向无人机下方(遵循“右手法则”)。在机体坐标下,无人机围绕X轴、Y轴和Z轴旋转时的飞行动作,可称为横滚(无人机仅绕X轴旋转)、俯仰(无人机仅绕Y轴旋转)和偏航(无人机仅绕Z轴旋转)。 -
大地坐标系
大地坐标系也称世界坐标系或当地水平坐标系,在该坐标系中,无人机指向地球正北的方向为X轴,正东的方向为Y轴,X轴与Y轴相互垂直,Z轴竖直指向无人机下方,在满足“右手法则”的前提下,Z轴将根据无人机飞行的实际情况调节角度,因此该坐标系也称为“北-东-地(N-E-D)坐标系”。
设置水平控制模式
- 姿态角控制模式:在该模式下,水平方向的指令为无人机的姿态角。(在机体坐标系下,该角度为roll 和pitch)
- 速度控制模式:在该模式下,水平方向的指令为无人机的速度
- 位置控制模式:在该模式下,水平方向的指令为无人机的位置,当位置指令的模不为0时,无人机会以指定的速度向前飞行,否则,无人机将悬停在指定的位置
- 角速度控制:在该模式下,水平方向的指令为无人机的旋转角速度
设置无人机悬停模式
仅在水平控制模式中的速度控制模式下,开发者可以设置无人机的悬停模式:
- 开启稳定模式:开启稳定模式后,无人机将在指定的位置上悬停
- 关闭稳定模式:关闭稳定模式后,无人机将按照速度命令飞行,当无人机的前进速度为0时候,无人机可能会随风飘动
设置垂直控制模式
- 速度控制模式:控制无人机垂直方向的速度
- 位置控制模式:控制无人机垂直方向的位置,该位置为相对于起飞点的绝对位置
- 油门控制模式:控制无人机的油门
设置yaw角度控制模式
yaw偏航角,绕着重力方向
- 角度控制模式:在该模式下,yaw方向旋转的指令为yaw 的角度
- 角速率控制模式:在该模式下,yaw方向旋转的指令为yaw 的角速率
使用无人机飞行控制功能
使用无人机飞行控制功能需要先实例化对象,再设置无人机的基础参数,最后再使用Joystick 中的功能。
1.实例化对象
使用OSDK 开发应用程序前先实例化无人机的对象。
FlightSample* flightSample = new FlightSample(vehicle);
2.设置无人机基础参数
使用flightSample
和 flightController
中的接口设置无人机的基础参数。
flightSample->monitoredTakeoff();
vehicle->flightController->setCollisionAvoidanceEnabledSync(
FlightController::AvoidEnable::AVOID_ENABLE, 1);
/*! 飞行较长的距离 */
flightSample->moveByPositionOffset((FlightSample::Vector3f){0, 0, 30}, 0);
/*! 飞行较短的距离 */
flightSample->moveByPositionOffset((FlightSample::Vector3f){10, 0, 0}, 0);
/*!设置无人机当前位置为返航点 */
flightSample->setNewHomeLocation();
/*! 设置无人机返航高度 */
flightSample->setGoHomeAltitude(50);
/*! 飞至另一个位置 */
flightSample->moveByPositionOffset((FlightSample::Vector3f){20, 0, 0}, 0);
vehicle->flightController->setCollisionAvoidanceEnabledSync(
FlightController::AvoidEnable::AVOID_DISABLE, 1);
/*! 返航并确认降落*/
flightSample->goHomeAndConfirmLanding();
vehicle->flightController->setCollisionAvoidanceEnabledSync(
FlightController::AvoidEnable::AVOID_ENABLE, 1);
3. 使用Joystick 中的功能
使用Joystick 功能需要先设置Joystick 的模式和对应的控制指令,再执行Joystick 指令,实现对无人机的控制。如下代码以在flightSample->moveByPositionOffset()
中,使用Joystick 功能控制无人机在水平和垂直方向上运动到指定的位置。
1.设置joystick的模式
FlightController::JoystickMode joystickMode = {
FlightController::HorizontalLogic::HORIZONTAL_POSITION,
FlightController::VerticalLogic::VERTICAL_POSITION,
FlightController::YawLogic::YAW_ANGLE,
FlightController::HorizontalCoordinate::HORIZONTAL_GROUND,
FlightController::StableMode::STABLE_ENABLE,
};
vehicle->flightController->setJoystickMode(joystickMode);
2.设置Joystick 控制指令并执行Joystick 功能
while (elapsedTimeInMs < timeoutInMilSec) {
限定无人机飞行范围
......
// 设置Joystick 控制指令
FlightController::JoystickCommand joystickCommand = {
positionCommand.x, positionCommand.y,
offsetDesired.z + originHeightBaseHomepoint, yawDesiredInDeg};
vehicle->flightController->setJoystickCommand(joystickCommand);
// 执行Joystick 功能
vehicle->flightController->joystickAction();
......
判断无人机是否进入设定位置的阈值
}
广播与订阅
OSDK提供了数据广播和数据订阅功能,具有广播功能的应用程序能够实时向使用移动端APP(基于MSDK 开发)的用户、基于OSDK 开发的地面设备及其他设备发送无人机当前的飞行状态以及无人机上传感器产生的数据信息;具有消息订阅功能的应用程序,能够实时获取无人机上传感器产生的数据和无人机的飞行状态。
消息订阅(建议使用)
无人机上的各个部件以及第三方传感器根据无人机实际的飞行状况,会实时产生大量的数据信息并被无人机推送给其他模块,具有消息订阅功能的应用程序,能够记录用户所需订阅的数据。
订阅项
使用OSDK 消息订阅功能可订阅的数据信息如下:
数据类型 | 订阅项(Topic) | 最大订阅频率(Hz) |
---|---|---|
无人机基础信息 | 姿态四元数 | 200 |
硬件同步 | 400 | |
返航点信息和设置状态 | 50 | |
遥控器信息 | 50 | |
无人机状态信息 | 无人机飞行状态 | 50 |
无人机飞行模式 | 50 | |
起落架状态 | 50 | |
电调 | 50 | |
电池 | 50 | |
解锁电机错误码 | 50 | |
飞行异常 | 50 | |
无人机速度信息 | 飞行速度 | 200 |
融合角速度 | 200 | |
原始角速度 | 400 | |
原始加速度 | 400 | |
大地坐标系下的加速度 | 200 | |
机体坐标系下的加速度 | 200 | |
传感器信息 | ||
气压计高度 | 200 | |
融合气压计高度 | 200 | |
返航点气压计高度 | 1 | |
融合相对高度 | 100 | |
指南针 | 100 | |
VO视觉位置 | 200 | |
避障信息 | 100 | |
云台信息 | ||
云台状态 | 50 | |
云台角度 | 200 | |
云台控制模式 | 50 | |
定位信息 | ||
GPS信号水平 | 50 | |
GPS原始信息 | 5 | |
GPS融合信息 | 50 | |
RTK连接状态 | 50 | |
RTK原始信息 | 5 |
订阅规则
- 消息订阅功能最多支持订阅0Hz、1Hz、10Hz、50Hz、100Hz,5类频率,每个订阅项只能被订阅一次。
- 指定订阅频率时,任何参数的订阅频率不能小于或等于0 ,相同订阅频率的主题的数据长度总和须小于或等于242。
使用消息订阅功能
1. 验证消息订阅功能
ACK::ErrorCode subscribeStatus;
subscribeStatus = vehicle->subscribe->verify(timeout);
if (ACK::getError(subscribeStatus) != ACK::SUCCESS) {
ACK::getErrorCodeMessage(subscribeStatus, __func__);
return false;
}
2. 消息订阅功能初始化
初始化消息订阅功能后,开发者需指定订阅项、订阅频率、包编号、数据大小。
bool enableTimestamp = false;
bool pkgStatus = vehicle->subscribe->initPackageFromTopicList(
pkgIndex, topicSize, topicList, enableTimestamp, freq);
if (!(pkgStatus)) {
return pkgStatus;
}
3. 获取订阅数据
subscribeStatus = vehicle->subscribe->startPackage(pkgIndex, timeout);
if (ACK::getError(subscribeStatus) != ACK::SUCCESS) {
ACK::getErrorCodeMessage(subscribeStatus, __func__);
4. 清除订阅项
ACK::ErrorCode ack = vehicle->subscribe->removePackage(pkgIndex, timeout);
if (ACK::getError(ack)) {
DERROR(
"Error unsubscription; please restart the drone/FC to get "
"back to a clean state");
}
return false;
}
return true;
} else {
DERROR("vehicle haven't been initialized", __func__);
return false;
}
开发者可选择一组“主题”或“订阅”数据集,将它们添加到“订阅”包中,并配置相应的订阅频率。用户可以通过DJI Onboard API配置五个数据包。同时可为每个数据包设置单独的频率,OSDK 为开发者提供了300 字节的缓冲区,允许用户根据需要为每个软件包添加较多的订阅项。
广播(不建议使用)
无人机上的各个部件以及第三方传感器根据无人机实际的飞行状况,会实时产生大量的数据信息,OSDK 的广播功能,不仅能够接收到无人机主动推送给其他模块的数据,还能够接收第三方传感器的数据,并通过广播功能广播所接收到的数据,基于MSDK 开发的移动端APP、基于OSDK 开发的地面设备、可接收无人机广播信息的设备或云端应用平台等可记录无人机广播的数据信息。
广播项
具有OSDK 广播功能的应用程序可广播如下数据信息:
- 传感器数据信息:陀螺仪和磁力计等传感器读数
- 融合数据,例如态度和全球位置
- 无人机基础信息:电池,云台和飞行状态
广播频率
- OSDK 的广播功能最多支持以0Hz、1Hz、10Hz、50Hz、100Hz,5类频率广播数据,每个广播项只能被广播一次。例如,如果将GPS指定为50Hz,将陀螺仪指定为10Hz,则来自广播的整个数据包将以50Hz传入,但是陀螺仪将仅每5个数据包中出现一次。
- 使用OSDK 的广播功能后,基于OSDK 开发的应用程序将以默认频率广播数据,如需更改广播频率,请调用广播功能中的API 或使用DJI Assistant 2 更改广播频率。
- 指定订阅频率时,任何参数的订阅频率不能小于或等于0 ,相同订阅频率的主题的数据长度总和须小于或等于242。
由于广播功能占用的带宽较大且OSDK 将不再开发广播功能,建议使用消息订阅功能。
时间同步
时间同步是一种使用PPS 信号,通过与GPS 卫星通信,实现无人机与GPS 系统时间同步的功能;具有“时间同步”功能的应用程序,不仅方便用户快速分析传感器采样的数据,还能提高相机曝光时间的精准度,以及实现获取精准定位信息等高级功能;时间同步功能支持以1Hz 的频率获取RTK 数据,支持以5Hz 的频率获取GPS 数据。
OSDK 时间同步的流程如下所示:
- 无人机通过指定的硬件接口发送PPS 信号和UTC 时间戳,用于同步机载计算机和传感器上的时间;
- 无人机与RTK 或GPS 卫星在通信状态良好的情况下,无人机将以1Hz 的频率发送RTK 数据包,以5Hz 的频率发送GPS 数据包,其中包含NMEA 数据。
无人机脉冲在上升沿处(从0V上升到3.3V)产生UTC 时间戳。
获取NMEA 信息
- 以异步的方式获取NMEA 信息
void subscribeNMEAMsgs(VehicleCallBack cb, void *userData);
void unsubscribeNMEAMsgs();
- 以同步的方式获取NMEA 信息
bool getNMEAMsg(NMEAType type, NMEAData &nmea);
获取UTC 时间戳
- 以异步的方式
void subscribeUTCTime(VehicleCallBack cb, void *userData);
void unsubscribeUTCTime();
- 以同步的方式获取UTC 时间戳
bool getUTCTime(NMEAData &utc);
获取无人机上的时间
- 以异步的方式获取无人机上的时间
void subscribeFCTimeInUTCRef(VehicleCallBack cb, void *userData);
void unsubscribeFCTimeInUTCRef();
- 以同步的方式获取无人机上的时间
bool getFCTimeInUTCRef(DJI::OSDK::ACK::FCTimeInUTC &fcTimeInUTC);
获取无人机上的PPS 信息
- 以异步的方式获取无人机上的PPS 信息
void subscribePPSSource(VehicleCallBack cb, void *userData);
void unsubscribePPSSource();
- 以同步的方式获取无人机上的PPS 信息
bool getPPSSource(PPSSource &source);
断连重连
M210 V2系列和M300飞机在飞行时,由于使用USB A口的抗抖动性欠佳,可能会偶现OSDK USB短暂断连的情况(类似于USB拔插情况)。在OSDK 4.0及以前版本,一旦USB短暂断连发生,OSDK程序将无法重新恢复通信。为解决该问题,从OSDK 4.1起OSDK程序将提供USB的断连重连的支持,该功能作为可选项在程序编译时选择是否启用。目前STM32平台已经默认支持USB断连重连,ROS平台的断连重连依赖Linux版本程序,因此本次支持的重点主要是Linux平台。
断连重连监控线程
在启用断连重连功能后,OSDK Linux平台代码将新增一个线程监控断连重连情况是否发生。如果检测到断连和重连情况出现,OSDK将会有log输出提示。在重连发生后,监控线程将重新初始化硬件设备并更新Hal层对应相关的句柄,从而恢复线程。
具体的工作流程局部图如下:
Udev机制
OSDK通过Udev机制来监控Linux系统平台的热拔插事件,并与OSDK通信链路初始化时的硬件特征进行对比,从而来获取OSDK通信链路的通断事件。
断连重连配置
首先需要支持libudev
sudo apt-get install libudev-dev
在Onboard SDK执行CMake编译时需要加上 -DOSDK_HOTPLUG=ON
$:~/Onboard-SDK/build$ cmake .. -DOSDK_HOTPLUG=ON
如果出现以下log则表明编译OSDK Lib时,断链重连机制将生效:
-- Enable OSDK Hotplug monitoring.
-- Found libudev:
-- - Includes: /usr/include
-- - Libraries: /lib/x86_64-linux-gnu/libudev.so
- Linux平台的断连重连功能仅支持USB设备,并且在USB设备初始化成功后才能生效。
- OSDK工程在Linux平台默认不支持断连重连特性,需要在Cmake时加上宏定义-DOSDK_HOTPLUG=ON才生效。
云台管理
云台关节是云台上带动负载设备转动的结构件:云台电机,云台关节角即云台电机转动的角度。
根据用户的控制指令,云台能够调整姿态;云台姿态角即使用大地坐标系(NED,北东地坐标系)描述云台上负载设备的角度,该角度也称为欧拉角。
云台控制
控制方式
- 绝对角度控制:使用OSDK 开发的云台根据用户的指令,在规定的时间内,从当前的位置转动到指定的位置。
- 在角度控制模式下,云台转动的时间受云台最大旋转速度和最大加速度限制,实际的转动角度受云台限位角度的限制。
- 速度控制:用户可控制使用OSDK 开发的云台的转动速度。
- 在速度控制模式下,云台根据用户指定的速度转动0.5s,当云台转动到限位角时,将会停止转动。
目前OSDK 的云台控制功能仅支持自由模式,在该模式下,当无人机的姿态改变时,云台将不会转动。
使用云台管理功能
1. 云台控制功能模块初始化
使用“云台控制”功能前,需要调用云台控制功能的类GimbalManagerSyncSample
,创建云台控制功能的对象并初始化指定的云台。
GimbalManagerSyncSample *g = new GimbalManagerSyncSample(vehicle);
ret = vehicle->gimbalManager->initGimbalModule(PAYLOAD_INDEX_0,
"Sample_gimbal_1");
if (ret != ErrorCode::SysCommonErr::Success)
{
DERROR("Init Camera module Sample_gimbal_1 failed.");
ErrorCode::printErrorCodeMsg(ret);
}
ret = vehicle->gimbalManager->initGimbalModule(PAYLOAD_INDEX_1,
"Sample_gimbal_2");
2. 获取云台的状态信息
创建云台控制功能的对象并初始化指定的云台后,即可获取用户指定的云台的信息。
DSTATUS("Current gimbal %d angle (p,r,y) = (%0.2f°, %0.2f°, %0.2f°)", PAYLOAD_INDEX_0,
g->getGimbalData(PAYLOAD_INDEX_0).pitch,
g->getGimbalData(PAYLOAD_INDEX_0).roll,
g->getGimbalData(PAYLOAD_INDEX_0).yaw);
3. 控制云台转动
通过GimbalManagerSyncSample
中指定的接口控制云台的姿态。
GimbalModule::Rotation rotation;
rotation.roll = 0.0f;
rotation.pitch = 25.0f;
rotation.yaw = 90.0f;
rotation.rotationMode = 0;
rotation.time = 0.5;
g->rotateSyncSample(PAYLOAD_INDEX_0, rotation);
4. 云台回中
使用OSDK 开发的应用程序支持将云台的俯仰轴和偏航轴转动至中位。
g->resetSyncSample(PAYLOAD_INDEX_0);
相机管理
OSDK 提供了相机管理功能,即CameraManager
类。开发者使用OSDK 中的CameraManager
类,能够同时设置并获取无人机上多个相机的感光度、光圈、快门和曝光等参数的值,控制相机实现拍照、录像及指点变焦等功能。
在使用相机管理功能时,开发者需要先初始化OSDK 中的相机模块,创建实例化对象CameraManager
,再根据实际的使用需要设置相机的模式,最后根据用户的使用逻辑实现所需使用的功能,如设置相机的参数或检查功能的状态等。
CameraManager 支持功能
X4S | X5S | X7 | Z30 | XTS | XT2 | H20/H20T | |
---|---|---|---|---|---|---|---|
设置/获取相机工作模式 | √ | √ | √ | √ | √ | √ | √ |
设置/获取相机对焦模式 | √ | √ | √ | √ | × | × | √ |
设置/获取曝光模式 | √ | √ | √ | × | × | × | √ |
设置/获取ISO | √ | √ | √ | √ | × | × | √ |
设置/获取光圈 | √ | √ | √ | √ | × | × | √ |
设置/获取快门 | √ | √ | √ | × | × | × | √ |
设置/获取EV(曝光补偿) | √ | √ | √ | × | × | × | √ |
设置/获取分接缩放参数 | × | × | × | √ | × | × | √ |
设置/获取分接缩放点 | × | × | × | √ | × | × | √ |
设置/获取焦点 | √ | √ | √ | × | × | × | √ |
设置/获取拍摄照片模式 | √ | √ | √ | × | × | × | √ |
拍摄单张照片 | √ | √ | √ | √ | √ | √ | √ |
设置/获取间隔拍摄参数 | √ | √ | √ | × | × | × | × |
拍摄间隔照片 | √ | √ | √ | √ | √ | √ | × |
设置/获取自动包围曝光拍摄参数 | √ | √ | √ | × | × | × | × |
拍摄自动包围曝光照片 | √ | √ | √ | × | × | × | × |
设置/获取连拍参数 | √ | √ | √ | × | × | × | × |
拍摄连拍照片 | √ | √ | √ | √ | × | × | × |
录制视频 | √ | √ | √ | √ | √ | √ | √ |
下载文件列表(仅M300 RTK) | × | × | × | √ | √ | √ | √(beta) |
下载媒体文件(仅M300 RTK) | × | × | × | √ | √ | √ | √(beta) |
1. 相机模块初始化
无人机初始化后,需初始化无人机上的相机模块,并将所需控制的相机注册到vehicle->cameraManager
中,如下为初始化无人机I 号云台和II 号云台上的相机模块。
ErrorCode::ErrorCodeType
ret = vehicle->cameraManager->initCameraModule(PAYLOAD_INDEX_0,"Sample_camera_1");
if (ret != ErrorCode::SysCommonErr::Success) {
DERROR("Init Camera module Sample_camera_1 failed.");
ErrorCode::printErrorCodeMsg(ret);
}
ret = vehicle->cameraManager->initCameraModule(PAYLOAD_INDEX_1,"Sample_camera_2");
if (ret != ErrorCode::SysCommonErr::Success) {
DERROR("Init Camera module Sample_camera_2 failed.");
ErrorCode::printErrorCodeMsg(ret);
}
2. CameraManager 对象实例化
如需使用OSDK CameraManager
中功能,创建CameraManager
实例。
- 以同步的方式创建实例
CameraManagerSyncSample *p = new CameraManagerSyncSample(vehicle);
- 以异步的方式创建实例
CameraManagerAsyncSample *p = new CameraManagerAsyncSample(vehicle);
3. 设置或获取相机参数
CameraManager 对象实例化后,开发者需根据使用相机功能的逻辑,设置或获取相机的模式,如设置相机的光圈模式前需先设置相机的曝光模式,详情请参见OSDK API 文档,如下以同步和异步的方法介绍获取相机光圈值的方法。
以同步的方式获取相机的光圈模式
retCode = pm->getApertureSync(index, apertureGet, 1);
以异步的方式获取相机的光圈模式
- 构造回调函数
void CameraManagerAsyncSample::getApertureCb(ErrorCode::ErrorCodeType retCode,
CameraModule::Aperture apertureGet,
UserData userData) {
AsyncSampleData *uData = (AsyncSampleData *)userData;
DSTATUS("retCode : 0x%lX", retCode);
if (!uData) {
DERROR("User data is a null value.");
return;
}
if (retCode == ErrorCode::SysCommonErr::Success) {
DSTATUS("Get aperture = %d", apertureGet);
if (uData->pm) {
if (*(CameraModule::Aperture *)uData->dataTarget == apertureGet) {
DSTATUS("The aperture value is already %d.", apertureGet);
if (uData->userCallBack) {
void (*cb)(ErrorCode::ErrorCodeType, UserData);
cb =
(void (*)(ErrorCode::ErrorCodeType, UserData))uData->userCallBack;
cb(ErrorCode::SysCommonErr::Success, uData->userData);
}
} else {
uData->pm->setApertureAsync(
uData->index, *(CameraModule::Aperture *)uData->dataTarget,
(void (*)(ErrorCode::ErrorCodeType, UserData))uData->userCallBack,
uData->userData);
}
}
} else {
DERROR("Get aperture error. Error code : 0x%lX", retCode);
ErrorCode::printErrorCodeMsg(retCode);
if (uData->userCallBack) {
void (*cb)(ErrorCode::ErrorCodeType, UserData);
cb = (void (*)(ErrorCode::ErrorCodeType, UserData))uData->userCallBack;
cb(retCode, uData->userData);
}
}
}
- 注册回调函数
DSTATUS("Get aperture now ...");
pm->getApertureAsync(index, getApertureCb, &uData);
}
4. 控制相机执行指定的动作
开发者通过CameraManager 能够控制相机执行指定的动作,如指点变焦等,详情请参见OSDK API 文档,如下以同步和异步的方法介绍控制相机执行指点变焦功能的方法。
以同步的方式控制相机指点变焦
ErrorCode::ErrorCodeType CameraManagerSyncSample::setTapZoomPointSyncSample(
PayloadIndexType index, uint8_t multiplier, float x, float y) {
if (!vehicle || !vehicle->cameraManager) {
DERROR("vehicle or cameraManager is a null value.");
return ErrorCode::SysCommonErr::InstInitParamInvalid;
}
ErrorCode::ErrorCodeType retCode;
CameraManager *pm = vehicle->cameraManager;
/*开启指点变焦功能*/
DSTATUS("Set tap zoom enable = %d", true);
retCode = pm->setTapZoomEnabledSync(index, true, 1);
if (retCode != ErrorCode::SysCommonErr::Success) {
DERROR("Set tap zoom enable fail. Error code : 0x%lX", retCode);
ErrorCode::printErrorCodeMsg(retCode);
DERROR("It is only supported Z30 camera.");
return retCode;
}
/*设置指点变焦功能的参数*/
DSTATUS("Set tap zoom multiplier = %d", multiplier);
retCode = pm->setTapZoomMultiplierSync(index, multiplier, 1);
if (retCode != ErrorCode::SysCommonErr::Success) {
DERROR("Set tap zoom multiplier fail. Error code : 0x%lX", retCode);
ErrorCode::printErrorCodeMsg(retCode);
DERROR("It is only supported Z30 camera.");
return retCode;
}
/*设置指点变焦对象*/
DSTATUS("Set tap zoom target point : (%f,%f)", x, y);
retCode = pm->tapZoomAtTargetSync(index, {x, y}, 1);
if (retCode != ErrorCode::SysCommonErr::Success) {
DERROR("Set tap zoom target fail. Error code : 0x%lX", retCode);
ErrorCode::printErrorCodeMsg(retCode);
DERROR("It is only supported Z30 camera.");
return retCode;
} else {
DSTATUS(
"tap zoom at target (%0.2f, %0.2f) successfully, need several seconds "
"to zoom.",
x, y);
}
return retCode;
}
以异步的方式控制相机指点变焦
- 构造回调函数
/*设置指点变焦系数*/
void CameraManagerAsyncSample::setTapZoomMultiplierCb(
ErrorCode::ErrorCodeType retCode, UserData userData) {
AsyncSampleData *uData = (AsyncSampleData *)userData;
DSTATUS("retCode : 0x%lX", retCode);
if (!uData) {
DERROR("User data is a null value.");
return;
}
if (retCode == ErrorCode::SysCommonErr::Success) {
DSTATUS("Set tap zoom multiplier successfully ");
if (uData->pm) {
/*注册开启指点变焦功能的函数*/
uData->pm->setTapZoomEnabledAsync(uData->index, true, setTapZoomEnableCb,
uData);
}
} else {
DERROR("Set tap zoom multiplier error. Error code : 0x%lX", retCode);
ErrorCode::printErrorCodeMsg(retCode);
if (uData->userCallBack) {
void (*cb)(ErrorCode::ErrorCodeType, UserData);
cb = (void (*)(ErrorCode::ErrorCodeType, UserData))uData->userCallBack;
cb(retCode, uData->userData);
}
}
}
/*开启指点变焦功能*/
void CameraManagerAsyncSample::setTapZoomEnableCb(
ErrorCode::ErrorCodeType retCode, UserData userData) {
AsyncSampleData *uData = (AsyncSampleData *)userData;
DSTATUS("retCode : 0x%lX", retCode);
if (!uData) {
DERROR("User data is a null value.");
return;
}
if (retCode == ErrorCode::SysCommonErr::Success) {
DSTATUS("Set tap zoom enable successfully ");
if (uData->pm) {
/* 设置指点变焦的对象 */
uData->pm->tapZoomAtTargetAsync(
uData->index, *(CameraModule::TapZoomPosData *)uData->dataTarget,
(void (*)(ErrorCode::ErrorCodeType, UserData))uData->userCallBack,
uData->userData);
}
} else {
DERROR("Tap zoom at enable error. Error code : 0x%lX", retCode);
ErrorCode::printErrorCodeMsg(retCode);
if (uData->userCallBack) {
void (*cb)(ErrorCode::ErrorCodeType, UserData);
cb = (void (*)(ErrorCode::ErrorCodeType, UserData))uData->userCallBack;
cb(retCode, uData->userData);
}
}
}
- 注册回调函数
DSTATUS("Set tap zoom multiplier = %d", multiplier);
pm->setTapZoomMultiplierAsync(index, multiplier, setTapZoomMultiplierCb, &uData);
}
SDK互联互通
https://developer.dji.com/cn/document/e37514a6-5803-4abf-a5b6-6da844bd56f6
HMS(健康管理系统)
HMS(健康管理系统)是对飞机各模块的运行健康状态的一个监控系统。在DJI Pilot上已经提供了交互界面查看各模块的工作状态是否有异常。OSDK则提供了基本接口来获取各模块异常的错误信息,便于OSDK程序获知飞机各模块的工作状态。
HMS错误信息
单条HMS的错误信息包括一个错误ID,错误的Index和一个错误的等级,在订阅HMS信息后,这些错误信息将会打包发送给OSDK。OSDK根据这些错误信息的数据解析成字符串,并从终端打印出来。
使用HMS
OSDK的HMS功能支持M300机型,其错误信息通过USB ACM口与飞机进行通信实现。
初始化HMS Sample
OSDK已经通过HMSSample这个类封装好了HMS的API的使用方法,在环境初始化之后仅需要创建该类即可快速使用HMS功能。
HMSSample* hmsSample = new HMSSample(vehicle);
订阅飞行状态信息
在HMS模块内会根据TOPIC_STATUS_FLIGHT的订阅数据来判断飞机是否在空中,从而区分HMS信息是在飞行过程中接收到的还是在地上接收到的。
//! 1.subscribe flight'status.(or default Error Information is about on the ground)
int pkgIndex = 0;
if (!hmsSample->subscribeFlightStatus(pkgIndex))
{
return -1;
}
订阅HMS信息
订阅HMS信息,订阅成功后,HMS信息将推送给OSDK。该命令请求以及推送数据都是通过USB ACM口进行通信,需要确认USB ACM是否已经连接成功才能正常使用。
//! 2.subscribe HMS's pushing.(Then it will print all error information with 5HZ in English)
if (!hmsSample->subscribeHMSInf(true, timeOutMs))
{
hmsSample->unsubscribeFlightStatus((pkgIndex));
return -1;
}
打印HMS信息
OSDK接收到的HMS可以通过 void HMSSample::printAllError(void)进行打印。开发者也可以参考该函数的实现方式,调用接口 HMSPushPacket DJIHMS::getHMSPushPacket() 获取原始的HMS信息包进行自定义处理。
//! 2.1 print all pushed raw error data with 5HZ in 30 senconds
const int waitTimeMs = 200;
int timeSoFar = 0;
int totalTimeMs = 30*1000; // 30 secs
while(timeSoFar < totalTimeMs)
{
OsdkLinux_TaskSleepMs(waitTimeMs);
hmsSample->printAllError();
DSTATUS("now flight status is %d\n", hmsSample->getFlightStatus());
timeSoFar += waitTimeMs;
}
取消订阅
在HMS任务完成时可以取消HMS和飞行状态信息的订阅。
//! 3. reset osdk's subscription for HMS's pushing
hmsSample->subscribeHMSInf(false, timeOutMs);
//! 4. unsubscribe flight status
hmsSample->unsubscribeFlightStatus(pkgIndex);
运动规划
使用运动规划功能,可根据实际的使用需求设计相应的航点任务和热点任务,制定控制无人机自动化飞行的控制逻辑。
航点任务
航点规划是一个控制无人机按照指定的航线飞行,实现无人机飞行自动化的控制功能。开发者通过调用DJI OSDK 的接口,能够控制无人机以指定的高度飞往指定位置并执行相应动作,根据实际的使用需求,还可控制无人机重复多次执行指定的任务,实现自动化巡航等功能。
航点
开发者在使用航点任务时,需要指定航点数量和对应的航点类型。
- 航点数量
使用航点任务时,开发者需使用OSDK 中的API 接口指定无人机所需飞达的航点,DJI OSDK 支持开发者在一个任务中最多添加65535 个航点,至少2个航点。 - 航点类型
航点类型是指无人机在执行航点任务时,飞向该航点的方式,其中包含曲率飞行、直线飞行和协调转弯。- 曲率飞行
- 无人机以曲率连续的方式执行飞行任务时,到达指定的航点不停留。
- 无人机以曲率连续的方式执行飞行任务时,在航点处停留。
- 无人机以曲率不连续的方式执行飞行任务时,在航点处停留。
- 直线飞行
- 直线进入
- 直线退出
- 协调转弯:无人机在到达航点前提前转弯
- 曲率飞行
动作
开发者在使用航点任务时,开发者或用户可为无人机在指定的航点处添加对应的动作,如拍照、录像或悬停等。
- 动作信息
动作信息主要由动作ID、触发该动作的触发器和执行该动作的执行器组成。- 动作ID,开发者可指定全局唯一的动作ID 标识用户设置的动作。
- 触发器:触发无人机执行动作的触发器,包含触发器的类型,以及该触发类型所对应的参数。每个触发器仅支持一种触发类型。
- 执行器:执行用户指定动作的模块,包含执行器的类型和执行器的编号,以及该执行器所对应的参数。每个执行器仅支持一种执行类型
- 动作数量
DJI OSDK 支持开发者最多在一个任务中总共可添加65535 个动作。 - 动作管理:
- 支持将相机对焦变焦的动作配置到地面站中自动执行。
- 支持云台角度增量控制
- 支持配置多个云台多个相机
- 动作触发
如需在无人机执行航点任务的过程中,触发无人机执行指定的动作,需要添加触发该动作的条件:- 定时触发
- 距离触发
- 动作串行触发
- 动作并行触发
- 航点触发:无人机在航点飞行时,在第N个航点结束航点任务
速度控制
OSDK 为开发者提供了速度控制功能,能够使开发者为指定的航点配置不同的速度(为同一个航点设置多个速度),支持开发者在无人机执行航线飞行任务时,修改或查询无人机全局巡航速度。
断连控制
新增支持配置遥控器失联后继续执行航线任务的功能。
- 基于OSDK 开发的应用程序在控制无人机执行任务时,用户可使用遥控器控制无人机如无人机的飞行速度、飞行高度和飞行航向等。
- 无人机每次只能执行一个自动化飞行任务,上传新的任务后,已有的飞行任务将会被覆盖。
- 在航点任务中,无人机的航点与无人机的动作没有必然关系,开发者可根据实际情况添加航点和无人机飞行时的动作。
工作流程
航点任务功能按如下流程,控制无人机执行航点任务:
- 上传航线任务的整体信息
一个航点任务包含航点任务的ID、航点任务的航点数、任务重复次数、航点任务结束后的动作、最大飞行速度和巡航速度。 - 上传航点信息
- 基础参数:航点坐标(设置航点的经度、纬度和相对于起飞点的高度)、航点类型、航向类型和飞行速度。
- 可选参数:缓冲距离、航向角度、转向模式、兴趣点、单点最大飞行速度、单点巡航速度。仅开发者设置航点信息所有的基础参数后,才能设置航点信息的可选参数。
- 设置动作信息(可选)
设置动作的ID、触发器和执行器 - 上传无人机的航点任务的信息
- 控制无人机执行航点任务
上传无人机航点和对应的动作信息后,开发者即可通过指定的接口控制航点任务,如开始、停止或暂停任务,设置或获取巡航速度等。
使用航点任务功能
如需使用航点任务,使用DJI OSDK 的WaypointV2MissionOperator 类实现航点任务功能。
在使用航点任务前,请先完成无人机飞行准备,如初始化无人机、获取控制无人机的权限,创建实例并执行航点任务。
- 初始化无人机的飞行环境
LinuxSetup linuxEnvironment(argc, argv);
Vehicle* vehicle = linuxEnvironment.getVehicle();
if (vehicle == NULL)
{
std::cout << "Vehicle not initialized, exiting.\n";
return -1;
}
- 获取无人机的控制权限
/*! Obtain Control Authority*/
vehicle->control->obtainCtrlAuthority(functionTimeout);
- 创建航点任务的示例,并执行航点任务
/*! Initialize a new WaypointV2 mission sample*/
auto *sample = new WaypointV2MissionSample(vehicle);
/*! run a new WaypointV2 mission sample*/
sample->runWaypointV2Mission();
1. 航点任务初始化
初始化航点任务的信息,向无人机飞行控制器发送航点任务的整体信息和航点信息,其中航点任务的整体信息包括无人机的巡航速度、断连控制和航点信息等;航点信息包含航点高度,航点经纬度等。
ret = initMissionSetting(timeout);
if(ret != ErrorCode::SysCommonErr::Success)
return ret;
sleep(timeout);
......
......
/*设置航点任务的整体信息*/
WayPointV2InitSettings missionInitSettings;
missionInitSettings.missionID = rand();
missionInitSettings.repeatTimes = 1;
missionInitSettings.finishedAction = DJIWaypointV2MissionFinishedGoHome;
missionInitSettings.maxFlightSpeed = 10;
missionInitSettings.autoFlightSpeed = 3;
missionInitSettings.exitMissionOnRCSignalLost = 1;
missionInitSettings.gotoFirstWaypointMode = DJIWaypointV2MissionGotoFirstWaypointModePointToPoint;
missionInitSettings.mission = generatePolygonWaypoints(radius, polygonNum);
missionInitSettings.missTotalLen = missionInitSettings.mission.size();
this->actions = generateWaypointActions(actionNum);
ErrorCode::ErrorCodeType ret = vehiclePtr->waypointV2Mission->init(&missionInitSettings,timeout);
/*设置航点任务中航点的信息*/
missionInitSettings.mission = generatePolygonWaypoints(radius, polygonNum);
/*设置无人机的动作信息(可选)*/
this->actions = generateWaypointActions(actionNum);
std::vector<DJIWaypointV2Action> actionVector;
for(uint16_t i = 0; i < actionNum; i++)
{
DJIWaypointV2SampleReachPointTriggerParam sampleReachPointTriggerParam;
sampleReachPointTriggerParam.waypointIndex = i;/*设置动作的编号*/
sampleReachPointTriggerParam.terminateNum = 0;
/*设置触发器*/
auto *trigger = new DJIWaypointV2Trigger(DJIWaypointV2ActionTriggerTypeSampleReachPoint,&sampleReachPointTriggerParam);
/*设置执行器*/
auto *cameraActuatorParam = new DJIWaypointV2CameraActuatorParam(DJIWaypointV2ActionActuatorCameraOperationTypeTakePhoto, nullptr);
auto *actuator = new DJIWaypointV2Actuator(DJIWaypointV2ActionActuatorTypeCamera, 0, cameraActuatorParam);
auto *action = new DJIWaypointV2Action(i, *trigger,*actuator);
actionVector.push_back(*action);
}
return actionVector;
2. 上传或下载航点任务
- 上传航点任务
uploadWaypointMission(timeout);
if(ret != ErrorCode::SysCommonErr::Success)
return ret;
sleep(waitTime);
- 下载航点任务
std::vector<DJIWaypointV2> mission;
dowloadWaypointMission(mission,timeout);
if(ret != ErrorCode::SysCommonErr::Success)
return ret;
sleep(timeout);
3. 上传“动作”(可选)
在上传无人机所需执行的“动作”的过程中,无人机会检查“动作”的合法性检查,若检查不通过,则无法上传动作(但不影响无人机飞行),详情请参考相应的错误码。
uploadWapointActions(timeout);
if(ret != ErrorCode::SysCommonErr::Success)
return ret;
sleep(timeout);
4. 控制无人机执行航点任务
- 开始执行航点任务
无人机开始执行航点任务前,将先检查用户上传的任务信息,若检查失败,无人机将无法执行航点任务,详情请查看错误码信息。
startWaypointMission(timeout);
if(ret != ErrorCode::SysCommonErr::Success)
return ret;
sleep(40);
- 暂停执行航点任务
控制无人机暂停执行航点任务。
pauseWaypointMission(timeout);
if(ret != ErrorCode::SysCommonErr::Success)
return ret;
sleep(5);
- 恢复执行航点任务
控制无人机从暂停的位置,继续执行为完成的航点任务。
resumeWaypointMission(timeout);
if(ret != ErrorCode::SysCommonErr::Success)
return ret;
5. 设置无人机的巡航速度
设置或获取无人机在执行航点任务时的巡航速度。
- 设置巡航速度
setGlobalCruiseSpeed(1.5, timeout);
if(ret != ErrorCode::SysCommonErr::Success)
return ret;
sleep(timeout);
- 获取巡航速度
getGlobalCruiseSpeed(timeout);
if(ret != ErrorCode::SysCommonErr::Success)
return ret;
sleep(timeout);
热点任务
航点任务是一种控制无人机以指定的飞行半径围绕某一区域(兴趣点)飞行的控制功能。在使用热点任务时,开发者需设置兴趣点的坐标,无人机的飞行高度及飞行半径等参数,控制无人机围绕某一区域飞行。
使用热点任务功能
如需使用航点任务,使用DJI OSDK 的HotpointMission 类实现航点任务功能。
1. 初始化热点任务
在使用热点任务前,请先初始化热点任务,并调用接口设置热点任务的信息。其中热点任务的信息包括无人机执行热点任务时兴趣点的经纬度信息、飞行半径和相对飞行高度等。
vehicle->missionManager->init(DJI_MISSION_TYPE::HOTPOINT, responseTimeout,NULL);
2. 设置热点(兴趣点)
控制无人机执行热点任务时,需为无人机指定热点(兴趣点)
vehicle->missionManager->hpMission->setHotPoint(broadcastGPosition.longitude, broadcastGPosition.latitude, initialRadius);;
3. 控制无人机起飞
控制无人机执行热点任务时,需先控制无人机起飞。
ACK::ErrorCode takeoffAck = vehicle->control->takeoff(responseTimeout);
if (ACK::getError(takeoffAck))
{
ACK::getErrorCodeMessage(takeoffAck, __func__);
if(takeoffAck.info.cmd_set == OpenProtocolCMD::CMDSet::control
&& takeoffAck.data == ErrorCode::CommonACK::START_MOTOR_FAIL_MOTOR_STARTED)
{
DSTATUS("Take off command sent failed. Please Land the drone and disarm the motors first.\n");
}
4. 开始执行热点任务
控制无人机根据热点任务中的信息执行热点任务。
std::cout << "Start with default rotation rate: 15 deg/s" << std::endl;
ACK::ErrorCode startAck = vehicle->missionManager->hpMission->start(responseTimeout);
if (ACK::getError(startAck))
{
ACK::getErrorCodeMessage(startAck, __func__);
}
5. 暂停执行热点任务
控制无人机暂停执行的热点任务。
std::cout << "Pause for 5s" << std::endl;
ACK::ErrorCode pauseAck =
vehicle->missionManager->hpMission->pause(responseTimeout);
if (ACK::getError(pauseAck))
{
ACK::getErrorCodeMessage(pauseAck, __func__);
}
6. 恢复执行热点任务
控制无人机从暂停的位置,继续执行为完成的热点任务。
std::cout << "Resume" << std::endl;
ACK::ErrorCode resumeAck =
vehicle->missionManager->hpMission->resume(responseTimeout);
if (ACK::getError(resumeAck))
{
ACK::getErrorCodeMessage(resumeAck, __func__);
}
7. 更新飞行数据
在无人机执行热点任务的过程中,开发者可更新无人机的参数,如下以更新飞行半径为例。
std::cout << "Update radius to 1.5x: new radius = " << 1.5 * initialRadius
<< std::endl;
vehicle->missionManager->hpMission->updateRadius(1.5 * initialRadius);
8. 停止执行热点任务
控制无人机停止执行热点任务。
std::cout << "Stop" << std::endl;
ACK::ErrorCode stopAck =
vehicle->missionManager->hpMission->stop(responseTimeout);
高级视觉功能
- 仅使用Linux 和ROS 系统开发的应用程序支持开发者使用DJI 的高级感知功能。
- 使用DJI OSDK 的高级感知功能时,需使用USB 线接收无人机视觉感知系统的原始图像数据。
相机码流
为满足开发者使用OSDK 开发的应用程序对获取相机码流的功能,OSDK 提供了获取相机码流的功能,支持获取FPV 相机或获取I 号云台相机H.264 码流和RGB 图像。
- M210 系列无人机支持获取FPV 相机和I 号云台上相机的H.264 码流或RGB 图像;M300 RTK 无人机支持获取FPV 相机和所有云台相机的H.264 码流。
- 由于获取FPV 相机码流和获取I 号云台上相机码流的回调函数在各自独立的线程中运行,OpenCV 的imshow 模块仅支持在一个线程中运行,因此仅支持开发者获取FPV 相机或获取I 号云台相机H.264 码流和RGB 图像。
- 获取相机码流后,请安装FFmpeg 等解码器解码。
分辨率和帧频
OSDK 支持开发者获取M210 系列和M300 RTK 无人机上I 号云台上相机的码流,开发者或用户可根据实际的使用需求挂载不同型号的相机,根据相机的型号以及相机的工作模式,指定帧速率,获取所需的码流。
- 拍照模式:
- FPV 相机码流:支持获取分辨率为608x448 的图像。
- 相机模式:支持获取分辨率为1440x1080(1080p)、960x720(720p)的图像。
- 视频模式:支持获取分辨率为1920x1080(1080p)、1280x720(720p)的视频。
获取FPV 相机和主相机码流的帧速率均为30 FPS。
相机H.264 码流
获取M210 系列和M300 RTK 无人机上相机H.264 码流的流程如下所示:
- 使用获取相机H.264 码流的功能前,开发者根据实际的使用需要先实现
liveViewSampleCb
函数,用于获取并处理相机H.264 码流。 - 调用
startH264Stream()
接口,指定所需获取码流的相机、接收相机H.264 码流的回调函数和用户信息; - 开启无人机和机载计算机,运行使用基于OSDK 开发的应用程序,此时无人机将会向机载计算机推送H.264 码流;
- 机载计算机接收到H.264 码流的数据后,将触发(作为入参传入开发者设置的回调函数中)基于OSDK 开发的应用程序;
- 开发者根据实际需求设计的函数
liveViewSampleCb
在获取相机H.264 码流后,将对所获得的H.264 码流执行存储、解码及转发等相应的操作。
无人机视觉感知系统
DJI 无人机的视觉感知系统主要由无人机视感知传感器和视觉感知算法构成,在无人机飞行的过程中,感知传感器能够获取周围环境的状态,协助无人机刹车、避障和精确悬停。
- Matrice 210 V2 系列无人机仅支持开发者获取无人机前视感知传感器和下视感知传感器的码流数据;
- Matrice 300 RTK 支持开发者获取无人机上所有感知传感器的码流数据。
在悬停期间若无人机受到外部干扰,无人机将会返回到原悬停点。
使用立体感知功能时,若无人机与无人机遥控器断开连接,无人机将会悬停。
图像类型
立体感知功能支持开发者获取的图像类型和分辨率如下所示:
- M300 RTK:支持开发者以20 FPS,640x480 的分辨率,获取VGA 灰度图像。
- M210 V2:
- 前视视觉传感器:以10 FPS 或20 FPS,640x480 的分辨率,获取VGA 灰度图像;以20 FPS,320x240的分辨率获取灰度图像;以10 FPS,320x240 的分辨率获取视差图。
- 下视视觉传感器:以20 FPS,320x240 的分辨率获取QVGA 灰度图。
元数据
无人机视觉传感器产生的图像主要包含帧索引和时间戳两种元数据。
- 时间戳:无人机上的视觉传感器同时共享一个计时器,该计时器与M210 V2 系列无人机上的时钟同步,有关时间同步的详细说明请参见时间同步。
- 帧索引:开发者订阅感知灰度图后,无人机感知灰度图将会附带帧索引。订阅或取消订阅感知灰度图,将不会改变或中断帧索引的序号。
获取无人机的图像
使用高级视觉功能时,开发者需要调用指定的API 通过USB 接口在应用程序运行的周期内订阅(或退订)相机视觉传感器的图像,并使用回调函数在专用的读取线程内获取订阅的图像。
- 获取无人机视觉传感器图像时,建议安装OpenCV 以显示图像。
- 为避免处理视觉传感器图像的工作阻塞主线程,创建一个独立的线程处理无人机视觉传感器的图像。在ROS 系统上使用高级视觉功能时,使用
ROS services
订阅并通过image_view
显示视觉传感器的图像。
使用目标跟踪功能
- 使用KCFcpp 库编译目标跟踪的示例代码时,请使用命令
cmake -DADVANCED_SENSING=ON -DTARGET_TRACKING_SAMPLE=ON make
编译该程序。 - 从键盘中输入g 后,即可创建KCF跟踪器;选择跟踪新的对象时请输入s,如需退出该程序,请按ESC 键。
1. 指定跟踪目标
为了方便用户指定所需跟踪的目标,OSDK 提供了TrackingUtility
类,用于获取目标对象和跟踪状态。
tracker = new KCFTracker(true, true, false, false);
tracker->init(roi, frame);
2. 获取目标对象
目标跟踪算法根据视觉传感器或相机每一帧的图像,确定目标图像的位置并计算目标对象移动的距离(单位:1 px)。
/*获取目标对象*/
roi = tracker->update(frame);
/*确定目标对象移动的位置*/
dx = (int)(roi.x + roi.width/2 - mainImg.width/2);
dy = (int)(roi.y + roi.height/2 - mainImg.height/2);
3. 计算云台转动角度
根据目标对象移动的状态,计算云台所需改变的偏航角度和俯仰角度,并将该角度发送给DJI::OSDK::Gimbal::SpeedData gimbalSpeed;
。
- 目标对象每变化10个像素的位置,无人机云台转动1度/秒。
- 若目标对象变动的距离小于10像素,无人机的云台将不会转动。
DJI::OSDK::Gimbal::SpeedData gimbalSpeed;
gimbalSpeed.roll = 0;
gimbalSpeed.pitch = pitchRate;
gimbalSpeed.yaw = yawRate;
gimbalSpeed.gimbal_control_authority = 1;
vehicle->gimbal->setSpeed(&gimbalSpeed);
使用获取相机RGB 图像的功能
以轮询的方式获取RGB 图像
在主线程中以轮询的方式获取RGB 图像。
- 1. 创建主线程
- 调用
vehicle->advancedSensing->startFPVCameraStream()
接口创建一个线程,用于读取相机原始的码流并解码成图像。
- 调用
- 2. 检查码流状态
- 开发者在主循环中,需调用
vehicle->advancedSensing->newFPVCameraImageIsReady()
接口,检查相机码流的状态,若相机中有可用的码流,则调用vehicle->advancedSensing->getFPVCameraImage(fpvImage)
获取该图像。
- 开发者在主循环中,需调用
若开发者安装了OpenCV 库,则可通过
show_rgb
函数调用cv::imshow()
接口显示解码后的RGB 图像。
- 3. 销毁线程
- 调用
vehicle->advancedSensing->stopFPVCameraStream()
接口,断开与相机的链接,销毁读取相机码流的线程。
- 调用
以回调函数的方式获取RGB 图像
通过回调函数的方式获取RGB 图像。
- 1. 创建获取相机码流的线程
- 调用
vehicle->advancedSensing->startFPVCameraStream(&show_rgb, NULL)
接口,创建获取相机码流的线程,同时在该接口中注册回调函数show_rgb
,用于处理接收到的码流。
- 调用
- 2. 销毁线程
- 调用
vehicle->advancedSensing->stopFPVCameraStream()
接口,断开与相机的连接,销毁读取相机码流的线程。
- 调用
使用获取相机H.264 码流的功能
1. 开始获取相机H.264 码流
控制应用程序接收指定相机的H.264 码流。
LiveView::LiveViewErrCode startH264Stream(LiveView::LiveViewCameraPosition pos, H264Callback cb, void *userData);
2. 停止获取H.264 码流
控制应用程序停止接收相机的H.264 码流。
LiveView::LiveViewErrCode stopH264Stream(LiveView::LiveViewCameraPosition pos);
3. 保存或处理H.264 码流
基于OSDK 开发的应用程序获取H.264 码流后,开发者即可对所获取的H.264 码流执行所需的操作。
typedef void (*H264Callback)(uint8_t* buf, int bufLen, void* userData);
- 开发者获取指定相机的H.264 码流后,使用
ffplay -flags2 showall xxx.h264
命令即可播放所获取的H.264 文件。 - 借助OSDK 提供的Sample (djiosdk-liveview-sample) 获取H.264 码流数据,并将接收到的H.264 码流数据以H.264 文件的形式记录在本地,该文件名为
userData
。 - 使用Sample camera-stream-callback-sample和camera-stream-poll-sample 可借助FFMpeg 对H.264 码流解码。开发者可借助Sample 实现所需的功能。
- OSDK仅提供了对H264纯软件解码的代码实现
- 如有提高解码性能的需求,用户可使用硬件解码器以提高解码效率
适用产品
- Matrice 300 RTK:支持挂载Z30、XTS、XT2、H20、H20T(开发者无需使用遥控器即可获取视频流)
- Matrice 210 V2 系列:仅支持在主云台上挂载X4S、X5S、X7、Z30、XTS、XT2、H20、H20T (开发者无需使用遥控器即可获取视频流)