尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于DJI Onboard SDK的无人机飞行控制系统源码解析与实战

基于DJI Onboard SDK的无人机飞行控制系统源码解析与实战 简介基于DJI Onboard SDK的无人机飞行控制系统源码包面向无人机二次开发工程师、嵌入式爱好者及计算机专业学生解决在受限Linux设备上与DJI飞控建立双向通信并执行多种飞行任务的问题。项目以C实现通过UART对接飞控涵盖位置、速度、偏移、航点等任务类型并设计紧急制动/解除、控制台交互、日志记录与Linux自动启动服务适合作为移动机器人平台和无人系统课程的课程设计素材。压缩包共529个文件、仅4.07MB核心代码为22个C源文件与22个头文件包含飞控核心类、任务调度与坐标转换等模块另有makefile、service脚本支撑构建与自动部署html/js/tex等文件作为项目说明与代码文档便于查阅与二次开发。当前已有367人学习浏览可帮助开发者缩短DJI Onboard SDK的移植与调测周期具有较强的工程参考价值。1. 先拆解“源码基于DJIOnboardSDK的无人机飞行控制系统.zip”到底给你什么拿到这类压缩包我拿到手第一件事不是解压看代码而是先问自己三个问题这套源码跑在什么硬件上、对接的是 DJI 哪一代飞控、里面是完整业务系统还是只是 demo。这三问直接决定后面所有排查方向。DJIOnboardSDK 是大疆开放给开发者控制飞控的软件开发套件它不负责无人机的姿态解算和电机控制而是把导航、RTK、IMU、电调这些飞控内部能力封装成稳定的 API机载计算机通过串口或网线读取状态、下发任务。标题里的“无人机飞行控制系统”本质是在这套 API 之上写的业务层自动起飞、航线飞行、失控返航、传感器联动。下面我会把从解压、编译、上机到模拟器验证的完整路径讲一遍新手能跟着复现老手也能看到边界条件。2. 源码包背后的 DJIOnboardSDK 架构2.1 数据链路不是简单串口而是一套带校验的私有协议先说清楚一个最容易误解的点DJIOnboardSDK 不是直接往飞控发 AT 指令飞控和机载计算机之间跑的是一整套二进制封装协议。帧头、会话 ID、命令字节、数据段、CRC16 校验收敛在一个链路层里。官方库把这些全部藏在Link层里你拿到源码后如果要移植到 MCU这部分才是真正的“嵌入式内核源码”级工作量应用层反而简单得多。通信方式物理层常见速率适用机型/场景UART3.3V TTL通常通过 USB 转串口115200 或 921600A3、N3、M600 等接线简单Ethernet网线直连或机载局域网10/100/1000 MbpsM300 RTK、M200 V2适合大吞吐遥测我一般建议直接用 UART 起步因为它不依赖网络环境拔插方便。源码包里如果你看到UserConfig.txt里写着device /dev/ttyTHS1或ttyUSB0说明作者默认用的是外部串口模块。要记住飞控侧的波特率必须和工具DJI Assistant 2里设置的一致否则后面所有数据都会是乱码。2.2 源码常见目录结构别被“源码”两个字骗了解压后先不要急着打开main.cpp先看一眼目录结构。绝大多数 OSDK 工程都会按功能分目录你可以对照自己拿到的 zip 进行映射flight_control_system/ ├── src/ │ ├── control/ # 起飞、降落、位置控制、姿态控制 │ ├── telemetry/ # 遥测数据订阅GPS、姿态角、电量、RTK │ ├── mission/ # 航线任务、航点管理、返航逻辑 │ ├── camera/ # 云台与相机联动可选 │ └── main.cpp # 程序入口初始化 Vehicle 对象 ├── lib/ │ └── libdjiosdk-core.a # OSDK 预编译静态库 ├── include/ │ └── dji_vehicle.hpp # 核心 Vehicle 类头文件 ├── config/ │ └── UserConfig.txt # 串口、波特率、App ID、Key └── README.md # 版本和编译说明这里要特别提醒lib目录里的库文件通常按架构区分比如libdjiosdk-core.a只适配 ARM Linux 或 x86 中的一种。如果源码包里只有 cpu 版本的库你却想跑在 Jetson AGX Orin 上那就要确认作者的库是否匹配否则链接阶段会报一堆未定义引用。2.3 机载设备选型为什么推荐 Linux ARM 板一个可复现的 OSDK 工程硬件选型决定了后面所有麻烦。常见方案是树莓派 4B、Jetson Nano 或更工业化的 RK3568 核心板。我个人的倾向是 RK3568 或 Jetson因为它们处理日志、网络、视觉算法时不会把 CPU 占满。如果你是拿源码做产品原型建议把“能跑 demo”和“能长时间稳定运行”分开考量。OSDK 的官方库面向 Linux 用户态不需要你改内核但如果你的板卡是自己裁剪的嵌入式 Linux Linux 内核必须确认三件事串口驱动有没有被菜单配置裁剪掉、USB 转串口芯片驱动是否内置、系统时钟是否与 NTP 校准。很多机器启动后收不到数据最后查出来是内核里把CONFIG_USB_SERIAL_CH341关了。3. 编译与最小运行把源码从 zip 变成能执行的程序3.1 交叉编译工具链与依赖准备拿到源码后先检查 README看作者用的 OSDK 版本。OSDK 3.8、3.9、4.0 对固件版本要求不同比如 M300 RTK 需要 OSDK 4.0 以上。如果你板卡是 ARM64而源码提供的库是 ARMHF依然可以用但需要装对应架构库。在板卡上安装基础依赖sudo apt update sudo apt install -y cmake g libcurl4-openssl-dev libssl-dev如果板卡不能联网就把 zip 里的lib目录直接拷到/usr/local/osdk/lib并把头文件拷到/usr/local/osdk/include。有人问为什么还需要libcurl和openssl因为 OSDK 里和 DJI 云平台通信、流量统计、固件校验的模块会用到即使你不连云端链接时这些符号也躲不掉。3.2 最小 CMake 配置与编译命令不需要把整个 zip 的工程当成黑盒你可以自己写一个极简 CMake只编译main.cpp加 OSDK 库。示例CMakeLists.txtcmake_minimum_required(VERSION 3.10) project(dji_flight_control) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 指定 OSDK 头文件和静态库路径 set(OSDK_ROOT /usr/local/osdk) include_directories(${OSDK_ROOT}/include) link_directories(${OSDK_ROOT}/lib) add_executable(flight_control src/main.cpp) # 链接顺序敏感djiosdk-core 放前面 target_link_libraries(flight_control djiosdk-core pthread rt curl ssl crypto)编译命令mkdir build cd build cmake .. -DCMAKE_C_COMPILERaarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILERaarch64-linux-gnu-g make -j4如果你的板卡是原生 Ubuntu ARM比如 Jetson直接g就行。link_directories之后链接器会在/usr/local/osdk/lib里找libdjiosdk-core.a。这里列的pthread、rt是必须的OSDK 内部多线程发送和计时器依赖它们。3.3 上机前核对四个参数程序编出来后别急着插飞机。先打开config/UserConfig.txt把下面四个参数和实际情况对齐。参数示例值说明serial_device/dev/ttyUSB0串口设备节点jetson 上常是/dev/ttyTHS1baudrate115200必须与 DJI Assistant 2 设置一致app_id123456DJI 开发者平台申请的 ID和飞控绑定sdk_keyabcdef...与 App ID 成对不能随意改3.3.1 串口设备名和权限ls -l /dev/ttyUSB0后如果显示dialout组那当前用户需要加入dialout组否则打开串口会提示Permission denied。执行sudo usermod -aG dialout $USER后注销重新登录。这个坑几乎每个 OSDK 初学者都会遇到。3.3.2 波特率与固件 SDK 版本接着用stty -F /dev/ttyUSB0 115200先手动测试一次再用minicom打开。如果收到的是0x55 0xAA这类疑似帧头但又没有后续数据的内容大概率是波特率不匹配。另外OSDK 版本和飞控固件版本必须配对。A3/N3 刷了最新固件老 SDK 的帧头校验会直接失败表现就是程序一直卡在Vehicle initialization超时。4. 飞行控制系统的核心实现路径状态机、遥测与任务4.1 从待机到返航状态机先于代码一个“飞行控制系统”和普通 OSDK demo 的最大区别在于有没有状态机。我不会一上来就写飞行动作而是先在main.cpp上面定义一套状态枚举enum FlightState { STATE_INIT, // 程序启动检查SDK连接 STATE_STANDBY, // 飞控在线等待指令 STATE_TAKE_OFF, // 正在起飞 STATE_HOVER, // 定点悬停 STATE_NAVIGATING, // 执行航线 STATE_RTH, // 返航 STATE_LANDING, // 降落 STATE_ERROR // 异常等待处理 };状态机的好处是能把维护成本和风险隔离开。例如在STATE_NAVIGATING时如果 GPS 丢星或者电量过低状态机必须快速迁移到STATE_RTH而不是继续发送位置指令。OSDK 本身给你的只是底层控制能力状态往哪里迁移是业务代码的责任。4.2 订阅 GPS 位置并发送位置控制指令下面是最小的起飞后移动到目标点的代码基于官方dji_vehicle.hpp的接口。#include dji_vehicle.hpp #include dji_control.hpp #include dji_telemetry.hpp using namespace DJI::OSDK; using namespace DJI::OSDK::Telemetry; int main(int argc, char** argv) { // argv[1] 串口设备argv[2] 波特率 Vehicle vehicle(argv[1], argv[2]); if (!vehicle.protocolStatus) { std::cerr SDK 协议连接失败请检查串口和固件版本 std::endl; return -1; } vehicle.obtainCtrlAuthority(1); // 1 表示申请控制权 vehicle.control-takeOff(1, 10); // 起飞超时10秒 sleep(5); // 目标北移5米东移3米高度2米偏航角保持0 vehicle.control-positionAndYawCtrl(5.0f, 3.0f, 2.0f, 0.0f); sleep(10); vehicle.control-land(1, 15); vehicle.releaseCtrlAuthority(1); return 0; }这段代码里最容易被忽略的是positionAndYawCtrl的坐标系。OSDK 默认使用的是 NED北东地坐标系所以第一个参数 5.0 表示向北移动 5 米第二个参数 3.0 表示向东移动 3 米。如果你在无人机行业习惯用经纬度需要先通过齐次坐标转换把 GPS 偏移换算成 NED 偏移直接传经纬度进去会让飞机朝随机方向飞。起飞的takeOff(1, 10)中第二个参数是超时时间单位秒。如果飞机没有在 10 秒内真正离地OSDK 会返回失败。你需要额外做一次vehicle.telemetry-getGlobalPosition()读取高度变化来确认离地而不是盲目继续位置控制。4.3 航线任务上传与执行中的错误处理对于飞行控制系统来说航线飞行是核心能力。OSDK 的 waypoint mission 接口在不同版本里命名差异很大我以 3.8/3.9 时代最常见的WaypointMission风格为例你拿到的源码里如果头文件不同按对应样例类替换即可。// 初始化航点任务 WaypointMission mission; mission.initialization(0x00); mission.addWaypoint(22.5411f, 113.9455f, 20.0f, 0.0f); // 纬度、经度、高度、偏航角 mission.addWaypoint(22.5420f, 113.9470f, 25.0f, 90.0f); mission.upload(); // 上传到飞控 mission.start(); // 开始执行这段代码只是示意实际WaypointMission构造函数需要传入WaypointInitSettings。执行过程中你需要监听飞控返回的 mission 状态判断当前航点序号。最容易出的错是某个航点高度超过限高或者两个航点间距不足飞控会把整条航线拒掉。常见的 OSDK 错误码如下错误码宏可能原因排查方向OSDK_MISSION_AVOIDANCE_FAILED航线与障碍物过于接近检查现场障碍物或禁飞区OSDK_CTRL_CMD_NOT_SUPPORTED当前飞控固件不支持该指令升级 OSDK 版本或降级固件OSDK_FRAMING_TIMEOUT数据链路响应超时串口干扰、USB 线过长、缓冲区满OSDK_REQUEST_TIMEOUT指令发送后无回包确认控制权是否在自己手上4.4 日志与遥测数据持久化飞行控制系统的另一个关键点是日志。我见过太多源码包在控制逻辑上写得非常漂亮日志却只打到终端一断电就什么都没有。工程实战中建议每个状态机的状态迁移都写一条 CSV 记录方便后续分析定点精度和航线偏差。std::ofstream log(/var/log/flight.csv, std::ios::app); log time,state,lat,lon,height,speed,voltage std::endl; while (running) { auto gps vehicle.telemetry-getGlobalPosition(); auto battery vehicle.telemetry-getBatteryInfo(); log std::time(nullptr) , currentState , gps.latitude , gps.longitude , gps.height , vehicle.telemetry-getVelocity().data.speed , battery.voltage std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); }写日志时要对 SD 卡寿命有意识。频繁flush会减少 eMMC 寿命我一般设置循环中每 10 秒才 flush 一次断电丢几秒数据可以接受。另外日志目录要mkdir -p提前建好否则程序一启动就打开文件失败你会误以为飞控数据没进来。5. 进阶验证在 DJI 模拟器里跑完整闭环5.1 用 Assistant 2 打开模拟器没有真机也能验证源码能不能跑通。把飞控通过 USB 连到电脑打开 DJI Assistant 2在“模拟器”页面选择“模拟位置”。注意选择“无 GPS”或“室内模式”会直接屏蔽位置控制所以要选室外模拟。启动模拟器后飞控会向你的串口发送模拟的 GPS 位置OSDK 代码里的telemetry-getGlobalPosition()就能读到数据了。5.2 三个必调的参数在模拟器调试阶段我建议先把下面三个参数调对再上真机。参数项目对应位置建议值obtainCtrlAuthority超时控制权申请等待模拟器下 5 秒超时即可位置控制响应系数你的 PID 外环或 OSDK 的水平和垂直速度限制水平 2 m/s垂直 1 m/s日志打印间隔遥测循环 sleep 时间模拟器调试用 200ms真机用 1s5.3 悬停漂移判断法在模拟器里跑起来后不要急着看代码。先把无人机悬停 30 秒同时记录positionAndYawCtrl设定值与实际位置的差。如果偏差随时间呈现固定的单向漂移说明你外环控制目标点计算有坐标系偏移如果是随机震荡说明位置反馈频率不够或者控制周期不匹配。用这个方法能在 5 分钟内定位出 80% 的悬停漂移问题比反复看源码猜效率高得多。本文还有配套的精品资源点击获取
返回列表