ArduPilot Lua 脚本系统完整指南:不改固件为飞控加装自定义能力
【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot
ArduPilot 内置的 Lua 脚本引擎允许你在飞控上直接运行 .lua 脚本:读传感器、覆盖舵机、响应任务指令,全程不需要重新编译固件。本文基于源码仓库里的 AP_Scripting 模块拆解三件事:引擎如何加载和调度脚本、一套 SCR_ 参数怎么配、如何在 SITL 里跑通第一个脚本并处理生产环境的坑。读完后你能独立完成"写脚本 → 本地验证 → 上车部署"的完整闭环。
引擎解剖:一个线程、一座沙箱、一个熔断器 🚀
很多人以为脚本和飞控主循环抢 CPU,实际 ArduPilot 的做法是把脚本关进一个独立空间。初始化时,AP_Scripting/核心模块 会创建一个名为 "Scripting" 的专用线程(默认栈 17KB),所有 .lua 脚本共享这一个线程轮流执行。
隔离由三层机制保证,理解它们之后调参就不玄学了:
- 独立堆:脚本拥有单独的 Lua 堆,大小按平台分级——小容量平台 43KB,MEM_CLASS_500 档 100KB,MEM_CLASS_1000 及以上(含 SITL/Linux)200KB,与飞控主程序内存互不侵占
- 协作式调度:脚本"函数 + 毫秒延迟"的返回值决定下次执行时间,调度器按时间先后排列所有脚本,谁也不阻塞谁
- 指令计数熔断:Lua 虚拟机挂了 MASKCOUNT 钩子,累计执行指令数超过SCR_VM_I_COUNT(默认 10000)立即抛出 "Exceeded CPU time",防止死循环脚本烧掉整块飞控
注意:熔断器保护的是 CPU,不是安全。一个脚本用死循环烧死自己后,调度器会清理资源并每秒检查一次是否需要重启,期间其他脚本照常运行。
加载机制:两个目录与一组 SCR_ 参数 🔧
脚本从哪来、以什么条件运行,全部由参数和目录约定控制。上电后引擎扫描两个位置:SD 卡的/APM/scripts/目录和编译进固件的 ROMFS 内置目录,SCR_DIR_DISABLE可按位屏蔽(bit 0=ROMFS,bit 1=APM/scripts)。SITL 环境下脚本直接放在仓库工作目录的scripts/文件夹即可。
核心参数一览,全部以 SCR_ 为前缀,可在 Mission Planner 等地面站直接修改:
- SCR_ENABLE:总开关,1 启用 Lua 脚本,0 禁用,修改后需重启飞控
- SCR_HEAP_SIZE:脚本堆大小,1KB 到 1MB,默认值按平台分级,改完需重启
- SCR_VM_I_COUNT:单脚本指令熔断阈值,范围 1000~1000000,默认 10000
- SCR_THD_PRIORITY:脚本线程优先级,0(Normal)到 8(Boost),默认 Normal,即低于 IO、存储、主循环等所有系统线程
- SCR_LD_CHECKSUM / SCR_RUN_CHECKSUM:已加载/正在运行的脚本 CRC32 校验和,不匹配则拒绝解锁,-1 表示不检查
- SCR_DEBUG_OPTS:位掩码调试选项,bit 1 输出运行时内存与耗时,bit 3 将运行统计写入 dataflash
- SCR_USER1~USER6:六个 float 用户参数,脚本内可读,适合把"半径""阈值"这类调试点外提
参数配完,下一步是让引擎真正转起来。
跑通第一个脚本:SITL 四步法
不用真机也能完整走一遍流程,SITL 下脚本放在仓库工作目录的scripts/子文件夹:
- 启动仿真(脚本支持在闪存大于 1MB 的板型上自动启用):
Tools/autotest/sim_vehicle.py -v ArduPlane- 在地面站或 MAVProxy 中开启脚本引擎并重启:
param set SCR_ENABLE 1 reboot- 把你的 .lua 文件放入
scripts/目录,重启后自动加载 - 地面站控制台看到 "Scripting: restarted" 且无 Lua 报错即成功
仓库自带的 README 示例是个好起点——它展示了最小可用的"周期性函数"写法,也解释了返回值机制:
function update() local pos, home = ahrs:get_location(), ahrs:get_home() if pos and home then local d = math.min(pos:get_distance(home), 1000) SRV_Channels:set_output_pwm(96, 1000 + d) end return update, 1000 -- 1 秒后再跑一次 end return update, 1000末尾的return update, 1000就是协作调度的钩子:返回的函数会被按延迟重新排队。输出写到通道 96(SCRIPTING_3),这是专门留给脚本的保留通道,不会干扰真实舵机——验证脚本行为时这是个安全的观察出口。
图:运行 ArduPilot Lua 脚本的飞控,真实硬件上脚本存放于 SD 卡 /APM/scripts 目录
跑通之后,值得看看这个引擎到底开放了哪些手脚。
API 全景:脚本能碰什么
Lua 侧的绑定由 lua_bindings/C 函数绑定层 生成,按用途可以分四类记:
- 状态读取:
ahrs:get_location()、battery:voltage()、esc/motors转速与温度 - 控制输出:
SRV_Channels:set_output_pwm()写保留通道、vehicle:change_mode()切模式、RC 通道覆盖 - 任务集成:
mission:queue_item()动态追加任务点;注册mission:scripting_cmd_handler后,任务里的 NAV_SCRIPT 指令(cmd 18)执行时,飞控会把参数推给脚本处理——这是"任务规划调脚本"的正式通道,而不是轮询 - 硬件直连:I2C 设备、CAN 传感器、独立串行口(SCR_SDEV_EN)、网络 socket,等于把飞控当成一块可编程 IO 扩展板
任务指令的例子值得看,它展示了脚本如何被动响应而非主动轮询:
local function on_scripting_cmd(p1, p2, p3, p4) gcs:send_text(0, "NAV_SCRIPT received, p1=" .. p1) end mission:scripting_cmd_handler(on_scripting_cmd)注册后任务中执行 NAV_SCRIPT 指令时,p1 为整数参数、p2~p4 为 float 参数,回调被调度器统一触发,不会和主控制循环抢时间片。
图:ArduPilot 飞控接线示意,脚本可通过串口、I2C、CAN 等接口直接驱动扩展硬件
能碰的东西越多,越要懂得怎么把它管好。
生产环境调优与避坑
先说最常踩的三个坑:
- 内存不足:控制台报 "Insufficient memory loading" 时,加大SCR_HEAP_SIZE(改完重启)。堆是独立沙箱,加到 100KB 也不会挤占导航滤波的内存
- CPU 超限:报 "Exceeded CPU time" 说明单轮逻辑太重,正确做法是拆分逻辑、拉大调度间隔;直接调大 SCR_VM_I_COUNT 只是掩盖问题
- 优先级误区:SCR_THD_PRIORITY默认 Normal 是刻意压低,让脚本永远排在系统线程之后。参数注释里明确警告:提到 MAIN/BOOST 档前必须逐个审计所有脚本,因为所有脚本共享同一优先级,一个脚本失控会连坐全部
另外两个容易被忽略的运维能力:
- 热重启:地面站发送 MAVLink 的 SCRIPTING_CMD(STOP / STOP_AND_RESTART)即可停掉或重建整个脚本环境,不用断电重启飞控,调试迭代时非常顺手
- 校验和锁机:脚本更新后,置SCR_DEBUG_OPTSbit 5 可把当前脚本 CRC 存进 SCR_LD_CHECKSUM / SCR_RUN_CHECKSUM。之后只要有人误删或改动了脚本文件,飞控拒绝解锁——对多机车队和科研外场作业来说这是必要的保险
沙箱能防脚本写坏内存,防不了脚本用高频 PWM 输出写坏机械结构。任何直接输出到真实通道的脚本,务必先在 SITL 或地面台架上验证极限行为。
小结与延伸资源
本文覆盖了 AP_Scripting 的完整机制链路:独立线程沙箱与指令熔断、SCR_ 参数体系、SITL 验证流程、四类 API 表面,以及校验和与热重启这类生产手段。边界同样清楚:脚本运行在低优先级线程里,适合 IO、逻辑、数据处理类扩展,不适合替代主控制环;涉及解锁安全和机械输出的部分,校验和与地面测试不能省。
延伸入口:
- 示例脚本库:libraries/AP_Scripting/examples/,60 多个按场景分类的现成脚本(CAN 读写、电池监测、电机矩阵、翻车保护等)
- 模块说明:libraries/AP_Scripting/README.md,编译开关与最小脚本速览
- 内置 Lua 模块:libraries/AP_Scripting/modules/,MAVLink 消息封装、NMEA2000 等开箱即用的扩展
【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考