QGroundControl 航点规划文件(.plan)格式完全指南:Plan JSON 结构与源码级解析

原创2026-10-02 19:57:321,327 阅读
文章标签:无人机

QGroundControl 航点规划文件(.plan)格式完全指南:Plan JSON 结构与源码级解析

QGroundControl(QGC)使用 .plan 文件保存完整的航点规划数据:任务(Mission)航点、可选的地理围栏(GeoFence)与返航点(Rally Points)三大部分被统一封装在一个 JSON 文件中。本文以官方 Plan 文件格式文档(docs/en/qgc-dev-guide/file_formats/plan.md)为骨架,逐层解析 Plan 文件的顶层结构、任务项(SimpleItem/ComplexItem)、Survey/CorridorScan/StructureScan 复合任务、CameraCalc 相机参数、围栏与返航点的字段含义与取值约束,并对照 PlanMasterController.cc 的加载/保存实现与 PlanManager.h 的协议层,说明这些 JSON 字段在源码中如何被校验、解析和写出。读完本文,你将能够手写或程序化生成一个合法的 .plan 文件,也能准确理解 QGC 生成的规划文件中每个字段的语义。

1. Plan 文件顶层结构

Plan 文件使用 JSON 格式存储,由三个顶层对象组成:mission(任务,必填)、geoFence(地理围栏,可选)、rallyPoints(返航点,可选)。QGC 官方文档给出的“近最小”示例(plan 必须至少包含一个任务项)如下:

{
  "fileType": "Plan",
  "geoFence": {
    "circles": [],
    "polygons": [],
    "version": 2
  },
  "groundStation": "QGroundControl",
  "mission": {},
  "rallyPoints": {
    "points": [],
    "version": 2
  },
  "version": 1
}

各顶层字段的含义如下表所示:

Key 描述
version 文件格式版本,当前为 1。
fileType 必须为 "Plan",用于标识文件类型。
groundStation 创建该文件的 GCS 名称(示例为 QGroundControl)。
mission 与飞行计划关联的任务对象(至少包含一个任务项)。
geoFence (可选)该计划的地理围栏信息。
rallyPoints (可选)该计划的返航/安全点信息。

需要特别注意的是:围栏与返航点不仅用于任务飞行过程中,在没有任务运行时同样生效(文档原话:“The plan fence and rally points are also used in modes when no mission is running”)。也就是说,即使 mission.items 为空,只要文件中声明了 geoFence 或 rallyPoints,对应的约束在无任务模式下依然会被使用。

1.1 源码中的文件头校验逻辑

在源码中,fileType 与 version 的取值是被硬编码并强制校验的。在 PlanMasterController.h 中定义了:

static constexpr int kPlanFileVersion = 1;
static constexpr const char* kPlanFileType = "Plan";
static constexpr const char* kJsonMissionObjectKey = "mission";
static constexpr const char* kJsonGeoFenceObjectKey = "geoFence";
static constexpr const char* kJsonRallyPointsObjectKey = "rallyPoints";

而 PlanMasterController.cc 中的 _loadPlanJson() 则是实际的解析入口:先用 JsonParsing::isJsonFile 校验文件是否为合法 JSON,再调用 JsonParsing::validateExternalQGCJsonFile(json, kPlanFileType, kPlanFileVersion, kPlanFileVersion, version, errorString) 强制校验 fileType == "Plan" 且 version 在允许范围内,最后通过 validateKeys 确认 mission、geoFence、rallyPoints 三个键均存在且为 JSON 对象——注意这里三个键都必须存在(即使内容是空对象),否则加载失败。

对应的写出路径是 saveToJson()(PlanMasterController.cc):调用 JsonParsing::saveQGCJsonFileHeader(planJson, kPlanFileType, kPlanFileVersion) 写入 fileType 与 version 文件头,随后依次保存 mission、geoFence、rallyPoints 三个对象。整个加载/保存流程还开放了插件钩子(preLoadFromJson / postLoadFromJson / preSaveToJson / postSaveToJson),允许 QGCCorePlugin 对 Plan 内容做预处理,例如 custom-example 中 CustomPlugin.cc 的自定义加载逻辑。

2. Mission 任务对象

mission 对象承载了任务的核心数据:飞行速度、机型信息、全局高度模式以及任务项列表。其结构如下:

"mission": {
    "cruiseSpeed": 15,
    "firmwareType": 12,
    "globalPlanAltitudeMode": 1,
    "hoverSpeed": 5,
    "items": [
        {
            "AMSLAltAboveTerrain": null,
            "Altitude": 50,
            "AltitudeMode": 0,
            "autoContinue": true,
            "command": 22,
            "doJumpId": 1,
            "frame": 3,
            "params": [
                15,
                0,
                0,
                null,
                47.3985099,
                8.5451002,
                50
            ],
            "type": "SimpleItem"
        }
    ],
    "plannedHomePosition": [
        47.3977419,
        8.545594,
        487.989
    ],
    "vehicleType": 2,
    "version": 2
}

必填字段说明:

Key 描述
version Mission 对象格式版本,当前为 2。
firmwareType 创建该任务时的飞控固件类型,取值为 MAV_AUTOPILOT 枚举值(示例 12 即 MAV_AUTOPILOT_PX4)。
globalPlanAltitudeMode 全局任务级高度模式,作用于未显式指定 AltitudeMode 的任务项。
vehicleType 创建该任务时的机型类型,取值为 MAV_TYPE 枚举值(示例 2 即 MAV_TYPE_QUADROTOR)。
cruiseSpeed 固定翼或 VTOL 机型在两个航点之间飞行的默认前向速度(m/s)。
hoverSpeed 多旋翼机型的默认前向速度(m/s)。
items 任务项对象列表,可混合包含 SimpleItem 与 ComplexItem,至少包含一个任务项。
plannedHomePosition 规划起飞点(Home)位置,用于无飞机连接时在地图上显示并参与任务规划。数组元素依次为纬度、经度、AMSL 海拔高度。

从源码角度看,MissionController.cc 的 save() 完整印证了这些字段的写出顺序:version 使用 _missionFileVersion;plannedHomePosition 通过 GeoJsonHelper::saveGeoCoordinate() 序列化(会带上高度,writeAltitude = true);firmwareType、vehicleType、cruiseSpeed、hoverSpeed 直接取自当前控制器关联的 Vehicle(_controllerVehicle->firmwareType() 等);globalPlanAltitudeMode 则来自 _globalAltFrame。随后遍历 _visualItems 逐个调用 visualItem->save() 生成 items 数组。值得留意的是“结束任务动作”(End Mission Action)是作为最后一个隐藏任务项追加进 items 的(见 save() 中 settingsItem 的特殊处理分支)。

3. SimpleItem:简单任务项

一个 SimpleItem 对应一条 MAVLink MISSION_ITEM 命令,是最基本的任务元素(航点、速度设置、悬停、相机命令等都属于此类)。完整示例:

{
    "AMSLAltAboveTerrain": null,
    "Altitude": 50,
    "AltitudeMode": 0,
    "autoContinue": true,
    "command": 22,
    "doJumpId": 1,
    "frame": 3,
    "params": [
        15,
        0,
        0,
        null,
        47.3985099,
        8.5451002,
        50
    ],
    "type": "SimpleItem"
}

字段映射:

Key 描述
type 固定为 SimpleItem。
AMSLAltAboveTerrain 展示给用户的“高于地形”AMSL 高度值。
Altitude 任务项高度。
AltitudeMode 高度模式(相对/绝对等)。
autoContinue 对应 MISSION_ITEM.autoContinue,控制当前项完成后是否自动继续下一项。
command 该任务项的命令(MAV_CMD),对应 MISSION_ITEM.command。
doJumpId 当前任务项在 DO_JUMP 命令中作为跳转目标的编号,从 1 开始自动编号。
frame MAV_FRAME 坐标参考系,对应 MISSION_ITEM.frame。
params 对应 MISSION_ITEM 的 param1、param2、param3、param4、x、y、z,具体语义取决于 command 对应的 MAV_CMD。

以上面示例为例做一次实战解码:command: 22 即 MAV_CMD_NAV_WAYPOINT(飞向航点),frame: 3 即 MAV_FRAME_GLOBAL_RELATIVE_ALT(相对起飞点高度的全局坐标)。此时 params 的语义为:param1=15(悬停停留时间,秒)、param2=0(接受半径,米)、param3=0(通过半径,米)、param4=null(航向角,未指定)、x/y/z 依次为纬度 47.3985099、经度 8.5451002、高度 50(米,相对高度)。

4. ComplexItem:复合任务项

复合任务项(ComplexItem)是多个 MISSION_ITEM 的高层封装,对用户表现为一个可编辑的整体实体,保存时由 QGC 展开为具体命令序列。目前共有三种类型:

4.1 Survey 复合任务项

Survey(测绘扫描)用于按航带(transect)对指定多边形区域进行覆盖拍摄。对象定义:

{
    "TransectStyleComplexItem": {
        ...
    },
    "angle": 0,
    "complexItemType": "survey",
    "entryLocation": 0,
    "flyAlternateTransects": false,
    "polygon": [
        [
            -37.75170619863631,
            144.98414811224316
        ],
        ...
        [
            -37.75170619863631,
            144.99457681259048
        ]
    ],
    "type": "ComplexItem",
    "version": 4
}
Key 描述
version 该 survey 定义的版本号(文档中注记为 3,示例文件为 4,以实际导出为准)。
type 固定为 ComplexItem。
complexItemType 固定为 survey(小写)。
TransectStyleComplexItem Survey 与 CorridorScan 共用的基础定义(见第 6 节)。
angle 航带路径的方向角(度)。
entryLocation 进入点位置(文档未详细展开,QGC 生成文件中通常为 0)。
flyAlternateTransects 若为 true,飞行器将先隔行飞行所有奇数/偶数航带,再回头补飞被跳过的航带;用于转弯半径过小、固定翼无法完成急转的场景。
polygon 多边形测绘区域顶点数组,每个顶点为 [纬度, 经度] 二元组。

4.2 CorridorScan 复合任务项

CorridorScan(走廊扫描)沿一条折线路径生成覆盖航带,常用于河流、道路、输电线巡检。对象定义:

{
    "CorridorWidth": 50,
    "EntryPoint": 0,
    "TransectStyleComplexItem": {
        ...
    },
    "complexItemType": "CorridorScan",
    "polyline": [
        [
            -37.75234887156983,
            144.9893624624168
        ],
        ...
        [
            -37.75491914850321,
            144.9893624624168
        ]
    ],
    "type": "ComplexItem",
    "version": 2
}
Key 描述
version 该 CorridorScan 定义的版本号(文档中注记为 3,示例为 2)。
type 固定为 ComplexItem。
complexItemType 固定为 CorridorScan。
CorridorWidth 走廊扫描宽度(米),决定航带向折线两侧展开的覆盖范围(文档未详细展开,结合 CorridorScanComplexItem.h 的语义可推断)。
EntryPoint 扫描入口点(文档未详细展开)。
TransectStyleComplexItem 与 Survey 共用的基础定义(见第 6 节)。
polyline 走廊中心线折线点数组,每个点同样为 [纬度, 经度] 二元组。

4.3 StructureScan 复合任务项

StructureScan(结构扫描)针对单个建筑/结构物,围绕其多边形底面按层(Layers)从下至上生成环绕拍摄路径。对象定义:

{
    "Altitude": 50,
    "CameraCalc": {
        "AdjustedFootprintFrontal": 25,
        "AdjustedFootprintSide": 25,
        "CameraName": "Manual (no camera specs)",
        "DistanceToSurface": 10,
        "DistanceToSurfaceRelative": true,
        "version": 1
    },
    "Layers": 1,
    "StructureHeight": 25,
    "altitudeRelative": true,
    "complexItemType": "StructureScan",
    "polygon": [
        [
            -37.753184359536355,
            144.98879374063998
        ],
        ...
        [
            -37.75408368012594,
            144.98879374063998
        ]
    ],
    "type": "ComplexItem",
    "version": 2
}
Key 描述
version 该 StructureScan 定义的版本号,当前为 2。
type 固定为 ComplexItem。
complexItemType 固定为 StructureScan。
Altitude 扫描飞行高度(米)。
CameraCalc 相机参数(见第 7 节),决定相邻拍摄点的间隔与重叠率。
Layers 扫描层数:结构物越高,需要的环绕层越多。
StructureHeight 结构物高度(米),用于计算各层高度。
altitudeRelative true:高度相对起飞点(home);false:高度为 AMSL 绝对高度。
polygon 结构物底面多边形顶点数组([纬度, 经度] 二元组列表)。

5. TransectStyleComplexItem:Survey 与 CorridorScan 的公共基定义

TransectStyleComplexItem 承载了 Survey 与 CorridorScan 两类航带类任务的全部共性配置(相机、地形跟随、转弯参数等):

"TransectStyleComplexItem": {
    "CameraCalc": {
        ...
    },
    "CameraTriggerInTurnAround": true,
    "FollowTerrain": false,
    "HoverAndCapture": false,
    "Items": [
        ...
    ],
    "Refly90Degrees": false,
    "TurnAroundDistance": 10,
    "VisualTransectPoints": [
        [
            -37.75161626657736,
            144.98414811224316
        ],
        ...
        [
            -37.75565155437309,
            144.99438539496475
        ]
    ],
    "version": 1
}
Key 描述
version 该定义的版本号,当前为 1。
CameraCalc 相机参数定义(见第 7 节)。
CameraTriggerInTurnAround (布尔)是否在航带转弯区域也触发相机拍照。
FollowTerrain (布尔)是否跟随地形起伏调整高度。
HoverAndCapture (布尔)是否采用“悬停拍摄”模式(多旋翼在每拍点悬停后拍照)。
Items 由复合任务展开生成的实际任务项列表(QGC 保存时写入)。
Refly90Degrees (布尔)是否以 +90° 交叉方向再扫描一遍(提高重叠率)。
TurnAroundDistance 到达多边形边缘后、转向下一条航带之前,继续向前飞行的距离(米),即“转弯外扩距离”。
VisualTransectPoints 可视化用的航带端点列表([纬度, 经度] 二元组数组)。

6. CameraCalc:相机参数

CameraCalc 用于 Survey、Corridor 或 Structure Scan,描述相机型号、镜头参数与覆盖重叠率,QGC 据此计算航带间距与拍照间隔。完整示例:

"CameraCalc": {
    "AdjustedFootprintFrontal": 272.4,
    "AdjustedFootprintSide": 409.2,
    "CameraName": "Sony ILCE-QX1",
    "DistanceToSurface": 940.6896551724138,
    "DistanceToSurfaceRelative": true,
    "FixedOrientation": false,
    "FocalLength": 16,
    "FrontalOverlap": 70,
    "ImageDensity": 25,
    "ImageHeight": 3632,
    "ImageWidth": 5456,
    "Landscape": true,
    "MinTriggerInterval": 0,
    "SensorHeight": 15.4,
    "SensorWidth": 23.2,
    "SideOverlap": 70,
    "ValueSetIsDistance": false,
    "version": 1
}
Key 描述
version 该 CameraCalc 定义的版本号,当前为 1。
AdjustedFootprintFrontal 调整后的前向(沿航线方向)地面覆盖尺寸。
AdjustedFootprintSide 调整后的侧向(垂直航线方向)地面覆盖尺寸。
DistanceToSurface 相机到地面/目标表面的距离。
DistanceToSurfaceRelative 距离是否为相对值。
CameraName 相机名称,必须匹配 QGC 已知的相机,或使用 Manual (no camera specs)(手动配置)、Custom Camera(自定义相机)。对于 “Manual” 模式,其后各字段不写出/不使用。
FixedOrientation (布尔)相机是否固定朝向。
FocalLength 镜头焦距(毫米)。
FrontalOverlap 前向(航向方向)图像重叠率百分比。
ImageDensity 图像密度(决定航带间距的附加参数)。
ImageHeight 图像高度(像素)。
ImageWidth 图像宽度(像素)。
Landscape true:相机横置安装;false:相机竖置安装。
MinTriggerInterval 两次拍照之间的最小时间间隔(秒),用于规避快门过速。
SensorHeight 传感器高度(毫米)。
SensorWidth 传感器宽度(毫米)。
SideOverlap 侧向图像重叠率百分比。
ValueSetIsDistance (布尔)当前参数集是以“距离”还是“重叠率”为核心设置方式。

其中 SensorWidth/SensorHeight/FocalLength/ImageWidth/ImageHeight 共同决定了单张照片的地面覆盖范围,FrontalOverlap/SideOverlap 则决定相邻航带与相邻拍照点的间距,最终换算为 AdjustedFootprintFrontal/AdjustedFootprintSide 与航带布局。

7. GeoFence 地理围栏

地理围栏信息是可选的,一个 Plan 可包含任意数量的围栏,由多边形(polygons)与圆形(circles)两类构成。最小定义:

"geoFence": {
    "circles": [],
    "polygons": [],
    "version": 2
}
Key 描述
version 围栏部分格式版本号,文档版本为 2。
circles 圆形围栏定义列表(多个以逗号分隔)。
polygons 多边形围栏定义列表(多个以逗号分隔)。

7.1 Circle 圆形围栏

每个圆形围栏一个条目,定义圆心、半径以及该围栏是否启用:

{
    "circle": {
        "center": [47.39756763610029, 8.544649762407738],
        "radius": 319.85
    },
    "inclusion": true,
    "version": 1
}
Key 描述
version 圆形围栏条目格式版本,文档版本为 1。
circle 圆定义:center 为 [纬度, 经度],radius 为半径。
inclusion 该围栏是否启用(true 启用,false 禁用)。

7.2 Polygon 多边形围栏

每个多边形围栏一个条目,顶点按顺时针方向排列(必须包围一个闭合区域):

{
    "inclusion": true,
    "polygon": [
        [
            47.39807773798406,
            8.543834631785785
        ],
        [
            47.39983519888905,
            8.550024648373267
        ],
        [
            47.39641100087146,
            8.54499282423751
        ],
        [
            47.395590322265186,
            8.539435808992085
        ]
    ],
    "version": 1
}
Key 描述
version 多边形围栏条目格式版本,文档版本为 2。
polygon 多边形顶点列表,每个顶点包含纬度与经度,顶点按顺时针顺序排列。
inclusion 该围栏是否启用(true 启用,false 禁用)。

围栏数据在源码中由 GeoFenceManager 与 GeoFenceController 负责持久化与编辑;圆与多边形两类条目的加载校验同样经由 PlanMasterController::_loadPlanJson() 统一入口(PlanMasterController.cc)。

8. Rally Points 返航点

返航点信息可选,Plan 可包含任意数量的返航点,每个点由纬度、经度与相对起飞点的高度(above home position)构成。两个点的示例:

"rallyPoints": {
    "points": [
        [
            47.39760401,
            8.5509154,
            50
        ],
        [
            47.39902017,
            8.54263274,
            50
        ]
    ],
    "version": 2
}
Key 描述
version 返航点部分格式版本,文档版本为 2。
points 返航点列表;每个点为 [纬度, 经度, 高度] 三元组,高度为相对起飞点(home)的高度。

返航点数据由 RallyPointManager 与 RallyPointController 管理,与任务、围栏一样统一走 Plan 文件的加载/保存链路。

9. Plan 文件的加载与写出流程(源码级)

把前面的 JSON 结构与 QGC 源码串起来,完整的 Plan 文件生命周期如下:

  1. 选择文件:用户通过文件对话框选择 .plan 文件;PlanMasterController::loadFromFile()(PlanMasterController.cc)先按扩展名分流:若扩展名是 .waypoints(waypointsFileExtension)或 .txt,则走 _missionController.loadTextFile() 解析旧式纯文本航点文件;否则走 _loadPlanJson() 按 JSON 解析。
  2. JSON 校验:_loadPlanJson() 依次完成 JSON 语法检查、文件头校验(fileType/version)、顶层三键类型校验,再分别把 mission/geoFence/rallyPoints 三个子对象交给 MissionController、GeoFenceController、RallyPointController 的 load() 逐项解析。
  3. 加载成功:更新 currentPlanFile 路径,并将 dirty 状态置为“需上传、无需保存”。
  4. 写出:saveToJson() 组装文件头与三个子对象;saveToFile() 自动补 .plan 扩展名(fileExtension())后以 UTF-8 文本写盘,并校验写入字节数是否完整(PlanMasterController.cc)。此外还支持 saveToKml() 将任务导出为 KML(KMLPlanDomDocument)。

在协议层,任务项最终通过 PlanManager 与飞控交互——它是 Mission、GeoFence、Rally Point 三类管理器(MAV_MISSION_TYPE 区分)的公共基类,基于 mavlink v2 mission 协议实现 loadFromVehicle()(从飞控读回)、writeMissionItems()(写入飞控)与 removeAll(),并内置 ACK 超时与重试机制(_ackTimeoutMilliseconds = 1500ms、_retryTimeoutMilliseconds = 250ms、_maxRetryCount = 5),错误码覆盖 Ack 超时、协议错误、条目越界、条目不匹配、车辆 ACK 错误等场景(PlanManager.h)。也就是说:.plan 文件是磁盘上的持久化格式,而飞控与地面站之间的任务同步则走 MAVLink mission 协议,两者通过 MissionItem 的字段(command/frame/params)天然对应。

10. 版本与兼容性说明

  • Plan 文件顶层 version: 1、Mission 对象 version: 2、围栏 version: 2、返航点 version: 2 是各节当前的文档版本;复合任务项内部还有各自的小版本(TransectStyleComplexItem 为 1,CameraCalc 为 1)。QGC 的 JsonParsing::validateExternalQGCJsonFile 会校验顶层版本,读取旧版本文件时按向后兼容策略处理。
  • fileType 必须严格为 "Plan";mission、geoFence、rallyPoints 三个顶层键在文件中都必须存在(可空)。
  • Survey 示例中的 "complexItemType": "survey" 为小写,而 CorridorScan 与 StructureScan 使用驼峰命名(CorridorScan/StructureScan),手写文件时需注意区分。
  • 本文中的枚举数值(如 command: 22、frame: 3、firmwareType: 12、vehicleType: 2)均以 MAVLink 标准枚举定义为准,具体语义可对照 MAVLink 协议文档中的 MAV_CMD / MAV_FRAME / MAV_AUTOPILOT / MAV_TYPE 枚举。
  • 测试工程中保留了完整的使用范例:例如 MissionControllerTest.cc 通过 saveToFile() 写出 VTOLTakeoff.plan 再 loadFromFile() 回读验证保存-加载闭环,并通过 loadFromFile(":/unittest/SectionTest.plan") 加载内置测试计划;PlanMasterControllerTest.cc 则验证了加载/保存后的 dirty 状态语义。需要自定义格式联调或自研 GCS 对接时,这些测试用例是很好的参考基准。

实用建议:若需程序化生成 Plan 文件,最稳妥的方式是先用 QGC 规划一个包含航点、测绘、围栏与返航点的完整计划并导出,以此为模板修改坐标与参数;修改时注意保持各节 version 不变、params 数量与 command 的 MAV_CMD 语义一致、多边形围栏顶点保持顺时针,即可获得飞控可接受且 QGC 可正常回读的合法文件。

登录后查看全文
qgroundcontrol