MAVROS GPS Listener 📸 无人机传感器数据采集与图像元数据写入

C++14 ROS Melodic/Noetic MAVROS Exif/XMP OpenCV

一个完整的ROS功能包,用于订阅无人机传感器数据(GPS、IMU、姿态、位置),并通过服务调用将当前图像帧与传感器数据保存到图片元数据中,支持GPS坐标写入EXIF,扩展数据写入XMP或UserComment。

📖 概述

MAVROS GPS Listener 是一个基于ROS的无人机数据采集功能包,专为无人机航测、视觉定位、数据归档等场景设计。核心功能是订阅MAVROS发布的传感器话题和相机图像话题,当用户调用服务时,捕获当前图像帧,并将实时传感器数据(GPS坐标、姿态角、位置速度等)写入图像的元数据中(EXIF/XMP)。

📡 GPS数据

纬度、经度、海拔、卫星数、定位类型、HDOP/VDOP

🌀 姿态数据

Roll/Pitch/Yaw(弧度)、角速度

📍 位置数据

本地坐标系位置(X,Y,Z)、速度

📸 图像元数据

EXIF GPS标签 + XMP扩展数据 / UserComment JSON

🛠️ 服务接口

/capture_image 服务,支持自定义文件名/路径

🐍 跨平台查看

Python脚本 + PyQt5可视化工具

✨ 核心特性

⚡ 快速开始

最简单的使用方式: 启动节点后调用服务即可
# 终端1: 启动MAVROS
roslaunch mavros px4.launch fcu_url:=/dev/ttyACM0:921600

# 终端2: 启动相机驱动(根据实际相机)
roslaunch usb_cam usb_cam-test.launch

# 终端3: 启动GPS监听节点
roslaunch mavros_gps_listener gps_listener.launch

# 终端4: 调用服务捕获图像
rosservice call /capture_image "image_name: 'flight_001' image_path: ''"

# 查看结果
exiv2 -p e ~/gps_ws/images/flight_001.jpg | grep GPS

🏗️ 架构设计

功能包采用模块化设计,主要包含以下组件:

数据流: MAVROS → 话题回调 → 互斥锁保护 → 共享数据 → 服务触发 → 图像捕获 → 元数据写入 → 保存文件

📡 ROS话题订阅

节点默认订阅以下话题(可通过launch文件重映射):

话题名称消息类型数据内容
/uav1/mavros/global_position/globalsensor_msgs/NavSatFixGPS经纬度、海拔
/uav1/mavros/global_position/raw/fixmavros_msgs/GPSRAW卫星数、定位类型、HDOP/VDOP
/uav1/mavros/global_position/raw/gps_velgeometry_msgs/Vector3StampedGPS速度
/uav1/mavros/imu/datasensor_msgs/Imu姿态(四元数)、角速度
/uav1/mavros/local_position/posegeometry_msgs/PoseStamped本地位置
/uav1/mavros/local_position/velocity_localgeometry_msgs/TwistStamped本地速度
/uav1/camera/image_rawsensor_msgs/Image图像帧
话题名称可通过launch文件中的image_topic参数配置。

🛠️ 服务接口

/capture_image [mavros_gps_listener/CaptureImage]

请求参数

响应参数

# 调用示例
rosservice call /capture_image "image_name: 'waypoint_001' image_path: ''"
# 返回: success: True, saved_image_path: "/home/amov/gps_ws/images/waypoint_001.jpg"

📸 图像元数据结构

保存的图像包含以下元数据:

EXIF GPS标签(标准GPS数据)

UserComment JSON(扩展传感器数据)

{
  "latitude": 39.9042,
  "longitude": 116.4074,
  "altitude": 50.5,
  "roll": 0.04999,
  "pitch": -0.02356,
  "yaw": 2.79048,
  "roll_rate": 0.00191,
  "pitch_rate": 0.00093,
  "yaw_rate": -0.00026,
  "position_x": 10.5,
  "position_y": 20.3,
  "position_z": 50.2,
  "velocity_x": 0.5,
  "velocity_y": 0.3,
  "velocity_z": 0.1,
  "gps_velocity_x": 0.52,
  "gps_velocity_y": 0.31,
  "satellites_visible": 12,
  "fix_type": 3,
  "hdop": 1.2,
  "vdop": 1.5,
  "timestamp": 1774250591.408
}

XMP标签(可选,双备份)

Xmp.drone.Roll Xmp.drone.Pitch Xmp.drone.Yaw Xmp.drone.PositionX Xmp.drone.VelocityX Xmp.drone.SatellitesVisible Xmp.drone.Timestamp

🚀 使用指南

1. 创建功能包

cd ~/catkin_ws/src
catkin_create_pkg mavros_gps_listener roscpp sensor_msgs geometry_msgs mavros_msgs cv_bridge image_transport

2. 编译

cd ~/catkin_ws
catkin_make
source devel/setup.bash

3. 配置launch文件

<launch>
    <arg name="image_topic" default="/uav1/camera/image_raw" />
    <arg name="save_path" default="/home/amov/gps_ws/images" />
    <node name="gps_listener_node" pkg="mavros_gps_listener" type="gps_listener_node" output="screen">
        <param name="image_topic" value="$(arg image_topic)" />
        <param name="save_path" value="$(arg save_path)" />
    </node>
</launch>

4. 运行

roslaunch mavros_gps_listener gps_listener.launch

5. 调用服务

rosservice call /capture_image "image_name: 'test' image_path: ''"

📌 示例程序

示例文件描述
gps_listener_node.cpp主节点实现,包含话题回调和服务处理
gps_metadata_writer.cpp元数据写入实现,封装Exiv2操作
test_client.pyPython服务调用示例
read_metadata.pyPython读取图片元数据脚本
viewer.pyPyQt5可视化工具,支持批量查看

🐍 Python数据读取脚本

#!/usr/bin/env python3
import exiv2
import json

def read_image_metadata(image_path):
    image = exiv2.ImageFactory.open(image_path)
    image.readMetadata()
    
    # 读取GPS
    exif = image.exifData()
    if 'Exif.GPSInfo.GPSLatitude' in exif:
        print(f"GPS: {exif['Exif.GPSInfo.GPSLatitude']}")
    
    # 读取UserComment JSON
    if 'Exif.Photo.UserComment' in exif:
        comment = exif['Exif.Photo.UserComment'].toString()
        json_start = comment.find('{')
        if json_start != -1:
            data = json.loads(comment[json_start:])
            print(f"Roll: {data['roll']:.2f} rad")
            print(f"Pitch: {data['pitch']:.2f} rad")
            print(f"Yaw: {data['yaw']:.2f} rad")

if __name__ == '__main__':
    read_image_metadata('capture_20260323_152311.jpg')
需要安装: pip install python-exiv2

📊 数据可视化工具

提供基于PyQt5 + Matplotlib的可视化工具,支持:

python3 viewer.py
# 选择包含图片的文件夹,自动加载所有图片元数据

⚙️ 配置参数

参数名类型默认值描述
image_topicstring/uav1/camera/image_raw图像话题名称
save_pathstring/home/amov/gps_ws/images图片保存目录

📦 编译与依赖

依赖项

# Ubuntu 安装依赖
sudo apt install ros-${ROS_DISTRO}-mavros ros-${ROS_DISTRO}-mavros-extras
sudo apt install ros-${ROS_DISTRO}-cv-bridge ros-${ROS_DISTRO}-image-transport
sudo apt install libopencv-dev libeigen3-dev libexiv2-dev

# 编译
cd ~/catkin_ws
catkin_make --only-pkg-with-deps mavros_gps_listener

🔧 常见问题

Q: 服务调用返回"没有可用的图像数据"
A: 检查图像话题是否正确,使用rostopic list | grep image查看实际话题名称,并在launch文件中配置。
Q: GPS数据为0
A: 检查MAVROS连接状态和GPS信号,使用rostopic echo /mavros/global_position/global查看是否有数据。
Q: 编译时找不到exiv2
A: 安装libexiv2-dev: sudo apt install libexiv2-dev
Q: UserComment中的JSON缺少开头的{
A: 这是已知问题,已在新版本中修复。可使用Python脚本自动修复解析。
Q: 如何查看写入的元数据?
A: 使用exiv2 -p e image.jpg | grep GPS查看GPS,使用exiv2 -p x image.jpg | grep drone查看XMP。

⚖️ 许可证

MIT License

Copyright (c) 2025 MAVROS GPS Listener Contributors

允许自由使用、修改、分发,需保留版权声明。不提供任何担保。

📡 MAVROS GPS Listener · 文档生成于 2026-03-23 · 基于实际代码库

支持版本: ROS Melodic/Noetic | 最后更新: 2026-03-23