码流AI识别
功能说明
LiveView AI 功能用于开启 DJI PSDK LiveView 图像流,使用示例中的 YOLOvFastest 目标检测模块处理图像,并通过 MAVLink TUNNEL 消息输出识别框信息。
该功能同时会将 AI meta 信息回传给 DJI Pilot,并将检测结果转换为 MAVLink 自定义二进制 payload,方便地面端或伴随计算机解析。
注意:
当前仅在 Matrice 400 无人机挂载 H30 相机和 DJI 妙算3 场景下支持该功能。
编解码回调函数中均不允许进行阻塞耗时操作,推荐创建独立线程进行目标识别等功能。
相关文件
| 文件 | 说明 |
|---|---|
src/dji_mavlink/dji_liveview_ai_mavlink.hpp |
LiveView AI MAVLink 接口和 payload type 定义 |
src/dji_mavlink/dji_liveview_ai_mavlink.cpp |
LiveView 拉流、目标检测、MAVLink TUNNEL 输出 |
src/dji_mavlink/mavlink_connector.cpp |
MAV_CMD_USER_1 命令入口 |
samples/sample_c++/module_sample/liveview/image_processor_yolovfastest.* |
YOLOvFastest 示例处理器 |
启停命令
使用 MAVLink COMMAND_LONG 发送 MAV_CMD_USER_1。
| 参数 | 说明 |
|---|---|
param1 |
1 启动 LiveView AI;0 停止 LiveView AI |
param2 |
DJI LiveView camera position,默认 DJI_LIVEVIEW_CAMERA_POSITION_NO_1 |
param3 |
DJI LiveView camera source,默认 DJI_LIVEVIEW_CAMERA_SOURCE_DEFAULT |
命令执行后,程序返回 COMMAND_ACK。
| 返回值 | 说明 |
|---|---|
MAV_RESULT_ACCEPTED |
PSDK 接口调用成功 |
MAV_RESULT_FAILED |
PSDK 接口调用失败,或未编译 OpenCV 支持 |
MAVLink 输出
LiveView AI 识别结果通过 MAVLink TUNNEL 输出。
| 字段 | 值 |
|---|---|
payload_type |
32790 |
payload_length |
根据当前包内识别框数量变化 |
payload |
自定义二进制结构 |
Payload 格式
每个 TUNNEL payload 由一个 header 加若干个 box 组成。单包最多携带 8 个目标框;超过 8 个目标时自动拆成多包。
Header
| 字段 | 类型 | 说明 |
|---|---|---|
version |
uint8_t |
当前为 1 |
packet_index |
uint8_t |
当前分包序号,从 0 开始 |
packet_count |
uint8_t |
当前帧总分包数 |
box_count |
uint8_t |
当前包内目标框数量 |
frame_id |
uint32_t |
DJI LiveView 图像帧 ID |
width |
uint16_t |
图像宽度 |
height |
uint16_t |
图像高度 |
Box
| 字段 | 类型 | 说明 |
|---|---|---|
id |
uint16_t |
DJI 目标 ID |
type |
uint8_t |
目标类别 ID |
state |
uint8_t |
目标状态 |
cx |
uint16_t |
目标框中心 X,DJI 归一化坐标,范围 0 到 10000 |
cy |
uint16_t |
目标框中心 Y,DJI 归一化坐标,范围 0 到 10000 |
w |
uint16_t |
目标框宽度,DJI 归一化坐标,范围 0 到 10000 |
h |
uint16_t |
目标框高度,DJI 归一化坐标,范围 0 到 10000 |
distance_mm |
uint32_t |
目标距离,单位 mm;无距离时按 DJI 数据填充 |
使用流程
- 确认工程编译时开启
OPEN_CV_INSTALLED。 - 地面端发送
MAV_CMD_USER_1,param1=1。 - 程序开启 LiveView 图像流并启动 AI 处理线程。
- 地面端监听
MAVLink TUNNEL payload_type=32790。 - 停止时发送
MAV_CMD_USER_1,param1=0。
示例
启动默认相机源:
COMMAND_LONG.command = MAV_CMD_USER_1
COMMAND_LONG.param1 = 1
COMMAND_LONG.param2 = 0
COMMAND_LONG.param3 = 0停止:
COMMAND_LONG.command = MAV_CMD_USER_1
COMMAND_LONG.param1 = 0Python 例程
以下例程使用 pymavlink 启动 LiveView AI,监听 TUNNEL payload_type=32790,解析识别框并打印目标信息,最后发送停止命令。
import struct
import time
from pymavlink import mavutil
LIVEVIEW_AI_PAYLOAD_TYPE = 32790
MAV_CMD_USER_1 = mavutil.mavlink.MAV_CMD_USER_1
def get_current_millis():
return int(time.time() * 1000)
def send_liveview_ai_command(master, enable, camera_position=0, camera_source=0):
send_time = get_current_millis()
print(
f"[{send_time}] 发送 LiveView AI 命令: "
f"enable={enable}, position={camera_position}, source={camera_source}"
)
master.mav.command_long_send(
master.target_system,
master.target_component,
MAV_CMD_USER_1,
0,
1 if enable else 0, # param1: 1=start, 0=stop
camera_position, # param2: DJI LiveView camera position,0 使用默认值
camera_source, # param3: DJI LiveView camera source,0 使用默认值
0, 0, 0, 0
)
ack = master.recv_match(type="COMMAND_ACK", blocking=True, timeout=5.0)
if ack:
ack_time = get_current_millis()
print(
f"[{ack_time}] 收到 COMMAND_ACK: "
f"command={ack.command}, result={ack.result}, delay={ack_time - send_time}ms"
)
return ack.result == mavutil.mavlink.MAV_RESULT_ACCEPTED
print(f"[{get_current_millis()}] 超时:未收到 LiveView AI 命令应答")
return False
def parse_liveview_ai_payload(payload):
# Header: version, packet_index, packet_count, box_count, frame_id, width, height
header_fmt = "<BBBBIHH"
box_fmt = "<HBBHHHHI"
header_size = struct.calcsize(header_fmt)
box_size = struct.calcsize(box_fmt)
if len(payload) < header_size:
return None, []
version, packet_index, packet_count, box_count, frame_id, width, height = struct.unpack_from(
header_fmt, payload, 0
)
header = {
"version": version,
"packet_index": packet_index,
"packet_count": packet_count,
"box_count": box_count,
"frame_id": frame_id,
"width": width,
"height": height,
}
boxes = []
offset = header_size
for _ in range(box_count):
if offset + box_size > len(payload):
break
target_id, target_type, state, cx, cy, w, h, distance_mm = struct.unpack_from(
box_fmt, payload, offset
)
boxes.append({
"id": target_id,
"type": target_type,
"state": state,
"cx": cx,
"cy": cy,
"w": w,
"h": h,
"distance_mm": distance_mm,
})
offset += box_size
return header, boxes
# 连接无人机(UDP 端口 14550)
master = mavutil.mavlink_connection("udpin:0.0.0.0:14550")
print("等待无人机心跳...")
master.wait_heartbeat()
print(f"无人机已连接: system={master.target_system}, component={master.target_component}")
if not send_liveview_ai_command(master, enable=True):
raise SystemExit("LiveView AI 启动失败")
print("开始监听 LiveView AI 识别结果,按 Ctrl+C 停止")
try:
while True:
msg = master.recv_match(type="TUNNEL", blocking=True, timeout=5.0)
now_ms = get_current_millis()
if msg is None:
print(f"[{now_ms}] 超时:未收到 LiveView AI TUNNEL 数据")
continue
if msg.payload_type != LIVEVIEW_AI_PAYLOAD_TYPE:
continue
payload = bytes(msg.payload[:msg.payload_length])
header, boxes = parse_liveview_ai_payload(payload)
if header is None:
print(f"[{now_ms}] 收到无效 LiveView AI payload")
continue
print(
f"[{now_ms}] frame={header['frame_id']} "
f"packet={header['packet_index'] + 1}/{header['packet_count']} "
f"size={header['width']}x{header['height']} boxes={len(boxes)}"
)
for box in boxes:
print(
f" id={box['id']} type={box['type']} state={box['state']} "
f"cx={box['cx']} cy={box['cy']} w={box['w']} h={box['h']} "
f"distance={box['distance_mm']}mm"
)
except KeyboardInterrupt:
print("准备停止 LiveView AI")
finally:
send_liveview_ai_command(master, enable=False)
print("LiveView AI 例程结束")注意事项
- 如果未定义
OPEN_CV_INSTALLED,启动接口会返回DJI_ERROR_SYSTEM_MODULE_CODE_NONSUPPORT,MAVLink ACK 为MAV_RESULT_FAILED。 cx/cy/w/h是 DJI 归一化坐标,不是像素坐标。地面端如需像素坐标,需要结合width/height自行换算。- LiveView AI 使用 LiveView 图像流和编码回调。若系统同时使用普通视频流,需要联调确认 LiveView 初始化和回调不会互相抢占。
- 自定义 payload 为二进制结构,不是 JSON;地面端解析时需按小端结构体读取。
作者:bai 创建时间:2026-06-23 14:05
最后编辑:bai 更新时间:2026-07-08 09:44
最后编辑:bai 更新时间:2026-07-08 09:44