相机曝光模式
EXPOSURE_MODE
设置
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:E_DjiCameraManagerExposureMode
| 枚举值 | 值 | 说明 |
|---|---|---|
| DJI_CAMERA_MANAGER_EXPOSURE_MODE_PROGRAM_AUTO | 1 | 程序自动曝光模式(Program Auto,P档) |
| DJI_CAMERA_MANAGER_EXPOSURE_MODE_SHUTTER_PRIORITY | 2 | 快门优先模式(Shutter Priority,S/Tv档) |
| DJI_CAMERA_MANAGER_EXPOSURE_MODE_APERTURE_PRIORITY | 3 | 光圈优先模式(Aperture Priority,A/Av档) |
| DJI_CAMERA_MANAGER_EXPOSURE_MODE_EXPOSURE_MANUAL | 4 | 手动曝光模式(Manual,M档) |
| DJI_CAMERA_MANAGER_EXPOSURE_MODE_EXPOSURE_UNKNOWN | 0xFF | 未知曝光模式 |
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“EXPOSURE_MODE”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“EXPOSURE_MODE”
输入要设置的数值
选择参数类型“1”
成功返回设置值
相机感光度
ISO
设置
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:E_DjiCameraManagerISO
| 枚举值 | 值 | 说明 |
|---|---|---|
| DJI_CAMERA_MANAGER_ISO_AUTO | 0x00 | 自动 ISO |
| DJI_CAMERA_MANAGER_ISO_100 | 0x03 | ISO 100 |
| DJI_CAMERA_MANAGER_ISO_200 | 0x04 | ISO 200 |
| DJI_CAMERA_MANAGER_ISO_400 | 0x05 | ISO 400 |
| DJI_CAMERA_MANAGER_ISO_800 | 0x06 | ISO 800 |
| DJI_CAMERA_MANAGER_ISO_1600 | 0x07 | ISO 1600 |
| DJI_CAMERA_MANAGER_ISO_3200 | 0x08 | ISO 3200 |
| DJI_CAMERA_MANAGER_ISO_6400 | 0x09 | ISO 6400 |
| DJI_CAMERA_MANAGER_ISO_12800 | 0x0A | ISO 12800 |
| DJI_CAMERA_MANAGER_ISO_25600 | 0x0B | ISO 25600 |
| DJI_CAMERA_MANAGER_ISO_FIXED | 0xFF | 固定 ISO(由相机固件决定) |
注:该功能仅在相机曝光模式设置为“手动(Exposure_Manual)时可用。
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“ISO”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“ISO”
输入要设置的数值
选择参数类型“1”
成功返回设置值
快门速度
SHUTTER_SPEED
设置
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:
| 名称 | 枚举值 | 说明 |
|---|---|---|
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_8000 | 0 | 1/8000 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_6400 | 1 | 1/6400 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_6000 | 2 | 1/6000 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_5000 | 3 | 1/5000 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_4000 | 4 | 1/4000 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_3200 | 5 | 1/3200 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_3000 | 6 | 1/3000 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_2500 | 7 | 1/2500 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_2000 | 8 | 1/2000 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_1600 | 9 | 1/1600 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_1500 | 10 | 1/1500 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_1250 | 11 | 1/1250 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_1000 | 12 | 1/1000 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_800 | 13 | 1/800 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_725 | 14 | 1/725 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_640 | 15 | 1/640 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_500 | 16 | 1/500 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_400 | 17 | 1/400 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_350 | 18 | 1/350 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_320 | 19 | 1/320 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_250 | 20 | 1/250 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_240 | 21 | 1/240 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_200 | 22 | 1/200 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_180 | 23 | 1/180 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_160 | 24 | 1/160 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_125 | 25 | 1/125 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_120 | 26 | 1/120 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_100 | 27 | 1/100 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_90 | 28 | 1/90 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_80 | 29 | 1/80 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_60 | 30 | 1/60 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_50 | 31 | 1/50 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_40 | 32 | 1/40 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_30 | 33 | 1/30 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_25 | 34 | 1/25 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_20 | 35 | 1/20 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_15 | 36 | 1/15 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_12DOT5 | 37 | 1/12.5 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_10 | 38 | 1/10 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_8 | 39 | 1/8 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_6DOT25 | 40 | 1/6.25 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_5 | 41 | 1/5 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_4 | 42 | 1/4 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_3 | 43 | 1/3 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_2DOT5 | 44 | 1/2.5 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_2 | 45 | 1/2 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_1DOT67 | 46 | 1/1.67 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1_1DOT25 | 47 | 1/1.25 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1 | 48 | 1.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1DOT3 | 49 | 1.3 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_1DOT6 | 50 | 1.6 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_2 | 51 | 2.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_2DOT5 | 52 | 2.5 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_3 | 53 | 3.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_3DOT2 | 54 | 3.2 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_4 | 55 | 4.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_5 | 56 | 5.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_6 | 57 | 6.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_7 | 58 | 7.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_8 | 59 | 8.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_9 | 60 | 9.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_10 | 61 | 10.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_13 | 62 | 13.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_15 | 63 | 15.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_20 | 64 | 20.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_25 | 65 | 25.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_30 | 66 | 30.0 s |
| DJI_CAMERA_MANAGER_SHUTTER_SPEED_UNKNOWN | 0xFF | 未知快门速度 |
注意:该功能仅在相机曝光模式设置为“手动(Exposure_Manual)”或“快门优先(SHUTTER_PRIORITY)”时可用。
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“SHUTTER_SPEED”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“SHUTTER_SPEED”
输入要设置的数值
选择参数类型“1”
成功返回设置值
曝光补偿值
EXPOSURE_COMP
设置
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:
| 名称 | 枚举值 | 说明 |
|---|---|---|
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_5_0 | 1 | -5.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_4_7 | 2 | -4.7 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_4_3 | 3 | -4.3 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_4_0 | 4 | -4.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_3_7 | 5 | -3.7 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_3_3 | 6 | -3.3 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_3_0 | 7 | -3.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_2_7 | 8 | -2.7 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_2_3 | 9 | -2.3 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_2_0 | 10 | -2.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_1_7 | 11 | -1.7 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_1_3 | 12 | -1.3 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_1_0 | 13 | -1.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_0_7 | 14 | -0.7 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_0_3 | 15 | -0.3 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_N_0_0 | 16 | 0.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_0_3 | 17 | +0.3 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_0_7 | 18 | +0.7 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_1_0 | 19 | +1.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_1_3 | 20 | +1.3 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_1_7 | 21 | +1.7 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_2_0 | 22 | +2.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_2_3 | 23 | +2.3 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_2_7 | 24 | +2.7 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_3_0 | 25 | +3.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_3_3 | 26 | +3.3 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_3_7 | 27 | +3.7 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_4_0 | 28 | +4.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_4_3 | 29 | +4.3 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_4_7 | 30 | +4.7 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_P_5_0 | 31 | +5.0 EV 曝光补偿 |
| DJI_CAMERA_MANAGER_EXPOSURE_COMPENSATION_FIXED | 0xFF | 固定曝光补偿(由相机固件锁定) |
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“EXPOSURE_COMP”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“EXPOSURE_COMP”
输入要设置的数值
选择参数类型“1”
成功返回设置值
曝光锁定是否使能
AE_LOCK
设置
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:
| 类型 | 枚举值 | 说明 |
|---|---|---|
| bool | 0 | 禁用 曝光锁定 |
| bool | 1 | 启用 曝光锁定 |
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“AE_LOCK”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“AE_LOCK”
输入要设置的参数
选择参数类型“1”
成功返回设置值
开始录像
MAV_CMD_VIDEO_START_CAPTURE 2500
返回command = MAV_CMD_REQUEST_MESSAGE
返回值 param1 2500 执行命令id
返回值 param2 0或1 是否执行成功
返回值 param3 失败代码
0 无异常
1 无法初始化相机控制
2 设置相机为录像模式
3 获取相机版本失败
4 相机录像执行失败
示例
import time
from pymavlink import mavutil
timestamp = int(time.time() * 1000)
millis = int(time.time() * 1000)
master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()
boot_time = time.time()
def videoStart():
master.mav.command_long_send(
master.target_system,
master.target_component,
mavutil.mavlink.MAV_CMD_VIDEO_START_CAPTURE,
0,
0, 0, 0, 0, 0, 0, 0
)
videoStart()执行示例
结果
具体录像行为相关信息需用遥控器查看
MAV_CMD_VIDEO_STOP_CAPTURE 2501
返回command = MAV_CMD_REQUEST_MESSAGE
返回值 param1 2501 执行命令id
返回值 param2 0或1 是否执行成功
返回值 param3 失败代码
0 无异常
1 无法初始化相机控制
2 设置相机为录像模式
3 获取相机版本失败
4 相机录像执行失败
示例
import time
from pymavlink import mavutil
timestamp = int(time.time() * 1000)
millis = int(time.time() * 1000)
master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()
boot_time = time.time()
def videoStop():
master.mav.command_long_send(
master.target_system,
master.target_component,
mavutil.mavlink.MAV_CMD_VIDEO_STOP_CAPTURE,
0,
0, 0, 0, 0, 0, 0, 0
)
videoStop()执行示例
结果
具体录像行为相关信息需用遥控器查看
获取相机流媒体数据源范围
STRM_SRC_RNG
参数:
| 字段 | 类型 | 说明 |
|---|---|---|
| size | uint8_t | 有效列表长度(最大16) |
| value | union | 枚举取值列表(根据具体类型选择其中一个数组) |
| minValue | uint32_t | 数值范围最小值 |
| maxValue | uint32_t | 数值范围最大值 |
value(联合体说明)
该字段为联合体,同一时间仅有一个数组有效:
| 字段 | 类型 | 说明 |
|---|---|---|
| size | uint8_t | 有效列表长度(最大16) |
| photoStorageFormat | E_DjiCameraManagerPhotoStorageFormat[16] | 照片存储格式枚举列表 |
| videoStorageFormat | E_DjiCameraManagerVideoStorageFormat[16] | 视频存储格式枚举列表 |
| photoRatioFormat | E_DjiCameraManagerPhotoRatio[16] | 照片比例枚举列表 |
| streamSource | E_DjiCameraManagerStreamSource[16] | 视频流源枚举列表 |
| streamStorage | E_DjiCameraManagerStreamStorage[16] | 视频存储介质枚举列表 |
| nightSceneMode | E_DjiCameraManagerNightSceneMode[16] | 夜景模式枚举列表 |
| meteringMode | E_DjiCameraManagerMeteringMode[16] | 测光模式枚举列表 |
| minValue | uint32_t | 数值范围最小值(适用于数值型参数) |
| maxValue | uint32_t | 数值范围最大值(适用于数值型参数) |
示例
import time
import sys
import struct
import ctypes
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
# ============ 结构体打包函数 ============
def pack_uint8_xy(x, y):
"""打包 {uint8_t x; uint8_t y} 结构体 (2字节)"""
return struct.pack('<BB', x, y)
def pack_float32_xy(x, y):
"""打包 {float32 x; float32 y} 结构体 (8字节)"""
return struct.pack('<ff', x, y)
def pack_float32_lxlyrxry(LX, LY, RX, RY):
"""打包 {float32 LX; float32 LY; float32 RX; float32 RY;} 结构体 (16字节)"""
return struct.pack('<ffff', LX, LY, RX, RY)
def pack_ering_mode(size, eringMode, minValue, maxValue):
"""打包 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;} 结构体 (25字节)
Args:
size: uint8_t, eringMode数组的有效长度
eringMode: list of int (0-255), 16个uint8_t值
minValue: uint32_t, 最小值
maxValue: uint32_t, 最大值
Returns:
bytes: 打包后的二进制数据
"""
# 确保eringMode是16字节
eringMode_bytes = bytearray(16)
for i in range(min(len(eringMode), 16)):
eringMode_bytes[i] = eringMode[i] & 0xFF
return struct.pack('<B16sII', size, eringMode_bytes, minValue, maxValue)
def format_param_value(param_value):
"""格式化128字节参数值(接受二进制输入)"""
if isinstance(param_value, str):
# 如果是十六进制字符串(如 "48656c6c6f"),先转换为字节
if param_value.startswith('0x') or all(c in '0123456789abcdefABCDEF' for c in param_value.replace(' ', '')):
hex_str = param_value.replace('0x', '').replace(' ', '')
if len(hex_str) % 2 == 0:
param_value = bytes.fromhex(hex_str)
else:
raise ValueError("Invalid hex string: must have even number of characters")
else:
# 普通字符串转字节
param_value = param_value.encode('utf-8')
if len(param_value) > 128:
raise ValueError("param_value cannot exceed 128 bytes")
param_value_bytes = bytearray(128)
param_value_bytes[:len(param_value)] = param_value
return param_value_bytes
def hex_dump(data, bytes_per_line=16):
"""格式化二进制数据为 hex + ASCII 展示(类 hexdump 风格)
Returns:
str: 格式化后的多行字符串
"""
lines = []
for i in range(0, len(data), bytes_per_line):
chunk = data[i:i + bytes_per_line]
hex_part = ' '.join(f'{b:02X}' for b in chunk)
ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
lines.append(f" {i:04X} {hex_part:<{bytes_per_line*3}} {ascii_part}")
return '\n'.join(lines)
def send_param_ext_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_ext_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_EXT_REQUEST_READ for {target}")
def send_param_ext_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_ext_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_EXT_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def send_param_ext_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_ext_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
format_param_value(param_value),
param_type
)
print(f"📤 Sent PARAM_EXT_SET for '{param_id}' = '{param_value}' (type: {param_type})")
def receive_param_ext_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_EXT_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值(直接获取128字节原始数据)
param_value_raw = msg.param_value
# byte_pointer = (ctypes.c_ubyte * 128).from_buffer_copy(msg.param_value)
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
# param_value_bytes = bytes(byte_pointer)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00,还原为128字节
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "FLOAT",
10: "CHAR", 11: "CHAR", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_EXT_VALUE:")
print(f" 🆔 ID: {param_id}")
print(msg.get_msgbuf().hex())
print(f" 🧾 Type: {param_type} | Data: {len(param_value_bytes)} bytes")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
print(hex_dump(param_value_bytes))
return {
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_ext_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_EXT_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value_raw = msg.param_value
# 直接获取128字节原始数据
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE",
10: "CHAR", 11: "BOOL", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} [{param_type}][{len(param_value_bytes)}B]")
print(hex_dump(param_value_bytes))
received_params.append({
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def receive_param_ext_ack(vehicle, timeout=5.0):
"""接收参数设置确认"""
print(f"⏳ Waiting for PARAM_EXT_ACK ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_ACK', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_ACK received")
return None
param_id = msg.param_id
param_value = msg.param_value
# 结果代码映射
result_codes = {
0: "✅ PARAM_ACK_ACCEPTED",
1: "⚠️ PARAM_ACK_VALUE_UNSUPPORTED",
2: "❌ PARAM_ACK_FAILED",
3: "🔄 PARAM_ACK_IN_PROGRESS"
}
result = result_codes.get(msg.param_result, f"❓ UNKNOWN({msg.param_result})")
print("\n📥 Received PARAM_EXT_ACK:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 📌 Result: {result}")
return {
'id': param_id,
'value': param_value,
'result': result,
'result_code': msg.param_result
}
def main():
print("🚀 Starting MAVLink PARAM_EXT Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Read parameter value (by ID or index)")
print("2. Set parameter value (raw/hex/string)")
print("3. Request all parameters (PARAM_EXT_REQUEST_LIST)")
print("4. Set struct: uint8_xy {x, y}")
print("5. Set struct: float32_xy {x, y}")
print("6. Set struct: float32_lxlyrxry {LX, LY, RX, RY}")
print("7. Set struct: ering_mode {size, eringMode[16], minValue, maxValue}")
choice = input("\nSelect operation [1-7]: ").strip()
if choice == '1':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_ext_request_read(vehicle, param_id=param_id)
receive_param_ext_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_ext_request_read(vehicle, param_index=param_index)
receive_param_ext_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '2':
param_id = input("Enter parameter ID to set: ").strip()
print("\nEnter parameter value (supports):")
print(" - Plain string (e.g., hello)")
print(" - Hex string (e.g., 48656c6c6f or 0x48656c6c6f)")
print(" - Raw bytes (e.g., b'\\x48\\x65\\x6c\\x6c\\x6f')")
param_value_input = input("Enter value: ").strip()
# 解析输入值
if param_value_input.startswith("b'") or param_value_input.startswith('b"'):
# Python字节字面量格式
param_value = eval(param_value_input)
elif param_value_input.startswith('0x') or all(c in '0123456789abcdefABCDEF ' for c in param_value_input):
# 十六进制格式
hex_str = param_value_input.replace('0x', '').replace(' ', '')
try:
param_value = bytes.fromhex(hex_str)
except ValueError:
param_value = param_value_input.encode('utf-8')
else:
# 默认作为字符串处理
param_value = param_value_input.encode('utf-8')
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 13 = STRING")
print(" 10 = CHAR (raw bytes)")
print(" Other types require specific binary formats")
param_type = int(input("Enter type number [8]: ") or 8)
send_param_ext_set(vehicle, param_id, param_value, param_type)
receive_param_ext_ack(vehicle)
elif choice == '4':
"""设置 uint8_xy 结构体 {uint8_t x; uint8_t y}"""
param_id = input("Enter parameter ID: ").strip()
x = int(input("Enter x (0-255): "))
y = int(input("Enter y (0-255): "))
param_value = pack_uint8_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '5':
"""设置 float32_xy 结构体 {float32 x; float32 y}"""
param_id = input("Enter parameter ID: ").strip()
x = float(input("Enter x: "))
y = float(input("Enter y: "))
param_value = pack_float32_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '6':
"""设置 float32_lxlyrxry 结构体 {float32 LX; float32 LY; float32 RX; float32 RY;}"""
param_id = input("Enter parameter ID: ").strip()
LX = float(input("Enter LX: "))
LY = float(input("Enter LY: "))
RX = float(input("Enter RX: "))
RY = float(input("Enter RY: "))
param_value = pack_float32_lxlyrxry(LX, LY, RX, RY)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '7':
"""设置 ering_mode 结构体 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;}"""
param_id = input("Enter parameter ID: ").strip()
size = int(input("Enter size (0-16): "))
eringMode = []
print("Enter 16 eringMode values (0-255), one per line:")
for i in range(16):
val = int(input(f" eringMode[{i}]: "))
eringMode.append(val)
minValue = int(input("Enter minValue: "))
maxValue = int(input("Enter maxValue: "))
param_value = pack_ering_mode(size, eringMode, minValue, maxValue)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_ext_request_list(vehicle)
all_params = receive_all_param_ext_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“1”选择读取参数
输入对应参数“STRM_SRC_RNG”
成功返回该参数对应的数值
获取相机流媒体数据源范围
STRM_SRC_RNG
获取
mavlink消息:
接收PARAM_EXT_REQUEST_READ
发送PARAM_EXT_VALUE
| 字段 | 类型 | 说明 |
|---|---|---|
| size | uint8_t | 有效列表长度(最大16) |
| minValue | uint32_t | 数值范围最小值 |
| maxValue | uint32_t | 数值范围最大值 |
该字段为联合体,同一时间仅有一个数组有效:
| 字段 | 类型 | 说明 |
|---|---|---|
| photoStorageFormat | E_DjiCameraManagerPhotoStorageFormat[16] | 照片存储格式枚举列表 |
| videoStorageFormat | E_DjiCameraManagerVideoStorageFormat[16] | 视频存储格式枚举列表 |
| photoRatioFormat | E_DjiCameraManagerPhotoRatio[16] | 照片比例枚举列表 |
| streamSource | E_DjiCameraManagerStreamSource[16] | 视频流源枚举列表 |
| streamStorage | E_DjiCameraManagerStreamStorage[16] | 视频存储介质枚举列表 |
| nightSceneMode | E_DjiCameraManagerNightSceneMode[16] | 夜景模式枚举列表 |
| meteringMode | E_DjiCameraManagerMeteringMode[16] | 测光模式枚举列表 |
示例
import time
import sys
import struct
import ctypes
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
# ============ 结构体打包函数 ============
def pack_uint8_xy(x, y):
"""打包 {uint8_t x; uint8_t y} 结构体 (2字节)"""
return struct.pack('<BB', x, y)
def pack_float32_xy(x, y):
"""打包 {float32 x; float32 y} 结构体 (8字节)"""
return struct.pack('<ff', x, y)
def pack_float32_lxlyrxry(LX, LY, RX, RY):
"""打包 {float32 LX; float32 LY; float32 RX; float32 RY;} 结构体 (16字节)"""
return struct.pack('<ffff', LX, LY, RX, RY)
def pack_ering_mode(size, eringMode, minValue, maxValue):
"""打包 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;} 结构体 (25字节)
Args:
size: uint8_t, eringMode数组的有效长度
eringMode: list of int (0-255), 16个uint8_t值
minValue: uint32_t, 最小值
maxValue: uint32_t, 最大值
Returns:
bytes: 打包后的二进制数据
"""
# 确保eringMode是16字节
eringMode_bytes = bytearray(16)
for i in range(min(len(eringMode), 16)):
eringMode_bytes[i] = eringMode[i] & 0xFF
return struct.pack('<B16sII', size, eringMode_bytes, minValue, maxValue)
def format_param_value(param_value):
"""格式化128字节参数值(接受二进制输入)"""
if isinstance(param_value, str):
# 如果是十六进制字符串(如 "48656c6c6f"),先转换为字节
if param_value.startswith('0x') or all(c in '0123456789abcdefABCDEF' for c in param_value.replace(' ', '')):
hex_str = param_value.replace('0x', '').replace(' ', '')
if len(hex_str) % 2 == 0:
param_value = bytes.fromhex(hex_str)
else:
raise ValueError("Invalid hex string: must have even number of characters")
else:
# 普通字符串转字节
param_value = param_value.encode('utf-8')
if len(param_value) > 128:
raise ValueError("param_value cannot exceed 128 bytes")
param_value_bytes = bytearray(128)
param_value_bytes[:len(param_value)] = param_value
return param_value_bytes
def hex_dump(data, bytes_per_line=16):
"""格式化二进制数据为 hex + ASCII 展示(类 hexdump 风格)
Returns:
str: 格式化后的多行字符串
"""
lines = []
for i in range(0, len(data), bytes_per_line):
chunk = data[i:i + bytes_per_line]
hex_part = ' '.join(f'{b:02X}' for b in chunk)
ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
lines.append(f" {i:04X} {hex_part:<{bytes_per_line*3}} {ascii_part}")
return '\n'.join(lines)
def send_param_ext_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_ext_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_EXT_REQUEST_READ for {target}")
def send_param_ext_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_ext_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_EXT_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def send_param_ext_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_ext_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
format_param_value(param_value),
param_type
)
print(f"📤 Sent PARAM_EXT_SET for '{param_id}' = '{param_value}' (type: {param_type})")
def receive_param_ext_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_EXT_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值(直接获取128字节原始数据)
param_value_raw = msg.param_value
# byte_pointer = (ctypes.c_ubyte * 128).from_buffer_copy(msg.param_value)
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
# param_value_bytes = bytes(byte_pointer)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00,还原为128字节
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "FLOAT",
10: "CHAR", 11: "CHAR", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_EXT_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🧾 Type: {param_type} | Data: {len(param_value_bytes)} bytes")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
print(hex_dump(param_value_bytes))
return {
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_ext_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_EXT_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value_raw = msg.param_value
# 直接获取128字节原始数据
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE",
10: "CHAR", 11: "BOOL", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} [{param_type}][{len(param_value_bytes)}B]")
print(hex_dump(param_value_bytes))
received_params.append({
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def receive_param_ext_ack(vehicle, timeout=5.0):
"""接收参数设置确认"""
print(f"⏳ Waiting for PARAM_EXT_ACK ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_ACK', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_ACK received")
return None
param_id = msg.param_id
param_value = msg.param_value
# 结果代码映射
result_codes = {
0: "✅ PARAM_ACK_ACCEPTED",
1: "⚠️ PARAM_ACK_VALUE_UNSUPPORTED",
2: "❌ PARAM_ACK_FAILED",
3: "🔄 PARAM_ACK_IN_PROGRESS"
}
result = result_codes.get(msg.param_result, f"❓ UNKNOWN({msg.param_result})")
print("\n📥 Received PARAM_EXT_ACK:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 📌 Result: {result}")
return {
'id': param_id,
'value': param_value,
'result': result,
'result_code': msg.param_result
}
def main():
print("🚀 Starting MAVLink PARAM_EXT Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Read parameter value (by ID or index)")
print("2. Set parameter value (raw/hex/string)")
print("3. Request all parameters (PARAM_EXT_REQUEST_LIST)")
print("4. Set struct: uint8_xy {x, y}")
print("5. Set struct: float32_xy {x, y}")
print("6. Set struct: float32_lxlyrxry {LX, LY, RX, RY}")
print("7. Set struct: ering_mode {size, eringMode[16], minValue, maxValue}")
choice = input("\nSelect operation [1-7]: ").strip()
if choice == '1':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_ext_request_read(vehicle, param_id=param_id)
receive_param_ext_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_ext_request_read(vehicle, param_index=param_index)
receive_param_ext_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '2':
param_id = input("Enter parameter ID to set: ").strip()
print("\nEnter parameter value (supports):")
print(" - Plain string (e.g., hello)")
print(" - Hex string (e.g., 48656c6c6f or 0x48656c6c6f)")
print(" - Raw bytes (e.g., b'\\x48\\x65\\x6c\\x6c\\x6f')")
param_value_input = input("Enter value: ").strip()
# 解析输入值
if param_value_input.startswith("b'") or param_value_input.startswith('b"'):
# Python字节字面量格式
param_value = eval(param_value_input)
elif param_value_input.startswith('0x') or all(c in '0123456789abcdefABCDEF ' for c in param_value_input):
# 十六进制格式
hex_str = param_value_input.replace('0x', '').replace(' ', '')
try:
param_value = bytes.fromhex(hex_str)
except ValueError:
param_value = param_value_input.encode('utf-8')
else:
# 默认作为字符串处理
param_value = param_value_input.encode('utf-8')
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 13 = STRING")
print(" 10 = CHAR (raw bytes)")
print(" Other types require specific binary formats")
param_type = int(input("Enter type number [8]: ") or 8)
send_param_ext_set(vehicle, param_id, param_value, param_type)
receive_param_ext_ack(vehicle)
elif choice == '4':
"""设置 uint8_xy 结构体 {uint8_t x; uint8_t y}"""
param_id = input("Enter parameter ID: ").strip()
x = int(input("Enter x (0-255): "))
y = int(input("Enter y (0-255): "))
param_value = pack_uint8_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '5':
"""设置 float32_xy 结构体 {float32 x; float32 y}"""
param_id = input("Enter parameter ID: ").strip()
x = float(input("Enter x: "))
y = float(input("Enter y: "))
param_value = pack_float32_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '6':
"""设置 float32_lxlyrxry 结构体 {float32 LX; float32 LY; float32 RX; float32 RY;}"""
param_id = input("Enter parameter ID: ").strip()
LX = float(input("Enter LX: "))
LY = float(input("Enter LY: "))
RX = float(input("Enter RX: "))
RY = float(input("Enter RY: "))
param_value = pack_float32_lxlyrxry(LX, LY, RX, RY)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '7':
"""设置 ering_mode 结构体 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;}"""
param_id = input("Enter parameter ID: ").strip()
size = int(input("Enter size (0-16): "))
eringMode = []
print("Enter 16 eringMode values (0-255), one per line:")
for i in range(16):
val = int(input(f" eringMode[{i}]: "))
eringMode.append(val)
minValue = int(input("Enter minValue: "))
maxValue = int(input("Enter maxValue: "))
param_value = pack_ering_mode(size, eringMode, minValue, maxValue)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_ext_request_list(vehicle)
all_params = receive_all_param_ext_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“STRM_SRC_RNG”
成功返回完整消息
相机流媒体数据源
STREAM_SOURCE
设置
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:
| 名称 | 枚举值 | 说明 |
|---|---|---|
| DJI_CAMERA_MANAGER_SOURCE_DEFAULT_CAM | 0x0 | 默认相机源 |
| DJI_CAMERA_MANAGER_SOURCE_WIDE_CAM | 0x1 | 广角相机 |
| DJI_CAMERA_MANAGER_SOURCE_ZOOM_CAM | 0x2 | 变焦相机 |
| DJI_CAMERA_MANAGER_SOURCE_IR_CAM | 0x3 | 红外相机 |
| DJI_CAMERA_MANAGER_SOURCE_VISIBLE_CAM | 0x7 | 可见光相机 |
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“STREAM_SOURCE”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“STREAM_SOURCE”
输入要设置的数值
选择参数类型“1”
成功返回设置值
获取照片存储格式范围
PHT_STORE_FMTRNG
mavlink消息:
接收PARAM_EXT_REQUEST_READ
发送PARAM_EXT_VALUE
参数:
| 字段 | 类型 | 说明 |
|---|---|---|
| size | uint8_t | 有效列表长度(最大16) |
| minValue | uint32_t | 数值范围最小值 |
| maxValue | uint32_t | 数值范围最大值 |
union(同一时刻仅生效一个字段)
| 字段 | 类型 | 说明 |
|---|---|---|
| photoStorageFormat | E_DjiCameraManagerPhotoStorageFormat[16] | 照片存储格式枚举列表 |
| videoStorageFormat | E_DjiCameraManagerVideoStorageFormat[16] | 视频存储格式枚举列表 |
| photoRatioFormat | E_DjiCameraManagerPhotoRatio[16] | 照片比例枚举列表 |
| streamSource | E_DjiCameraManagerStreamSource[16] | 视频流源枚举列表 |
| streamStorage | E_DjiCameraManagerStreamStorage[16] | 视频存储介质枚举列表 |
| nightSceneMode | E_DjiCameraManagerNightSceneMode[16] | 夜景模式枚举列表 |
| meteringMode | E_DjiCameraManagerMeteringMode[16] | 测光模式枚举列表 |
示例
import time
import sys
import struct
import ctypes
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
# ============ 结构体打包函数 ============
def pack_uint8_xy(x, y):
"""打包 {uint8_t x; uint8_t y} 结构体 (2字节)"""
return struct.pack('<BB', x, y)
def pack_float32_xy(x, y):
"""打包 {float32 x; float32 y} 结构体 (8字节)"""
return struct.pack('<ff', x, y)
def pack_float32_lxlyrxry(LX, LY, RX, RY):
"""打包 {float32 LX; float32 LY; float32 RX; float32 RY;} 结构体 (16字节)"""
return struct.pack('<ffff', LX, LY, RX, RY)
def pack_ering_mode(size, eringMode, minValue, maxValue):
"""打包 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;} 结构体 (25字节)
Args:
size: uint8_t, eringMode数组的有效长度
eringMode: list of int (0-255), 16个uint8_t值
minValue: uint32_t, 最小值
maxValue: uint32_t, 最大值
Returns:
bytes: 打包后的二进制数据
"""
# 确保eringMode是16字节
eringMode_bytes = bytearray(16)
for i in range(min(len(eringMode), 16)):
eringMode_bytes[i] = eringMode[i] & 0xFF
return struct.pack('<B16sII', size, eringMode_bytes, minValue, maxValue)
def format_param_value(param_value):
"""格式化128字节参数值(接受二进制输入)"""
if isinstance(param_value, str):
# 如果是十六进制字符串(如 "48656c6c6f"),先转换为字节
if param_value.startswith('0x') or all(c in '0123456789abcdefABCDEF' for c in param_value.replace(' ', '')):
hex_str = param_value.replace('0x', '').replace(' ', '')
if len(hex_str) % 2 == 0:
param_value = bytes.fromhex(hex_str)
else:
raise ValueError("Invalid hex string: must have even number of characters")
else:
# 普通字符串转字节
param_value = param_value.encode('utf-8')
if len(param_value) > 128:
raise ValueError("param_value cannot exceed 128 bytes")
param_value_bytes = bytearray(128)
param_value_bytes[:len(param_value)] = param_value
return param_value_bytes
def hex_dump(data, bytes_per_line=16):
"""格式化二进制数据为 hex + ASCII 展示(类 hexdump 风格)
Returns:
str: 格式化后的多行字符串
"""
lines = []
for i in range(0, len(data), bytes_per_line):
chunk = data[i:i + bytes_per_line]
hex_part = ' '.join(f'{b:02X}' for b in chunk)
ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
lines.append(f" {i:04X} {hex_part:<{bytes_per_line*3}} {ascii_part}")
return '\n'.join(lines)
def send_param_ext_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_ext_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_EXT_REQUEST_READ for {target}")
def send_param_ext_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_ext_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_EXT_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def send_param_ext_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_ext_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
format_param_value(param_value),
param_type
)
print(f"📤 Sent PARAM_EXT_SET for '{param_id}' = '{param_value}' (type: {param_type})")
def receive_param_ext_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_EXT_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值(直接获取128字节原始数据)
param_value_raw = msg.param_value
# byte_pointer = (ctypes.c_ubyte * 128).from_buffer_copy(msg.param_value)
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
# param_value_bytes = bytes(byte_pointer)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00,还原为128字节
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "FLOAT",
10: "CHAR", 11: "CHAR", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_EXT_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🧾 Type: {param_type} | Data: {len(param_value_bytes)} bytes")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
print(hex_dump(param_value_bytes))
return {
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_ext_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_EXT_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value_raw = msg.param_value
# 直接获取128字节原始数据
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE",
10: "CHAR", 11: "BOOL", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} [{param_type}][{len(param_value_bytes)}B]")
print(hex_dump(param_value_bytes))
received_params.append({
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def receive_param_ext_ack(vehicle, timeout=5.0):
"""接收参数设置确认"""
print(f"⏳ Waiting for PARAM_EXT_ACK ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_ACK', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_ACK received")
return None
param_id = msg.param_id
param_value = msg.param_value
# 结果代码映射
result_codes = {
0: "✅ PARAM_ACK_ACCEPTED",
1: "⚠️ PARAM_ACK_VALUE_UNSUPPORTED",
2: "❌ PARAM_ACK_FAILED",
3: "🔄 PARAM_ACK_IN_PROGRESS"
}
result = result_codes.get(msg.param_result, f"❓ UNKNOWN({msg.param_result})")
print("\n📥 Received PARAM_EXT_ACK:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 📌 Result: {result}")
return {
'id': param_id,
'value': param_value,
'result': result,
'result_code': msg.param_result
}
def main():
print("🚀 Starting MAVLink PARAM_EXT Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Read parameter value (by ID or index)")
print("2. Set parameter value (raw/hex/string)")
print("3. Request all parameters (PARAM_EXT_REQUEST_LIST)")
print("4. Set struct: uint8_xy {x, y}")
print("5. Set struct: float32_xy {x, y}")
print("6. Set struct: float32_lxlyrxry {LX, LY, RX, RY}")
print("7. Set struct: ering_mode {size, eringMode[16], minValue, maxValue}")
choice = input("\nSelect operation [1-7]: ").strip()
if choice == '1':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_ext_request_read(vehicle, param_id=param_id)
receive_param_ext_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_ext_request_read(vehicle, param_index=param_index)
receive_param_ext_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '2':
param_id = input("Enter parameter ID to set: ").strip()
print("\nEnter parameter value (supports):")
print(" - Plain string (e.g., hello)")
print(" - Hex string (e.g., 48656c6c6f or 0x48656c6c6f)")
print(" - Raw bytes (e.g., b'\\x48\\x65\\x6c\\x6c\\x6f')")
param_value_input = input("Enter value: ").strip()
# 解析输入值
if param_value_input.startswith("b'") or param_value_input.startswith('b"'):
# Python字节字面量格式
param_value = eval(param_value_input)
elif param_value_input.startswith('0x') or all(c in '0123456789abcdefABCDEF ' for c in param_value_input):
# 十六进制格式
hex_str = param_value_input.replace('0x', '').replace(' ', '')
try:
param_value = bytes.fromhex(hex_str)
except ValueError:
param_value = param_value_input.encode('utf-8')
else:
# 默认作为字符串处理
param_value = param_value_input.encode('utf-8')
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 13 = STRING")
print(" 10 = CHAR (raw bytes)")
print(" Other types require specific binary formats")
param_type = int(input("Enter type number [8]: ") or 8)
send_param_ext_set(vehicle, param_id, param_value, param_type)
receive_param_ext_ack(vehicle)
elif choice == '4':
"""设置 uint8_xy 结构体 {uint8_t x; uint8_t y}"""
param_id = input("Enter parameter ID: ").strip()
x = int(input("Enter x (0-255): "))
y = int(input("Enter y (0-255): "))
param_value = pack_uint8_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '5':
"""设置 float32_xy 结构体 {float32 x; float32 y}"""
param_id = input("Enter parameter ID: ").strip()
x = float(input("Enter x: "))
y = float(input("Enter y: "))
param_value = pack_float32_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '6':
"""设置 float32_lxlyrxry 结构体 {float32 LX; float32 LY; float32 RX; float32 RY;}"""
param_id = input("Enter parameter ID: ").strip()
LX = float(input("Enter LX: "))
LY = float(input("Enter LY: "))
RX = float(input("Enter RX: "))
RY = float(input("Enter RY: "))
param_value = pack_float32_lxlyrxry(LX, LY, RX, RY)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '7':
"""设置 ering_mode 结构体 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;}"""
param_id = input("Enter parameter ID: ").strip()
size = int(input("Enter size (0-16): "))
eringMode = []
print("Enter 16 eringMode values (0-255), one per line:")
for i in range(16):
val = int(input(f" eringMode[{i}]: "))
eringMode.append(val)
minValue = int(input("Enter minValue: "))
maxValue = int(input("Enter maxValue: "))
param_value = pack_ering_mode(size, eringMode, minValue, maxValue)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_ext_request_list(vehicle)
all_params = receive_all_param_ext_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
运行后,成功建立连接
输入“1”选择读取参数
输入对应参数“PHT_STORE_FMTRNG”
成功返回完整消息
照片存储格式
PHOTO_FORMAT
设置
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:
| 名称 | 枚举值 | 说明 |
|---|---|---|
| DJI_CAMERA_MANAGER_PHOTO_STORAGE_FORMAT_RAW | 0 | RAW格式照片 |
| DJI_CAMERA_MANAGER_PHOTO_STORAGE_FORMAT_JPEG | 1 | JPEG格式照片 |
| DJI_CAMERA_MANAGER_PHOTO_STORAGE_FORMAT_RAW_JPEG | 2 | RAW + JPEG双格式 |
| DJI_CAMERA_MANAGER_PHOTO_STORAGE_FORMAT_YUV | 3 | YUV格式图像 |
| DJI_CAMERA_MANAGER_PHOTO_STORAGE_FORMAT_RJPEG | 7 | Radiometric JPEG) |
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“PHOTO_FORMAT”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“PHOTO_FORMAT”
选择参数类型“1”
发送成功返回设置值
获取视频存储格式范围
VID_FMT_RNG
mavlink消息:
接收PARAM_EXT_REQUEST_READ
发送PARAM_EXT_VALUE
参数:
| 字段 | 类型 | 说明 |
|---|---|---|
| size | uint8_t | 有效列表长度(最大16) |
| minValue | uint32_t | 数值范围最小值 |
| maxValue | uint32_t | 数值范围最大值 |
union(同一时刻仅生效一个字段)
| 字段 | 类型 | 说明 |
|---|---|---|
| photoStorageFormat | E_DjiCameraManagerPhotoStorageFormat[16] | 照片存储格式枚举列表 |
| videoStorageFormat | E_DjiCameraManagerVideoStorageFormat[16] | 视频存储格式枚举列表 |
| photoRatioFormat | E_DjiCameraManagerPhotoRatio[16] | 照片比例枚举列表 |
| streamSource | E_DjiCameraManagerStreamSource[16] | 视频流源枚举列表 |
| streamStorage | E_DjiCameraManagerStreamStorage[16] | 视频存储介质枚举列表 |
| nightSceneMode | E_DjiCameraManagerNightSceneMode[16] | 夜景模式枚举列表 |
| meteringMode | E_DjiCameraManagerMeteringMode[16] | 测光模式枚举列表 |
示例
import time
import sys
import struct
import ctypes
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
# ============ 结构体打包函数 ============
def pack_uint8_xy(x, y):
"""打包 {uint8_t x; uint8_t y} 结构体 (2字节)"""
return struct.pack('<BB', x, y)
def pack_float32_xy(x, y):
"""打包 {float32 x; float32 y} 结构体 (8字节)"""
return struct.pack('<ff', x, y)
def pack_float32_lxlyrxry(LX, LY, RX, RY):
"""打包 {float32 LX; float32 LY; float32 RX; float32 RY;} 结构体 (16字节)"""
return struct.pack('<ffff', LX, LY, RX, RY)
def pack_ering_mode(size, eringMode, minValue, maxValue):
"""打包 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;} 结构体 (25字节)
Args:
size: uint8_t, eringMode数组的有效长度
eringMode: list of int (0-255), 16个uint8_t值
minValue: uint32_t, 最小值
maxValue: uint32_t, 最大值
Returns:
bytes: 打包后的二进制数据
"""
# 确保eringMode是16字节
eringMode_bytes = bytearray(16)
for i in range(min(len(eringMode), 16)):
eringMode_bytes[i] = eringMode[i] & 0xFF
return struct.pack('<B16sII', size, eringMode_bytes, minValue, maxValue)
def format_param_value(param_value):
"""格式化128字节参数值(接受二进制输入)"""
if isinstance(param_value, str):
# 如果是十六进制字符串(如 "48656c6c6f"),先转换为字节
if param_value.startswith('0x') or all(c in '0123456789abcdefABCDEF' for c in param_value.replace(' ', '')):
hex_str = param_value.replace('0x', '').replace(' ', '')
if len(hex_str) % 2 == 0:
param_value = bytes.fromhex(hex_str)
else:
raise ValueError("Invalid hex string: must have even number of characters")
else:
# 普通字符串转字节
param_value = param_value.encode('utf-8')
if len(param_value) > 128:
raise ValueError("param_value cannot exceed 128 bytes")
param_value_bytes = bytearray(128)
param_value_bytes[:len(param_value)] = param_value
return param_value_bytes
def hex_dump(data, bytes_per_line=16):
"""格式化二进制数据为 hex + ASCII 展示(类 hexdump 风格)
Returns:
str: 格式化后的多行字符串
"""
lines = []
for i in range(0, len(data), bytes_per_line):
chunk = data[i:i + bytes_per_line]
hex_part = ' '.join(f'{b:02X}' for b in chunk)
ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
lines.append(f" {i:04X} {hex_part:<{bytes_per_line*3}} {ascii_part}")
return '\n'.join(lines)
def send_param_ext_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_ext_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_EXT_REQUEST_READ for {target}")
def send_param_ext_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_ext_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_EXT_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def send_param_ext_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_ext_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
format_param_value(param_value),
param_type
)
print(f"📤 Sent PARAM_EXT_SET for '{param_id}' = '{param_value}' (type: {param_type})")
def receive_param_ext_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_EXT_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值(直接获取128字节原始数据)
param_value_raw = msg.param_value
# byte_pointer = (ctypes.c_ubyte * 128).from_buffer_copy(msg.param_value)
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
# param_value_bytes = bytes(byte_pointer)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00,还原为128字节
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "FLOAT",
10: "CHAR", 11: "CHAR", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_EXT_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🧾 Type: {param_type} | Data: {len(param_value_bytes)} bytes")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
print(hex_dump(param_value_bytes))
return {
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_ext_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_EXT_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value_raw = msg.param_value
# 直接获取128字节原始数据
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE",
10: "CHAR", 11: "BOOL", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} [{param_type}][{len(param_value_bytes)}B]")
print(hex_dump(param_value_bytes))
received_params.append({
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def receive_param_ext_ack(vehicle, timeout=5.0):
"""接收参数设置确认"""
print(f"⏳ Waiting for PARAM_EXT_ACK ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_ACK', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_ACK received")
return None
param_id = msg.param_id
param_value = msg.param_value
# 结果代码映射
result_codes = {
0: "✅ PARAM_ACK_ACCEPTED",
1: "⚠️ PARAM_ACK_VALUE_UNSUPPORTED",
2: "❌ PARAM_ACK_FAILED",
3: "🔄 PARAM_ACK_IN_PROGRESS"
}
result = result_codes.get(msg.param_result, f"❓ UNKNOWN({msg.param_result})")
print("\n📥 Received PARAM_EXT_ACK:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 📌 Result: {result}")
return {
'id': param_id,
'value': param_value,
'result': result,
'result_code': msg.param_result
}
def main():
print("🚀 Starting MAVLink PARAM_EXT Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Read parameter value (by ID or index)")
print("2. Set parameter value (raw/hex/string)")
print("3. Request all parameters (PARAM_EXT_REQUEST_LIST)")
print("4. Set struct: uint8_xy {x, y}")
print("5. Set struct: float32_xy {x, y}")
print("6. Set struct: float32_lxlyrxry {LX, LY, RX, RY}")
print("7. Set struct: ering_mode {size, eringMode[16], minValue, maxValue}")
choice = input("\nSelect operation [1-7]: ").strip()
if choice == '1':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_ext_request_read(vehicle, param_id=param_id)
receive_param_ext_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_ext_request_read(vehicle, param_index=param_index)
receive_param_ext_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '2':
param_id = input("Enter parameter ID to set: ").strip()
print("\nEnter parameter value (supports):")
print(" - Plain string (e.g., hello)")
print(" - Hex string (e.g., 48656c6c6f or 0x48656c6c6f)")
print(" - Raw bytes (e.g., b'\\x48\\x65\\x6c\\x6c\\x6f')")
param_value_input = input("Enter value: ").strip()
# 解析输入值
if param_value_input.startswith("b'") or param_value_input.startswith('b"'):
# Python字节字面量格式
param_value = eval(param_value_input)
elif param_value_input.startswith('0x') or all(c in '0123456789abcdefABCDEF ' for c in param_value_input):
# 十六进制格式
hex_str = param_value_input.replace('0x', '').replace(' ', '')
try:
param_value = bytes.fromhex(hex_str)
except ValueError:
param_value = param_value_input.encode('utf-8')
else:
# 默认作为字符串处理
param_value = param_value_input.encode('utf-8')
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 13 = STRING")
print(" 10 = CHAR (raw bytes)")
print(" Other types require specific binary formats")
param_type = int(input("Enter type number [8]: ") or 8)
send_param_ext_set(vehicle, param_id, param_value, param_type)
receive_param_ext_ack(vehicle)
elif choice == '4':
"""设置 uint8_xy 结构体 {uint8_t x; uint8_t y}"""
param_id = input("Enter parameter ID: ").strip()
x = int(input("Enter x (0-255): "))
y = int(input("Enter y (0-255): "))
param_value = pack_uint8_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '5':
"""设置 float32_xy 结构体 {float32 x; float32 y}"""
param_id = input("Enter parameter ID: ").strip()
x = float(input("Enter x: "))
y = float(input("Enter y: "))
param_value = pack_float32_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '6':
"""设置 float32_lxlyrxry 结构体 {float32 LX; float32 LY; float32 RX; float32 RY;}"""
param_id = input("Enter parameter ID: ").strip()
LX = float(input("Enter LX: "))
LY = float(input("Enter LY: "))
RX = float(input("Enter RX: "))
RY = float(input("Enter RY: "))
param_value = pack_float32_lxlyrxry(LX, LY, RX, RY)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '7':
"""设置 ering_mode 结构体 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;}"""
param_id = input("Enter parameter ID: ").strip()
size = int(input("Enter size (0-16): "))
eringMode = []
print("Enter 16 eringMode values (0-255), one per line:")
for i in range(16):
val = int(input(f" eringMode[{i}]: "))
eringMode.append(val)
minValue = int(input("Enter minValue: "))
maxValue = int(input("Enter maxValue: "))
param_value = pack_ering_mode(size, eringMode, minValue, maxValue)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_ext_request_list(vehicle)
all_params = receive_all_param_ext_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“VID_FMT_RNG”
成功返回完整消息
视频存储格式
VID_STORE_FMT
设置
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:
| 名称 | 枚举值 | 说明 |
|---|---|---|
| DJI_CAMERA_MANAGER_VIDEO_STORAGE_FORMAT_MOV | 0 | MOV视频格式 |
| DJI_CAMERA_MANAGER_VIDEO_STORAGE_FORMAT_MP4 | 1 | MP4视频格式 |
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“VID_STORE_FMT”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“VID_STORE_FMT”
输入要设置的数值
选择参数类型“1”
发送成功返回设置值
获取照片比例范围
PHT_RATIO_RNG
mavlink消息:
接收PARAM_EXT_REQUEST_READ
发送PARAM_EXT_VALUE
参数:
| 字段 | 类型 | 说明 |
|---|---|---|
| size | uint8_t | 有效列表长度(最大16) |
| minValue | uint32_t | 数值范围最小值 |
| maxValue | uint32_t | 数值范围最大值 |
union(同一时刻仅生效一个字段)
| 字段 | 类型 | 说明 |
|---|---|---|
| photoStorageFormat | E_DjiCameraManagerPhotoStorageFormat[16] | 照片存储格式枚举列表 |
| videoStorageFormat | E_DjiCameraManagerVideoStorageFormat[16] | 视频存储格式枚举列表 |
| photoRatioFormat | E_DjiCameraManagerPhotoRatio[16] | 照片比例枚举列表 |
| streamSource | E_DjiCameraManagerStreamSource[16] | 视频流源枚举列表 |
| streamStorage | E_DjiCameraManagerStreamStorage[16] | 视频存储介质枚举列表 |
| nightSceneMode | E_DjiCameraManagerNightSceneMode[16] | 夜景模式枚举列表 |
| meteringMode | E_DjiCameraManagerMeteringMode[16] | 测光模式枚举列表 |
示例
import time
import sys
import struct
import ctypes
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
# ============ 结构体打包函数 ============
def pack_uint8_xy(x, y):
"""打包 {uint8_t x; uint8_t y} 结构体 (2字节)"""
return struct.pack('<BB', x, y)
def pack_float32_xy(x, y):
"""打包 {float32 x; float32 y} 结构体 (8字节)"""
return struct.pack('<ff', x, y)
def pack_float32_lxlyrxry(LX, LY, RX, RY):
"""打包 {float32 LX; float32 LY; float32 RX; float32 RY;} 结构体 (16字节)"""
return struct.pack('<ffff', LX, LY, RX, RY)
def pack_ering_mode(size, eringMode, minValue, maxValue):
"""打包 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;} 结构体 (25字节)
Args:
size: uint8_t, eringMode数组的有效长度
eringMode: list of int (0-255), 16个uint8_t值
minValue: uint32_t, 最小值
maxValue: uint32_t, 最大值
Returns:
bytes: 打包后的二进制数据
"""
# 确保eringMode是16字节
eringMode_bytes = bytearray(16)
for i in range(min(len(eringMode), 16)):
eringMode_bytes[i] = eringMode[i] & 0xFF
return struct.pack('<B16sII', size, eringMode_bytes, minValue, maxValue)
def format_param_value(param_value):
"""格式化128字节参数值(接受二进制输入)"""
if isinstance(param_value, str):
# 如果是十六进制字符串(如 "48656c6c6f"),先转换为字节
if param_value.startswith('0x') or all(c in '0123456789abcdefABCDEF' for c in param_value.replace(' ', '')):
hex_str = param_value.replace('0x', '').replace(' ', '')
if len(hex_str) % 2 == 0:
param_value = bytes.fromhex(hex_str)
else:
raise ValueError("Invalid hex string: must have even number of characters")
else:
# 普通字符串转字节
param_value = param_value.encode('utf-8')
if len(param_value) > 128:
raise ValueError("param_value cannot exceed 128 bytes")
param_value_bytes = bytearray(128)
param_value_bytes[:len(param_value)] = param_value
return param_value_bytes
def hex_dump(data, bytes_per_line=16):
"""格式化二进制数据为 hex + ASCII 展示(类 hexdump 风格)
Returns:
str: 格式化后的多行字符串
"""
lines = []
for i in range(0, len(data), bytes_per_line):
chunk = data[i:i + bytes_per_line]
hex_part = ' '.join(f'{b:02X}' for b in chunk)
ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
lines.append(f" {i:04X} {hex_part:<{bytes_per_line*3}} {ascii_part}")
return '\n'.join(lines)
def send_param_ext_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_ext_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_EXT_REQUEST_READ for {target}")
def send_param_ext_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_ext_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_EXT_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def send_param_ext_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_ext_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
format_param_value(param_value),
param_type
)
print(f"📤 Sent PARAM_EXT_SET for '{param_id}' = '{param_value}' (type: {param_type})")
def receive_param_ext_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_EXT_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值(直接获取128字节原始数据)
param_value_raw = msg.param_value
# byte_pointer = (ctypes.c_ubyte * 128).from_buffer_copy(msg.param_value)
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
# param_value_bytes = bytes(byte_pointer)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00,还原为128字节
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "FLOAT",
10: "CHAR", 11: "CHAR", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_EXT_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🧾 Type: {param_type} | Data: {len(param_value_bytes)} bytes")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
print(hex_dump(param_value_bytes))
return {
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_ext_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_EXT_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value_raw = msg.param_value
# 直接获取128字节原始数据
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE",
10: "CHAR", 11: "BOOL", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} [{param_type}][{len(param_value_bytes)}B]")
print(hex_dump(param_value_bytes))
received_params.append({
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def receive_param_ext_ack(vehicle, timeout=5.0):
"""接收参数设置确认"""
print(f"⏳ Waiting for PARAM_EXT_ACK ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_ACK', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_ACK received")
return None
param_id = msg.param_id
param_value = msg.param_value
# 结果代码映射
result_codes = {
0: "✅ PARAM_ACK_ACCEPTED",
1: "⚠️ PARAM_ACK_VALUE_UNSUPPORTED",
2: "❌ PARAM_ACK_FAILED",
3: "🔄 PARAM_ACK_IN_PROGRESS"
}
result = result_codes.get(msg.param_result, f"❓ UNKNOWN({msg.param_result})")
print("\n📥 Received PARAM_EXT_ACK:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 📌 Result: {result}")
return {
'id': param_id,
'value': param_value,
'result': result,
'result_code': msg.param_result
}
def main():
print("🚀 Starting MAVLink PARAM_EXT Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Read parameter value (by ID or index)")
print("2. Set parameter value (raw/hex/string)")
print("3. Request all parameters (PARAM_EXT_REQUEST_LIST)")
print("4. Set struct: uint8_xy {x, y}")
print("5. Set struct: float32_xy {x, y}")
print("6. Set struct: float32_lxlyrxry {LX, LY, RX, RY}")
print("7. Set struct: ering_mode {size, eringMode[16], minValue, maxValue}")
choice = input("\nSelect operation [1-7]: ").strip()
if choice == '1':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_ext_request_read(vehicle, param_id=param_id)
receive_param_ext_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_ext_request_read(vehicle, param_index=param_index)
receive_param_ext_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '2':
param_id = input("Enter parameter ID to set: ").strip()
print("\nEnter parameter value (supports):")
print(" - Plain string (e.g., hello)")
print(" - Hex string (e.g., 48656c6c6f or 0x48656c6c6f)")
print(" - Raw bytes (e.g., b'\\x48\\x65\\x6c\\x6c\\x6f')")
param_value_input = input("Enter value: ").strip()
# 解析输入值
if param_value_input.startswith("b'") or param_value_input.startswith('b"'):
# Python字节字面量格式
param_value = eval(param_value_input)
elif param_value_input.startswith('0x') or all(c in '0123456789abcdefABCDEF ' for c in param_value_input):
# 十六进制格式
hex_str = param_value_input.replace('0x', '').replace(' ', '')
try:
param_value = bytes.fromhex(hex_str)
except ValueError:
param_value = param_value_input.encode('utf-8')
else:
# 默认作为字符串处理
param_value = param_value_input.encode('utf-8')
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 13 = STRING")
print(" 10 = CHAR (raw bytes)")
print(" Other types require specific binary formats")
param_type = int(input("Enter type number [8]: ") or 8)
send_param_ext_set(vehicle, param_id, param_value, param_type)
receive_param_ext_ack(vehicle)
elif choice == '4':
"""设置 uint8_xy 结构体 {uint8_t x; uint8_t y}"""
param_id = input("Enter parameter ID: ").strip()
x = int(input("Enter x (0-255): "))
y = int(input("Enter y (0-255): "))
param_value = pack_uint8_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '5':
"""设置 float32_xy 结构体 {float32 x; float32 y}"""
param_id = input("Enter parameter ID: ").strip()
x = float(input("Enter x: "))
y = float(input("Enter y: "))
param_value = pack_float32_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '6':
"""设置 float32_lxlyrxry 结构体 {float32 LX; float32 LY; float32 RX; float32 RY;}"""
param_id = input("Enter parameter ID: ").strip()
LX = float(input("Enter LX: "))
LY = float(input("Enter LY: "))
RX = float(input("Enter RX: "))
RY = float(input("Enter RY: "))
param_value = pack_float32_lxlyrxry(LX, LY, RX, RY)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '7':
"""设置 ering_mode 结构体 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;}"""
param_id = input("Enter parameter ID: ").strip()
size = int(input("Enter size (0-16): "))
eringMode = []
print("Enter 16 eringMode values (0-255), one per line:")
for i in range(16):
val = int(input(f" eringMode[{i}]: "))
eringMode.append(val)
minValue = int(input("Enter minValue: "))
maxValue = int(input("Enter maxValue: "))
param_value = pack_ering_mode(size, eringMode, minValue, maxValue)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_ext_request_list(vehicle)
all_params = receive_all_param_ext_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“PHT_RATIO_RNG”
成功返回完整消息
照片比例
PHOTO_RATIO
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:
| 名称 | 枚举值 | 说明 |
|---|---|---|
| DJI_CAMERA_MANAGER_PHOTO_RATIO_4X3 | 0 | 4:3 照片比例 |
| DJI_CAMERA_MANAGER_PHOTO_RATIO_16X9 | 1 | 16:9 照片比例 |
| DJI_CAMERA_MANAGER_PHOTO_RATIO_3X2 | 2 | 3:2 照片比例 |
| DJI_CAMERA_MANAGER_PHOTO_RATIO_1X1 | 3 | 1:1 正方形比例 |
| DJI_CAMERA_MANAGER_PHOTO_RATIO_18X3 | 4 | 18:3 超宽比例 |
| DJI_CAMERA_MANAGER_PHOTO_RATIO_5X4 | 5 | 5:4 照片比例 |
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“PHOTO_RATIO”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“PHOTO_RATIO”
输入要设置的数值
选择参数类型“1”
成功返回设置值
获取夜景模式范围
NGT_SCN_MD_RNG
mavlink消息:
接收PARAM_EXT_REQUEST_READ
发送PARAM_EXT_VALUE
参数:
| 字段 | 类型 | 说明 |
|---|---|---|
| size | uint8_t | 有效列表长度(最大16) |
| minValue | uint32_t | 数值范围最小值 |
| maxValue | uint32_t | 数值范围最大值 |
union(同一时刻仅生效一个字段)
| 字段 | 类型 | 说明 |
|---|---|---|
| photoStorageFormat | E_DjiCameraManagerPhotoStorageFormat[16] | 照片存储格式枚举列表 |
| videoStorageFormat | E_DjiCameraManagerVideoStorageFormat[16] | 视频存储格式枚举列表 |
| photoRatioFormat | E_DjiCameraManagerPhotoRatio[16] | 照片比例枚举列表 |
| streamSource | E_DjiCameraManagerStreamSource[16] | 视频流源枚举列表 |
| streamStorage | E_DjiCameraManagerStreamStorage[16] | 视频存储介质枚举列表 |
| nightSceneMode | E_DjiCameraManagerNightSceneMode[16] | 夜景模式枚举列表 |
| meteringMode | E_DjiCameraManagerMeteringMode[16] | 测光模式枚举列表 |
示例
import time
import sys
import struct
import ctypes
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
# ============ 结构体打包函数 ============
def pack_uint8_xy(x, y):
"""打包 {uint8_t x; uint8_t y} 结构体 (2字节)"""
return struct.pack('<BB', x, y)
def pack_float32_xy(x, y):
"""打包 {float32 x; float32 y} 结构体 (8字节)"""
return struct.pack('<ff', x, y)
def pack_float32_lxlyrxry(LX, LY, RX, RY):
"""打包 {float32 LX; float32 LY; float32 RX; float32 RY;} 结构体 (16字节)"""
return struct.pack('<ffff', LX, LY, RX, RY)
def pack_ering_mode(size, eringMode, minValue, maxValue):
"""打包 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;} 结构体 (25字节)
Args:
size: uint8_t, eringMode数组的有效长度
eringMode: list of int (0-255), 16个uint8_t值
minValue: uint32_t, 最小值
maxValue: uint32_t, 最大值
Returns:
bytes: 打包后的二进制数据
"""
# 确保eringMode是16字节
eringMode_bytes = bytearray(16)
for i in range(min(len(eringMode), 16)):
eringMode_bytes[i] = eringMode[i] & 0xFF
return struct.pack('<B16sII', size, eringMode_bytes, minValue, maxValue)
def format_param_value(param_value):
"""格式化128字节参数值(接受二进制输入)"""
if isinstance(param_value, str):
# 如果是十六进制字符串(如 "48656c6c6f"),先转换为字节
if param_value.startswith('0x') or all(c in '0123456789abcdefABCDEF' for c in param_value.replace(' ', '')):
hex_str = param_value.replace('0x', '').replace(' ', '')
if len(hex_str) % 2 == 0:
param_value = bytes.fromhex(hex_str)
else:
raise ValueError("Invalid hex string: must have even number of characters")
else:
# 普通字符串转字节
param_value = param_value.encode('utf-8')
if len(param_value) > 128:
raise ValueError("param_value cannot exceed 128 bytes")
param_value_bytes = bytearray(128)
param_value_bytes[:len(param_value)] = param_value
return param_value_bytes
def hex_dump(data, bytes_per_line=16):
"""格式化二进制数据为 hex + ASCII 展示(类 hexdump 风格)
Returns:
str: 格式化后的多行字符串
"""
lines = []
for i in range(0, len(data), bytes_per_line):
chunk = data[i:i + bytes_per_line]
hex_part = ' '.join(f'{b:02X}' for b in chunk)
ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
lines.append(f" {i:04X} {hex_part:<{bytes_per_line*3}} {ascii_part}")
return '\n'.join(lines)
def send_param_ext_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_ext_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_EXT_REQUEST_READ for {target}")
def send_param_ext_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_ext_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_EXT_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def send_param_ext_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_ext_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
format_param_value(param_value),
param_type
)
print(f"📤 Sent PARAM_EXT_SET for '{param_id}' = '{param_value}' (type: {param_type})")
def receive_param_ext_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_EXT_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值(直接获取128字节原始数据)
param_value_raw = msg.param_value
# byte_pointer = (ctypes.c_ubyte * 128).from_buffer_copy(msg.param_value)
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
# param_value_bytes = bytes(byte_pointer)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00,还原为128字节
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "FLOAT",
10: "CHAR", 11: "CHAR", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_EXT_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🧾 Type: {param_type} | Data: {len(param_value_bytes)} bytes")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
print(hex_dump(param_value_bytes))
return {
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_ext_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_EXT_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value_raw = msg.param_value
# 直接获取128字节原始数据
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE",
10: "CHAR", 11: "BOOL", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} [{param_type}][{len(param_value_bytes)}B]")
print(hex_dump(param_value_bytes))
received_params.append({
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def receive_param_ext_ack(vehicle, timeout=5.0):
"""接收参数设置确认"""
print(f"⏳ Waiting for PARAM_EXT_ACK ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_ACK', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_ACK received")
return None
param_id = msg.param_id
param_value = msg.param_value
# 结果代码映射
result_codes = {
0: "✅ PARAM_ACK_ACCEPTED",
1: "⚠️ PARAM_ACK_VALUE_UNSUPPORTED",
2: "❌ PARAM_ACK_FAILED",
3: "🔄 PARAM_ACK_IN_PROGRESS"
}
result = result_codes.get(msg.param_result, f"❓ UNKNOWN({msg.param_result})")
print("\n📥 Received PARAM_EXT_ACK:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 📌 Result: {result}")
return {
'id': param_id,
'value': param_value,
'result': result,
'result_code': msg.param_result
}
def main():
print("🚀 Starting MAVLink PARAM_EXT Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Read parameter value (by ID or index)")
print("2. Set parameter value (raw/hex/string)")
print("3. Request all parameters (PARAM_EXT_REQUEST_LIST)")
print("4. Set struct: uint8_xy {x, y}")
print("5. Set struct: float32_xy {x, y}")
print("6. Set struct: float32_lxlyrxry {LX, LY, RX, RY}")
print("7. Set struct: ering_mode {size, eringMode[16], minValue, maxValue}")
choice = input("\nSelect operation [1-7]: ").strip()
if choice == '1':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_ext_request_read(vehicle, param_id=param_id)
receive_param_ext_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_ext_request_read(vehicle, param_index=param_index)
receive_param_ext_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '2':
param_id = input("Enter parameter ID to set: ").strip()
print("\nEnter parameter value (supports):")
print(" - Plain string (e.g., hello)")
print(" - Hex string (e.g., 48656c6c6f or 0x48656c6c6f)")
print(" - Raw bytes (e.g., b'\\x48\\x65\\x6c\\x6c\\x6f')")
param_value_input = input("Enter value: ").strip()
# 解析输入值
if param_value_input.startswith("b'") or param_value_input.startswith('b"'):
# Python字节字面量格式
param_value = eval(param_value_input)
elif param_value_input.startswith('0x') or all(c in '0123456789abcdefABCDEF ' for c in param_value_input):
# 十六进制格式
hex_str = param_value_input.replace('0x', '').replace(' ', '')
try:
param_value = bytes.fromhex(hex_str)
except ValueError:
param_value = param_value_input.encode('utf-8')
else:
# 默认作为字符串处理
param_value = param_value_input.encode('utf-8')
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 13 = STRING")
print(" 10 = CHAR (raw bytes)")
print(" Other types require specific binary formats")
param_type = int(input("Enter type number [8]: ") or 8)
send_param_ext_set(vehicle, param_id, param_value, param_type)
receive_param_ext_ack(vehicle)
elif choice == '4':
"""设置 uint8_xy 结构体 {uint8_t x; uint8_t y}"""
param_id = input("Enter parameter ID: ").strip()
x = int(input("Enter x (0-255): "))
y = int(input("Enter y (0-255): "))
param_value = pack_uint8_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '5':
"""设置 float32_xy 结构体 {float32 x; float32 y}"""
param_id = input("Enter parameter ID: ").strip()
x = float(input("Enter x: "))
y = float(input("Enter y: "))
param_value = pack_float32_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '6':
"""设置 float32_lxlyrxry 结构体 {float32 LX; float32 LY; float32 RX; float32 RY;}"""
param_id = input("Enter parameter ID: ").strip()
LX = float(input("Enter LX: "))
LY = float(input("Enter LY: "))
RX = float(input("Enter RX: "))
RY = float(input("Enter RY: "))
param_value = pack_float32_lxlyrxry(LX, LY, RX, RY)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '7':
"""设置 ering_mode 结构体 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;}"""
param_id = input("Enter parameter ID: ").strip()
size = int(input("Enter size (0-16): "))
eringMode = []
print("Enter 16 eringMode values (0-255), one per line:")
for i in range(16):
val = int(input(f" eringMode[{i}]: "))
eringMode.append(val)
minValue = int(input("Enter minValue: "))
maxValue = int(input("Enter maxValue: "))
param_value = pack_ering_mode(size, eringMode, minValue, maxValue)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_ext_request_list(vehicle)
all_params = receive_all_param_ext_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“NGT_SCN_MD_RNG”
成功返回完整消息
注:M3T不支持该功能。
夜景模式
NIGHT_SCENE_MODE
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:
| 名称 | 枚举值 | 说明 |
|---|---|---|
| DJI_CAMERA_MANAGER_NIGHT_SCENE_MODE_DISABLE | 0 | 关闭夜景模式 |
| DJI_CAMERA_MANAGER_NIGHT_SCENE_MODE_ENABLE | 1 | 开启夜景模式 |
| DJI_CAMERA_MANAGER_NIGHT_SCENE_MODE_AUTO | 2 | 自动夜景模式 |
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“NIGHT_SCENE_MODE”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“NIGHT_SCENE_MODE”
输入要设置的数值
选择参数类型“1”
成功返回设置值
注:M3T不支持该功能。
获取拍摄或录像时流媒体数据源的存储范围
STRM_STORE_RNG
mavlink消息:
接收PARAM_EXT_REQUEST_READ
发送PARAM_EXT_VALUE
参数:
| 字段 | 类型 | 说明 |
|---|---|---|
| size | uint8_t | 有效列表长度(最大16) |
| minValue | uint32_t | 数值范围最小值 |
| maxValue | uint32_t | 数值范围最大值 |
union(同一时刻仅生效一个字段)
| 字段 | 类型 | 说明 |
|---|---|---|
| photoStorageFormat | E_DjiCameraManagerPhotoStorageFormat[16] | 照片存储格式枚举列表 |
| videoStorageFormat | E_DjiCameraManagerVideoStorageFormat[16] | 视频存储格式枚举列表 |
| photoRatioFormat | E_DjiCameraManagerPhotoRatio[16] | 照片比例枚举列表 |
| streamSource | E_DjiCameraManagerStreamSource[16] | 视频流源枚举列表 |
| streamStorage | E_DjiCameraManagerStreamStorage[16] | 视频存储介质枚举列表 |
| nightSceneMode | E_DjiCameraManagerNightSceneMode[16] | 夜景模式枚举列表 |
| meteringMode | E_DjiCameraManagerMeteringMode[16] | 测光模式枚举列表 |
示例
import time
import sys
import struct
import ctypes
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
# ============ 结构体打包函数 ============
def pack_uint8_xy(x, y):
"""打包 {uint8_t x; uint8_t y} 结构体 (2字节)"""
return struct.pack('<BB', x, y)
def pack_float32_xy(x, y):
"""打包 {float32 x; float32 y} 结构体 (8字节)"""
return struct.pack('<ff', x, y)
def pack_float32_lxlyrxry(LX, LY, RX, RY):
"""打包 {float32 LX; float32 LY; float32 RX; float32 RY;} 结构体 (16字节)"""
return struct.pack('<ffff', LX, LY, RX, RY)
def pack_ering_mode(size, eringMode, minValue, maxValue):
"""打包 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;} 结构体 (25字节)
Args:
size: uint8_t, eringMode数组的有效长度
eringMode: list of int (0-255), 16个uint8_t值
minValue: uint32_t, 最小值
maxValue: uint32_t, 最大值
Returns:
bytes: 打包后的二进制数据
"""
# 确保eringMode是16字节
eringMode_bytes = bytearray(16)
for i in range(min(len(eringMode), 16)):
eringMode_bytes[i] = eringMode[i] & 0xFF
return struct.pack('<B16sII', size, eringMode_bytes, minValue, maxValue)
def format_param_value(param_value):
"""格式化128字节参数值(接受二进制输入)"""
if isinstance(param_value, str):
# 如果是十六进制字符串(如 "48656c6c6f"),先转换为字节
if param_value.startswith('0x') or all(c in '0123456789abcdefABCDEF' for c in param_value.replace(' ', '')):
hex_str = param_value.replace('0x', '').replace(' ', '')
if len(hex_str) % 2 == 0:
param_value = bytes.fromhex(hex_str)
else:
raise ValueError("Invalid hex string: must have even number of characters")
else:
# 普通字符串转字节
param_value = param_value.encode('utf-8')
if len(param_value) > 128:
raise ValueError("param_value cannot exceed 128 bytes")
param_value_bytes = bytearray(128)
param_value_bytes[:len(param_value)] = param_value
return param_value_bytes
def hex_dump(data, bytes_per_line=16):
"""格式化二进制数据为 hex + ASCII 展示(类 hexdump 风格)
Returns:
str: 格式化后的多行字符串
"""
lines = []
for i in range(0, len(data), bytes_per_line):
chunk = data[i:i + bytes_per_line]
hex_part = ' '.join(f'{b:02X}' for b in chunk)
ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
lines.append(f" {i:04X} {hex_part:<{bytes_per_line*3}} {ascii_part}")
return '\n'.join(lines)
def send_param_ext_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_ext_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_EXT_REQUEST_READ for {target}")
def send_param_ext_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_ext_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_EXT_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def send_param_ext_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_ext_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
format_param_value(param_value),
param_type
)
print(f"📤 Sent PARAM_EXT_SET for '{param_id}' = '{param_value}' (type: {param_type})")
def receive_param_ext_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_EXT_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值(直接获取128字节原始数据)
param_value_raw = msg.param_value
# byte_pointer = (ctypes.c_ubyte * 128).from_buffer_copy(msg.param_value)
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
# param_value_bytes = bytes(byte_pointer)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00,还原为128字节
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "FLOAT",
10: "CHAR", 11: "CHAR", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_EXT_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🧾 Type: {param_type} | Data: {len(param_value_bytes)} bytes")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
print(hex_dump(param_value_bytes))
return {
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_ext_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_EXT_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_EXT_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value_raw = msg.param_value
# 直接获取128字节原始数据
if isinstance(param_value_raw, (bytes, bytearray)):
param_value_bytes = bytes(param_value_raw)
elif isinstance(param_value_raw, str):
# 替换无法编码的字符(如 \ufffd)为 0x00
param_value_bytes = param_value_raw.encode('latin1', errors='replace')
else:
param_value_bytes = bytes(param_value_raw)
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE",
10: "CHAR", 11: "BOOL", 12: "UINT8_ENUM", 13: "STRING"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} [{param_type}][{len(param_value_bytes)}B]")
print(hex_dump(param_value_bytes))
received_params.append({
'id': param_id,
'value': param_value_bytes,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def receive_param_ext_ack(vehicle, timeout=5.0):
"""接收参数设置确认"""
print(f"⏳ Waiting for PARAM_EXT_ACK ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_EXT_ACK', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_EXT_ACK received")
return None
param_id = msg.param_id
param_value = msg.param_value
# 结果代码映射
result_codes = {
0: "✅ PARAM_ACK_ACCEPTED",
1: "⚠️ PARAM_ACK_VALUE_UNSUPPORTED",
2: "❌ PARAM_ACK_FAILED",
3: "🔄 PARAM_ACK_IN_PROGRESS"
}
result = result_codes.get(msg.param_result, f"❓ UNKNOWN({msg.param_result})")
print("\n📥 Received PARAM_EXT_ACK:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 📌 Result: {result}")
return {
'id': param_id,
'value': param_value,
'result': result,
'result_code': msg.param_result
}
def main():
print("🚀 Starting MAVLink PARAM_EXT Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Read parameter value (by ID or index)")
print("2. Set parameter value (raw/hex/string)")
print("3. Request all parameters (PARAM_EXT_REQUEST_LIST)")
print("4. Set struct: uint8_xy {x, y}")
print("5. Set struct: float32_xy {x, y}")
print("6. Set struct: float32_lxlyrxry {LX, LY, RX, RY}")
print("7. Set struct: ering_mode {size, eringMode[16], minValue, maxValue}")
choice = input("\nSelect operation [1-7]: ").strip()
if choice == '1':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_ext_request_read(vehicle, param_id=param_id)
receive_param_ext_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_ext_request_read(vehicle, param_index=param_index)
receive_param_ext_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '2':
param_id = input("Enter parameter ID to set: ").strip()
print("\nEnter parameter value (supports):")
print(" - Plain string (e.g., hello)")
print(" - Hex string (e.g., 48656c6c6f or 0x48656c6c6f)")
print(" - Raw bytes (e.g., b'\\x48\\x65\\x6c\\x6c\\x6f')")
param_value_input = input("Enter value: ").strip()
# 解析输入值
if param_value_input.startswith("b'") or param_value_input.startswith('b"'):
# Python字节字面量格式
param_value = eval(param_value_input)
elif param_value_input.startswith('0x') or all(c in '0123456789abcdefABCDEF ' for c in param_value_input):
# 十六进制格式
hex_str = param_value_input.replace('0x', '').replace(' ', '')
try:
param_value = bytes.fromhex(hex_str)
except ValueError:
param_value = param_value_input.encode('utf-8')
else:
# 默认作为字符串处理
param_value = param_value_input.encode('utf-8')
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 13 = STRING")
print(" 10 = CHAR (raw bytes)")
print(" Other types require specific binary formats")
param_type = int(input("Enter type number [8]: ") or 8)
send_param_ext_set(vehicle, param_id, param_value, param_type)
receive_param_ext_ack(vehicle)
elif choice == '4':
"""设置 uint8_xy 结构体 {uint8_t x; uint8_t y}"""
param_id = input("Enter parameter ID: ").strip()
x = int(input("Enter x (0-255): "))
y = int(input("Enter y (0-255): "))
param_value = pack_uint8_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '5':
"""设置 float32_xy 结构体 {float32 x; float32 y}"""
param_id = input("Enter parameter ID: ").strip()
x = float(input("Enter x: "))
y = float(input("Enter y: "))
param_value = pack_float32_xy(x, y)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '6':
"""设置 float32_lxlyrxry 结构体 {float32 LX; float32 LY; float32 RX; float32 RY;}"""
param_id = input("Enter parameter ID: ").strip()
LX = float(input("Enter LX: "))
LY = float(input("Enter LY: "))
RX = float(input("Enter RX: "))
RY = float(input("Enter RY: "))
param_value = pack_float32_lxlyrxry(LX, LY, RX, RY)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '7':
"""设置 ering_mode 结构体 {uint8_t size; uint8_t eringMode[16]; uint32_t minValue; uint32_t maxValue;}"""
param_id = input("Enter parameter ID: ").strip()
size = int(input("Enter size (0-16): "))
eringMode = []
print("Enter 16 eringMode values (0-255), one per line:")
for i in range(16):
val = int(input(f" eringMode[{i}]: "))
eringMode.append(val)
minValue = int(input("Enter minValue: "))
maxValue = int(input("Enter maxValue: "))
param_value = pack_ering_mode(size, eringMode, minValue, maxValue)
print(f" Packed: {param_value.hex()} ({len(param_value)} bytes)")
send_param_ext_set(vehicle, param_id, param_value, 10) # CHAR type
receive_param_ext_ack(vehicle)
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_ext_request_list(vehicle)
all_params = receive_all_param_ext_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“模板”
成功返回完整消息
同步分屏缩放功能
SYNC_SPLT_ZM
设置
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:
| 类型 | 枚举值 | 说明 |
|---|---|---|
| bool | 0 | 禁用同步分屏缩放功能 |
| bool | 1 | 启用同步分屏缩放功能 |
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“SYNC_SPLT_ZM”
成功返回该参数对应的数值
相机测光模式
METER_MODE
设置
mavlink消息:
接收PARAM_SET
发送PARAM_VALUE
获取
mavlink消息
接收PARAM_REQUEST_READ
发送PARAM_VALUE
参数:
| 枚举值 | 值 | 说明 |
|---|---|---|
| DJI_CAMERA_MANAGER_METERING_MODE_CENTRAL | 0 | 中央重点测光(Center-Weighted Metering) |
| DJI_CAMERA_MANAGER_METERING_MODE_AVERAGE | 1 | 平均测光(Average Metering) |
| DJI_CAMERA_MANAGER_METERING_MODE_SPOT | 2 | 点测光(Spot Metering) |
示例
import time
import sys
from pymavlink import mavutil
def connect_to_vehicle():
"""建立 UDP 监听连接(默认监听所有接口的 14550 端口)"""
connection_string = "udpin:0.0.0.0:14550"
print(f"Listening for MAVLink on: {connection_string}")
vehicle = mavutil.mavlink_connection(connection_string)
print("Waiting for HEARTBEAT (timeout: 10s)...")
try:
vehicle.wait_heartbeat(timeout=10)
print(f"✅ Heartbeat received from system {vehicle.target_system}, component {vehicle.target_component}")
return vehicle
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print("Ensure your vehicle is sending MAVLink messages to 14550 port")
print("Example QGroundControl setup: Settings > General > MAVLink > Listening Port = 14550")
sys.exit(1)
def format_param_id(param_id):
"""格式化16字节参数ID(符合MAVLink协议要求)"""
if len(param_id) > 16:
raise ValueError("param_id cannot exceed 16 characters")
param_id_bytes = bytearray(16)
for i in range(min(len(param_id), 16)):
param_id_bytes[i] = ord(param_id[i])
# 长度<16时添加NULL终止符
if len(param_id) < 16:
param_id_bytes[len(param_id)] = 0
return param_id_bytes
def send_param_set(vehicle, param_id, param_value, param_type):
"""发送参数设置请求"""
vehicle.mav.param_set_send(
vehicle.target_system,
vehicle.target_component,
format_param_id(param_id),
param_value,
param_type
)
print(f"📤 Sent PARAM_SET for '{param_id}' = {param_value} (type: {param_type})")
def send_param_request_read(vehicle, param_id=None, param_index=-1):
"""发送参数读取请求"""
param_id_bytes = format_param_id(param_id) if param_id else bytearray(16)
vehicle.mav.param_request_read_send(
vehicle.target_system,
vehicle.target_component,
param_id_bytes,
param_index
)
target = f"index {param_index}" if param_index >= 0 else f"ID '{param_id}'"
print(f"📤 Sent PARAM_REQUEST_READ for {target}")
def send_param_request_list(vehicle):
"""发送请求所有参数列表"""
vehicle.mav.param_request_list_send(
vehicle.target_system,
vehicle.target_component
)
print(f"📤 Sent PARAM_REQUEST_LIST to system {vehicle.target_system}, component {vehicle.target_component}")
def receive_param_value(vehicle, timeout=5.0):
"""接收参数值响应"""
print(f"⏳ Waiting for PARAM_VALUE ({timeout}s timeout)...")
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print("❌ Timeout: No PARAM_VALUE received")
return None
# 解析参数ID
param_id = msg.param_id
# 解析参数值
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
print("\n📥 Received PARAM_VALUE:")
print(f" 🆔 ID: {param_id}")
print(f" 🔢 Value: {param_value}")
print(f" 🧾 Type: {param_type}")
print(f" 📊 Index/Total: {msg.param_index}/{msg.param_count}")
return {
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
}
def receive_all_param_values(vehicle, timeout=10.0):
"""接收所有参数值,直到收到 param_count 条消息后退出循环"""
received_params = []
expected_count = None
print(f"⏳ Waiting for all PARAM_VALUE messages...")
while True:
msg = vehicle.recv_match(type='PARAM_VALUE', blocking=True, timeout=timeout)
if not msg:
print(f"❌ Timeout: Received {len(received_params)} parameters (expected {expected_count if expected_count else 'unknown'})")
break
param_id = msg.param_id
param_value = msg.param_value
# 参数类型映射
type_names = {
0: "INT8", 1: "UINT8", 2: "INT16", 3: "UINT16", 4: "INT32",
5: "UINT32", 6: "INT64", 7: "UINT64", 8: "FLOAT", 9: "DOUBLE"
}
param_type = type_names.get(msg.param_type, f"UNKNOWN({msg.param_type})")
# 首次收到消息时记录总参数数量
if expected_count is None:
expected_count = msg.param_count
print(f"📊 Total parameters to receive: {expected_count}")
print(f" [{msg.param_index}/{expected_count}] {param_id} = {param_value} ({param_type})")
received_params.append({
'id': param_id,
'value': param_value,
'type': param_type,
'index': msg.param_index,
'count': msg.param_count
})
# 当已接收数量达到预期总数时退出循环
if len(received_params) >= expected_count:
print(f"\n✅ Successfully received all {expected_count} parameters")
break
return received_params
def main():
print("🚀 Starting MAVLink PARAM_SET Tool (UDP Listening Mode)")
print("-----------------------------------------------------")
print("This script listens for MAVLink on ALL interfaces:14550")
print("Ensure your vehicle is configured to send to this port")
print("-----------------------------------------------------\n")
vehicle = connect_to_vehicle()
# 操作选择
print("\nAvailable operations:")
print("1. Set parameter value")
print("2. Read parameter value (by ID or index)")
print("3. Request all parameters (PARAM_REQUEST_LIST)")
choice = input("\nSelect operation [1-3]: ").strip()
if choice == '1':
param_id = input("Enter parameter ID to set: ").strip()
param_value = float(input("Enter new value: ").strip())
print("\nParameter types:")
print(" 8 = FLOAT (most common)")
print(" 9 = DOUBLE")
print(" 4 = INT32")
print(" 1 = UINT8")
param_type = int(input("Enter type number [8]: ").strip() or 8)
send_param_set(vehicle, param_id, param_value, param_type)
receive_param_value(vehicle)
elif choice == '2':
# mode = input("Read by [id/index]: ").lower()
mode = 'id'
if mode == 'id':
param_id = input("Enter parameter ID: ").strip()
send_param_request_read(vehicle, param_id=param_id)
receive_param_value(vehicle)
elif mode == 'index':
param_index = int(input("Enter parameter index: "))
send_param_request_read(vehicle, param_index=param_index)
receive_param_value(vehicle)
else:
print("Invalid mode. Use 'id' or 'index'")
elif choice == '3':
print("\n📤 Requesting all parameters...")
send_param_request_list(vehicle)
all_params = receive_all_param_values(vehicle)
print(f"\n📋 Total parameters received: {len(all_params)}")
else:
print("Invalid choice. Select 1, 2, or 3.")
if __name__ == "__main__":
main()示例使用说明
获取
运行后,成功建立连接
输入“2”选择读取参数
输入对应参数“METER_MODE”
成功返回该参数对应的数值
设置
运行后,成功建立连接
输入“1”选择设置参数
输入对应参数“METER_MODE”
输入要设置的数值
选择参数类型“1”
成功返回设置值
MAV_CMD_DO_TRIGGER_CONTROL 203
单张拍摄
返回消息:COMMAND_ACK
返回值 command 203 执行命令id
返回值 result 0或4 0:执行成功,4:执行失败
示例
import time
from pymavlink import mavutil
timestamp = int(time.time() * 1000)
millis = int(time.time() * 1000)
master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()
boot_time = time.time()
def shoot():
master.mav.command_long_send(
master.target_system,
master.target_component,
mavutil.mavlink.MAV_CMD_DO_DIGICAM_CONTROL,
0,
0, 0, 0, 0, 0, 0, 0
)
shoot()执行
结果
MAV_CMD_VIDEO_STOP_CAPTURE 2501
返回command = MAV_CMD_REQUEST_MESSAGE
返回值 param1 2501 执行命令id
返回值 param2 0或1 是否执行成功
返回值 param3 失败代码
0 无异常
1 无法初始化相机控制
2 设置相机为录像模式
3 获取相机版本失败
4 相机录像执行失败
示例
import time
from pymavlink import mavutil
timestamp = int(time.time() * 1000)
millis = int(time.time() * 1000)
master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()
boot_time = time.time()
def videoStop():
master.mav.command_long_send(
master.target_system,
master.target_component,
mavutil.mavlink.MAV_CMD_VIDEO_STOP_CAPTURE,
0,
0, 0, 0, 0, 0, 0, 0
)
videoStop()执行
结果
具体录像行为相关信息需用遥控器查看
MAV_CMD_VIDEO_STOP_CAPTURE 2501
返回command = MAV_CMD_REQUEST_MESSAGE
返回值 param1 2501 执行命令id
返回值 param2 0或1 是否执行成功
返回值 param3 失败代码
0 无异常
1 无法初始化相机控制
2 设置相机为录像模式
3 获取相机版本失败
4 相机录像执行失败
示例
import time
from pymavlink import mavutil
timestamp = int(time.time() * 1000)
millis = int(time.time() * 1000)
master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()
boot_time = time.time()
def videoStop():
master.mav.command_long_send(
master.target_system,
master.target_component,
mavutil.mavlink.MAV_CMD_VIDEO_STOP_CAPTURE,
0,
0, 0, 0, 0, 0, 0, 0
)
videoStop()执行
结果
具体录像行为相关信息需用遥控器查看
最后编辑:孙渝泓 更新时间:2026-06-18 17:59