4、MAVSDK-Python环境搭建
好,咱们进入第四章。前面几章我们把仿真环境、MAVLink基础都捋了一遍,现在该动真格的了——把MAVSDK-Python装起来,写几行代码跟无人机打个招呼。
MAVSDK是什么?说白了,它就是一套帮你跟无人机对话的SDK。你不用去手撕MAVLink协议里的那些字节流,直接调Python函数就行。我个人习惯用Python做原型验证,因为快,改起来也方便。
本章核心目标:装好MAVSDK-Python,理解它的几个核心概念,然后写一个能发现无人机、读取姿态和位置的脚本。
4.1 安装MAVSDK-Python
安装其实很简单,一行命令的事。但我建议你先确认一下Python版本——我遇到过有人用Python 3.12装不上,后来发现是某些依赖还没跟上。
# 推荐Python 3.8 ~ 3.11
pip install mavsdk
嗯,就这么简单。如果你在Linux上装,可能会遇到grpcio编译报错。别慌,先装个系统依赖:
# Ubuntu/Debian
sudo apt install libssl-dev libffi-dev python3-dev
# 然后再装
pip install mavsdk
小技巧:我习惯在虚拟环境里装,避免污染全局Python。用python -m venv mavsdk_env创建一个干净的隔离环境,再pip install,出了问题直接删掉重来,省心。
装完之后验证一下:
python -c "import mavsdk; print(mavsdk.__version__)"
如果能打印出版本号,恭喜,环境通了。
4.2 MAVSDK核心概念介绍
MAVSDK的设计思路其实很清晰。我刚开始看的时候觉得概念有点多,但用起来发现就那么几个东西。你想想看,无人机系统无非就是:连接、系统、插件。
| 概念 | 说明 | 类比 |
|---|---|---|
| System | 代表一架无人机或地面站 | 就像你手机通讯录里的一个联系人 |
| Plugin | 功能模块,比如Telemetry、Action、Mission | 联系人下面的电话号码、地址、备注 |
| Connection | 通信方式,串口、UDP、TCP | 你用什么方式打电话——座机还是手机 |
| Core | 管理连接和系统发现的核心层 | 电话交换机 |
核心流程是这样的:你先建立一个连接,然后MAVSDK会自动发现网络里的无人机(也就是System),接着你就可以通过插件去读写数据了。
这张图你看明白了吗?从上往下,你写的代码调用Plugin,Plugin通过Core把请求发到连接层,最后送到无人机。反过来,无人机发回来的数据也走这条路。
4.3 连接与系统发现
连接方式有三种,我列个表给你对比一下:
| 方式 | 连接地址格式 | 适用场景 |
|---|---|---|
| UDP | udp://:14540 |
仿真、局域网内地面站 |
| TCP | tcp://192.168.1.10:5760 |
远程连接、桥接场景 |
| 串口 | serial:///dev/ttyUSB0:57600 |
物理数传、Pixhawk直连 |
咱们做仿真,用UDP最方便。还记得第三章的仿真环境吗?启动之后,PX4会在本机开一个UDP端口,MAVSDK连上去就行。
系统发现这块,我曾经踩过一个坑。一开始我以为连上就能直接用,结果发现system对象是异步发现的。什么意思呢?就是connect之后,MAVSDK需要一点时间去“找到”无人机。如果你立刻去读数据,大概率读到空值。
注意:一定要等system.is_connected变成True,或者用first_system()协程等待发现完成,再往下走。否则你会遇到各种诡异的NoneType错误。
正确的做法是这样:
from mavsdk import System
import asyncio
async def run():
drone = System()
# 连接仿真环境
await drone.connect(system_address="udp://:14540")
print("等待系统发现...")
# 这行会阻塞,直到发现无人机
async for state in drone.core.connection_state():
if state.is_connected:
print(f"发现无人机!系统UUID: {state.uuid}")
break
asyncio.run(run())
你看,用async for监听connection_state,一旦连上就跳出循环。我个人习惯把这个逻辑封装成一个wait_for_system()函数,后面每个脚本都能复用。
4.4 基础API调用示例
好,现在无人机连上了,我们来读点数据。最常用的就是遥测数据——姿态、位置、电池这些。
先看一个完整的例子:
import asyncio
from mavsdk import System
async def run():
drone = System()
await drone.connect(system_address="udp://:14540")
# 等待系统发现
async for state in drone.core.connection_state():
if state.is_connected:
print("系统已连接")
break
# 等待位置数据就绪
print("等待位置数据...")
async for position in drone.telemetry.position():
print(f"位置: 纬度={position.latitude_deg:.4f}, "
f"经度={position.longitude_deg:.4f}, "
f"相对高度={position.relative_altitude_m:.2f}m")
break # 只取第一个数据
# 读取姿态
async for attitude in drone.telemetry.attitude_euler():
print(f"姿态: 横滚={attitude.roll_deg:.1f}°, "
f"俯仰={attitude.pitch_deg:.1f}°, "
f"偏航={attitude.yaw_deg:.1f}°")
break
# 读取电池
async for battery in drone.telemetry.battery():
print(f"电池: {battery.remaining_percent:.0%}")
break
asyncio.run(run())
跑这个脚本之前,记得先启动仿真环境(第三章的内容)。如果一切正常,你会看到类似这样的输出:
系统已连接
位置: 纬度=47.3977, 经度=8.5456, 相对高度=0.00m
姿态: 横滚=0.0°, 俯仰=0.0°, 偏航=0.0°
电池: 100%
嗯,位置是苏黎世,因为PX4默认的起飞点就在那里。高度0米,说明无人机在地面待命。
经验之谈:MAVSDK的API几乎都是异步的,用async for可以持续接收数据流。如果你只想取一次,记得加break,不然它会一直等新数据。我刚开始写的时候就忘了break,结果脚本卡在那不动,还以为是bug。
除了读数据,你还可以发指令。比如让无人机起飞:
# 先确认无人机状态
async for health in drone.telemetry.health():
if health.is_global_position_ok and health.is_home_position_ok:
print("无人机状态正常,准备起飞")
break
# 起飞
print("起飞!")
await drone.action.arm()
await drone.action.takeoff()
# 等5秒
await asyncio.sleep(5)
# 降落
print("降落...")
await drone.action.land()
这里要注意,arm()和takeoff()都是协程,必须用await调用。而且起飞前最好检查一下health,确保GPS和home点都就绪了。我曾经在室内仿真时忘了开GPS模拟,结果arm()一直报错,折腾了半天才发现是这个问题。
4.5 本章小结
到这,你已经把MAVSDK-Python环境搭好了,也学会了怎么连接仿真无人机、读取遥测数据、发送起飞指令。这些东西看着简单,但它们是后面所有高级功能的基础——任务规划、航点飞行、参数调优,全都建立在今天这几行代码之上。