2. ArduPilot基础

在开始编写 Lua 脚本之前,必须先理解 ArduPilot 这套飞控软件的整体结构。Lua 脚本并不是独立运行的程序,而是被“挂载”在 ArduPilot 主循环中的一段可执行逻辑。只有弄清楚飞控架构、固件类型、参数系统与飞行模式之间的关系,才能判断脚本应该在哪里介入、能读写哪些数据、以及何时被调度执行。

一、飞控架构:从传感器到执行器的分层

ArduPilot 的固件可以抽象为若干层:底层是硬件抽象层(HAL),负责屏蔽不同飞控板(如 Pixhawk、Cube、Matek)的芯片差异;其上是传感器驱动与姿态估计(EKF),把 IMU、磁力计、气压计、GPS 的数据融合成姿态与位置;再往上是控制律与导航层,把目标姿态、目标位置转换为电机或舵机输出;最上层是飞行模式与任务逻辑,决定“现在该听谁的指令”。

Lua 脚本运行在脚本引擎层,它通过绑定接口访问参数、传感器数据、飞行状态,并能调用部分控制与任务函数。它不直接操作寄存器,也不替代控制律,而是作为“策略层”的扩展,去实现自定义行为、条件判断和外部设备联动。

ArduPilot 分层架构与 Lua 脚本定位 应用与策略层 飞行模式 · 任务(Mission) · 地理围栏 · Lua 脚本引擎 导航与控制层 位置控制器 · 姿态控制器 · 混控(Mixer) · 输出驱动 状态估计层 EKF3 姿态/位置融合 · AHRS · 传感器健康检查 传感器与驱动层 IMU · 磁力计 · 气压计 · GPS · 空速 · 电池监测 硬件抽象层 HAL Pixhawk · Cube · Matek · 不同 MCU 与板级外设 Lua 脚本 读写参数 / 监听状态 / 触发动作 Lua 位于策略层,通过绑定接口访问参数、状态与控制函数,不直接替代底层控制律

二、固件类型:不同载具,不同脚本能力

ArduPilot 按载具类型分为多旋翼(Copter)、固定翼(Plane)、无人车(Rover)、潜艇(Sub)、天线跟踪(AntennaTracker)等固件。它们共享大量底层代码,但飞行模式、参数命名和可用 Lua 绑定并不完全相同。例如 Copter 的 vehicle:set_mode() 与 Plane 的模式编号不同,脚本中不能混用。

此外,同一载具还有稳定版(Stable)、测试版(Beta)、最新开发版(Latest)之分。Lua 脚本引擎在较新固件中才逐步完善,部分绑定函数只在 4.3 及以上版本可用。因此写脚本前应确认固件版本与目标载具,避免调用不存在的接口。

固件类型 典型飞行模式 Lua 常见用途
Copter Loiter、AltHold、Auto、Guided 自定义返航条件、载荷投放、编队灯光
Plane FBWA、Cruise、Auto、Loiter 航线中继、空投逻辑、发动机保护
Rover Manual、Auto、Guided、RTL 避障策略、任务点动态调整
Sub Manual、AltHold、Auto、Guided 深度保持、机械臂联动

三、参数系统:脚本读写飞控的“公共变量”

ArduPilot 的所有可配置项都以参数形式存在,例如 WPNAV_SPEED、RTL_ALT、FS_THR_ENABLE。参数有名称、索引、类型和默认值,地面站通过 MAVLink 读写。Lua 脚本同样可以读取和修改参数,这是脚本影响飞控行为最直接的方式。

-- 读取参数
local rtl_alt = param:get("RTL_ALT")

-- 修改参数(单位与范围需符合原参数定义)
param:set("RTL_ALT", 3000)

-- 判断参数是否存在
if param:get("MY_CUSTOM_PARAM") == nil then
  gcs:send_text(0, "参数不存在")
end
注意:脚本修改参数会立即生效,但部分参数需要重启或重新进入模式才起作用。批量修改前建议先记录原值,避免飞行中参数被意外覆盖。

四、飞行模式与 Lua 的介入时机

飞行模式决定了当前由谁控制载具:手动模式直接响应遥控器,Auto 模式执行任务,Guided 模式响应地面站指令,RTL 自动返航。Lua 脚本通常不直接“抢”控制权,而是通过监听模式变化、在特定模式下执行附加逻辑,或调用 vehicle:set_mode() 请求切换模式。

一个典型场景是:在 Auto 模式下,脚本持续读取电池电压,当低于阈值时自动切换到 RTL。脚本并不参与姿态控制,只做条件判断和模式请求,这样既安全又易于调试。

function update()
  local mode = vehicle:get_mode()
  local volt = battery:voltage(0)

  if mode == 3 and volt < 21.0 then
    vehicle:set_mode(6)  -- 切换到 RTL
    gcs:send_text(0, "低电压,触发返航")
  end

  return update, 1000
end

return update, 1000

五、案例与常见误区

假设要做一个“到达航点后打开相机”的脚本。正确做法是监听任务状态或使用 mission 相关绑定,在到达目标航点时触发一次动作,并设置标志位防止重复触发。常见误区包括:在 update() 中每 100ms 重复执行动作、未判断当前模式就修改参数、以及把脚本逻辑写得过于复杂导致主循环超时。

Lua 脚本的 update() 函数应尽量短小,避免阻塞。耗时操作应拆分到多次调用中完成,或使用定时器控制频率。脚本执行时间过长会影响飞控主循环,进而影响飞行安全。

理解飞控架构、固件类型、参数系统和飞行模式之后,Lua 脚本的定位就清晰了:它是策略层的轻量扩展,通过参数和状态接口与飞控交互,在合适的模式下执行自定义逻辑。掌握这些基础,才能写出稳定、可维护、不干扰飞行安全的脚本。