ArduPilot不写C也能加飞行逻辑?Lua脚本引擎上手实录
【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot
你的飞机需要"到特定高度自动悬停拍照",但官方固件没这个功能。改C源码重刷固件至少折腾一天,其实不需要——ArduPilot 内置了 Lua 脚本引擎,把脚本丢进 SD 卡就能跑。这篇实测笔记带你把 ArduPilot Lua 脚本系统从参数配置到三个真实场景完整跑通,覆盖无人机自定义飞行指令最常见的开发需求。
它能做什么 🚀
- 在飞控内跑定时循环逻辑,读传感器、改参数、向地面站推数据(遥测监控、低电量报警)
- 切换飞行模式、设置目标位置与速度、触发起飞降落(自动化任务、特殊机动)
- 通过串口、CAN 与外挂设备对话(接新传感器、云台、LED 屏)
- 自建
SCR_前缀参数表,在地面站直接调脚本行为(带可调参数的飞行逻辑)
实测下来,这套机制的价值在于:飞控脚本开发不用动一行 C 代码,脚本改完重传文件即可,迭代速度完全不在一个量级。
跑起来之前,先搞清这些 🔧
硬件前提只有一条:flash 大于 1MB 的板子,构建时会自动启用脚本支持(见 AP_Scripting 的 README)。运行时再把SCR_ENABLE设为 1 并重启。脚本存放位置:真机是 SD 卡APM/scripts/目录,SITL 是工作目录下的scripts/,开机自动加载所有.lua文件。
核心参数都在SCR_命名空间下,定义见 脚本引擎源码,最常用的 6 个:
| 参数名 | 作用 | 默认值 | 注意事项 |
|---|---|---|---|
| SCR_ENABLE | 启用 Lua 脚本(1=启用) | 0 | 修改后必须重启 |
| SCR_HEAP_SIZE | 脚本可用堆内存 | 43/100/200KB 按板级 | 修改后必须重启,小内存板别乱调 |
| SCR_VM_I_COUNT | 单次迭代允许的虚拟机指令数上限 | 10000 | 超了会被判定耗时过长,是防死循环的看门狗 |
| SCR_DEBUG_OPTS | 调试位掩码(内存/耗时报告等) | 0 | bit1 开启运行期内存与耗时消息 |
| SCR_LD_CHECKSUM | 已加载脚本的校验和 | -1(不校验) | 配错校验和直接禁止解锁,防脚本被篡改 |
| SCR_THD_PRIORITY | 脚本线程优先级 | 0(Normal) | 官方明确警告:调高可能影响飞控稳定性 |
ArKV6S 这类板子 flash 超过 1MB,构建脚本支持是默认行为,无需额外配置。
环境搭建压缩成 4 步:
git clone https://gitcode.com/GitHub_Trending/ar/ardupilot && cd ardupilot- 跑
Tools/environment_install/install-prereqs-ubuntu.sh -y装依赖(脚本路径见 环境依赖安装脚本) ./waf configure --board=CubeBlack && ./waf plane构建,脚本支持自动包含- 不想刷机就先 SITL:
Tools/autotest/sim_vehicle.py -v ArduCopter,脚本放进工作目录scripts/
脚本若要挂外挂设备,第一步是查板子的引脚图确认哪路串口空闲,ACNS-CM4Pilot 的 pinout 就是典型样例。
三个真实场景跑通自定义指令 ⚡
仓库里现成的 130+ 个示例在 示例脚本库,下面挑三个不同难度的场景拆。
基础:Circle 模式恒定地速
场景:飞手用拨轮随时调整 Circle 模式半径,半径一变地速就变,你需要"速度恒定、半径随便改"。
思路:写一个定时循环,检查飞机已解锁、在飞、处于 Circle 模式(Copter 模式号 7)后才生效;每次读当前圈半径,用速度 / 周长 × 360反算角速度写回去。对应copter-circle-speed.lua:
local speed = Parameter("CSPD_SPEED") function update() if not arming:is_armed() or vehicle:get_mode() ~= 7 then return update, 1000 end local radius = vehicle:get_circle_radius() vehicle:set_circle_rate(360 * (speed:get() / (radius * math.pi * 2))) return update, 100 end return update()坑点提示:循环的"心跳"是return update, 毫秒数,忘了这行脚本只执行一次就永远停摆——这是新手最高频的错误。
Aeromind 6X 这类 H7 级飞控的脚本堆默认在 100~200KB 档,写复杂逻辑前先看一眼自己的堆余量。
进阶:状态机实现"前后往返"自动任务
场景:起飞→前飞 10 米→原路返回→落地,全程无人接管,是飞控脚本开发里最典型的自动化任务。
思路:用一个本地stage变量搭状态机,每个阶段只做一件事,且只在动作接口返回true时才进入下一阶段——vehicle:set_mode()、vehicle:start_takeoff()这些接口都带布尔返回,天然适合做"动作完成确认"。对应copter_pingpong.lua的骨架:
if stage == 0 then if vehicle:set_mode(copter_guided_mode_num) then stage = stage + 1 end elseif stage == 1 then if vehicle:start_takeoff(takeoff_alt) then stage = stage + 1 end elseif stage == 5 then vehicle:set_mode(copter_land_mode_num) end return update, 100坑点提示:示例里的模式号 4(Guided)、9(Land)是 Copter 专属,固定翼、Rover、Sub 的模式表各不相同,照抄模式号换机型必摔,写脚本前先对一遍自己机型的模式号。
边界:脚本崩溃、死循环与内存保护
场景:脚本里藏个逻辑错误,飞在天上怎么自保?这里要分清两层保护:Lua 层的错误捕获,和引擎层的资源限制。
思路:Lua 层用pcall包住业务逻辑,出错就通过gcs:send_text把错误甩给地面站(仓库里有现成的protected_call.lua可参考);引擎层则是前面参数表的三道闸——SCR_VM_I_COUNT掐死单次迭代的计算量,SCR_HEAP_SIZE封顶内存,SCR_LD_CHECKSUM在解锁前就拦住被篡改的脚本:
function update() local ok, err = pcall(work) if not ok then gcs:send_text(0, "script error: " .. tostring(err)) end return update, 500 end return update()坑点提示:pcall只接得住 Lua 运行期错误,接不住引擎层因指令数超限或堆耗尽而终止脚本——长脚本调试时把SCR_DEBUG_OPTS的 bit1 打开,盯着运行期内存与耗时消息才是对的姿势。
容易踩的坑 + 下一步
先列 4 个实测高频错误:
- 循环不转了:
update结尾忘了return update, ms,补上即可 - 飞控变卡:
SCR_THD_PRIORITY调太高抢了飞控循环的 CPU,退回 0,确实需要实时性再逐项审脚本 - 参数改了没生效:
SCR_ENABLE、SCR_HEAP_SIZE、SCR_THD_PRIORITY都标了 RebootRequired,改完必须重启 - 换机型脚本跑飞:跨机型照抄模式号,先查模式表再动手
下一步建议:
- 通读 官方示例脚本库,按你机型的目录前缀(copter-/plane-/rover-/sub)挑相近场景改
- 写代码时把 脚本绑定接口文档 配进 Lua 语言服务器,能直接获得接口补全和参数提示
- 接口行为拿不准时,回 脚本引擎源码 对一遍参数注释,比论坛猜得快
飞控逻辑的边界不该由固件版本决定——先把最简单的simple_loop.lua在 SITL 里跑起来,剩下的都是量的积累。
【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考