ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PSDK到ROS的GPS数据桥接:基于NavSatFix的无人机定位链路搭建与CMake构建实践

PSDK到ROS的GPS数据桥接:基于NavSatFix的无人机定位链路搭建与CMake构建实践 做无人机负载开发的同行应该都有同感PSDK把飞控的定位数据拿过来并不难难的是怎么把这些数据送进ROS生态里让感知、规划、建图那些节点能用上。我最近在妙算系列机载电脑上做了一个从PSDK到ROS节点的GPS数据链路核心就是两端打通一头从PSDK的飞行控制器信息接口里拿经纬度另一头用sensor_msgs/NavSatFix标准消息发布出去中间用CMake把PSDK的静态库和catkin编译系统拧在一起。这条链路做完之后RTK级别的定位数据可以直接被Rviz、导航栈、路径规划模块消费省掉了以前靠串口转发、自己解析协议的麻烦。这篇文章把整个实现过程记录下来重点放在三个容易被卡住的环节PSDK侧GPS数据结构的解析、ROS节点发布频率与线程模型的设计、以及CMakeLists.txt里PSDK库与catkin组件的链接配置。如果你正在做无人机巡检、测绘或者机载智能计算相关的项目尤其是准备在妙算或者其他Arm平台Linux环境下把PSDK数据和ROS打通这篇应该能帮你少踩不少坑。1. 项目背景与整体方案设计1.1 为什么需要PSDK与ROS打通先说一下这个项目的来源。我做的是一个机载实时目标检测系统无人机在飞行过程中需要根据当前GPS位置判断哪些目标区域已经覆盖、哪些还没有扫到。地面站那边已经有大疆自带的航线规划但机载端跑的是ROS节点里面有目标检测、地理标签、路径补全这些模块它们都需要知道飞机当前在哪。这时候问题就来了ROS节点本身感知不到飞机的位置大疆的飞控数据也不会自动出现在ROS话题里。如果只是在飞机上额外挂一个GPS模块又会和飞控自己的定位重复而且RTK数据的一致性没法保证。最直接的办法就是从PSDK取飞控的GPS数据然后写一个桥接节点发布到ROS话题上。PSDK的定位逻辑其实很清楚——它是跑在负载设备比如妙算上的一个SDK通过物理链路跟飞控通信。应用层的DjiAircraftInfo接口能拿到的数据很多GPS定位信息只是其中一类。所以整个项目的核心思路就是不重复造定位轮子把飞控已经算好的GPS结果作为唯一数据源PSDK只是搬运工ROS节点负责把数据翻译成标准格式。1.2 整体数据链路与时序设计数据流大概是这样的飞控GPS芯片 - 飞控内部处理 - PSDK链路 - DjiAircraftInfo回调 - 桥接节点 - /sensor/gps/navsatfix话题 - 下游ROS模块这里面有个容易忽略的细节PSDK拿到的GPS数据频率通常和飞控内部GPS模块的更新频率一致常见是5Hz到10Hz。ROS这边建议发布到一个NavSatFix话题上不要为了追求高频而做插值GPS本身的更新率就摆在那里插值出来的数据只会给你一种平滑了的错觉实际上精度并没有提升。时序上要处理好两个问题。第一PSDK回调线程和ROS发布线程的竞争关系GPS数据结构体可能会被同时读写不加锁会出现经纬度错乱。第二时间戳问题GPS数据带的是UTC时间ROS消息里的header.stamp要用接收时刻的ROS系统时间不要直接拿GPS自带的UTC时间往里填因为ROS的时间基准跟GPS时间基准不是一套体系后面做数据融合时会出偏差。在选型上我还对比过另一个方案用MAVROS连接飞控的串口输出。但那个方式对大疆定制飞控支持并不好很多数据通道根本没有按MAVLink协议暴露出来。而且PSDK作为官方推荐的负载开发方式在链路稳定性和数据权限上优势明显。实测下来PSDK通道拿到的GPS数据跟遥控器上显示的位置完全一致不用额外校准。2. PSDK侧GPS数据获取与处理2.1 PSDK GPS数据接口解析PSDK里获取GPS主要走的是DjiAircraftInfo相关的接口。在较新的PSDK 3.x版本里先要调用DjiAircraftInfo_GetGPSPosition拿到一个T_DjiAircraftInfoGPSPosition结构体里面包含了经纬度、高度、卫星数、定位状态这些字段。这个结构体的关键字段我列一下字段说明注意事项latitude纬度单位是10^7度也就是说425212345表示42.5212345度要除以1e7longitude经度单位是10^7度同样要除以1e7altitude海拔高度单位是毫米要除以1000转成米height相对起飞点高度单位毫米部分版本才有satelliteCount可见卫星数少于10颗时定位质量通常一般isFixedRTK固定解标志RTK固定时定位精度才能到厘米级这里有个新手最容易踩的坑PSDK返回的经纬度不是常规的浮点数度数而是整数形式的放大值。我在第一次联调时就因为没做除法直接把原始值发布到了ROS里结果Rviz里显示的位置直接跑到海沟里去了。所以拿到数据后第一步永远是做单位换算并且建议在代码里用常量定义换算系数避免魔法数字满天飞。另外GPS的定位状态判断也很关键。T_DjiAircraftInfoGPSPosition里有一个定位状态相关的枚举需要区分是RTK固定解、RTK浮点解还是普通单点定位。在测试时要加上判断逻辑只有当定位状态达到一定等级时才把数据发布出去否则跳过这一帧。我之前见过有人不管定位状态直接把所有数据都发出去结果飞机在车库里搜不到星发布了半个小时的无效GPS数据下游的航线规划模块全被带偏了。2.2 经纬度坐标系与高度基准GPS原始数据用的是WGS84坐标系这个坐标系在ROS里对应的就是NavSatFix消息的默认参考系。关于坐标系有几点必须搞清楚。经纬度本身没什么歧义关键在高度。GPS返回的高度一般有两种椭球高相对于WGS84椭球面的高度和海拔高相对于平均海平面的高度。PSDK返回的高度在大多数情况下是海拔高但不同固件版本可能不同。NavSatFix消息里有一个position_covariance_type字段和一个隐含的坐标系约定高度默认是相对WGS84椭球的如果你把海拔高直接塞进去在需要精密高程数据的场景里会出问题。我自己的做法是启动参数里加一个use_msl_height开关默认认为PSDK返回的是海拔高然后利用EGM96模型做椭球高与海拔高的换算。如果你的应用只是做二维覆盖规划这一步可以跳过但如果你要用GPS高度做地形跟随或者3D重建这个换算不做误差能到几十米。还有一个点是国内地图显示的偏移问题。WGS84坐标直接在高德或者百度地图上显示会有一两百米的偏移这是坐标系加密策略导致的。热搜词里也看到一个python 将gps经纬度转换为高德经纬度确实很多人卡在这里。但要注意这个转换不应该在桥接节点里做。ROS生态和飞控内部都用WGS84转换成火星坐标系GCJ02只应该发生在地图显示层。如果桥接节点里做了转换下游的定位定姿就全乱套了。正确做法是ROS里始终保持WGS84只在需要叠加国内地图可视化时用额外的工具节点做坐标转换。2.3 GPS数据质量评估与异常处理GPS数据完整性和可靠性是个容易被忽视但是非常关键的问题。飞控输出的GPS数据并不是每帧都可用的尤其是飞行到桥底、高楼密集区或者树荫下时卫星信号衰减会很严重。我在这套系统里做了三层数据质量把关第一层是PSDK接口层面的状态码检查接口返回错误直接丢弃这一帧。第二层是卫星数和定位状态检查当isFixed非RTK固定解且卫星数少于某一阈值时把数据的协方差标记为无穷大告诉下游这个数据不可信。第三层是数据跳变检测用上一帧有效位置和当前帧位置计算水平位移如果一帧时间内移动了几百米但飞机的实际速度不支持这个移动量说明数据异常直接丢弃。跳变检测的实现也不复杂维护一个上一帧有效经纬度用haversine公式算球面距离再除以时间差得到等效速度超过一个阈值就认为这帧数据有问题。这里阈值我设置为60m/s因为消费级无人机实际飞行速度很少超过这个值但GPS漂移的跳变速度远高于这个值。关于热词里出现的GPS误差和GPS翻转补丁这里多说一句。GPS周翻转GPS week rollover是接收机内部计时溢出导致的日期错乱问题如果你用的是比较老的GPS模块确实可能遇到。但PSDK链路里飞控已经处理过这一层了应用层不太需要关心周翻转问题。真正要注意的反而是飞行地区的地磁异常和电离层扰动导致的短时精度下降这类问题没有一劳永逸的补丁只能通过数据质量判断来降低影响。3. ROS节点的设计与实现3.1 节点架构与话题规划桥接节点我起名叫psdk_gps_bridge整体上是一个标准ROS节点不依赖任何自定义消息类型只用sensor_msgs/NavSatFix好处是下游工具生态完整Rviz、plotjuggler、rosbag都能直接消费。话题名我建议使用/sensor/gps/navsatfix这跟MAVROS的命名风格统一以后如果还要接入其他传感器比如机载RTK模块整体话题树不会乱。如果只是做内部调试也可以用/gps/fix这种短名字看个人习惯。节点内部结构分成三块PSDK初始化模块负责注册回调、启动PSDK服务线程。数据缓冲模块用一个互斥锁保护的全局结构体保存最新GPS数据。ROS发布模块用ros::Timer定时从缓冲里取数据发布到话题。为什么不直接在PSDK回调里发布ROS消息这涉及到ROS线程模型的安全问题。PSDK的回调线程不是ROS创建的如果直接在回调里调用publisher.publish()虽然大部分时候没问题但在回调频率高且下游处理耗时的场景下ROS内部的消息队列管理可能跟外部线程产生竞争。稳妥的做法是用锁保护共享数据ROS定时器线程负责真正的发布。这样设计的另一个好处是发布频率可以由你控制。PSDK回调来一帧存一帧ROS端按自己的节奏取最新帧发布。比如PSDK是10HzROS端也是10Hz基本一一对应如果ROS端设成20Hz那其中一半是重复帧但不会丢数据这种最新数据优先的模式比队列模式更适合GPS这种带时效性的传感器。3.2 NavSatFix消息的字段填充细节sensor_msgs/NavSatFix的字段不算多但每个字段都有讲究。sensor_msgs::NavSatFix gps_msg; gps_msg.header.stamp ros::Time::now(); gps_msg.header.frame_id gps; gps_msg.status.status sensor_msgs::NavSatStatus::STATUS_FIX; gps_msg.status.service sensor_msgs::NavSatStatus::SERVICE_GPS; gps_msg.latitude lat_deg; gps_msg.longitude lon_deg; gps_msg.altitude alt_meters;frame_id建议设成gps然后在TF树里把gps坐标系和机体坐标系base_link关联起来。如果你不做TF关联很多下游模块会默认GPS话题的数据是在全局坐标系里的可能在Rviz里显示正常但做传感器融合的时候对不上。NavSatStatus字段要特别注意status枚举里STATUS_NO_FIX表示无定位STATUS_FIX表示有定位还有STATUS_SBAS_FIX、STATUS_GBAS_FIX这些增强系统的标志。只填充SERVICE_GPS可能不够如果飞控用了RTK应该把service字段改成SERVICE_GLONASS的组合值或者直接加SERVICE_RTK相关的标志。我在代码里做了一个映射函数从PSDK的定位状态枚举映射到ROS的status枚举这样下游可以直接根据status判断要不要信任这帧数据。还有一个容易忽略的字段是position_covariance。这个9维协方差矩阵很多桥接节点都是留空的但导航栈里如果有EKF之类的滤波器协方差信息能大幅影响融合权重。PSDK没有直接输出协方差矩阵但可以通过satelliteCount和定位状态估算一个对角阵定位状态是RTK固定解时位置标准差设为0.2m单点定位时设为2m到5m。虽然不精准但至少给下游一个量级参考比填零好得多。3.3 完整代码实现下面是核心代码我简化了错误处理保留主逻辑。#include ros/ros.h #include sensor_msgs/NavSatFix.h #include mutex #include dji_sdk_app_info.h #include dji_aircraft_info.h class GpsBridge { public: GpsBridge(ros::NodeHandle nh) { pub_ nh.advertisesensor_msgs::NavSatFix(/sensor/gps/navsatfix, 10); timer_ nh.createTimer(ros::Duration(0.1), GpsBridge::timerCallback, this); } void onPsdkGpsData(const T_DjiAircraftInfoGPSPosition gps) { std::lock_guardstd::mutex lock(mutex_); latest_.valid (gps.isFixed || gps.satelliteCount 10); latest_.latitude gps.latitude / 1e7; latest_.longitude gps.longitude / 1e7; latest_.altitude gps.altitude / 1000.0; latest_.satelliteCount gps.satelliteCount; latest_.isFixed gps.isFixed; } private: void timerCallback(const ros::TimerEvent) { std::lock_guardstd::mutex lock(mutex_); if (!latest_.valid) return; sensor_msgs::NavSatFix msg; msg.header.stamp ros::Time::now(); msg.header.frame_id gps; msg.latitude latest_.latitude; msg.longitude latest_.longitude; msg.altitude latest_.altitude; if (latest_.isFixed) { msg.status.status sensor_msgs::NavSatStatus::STATUS_FIX; msg.position_covariance[0] 0.04; // 0.2m^2 msg.position_covariance[4] 0.04; msg.position_covariance[8] 0.09; } else { msg.status.status sensor_msgs::NavSatStatus::STATUS_FIX; msg.position_covariance[0] 4.0; // 2m^2 msg.position_covariance[4] 4.0; msg.position_covariance[8] 9.0; } msg.position_covariance_type sensor_msgs::NavSatFix::COVARIANCE_TYPE_DIAGONAL_KNOWN; pub_.publish(msg); } struct GpsCache { bool valid false; double latitude 0.0; double longitude 0.0; double altitude 0.0; int satelliteCount 0; bool isFixed false; }; ros::Publisher pub_; ros::Timer timer_; std::mutex mutex_; GpsCache latest_; };这段代码里我把协方差矩阵直接写成了常量更工程化的做法是利用satelliteCount做分段估计比如卫星数大于20且RTK固定时用0.1m标准差卫星数10到20之间用0.8m标准差少于10颗时干脆标记为无效。阈值可以根据你的实际测试数据来调整不同PSDK版本的行为会有细微差别。4. CMake构建配置实战4.1 为什么PSDK项目绕不开CMake聊完代码终于到了让我当时折腾最久的CMake配置。要说清楚CMake在这个项目里的角色得先明白PSDK和ROS的构建方式本来就是两套体系。PSDK官方推荐的构建方式是用它自带的CMake工程里面会把PSDK核心库编译成静态库再跟你的应用代码一起链接。ROS这边则有自己的catkin构建系统它的本质也是CMake只是额外管理了消息生成、依赖传递和devel空间这些ROS特有的东西。问题就出在这两套体系怎么融合。直接把PSDK的CMakeLists.txt放到ROS工作空间里catkin_make大概率会报各种稀奇古怪的错误。把ROS节点代码单独抽出来手动用PSDK的CMake去编译又没法自动生成ROS消息头文件和依赖。我最后采用的方案是以ROS catkin工程为主工程把PSDK当成一个外部依赖库在CMakeLists.txt里显式指定PSDK的路径和链接库。这个思路简单说就是ROS的catkin负责把节点编译成可执行文件PSDK的库文件libpayloaddk.a或者.so通过CMake的include_directories和target_link_libraries引入。PSDK自己的CMakeLists.txt不需要被包含到ROS工程里只是用它的编译产物。4.2 CMakeLists.txt逐段拆解完整的CMakeLists.txt大约是下面这样我加了很多注释方便理解cmake_minimum_required(VERSION 3.0.2) project(psdk_gps_bridge) # 找catkin和必要的ROS组件 find_package(catkin REQUIRED COMPONENTS roscpp sensor_msgs std_msgs ) # 加载PSDK的CMake配置如果有 set(PSDK_ROOT /home/user/Payload-SDK-master) set(PSDK_INCLUDE_DIRS ${PSDK_ROOT}/include) set(PSDK_LIBRARIES ${PSDK_ROOT}/lib/libpayloaddk.a)这里PSDK的路径在每台机器上不一样不建议写死。我自己的做法是在CMakeLists.txt里加一个set(PSDK_ROOT CACHE PATH Path to PSDK root directory)编译时通过-DPSDK_ROOT/path/to/PSDK传进来这样工程可以给别人复用。接下来是catkin_package部分这个块的作用是声明本包对外的依赖和导出信息catkin_package( INCLUDE_DIRS include LIBRARIES psdk_gps_bridge CATKIN_DEPENDS roscpp sensor_msgs std_msgs DEPENDS PSDK )然后是指定头文件搜索路径和可执行目标include_directories( include ${catkin_INCLUDE_DIRS} ${PSDK_INCLUDE_DIRS} ) add_executable(psdk_gps_bridge src/gps_bridge_node.cpp src/psdk_module.cpp ) add_dependencies(psdk_gps_bridge ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS} ) target_link_libraries(psdk_gps_bridge ${catkin_LIBRARIES} ${PSDK_LIBRARIES} pthread )这个看似简单的文件里有几个非常隐蔽的坑。第一个坑是add_dependencies。如果省略这一行在刚创建消息包或者工程第一次编译时会报找不到sensor_msgs/NavSatFix.h这一类错误。原因是catkin工作空间里消息包的头文件是在编译时动态生成的你的节点编译必须排在消息生成之后。add_dependencies就是用来保证这个顺序的。第二个坑是链接顺序。target_link_libraries里ROS库写在前面PSDK静态库写在后面。原因是静态库链接时是顺序敏感的一个静态库里的符号只有在其后面的库中找依赖。PSDK的静态库依赖了pthread、dl这些系统库所以把pthread放在最后。如果你发现链接报错说undefined reference to pthread_*多半就是pthread没有出现在target_link_libraries的末尾。第三个坑是PSDK库文件里用了C11甚至C14的特性但ROS默认的编译标准可能比较旧。在CMakeLists.txt里加上下面两行可以避免因为编译标准不匹配导致的语法错误add_compile_options(-stdc11) set(CMAKE_CXX_STANDARD 14)这里多说一句有些PSDK头文件在编译时会有-Werror相关的警告升级选项导致一堆警告直接变成编译错误。这时可以用add_compile_options(-Wno-deprecated-declarations)先压掉废弃API的警告后面再逐个清理。4.3 catkin_make与catkin build对PSDK工程的影响还有一个很多初学者搞不清楚的问题到底用catkin_make还是catkin build以及PSDK的整体编译能不能直接放进工作空间。catkin_make和catkin build是两个不同代的构建工具。catkin_make是传统方式在ROS1里用得最多它和CMake的关系比较直白——本质上是在工作空间根目录调CMake然后把所有包串起来编译。catkin build是后来为了避免依赖顺序问题而设计的每个包独立建build目录支持并行编译速度更快。我在这个项目里优先推荐catkin_make原因不是它多先进而是兼容性最好。PSDK工程里部分老版本代码或者第三方库的CMakeLists.txt可能不兼容catkin build的隔离构建环境因为他们会默认当前目录就是build目录导致路径错乱。如果你的PSDK版本较新catkin build也能用但尽量保持简单用catkin_make更稳。关于热词里出现的cmake卸载、cmake升级这些我必须提醒一个容易出问题的点不要在Ubuntu系统上折腾系统自带的CMake版本。很多人会因为PSDK要求CMake大于某个版本就系统层面卸载或升级CMake结果把Python、ROS、CUDA这些依赖系统CMake的组件全部弄坏。正确姿势是到CMake官网下载源码包编译安装到/usr/local然后通过export PATH/usr/local/bin:$PATH来优先使用新版CMake。catkin_make能找到的是终端里cmake --version显示的版本不影响系统其他组件。4.4 CMake常见报错速查我整理了几个PSDKROS场景下最常撞上的CMake报错按出现频率排个序报错信息原因解决方案fatal error: dji_sdk_app_info.h: No such file or directoryPSDK头文件路径没有加进include检查include_directories里的PSDK_INCLUDE_DIRS是否正确undefined reference to DjiAircraftInfo_GetGPSPositionPSDK库没链接上或者链接顺序不对把PSDK静态库放到target_link_libraries中确认路径正确sensor_msgs/NavSatFix.h: No such file or directory编译顺序问题消息包还没生成在target之前加add_dependencies或者重新catkin_make一次CMake Error at ... Could NOT find PSDKPSDK目录配置失败检查set(PSDK_ROOT ...)路径用-DPSDK_ROOT显式指定The compiler does not support C14GCC版本过旧或编译标准冲突升级GCC或调整CMAKE_CXX_STANDARD与PSDK要求一致libpayloaddk.a: error adding symbols: File format not recognized架构不匹配PSDK库是x86的但你在Arm上编译用匹配架构的PSDK库版本妙算上必须用Arm版静态库这里面最隐蔽的是最后一个架构不匹配。我在项目初期直接在x86的Ubuntu上编译了PSDK库然后拷到妙算上链接结果就是这种奇怪的格式报错。PSDK库必须跟目标平台架构一致在x86上编译的库在Arm上不能用除非你用Docker做了跨平台工具链否则没有捷径。5. 联调过程与数据验证5.1 启动与话题检查代码编译通过后的第一步不是急着接飞机而是先做一次无飞控的模拟运行。在PSDK的初始化代码里有一个关键点如果在没连接飞控的情况下直接运行PSDK会卡在认证和握手阶段。所以调试时要把PSDK伪装成非绑定模式或者在代码里跳过DjiCore_Init之后的链路检测等真正接飞机时再打开。接上飞机之后启动顺序有讲究。先启动PSDK的主程序也就是我们的psdk_gps_bridge节点再启动飞控和遥控器链路。因为PSDK的链路初始化有超时逻辑如果飞机没在等程序能跳过错误继续跑反过来如果飞机已经连接但程序还没起来飞控那边不会等可能要重新上电才能握手成功。跑起来之后用rostopic echo看一眼数据rostopic echo /sensor/gps/navsatfix正常情况下应该看到类似这样的输出header: seq: 87 stamp: secs: 1712345678 nsecs: 123456789 frame_id: gps status: status: 0 service: 1 latitude: 42.5212345 longitude: -83.1234567 altitude: 256.789 position_covariance: [0.04, 0.0, 0.0, 0.0, 0.04, 0.0, 0.0, 0.0, 0.09]有个细节要留意seq字段在持续增长说明ROS发布频率正常。如果seq一直不动大概率是Timer回调没触发或者锁没释放。再检查一下发布频率和PSDK回调频率是否匹配rostopic hz /sensor/gps/navsatfix输出会在9.9Hz到10.1Hz之间波动这是正常现象。如果只有2Hz到3Hz说明PSDK到ROS的数据链路有损耗检查是不是锁竞争导致PSDK回调被长时间阻塞。一个可能的优化方案是把锁改为无锁原子变量但考虑到GPS数据量很小优先级不高。5.2 Rviz里验证GPS位置rostopic echo只能确认数据在发不能确认数据对不对还需要空间上的验证。这一步最直观的办法就是用Rviz。在Rviz里添加NavSatFix显示把Topic设成/sensor/gps/navsatfix坐标参考系选gps画面中会出现一个表示当前GPS位置的小图标。把地图或者卫星图作为底图就能直接对比飞机的实际位置和显示位置是否一致。我这里遇到过一个很有意思的问题在Rviz里GPS位置一直显示在原点附近怎么都不动。排查半天发现是Rviz的Global Options里Fixed Frame设成了map而map坐标系没有跟gps坐标系建立TF关系Rviz自然无法正确显示GPS话题在map系下的位置。把Fixed Frame改成gps后数据显示正常了。这也说明如果后续要把GPS数据用到导航栈里必须处理TF树。至少需要发布一个gps坐标系到base_link或者odom的静态TF变换让系统知道GPS模块安装在飞机的哪个位置。如果GPS天线装在飞机的正中心这个变换就是一个只有z偏移的静态变换。5.3 数据质量与定位一致性对比为了验证桥接节点发布的数据没有偏差我做了两组对比。第一组是用遥控器屏幕上的坐标和rostopic echo输出对比。起飞前站在飞机旁边记录遥控器显示的经纬度同时在终端打印节点发布的经纬度。两者在小数点后四位应该完全一致大约相当于11米精度。如果差异很大检查PSDK的坐标系原点和飞控的坐标系原点是否是同一个有些时候GPS天线不在IMU中心飞控内部会做补偿但PSDK拿到的可能是天线位置的原始数据。第二组是空中悬停时的数据稳定性测试。飞机悬停30秒同时用rosbag record /sensor/gps/navsatfix录制话题然后用Python脚本统计数据中的经纬度标准差。在RTK固定解模式下悬停时经纬度标准差应该小于0.5米。如果标准差达到了几米甚至几十米可能是PSDK拿到的不是RTK数据而只是普通单点定位需要检查飞控有没有锁定RTK信号。还可以用plotjuggler直接以时间为横轴画出经纬度轨迹观察是否有跳变点。跳变点表现为曲线上的尖刺通常对应GPS信号遮挡或电磁干扰。我实测中最容易产生跳变的时间点是飞机转弯时因为天线方向和位置变化导致多径效应增强。这类跳变通过之前的跳变检测逻辑可以过滤掉一部分但完全消除需要硬件的支持比如双天线方案。6. 常见问题与避坑总结整个项目做完我把遇到过的和同行交流过的问题整理成了一张速查表算是给后来者的一份实战参考。现象根因处理方法PSDK初始化失败卡在设备认证波特率或串口设备号不对确认/dev/ttyUSB0存在PSDK配置里设备名改为实际设备节点运行但话题无数据PSDK链路握手成功后回调没注册检查有没有调用DjiAircraftInfo_RegGpsInfoCallback或其他注册接口GPS经纬度数值巨大没做1e7和1000的单位换算按2.1节中的字段说明逐个换算经纬度波动大GPS天线位置受遮挡或者飞控精度模式是单点定位找开阔地带测试确认RTK信号固定dev目录下没有串口设备Ubuntu下tty设备权限不足sudo usermod -aG dialout $USER重新登录roslaunch找不到psdk_gps_bridge启动文件包没有被sourcesource devel/setup.bash再rospack profile编译时提示PSDK头文件版本冲突工程里同时引用了两个不同版本的PSDK统一PSDK版本删除CMakeCache.txt重新编译运行一段时间后GPS数据卡住USB转串口芯片发热导致通信不稳定加散热片或更换更稳定的串口卡Arm平台下库文件从x86拷贝过来架构不匹配到PSDK官网下载对应架构的lib编译包飞机飞行时GPS数据出现周期性丢失无线链路天线遮挡GPS信号调整天线位置检查是否为硬件安装问题catkin_make反复失败且报错几乎没有信息编译缓存损坏删除build和devel目录重新catkin_make还有一个容易忽略的坑PSDK库的日志输出级别。默认情况下PSDK会打很多调试日志这些日志在终端上刷屏不说还可能拖慢程序运行导致ROS定时器回调不够准时。解决办法是在PSDK初始化时把日志级别调到ERROR级别只在出错时输出平时保持安静。这样既省CPU也方便自己用ROS_INFO打印关键数据。另外如果你用的是鱼香ROS一键安装这类社区工具搭的环境要注意它安装的ROS版本和PSDK系统依赖之间可能存在的GCC版本差异。我一朋友的项目就是装好ROS之后系统自带的GCC版本比较老PSDK库要求的新C特性编译不过。解决办法是用update-alternatives切换默认GCC版本或者给CMake指定-DCMAKE_CXX_COMPILER。再分享一个我自己用着很顺手的排查思路如果GPS数据在Rviz里显示但是位置跟实际对不上先把rostopic echo的数据粘贴到任意一个在线经纬度查询工具里看如果在线工具定位正常说明数据本身没问题问题出在Rviz的坐标系或者地图投影配置上。如果是数据本身有问题再回头看PSDK的原始输出把所有处理步骤的中间结果都打印出来逐步排查不要一上来就怀疑CMake配置。回到这个项目本身我觉得最核心的价值不在于代码量而在于把PSDK的私有数据接口和ROS的标准消息无缝对齐。这条链路搭好之后后面再加做航点规划、地理围栏、测绘拼接这些功能都方便了只需要专心写业务逻辑不用再跟底层通信打交道。根据我这次的经验整个流程里最花时间的其实不是功能实现而是把两套构建体系理顺。但理顺一次后面所有PSDK功能的ROS化都能复用同一套框架这个投入还是很值的。
返回列表