简介:StableApproach 是一套面向 X-Plane 11 飞行模拟爱好者与飞行训练学习者的飞行数据监控配置工具,聚焦稳定进近分析,可记录并评估速度、高度、姿态、航向、推力与燃油消耗等关键参数,帮助用户定位操作偏差、改进飞行技巧。压缩包共 19 个文件,约 19KB,以 15 个 sacfg 机型配置文件为主,覆盖 B789、B738、A319、A321、B772、C172 等常见机型,另含 2 个 json 版本与需求模板、1 个 README 说明文档及 gitignore 文件,结构轻量,便于按机型检索和导入。已有 700 人学习。用户可依据文档将配置应用到对应机型,定制监控范围、稳定性指标与警告阈值,重点分析着陆阶段的下降率、航道对准和速度控制;配置模板也可作为不同机型进近标准的对照参考,通过阈值提醒及时暴露不稳定进近风险,让日常练习更有针对性,适合希望系统提升进近质量的初中级模拟飞行员。
1. 手动飞一次落地,事后能复盘的东西其实很少
落完一次地,除了「感觉有点重」,多数人拿不出任何数字:接地率多少、500 英尺时下滑道偏了几个点、进跑道时速度比目标速度高了几节,全靠回忆。StableApproach 补的就是这块空白——一个挂在 X-Plane 11 上的飞行数据监控工具,按稳定进近判据逐帧采样、逐项打分,落地后直接给一份带数值的报告。它不教你怎么飞,但能把你每次的「差不多」变成可对比的曲线。自己练手飞、带学员复盘、验证某个机型的手感是否被改得失真,都用得上。
2. 判据先行:飞行数据监控到底在监控哪几个量
拿到这类工具,多数人第一反应是装上就飞。但如果不清楚它在采什么、按什么标准判,报告出来只会看到一片红叉,既不知道错在哪,也不知道该不该信。这一章先把判据和数据链路拆开,后面配置和排错才有参照。
2.1 采样源:从 dataref 到一条能算的时间序列
X-Plane 11 对外暴露的是一套 dataref 数据总线,每个物理量一个路径,插件、脚本、外部程序都靠它读写。飞行数据监控工具做的事情本质上很朴素:定时读一批 dataref,攒成时间序列,再按飞行阶段切开做判定。所谓「原理」到这一步就没了,剩下的全是工程细节。
第一个细节是采样率。整段飞行用 1Hz 看趋势足够,但进近到接地这一分钟必须加密。接地瞬间的垂直速度是一个几十毫秒量级的脉冲,1Hz 采到的值基本等于随机抽签,同一个落地飞十次能给你十个互相矛盾的接地率。常见做法是分档:巡航段 1Hz 省资源,AGL 低于 3000 英尺自动切到 10~20Hz,落地后按时间戳把两段拼起来。
第二个细节是时间基准。新手最容易在这里翻车——用运行环境的墙钟时间去打时间戳,开模拟加速时时间轴会被拉成一条斜率不对的斜线,暂停再恢复还会出现空洞。正确做法是读仿真时间sim/time/total_running_time_sec,它跟随仿真时钟,加速、暂停、回放都保持一致。
第三个细节是环形缓冲。数据不能无限往里塞,也不可能落地那一刻才去读历史值,所以要预先分配一个固定长度的环形数组,长度按「最长需要回看的窗口」定:五边到接地一般三分钟够用。
-- 固定步长采样 + 环形缓冲:把关键量写进一个三分钟窗口 local SAMPLE_HZ = 10 -- 巡航 1Hz,AGL<3000ft 时切 20Hz local WINDOW_SEC = 180 -- 保留 3 分钟,覆盖五边到接地 local CAP = SAMPLE_HZ * WINDOW_SEC local buf, head, acc = {}, 0, 0 local function push(row) head = (head % CAP) + 1 -- 环形推进,满了覆盖最旧的一帧 buf[head] = row end local function sample_step(dt) acc = acc + dt if acc < 1.0 / SAMPLE_HZ then return end -- 用仿真 dt 累加,不用墙钟 acc = 0 push({ t = dataref("sim/time/total_running_time_sec"), -- 仿真时间 agl_ft = dataref("sim/flightmodel/position/y_agl") * 3.28084, -- 米转英尺 vs_fpm = dataref("sim/flightmodel/position/vh_ind_fpm"), -- 仪表垂直速度 ias_kt = dataref("sim/cockpit2/gauges/indicators/airspeed_kts_pilot"), bank_deg = dataref("sim/flightmodel/position/phi"), -- 滚转角 loc_dot = dataref("sim/cockpit2/radios/indicators/nav1_hdef_dot"), gs_dot = dataref("sim/cockpit2/radios/indicators/nav1_vdef_dot"), g_norm = dataref("sim/flightmodel2/misc/gforce_normal"), -- 法向过载 on_gnd = dataref("sim/flightmodel/failures/onground_any"), }) end这段的关键不在语法,在三个取舍。y_agl是「离地高度」,单位是米,直接拿去和 1000 英尺的判据比必然全错,所以乘 3.28084;但注意这个换算系数写在采样里比写在判据里好,因为后面所有门限都以英尺为单位,只在一个地方做单位转换,出错面小。vh_ind_fpm是仪表垂直速度,带惯性滤波和延迟,适合做稳定进近判据;但接地那一帧要用瞬时值,就得换local_vy自己算,两者不能混着用,混用会出现「报告说接地率 -80 fpm、体感却砸得厉害」这种对不上的情况。nav1_hdef_dot、nav1_vdef_dot只有在 NAV1 正确调到盲降频率、且 OMNI/ILS 指示器接通时才有效,没调台时它们返回 0,报告会显示「完美对正」——这是最容易被骗过的一项,后面避坑章会专门说。
具体 dataref 路径在不同小版本里偶尔会挪位置,拿不准就用社区里的 dataref 查看工具现场确认一遍,别照抄别人博客里的路径。
2.2 稳定进近的两道门:1000 英尺与 500 英尺
稳定进近判据的核心是「在某个高度上,飞机必须已经处于一个不需要大动作修正的状态」。业界通用的做法是设两道检查门:IMC 条件下在 1000 英尺 AGL,VMC 条件下在 500 英尺 AGL。用 AGL 而不是 MSL 是硬性要求——高原机场标高两三千米,用 MSL 判定等于把门设到了天上。
| 判据项 | 1000 ft AGL(IMC) | 500 ft AGL(VMC) | 说明 |
|---|---|---|---|
| 航道偏差 | ≤ 1.0 dot | ≤ 1.0 dot | 以 ILS 偏差点为单位,非精密进近改用量化角度 |
| 下滑偏差 | ≤ 1.0 dot | ≤ 1.0 dot | 同上,无 GS 时该旋钮置空不参与打分 |
| 滚转角 | ≤ 30° | ≤ 20° | 500 英尺后收紧,防止临门一脚大坡度修正 |
| 下降率 | ≥ -1000 fpm | ≥ -1000 fpm | 注意负号方向,写反了整份报告全红 |
| 速度窗口 | 目标 -5 / +10 kt | 目标 -5 / +10 kt | 目标速度按机型、襟翼位、重量查表 |
| 构型 | 起落架放下、襟翼进近位 | 同左 | 读 gear_handle_down 与 flap_ratio |
| 推力 | 稳定、无大变动 | 同左 | 用油门杆位置方差近似,不必精确建模 |
这七项里,前五项是数值判定,后两项是状态判定。工具通常会给每项单独打勾或打叉,再汇总成一个通过率。要注意两个门限的判定逻辑是「到点采样」而不是「全程满足」——飞到 1000 英尺那一瞬间取最近若干帧的中位数做判定,而不是要求你从 3000 英尺开始一直完美。这也意味着,如果采样在那几秒断了,判定会直接判失败而不是跳过,这是设计上的保守选择。
打分口径各家不同,有的是「七项全过才算稳定」,有的是加权给分。我倾向看通过率而不是看总分:通过率是「程序执行到位率」,可训练、可改进;总分里混进了接地率这类受天气和机型影响大的项,波动大,看多了容易焦虑。
2.3 接地段:接地率、载荷与接地点怎么算才不玄学
接地段是所有人最关心的部分,也是最容易算出离谱数字的部分。三个量:接地率(fpm)、接地载荷(G)、接地点距跑道入口的距离(米)。
接地检测不能靠「高度等于 0」这一条,因为地形起伏和跑道坡度会让 AGL 在接地前就掉到很小的正数。稳妥的组合条件是:on_gnd由 0 变 1,且前一帧 AGL 小于某个阈值(比如 5 英尺)。两者同时满足才认为是真的接地,而不是掠过跑道头或者数据抖动。
接地率不能取接地那一帧的垂直速度。那一帧飞机已经压地,垂直速度被瞬间抹平,读出来往往是 -50 到 -100 fpm,看起来很漂亮,实际完全没反映接地前的状态。常见做法是回看接地前 0.3~0.5 秒的窗口,取中位数或做一次线性拟合外推。窗口太短会被噪声带偏,太长会把拉平段的减速算进去,0.5 秒是我试下来比较稳的值。
接地载荷取接地后 0.2 秒内gforce_normal的绝对值峰值。这里有个陷阱:法向过载在接地瞬间是「先小于 1 再反弹」,取最小值会得到一个负数,报告里看起来像 bug。取绝对值峰值,或者取偏离 1.0 的最大幅度。
接地点距离依赖跑道数据。插件一般会内置一份跑道入口坐标表,或者在 ILS 进近时用 DME 反推。如果这两条都不满足(目视进近、没调台),这一项应该留空而不是硬算一个数——硬算出来的距离能差出几百米,比不给还糟。
3. 装进去跑出第一份报告:目录、首飞与字段解读
3.1 目录结构、加载器选择与加载确认
这类工具的发布形态常见两种:一种是原生插件,编译好的.xpl二进制;一种是 Lua 脚本,需要配套的脚本加载器。两种装法完全不同,先看清楚包里是什么再动手。
原生插件的标准位置是X-Plane 11/Resources/plugins/,每个插件一个独立子目录,可执行文件放在该目录下的平台子目录里(Windows 是64/win.xpl,Linux 是64/lin.xpl,macOS 是64/mac.xpl)。脚本形态则要先把加载器装进 plugins,再把脚本丢进它的Scripts目录。之所以强调这一点,是因为把 Lua 脚本直接扔进plugins目录下是最高频的「装了但没反应」原因。
X-Plane 11/ ├── Resources/plugins/ │ ├── <脚本加载器>/ # 只有脚本形态才需要 │ │ └── Scripts/ │ │ └── StableApproach/ # 脚本与配套配置放这里 │ └── StableApproach/ # 原生插件形态 │ ├── 64/win.xpl │ ├── config/profiles.json # 阈值配置 │ └── reports/ # 报告输出目录 └── Log.txt # 加载日志,排错第一现场装完别急着飞,先启动一次到主菜单再退出,然后看Log.txt:
# 确认插件被加载、脚本没有语法错误 grep -i "stableapproach" "Log.txt" | tail -n 20 # 顺手看一眼有没有 Lua 报错(脚本形态必查) grep -iE "lua|script" "Log.txt" | grep -iE "error|fail" | tail -n 20第一行有输出说明加载器认到了这个目录,第二行没有输出说明脚本没语法问题。如果第一行是空的,八成是目录层级多套了一层——解压时常见的StableApproach-xxx/StableApproach/嵌套,插件会读不到。如果第二行刷出一堆attempt to index a nil value,通常是 dataref 路径在你的 X-Plane 11 小版本里不存在,去查一遍实际路径再改。
3.2 一次完整记录:从冷舱到关车的标准流程
第一份报告想拿到手,按这个顺序走一遍,能避开绝大多数「数据不全」的问题。
- 进 sim 后先确认插件状态灯或菜单项出现,没有就先回上一步查日志。
- 冷舱启动,完成惯导对齐和气压基准设置。气压基准不设,高度类判据参考系就是错的。
- 进跑道前把 NAV1 调到盲降频率,并确认 DME 有距离读数。这一步决定了航道和下滑偏差能不能算。
- 起飞、爬升、巡航按正常程序走,中途不要用「位置瞬移」功能跳到五边——瞬移会让时间序列出现巨大跳变,分段逻辑容易误判。
- 五边建立后保持稳定,接地后滑跑到低速,再关车。
- 退出飞行或切回菜单,让插件把缓冲区落盘。
第 4 条值得多说一句。很多人图省事,直接瞬移到五边练落地,结果报告里进近段只有十几秒数据,判据采样点稀疏,得分忽高忽低。练习可以瞬移,但想要一份能用来复盘的完整报告,至少飞一次完整的五边进近。
3.3 报告字段逐个读:哪些能信,哪些别当真
报告一般是 JSON 或 CSV,字段名各家不同但语义大同小异。下面这套是常见结构,对着看就行。
| 字段 | 单位 | 合理区间 | 异常时的含义 |
|---|---|---|---|
touchdown.sink_fpm | ft/min | -50 ~ -300 | 低于 -600 说明拉平不足或拉平过早后失速下沉 |
touchdown.g_normal | G | 1.1 ~ 1.5 | 超过 1.8 多半是硬接地,配合下沉率一起看 |
touchdown.distance_m | m | 依机型 | 明显偏小可能是擦跑道头,偏大说明拉平过久 |
gates.vmc_500ft.passed | 布尔 | true | false 时看子项,别只看总分 |
gates.vmc_500ft.speed_dev | kt | -5 ~ +10 | 正值大说明进跑道太快,负值大说明能量不足 |
approach.loc_dot_max | dot | ≤ 1.0 | 全程最大偏差,比到点采样更能反映修正幅度 |
meta.sample_hz | Hz | ≥ 10 | 低于 10 说明高频档没切上,接地率不可信 |
读报告有个顺序:先看采样率和数据完整性,再看两个门限的通过情况,最后才看接地率。反过来读的话,你会被接地率牵着走,去追求一个「好看的数字」,而忽略真正该改的程序问题。接地率在 200 到 300 fpm 之间都算正常范围,风切变、阵风、机型载重都会影响,单次数值说明不了太多。
4. 阈值配置:把通用标准改成你自己的机型标准
4.1 配置文件结构与几个必须改的字段
开箱即用的阈值一定是「某一类飞机的平均标准」,套到你的机型上大概率不合适。轻型螺旋桨飞机 500 英尺时 20° 滚转完全正常,重型机在同样高度 20° 已经算大了;窄体机目标速度 140 节,通航飞机 80 节。不改配置,报告的红叉反映的是「机型不匹配」而不是「你飞得差」。
配置一般是 JSON,结构分三块:机型档案、门限、接地段参数。
{ "aircraft_profile": "narrowbody_jet", "gates": { "imc_1000ft": { "agl_ft": 1000, "max_sink_fpm": -1000, "max_bank_deg": 30, "vref_window_kt": [-5, 10], "loc_dot": 1.0, "gs_dot": 1.0 }, "vmc_500ft": { "agl_ft": 500, "max_sink_fpm": -1000, "max_bank_deg": 20, "vref_window_kt": [-5, 10], "loc_dot": 1.0, "gs_dot": 1.0 } }, "touchdown": { "sink_window_ms": 500, "bounce_threshold_fpm": 300, "g_limit_normal": 1.8 } }四个字段最值得改。max_sink_fpm是负数,-1000 表示「下降率不得大于 1000 fpm」,如果你的机型进近速度低、正常下滑角对应的下降率只有 -700,这个门限等于形同虚设,应该收到 -900 左右。vref_window_kt用的是相对目标速度的偏移量而不是绝对速度,这样换机型只要改目标速度表,窗口不用动。sink_window_ms是接地率的回看窗口,小飞机惯性小,窗口可以收到 300ms;重型机惯量大,500ms 更稳。bounce_threshold_fpm用来识别弹跳,接地后垂直速度再次超过这个正阈值就标记一次弹跳。
4.2 按机型与气象条件分组,别维护一份万能配置
只有一份配置的时候,你会发现自己在反复改同一个文件:飞窄体机调一遍,换通航再调一遍,碰上下雨天 VMC 门限又不适用。正确做法是按机型分档,每档一份 profile,再在配置里用一个顶层字段指定当前用哪份。
分组维度建议两个:机型类别(轻型活塞 / 涡桨 / 窄体 / 重型)和进近类型(精密 / 非精密 / 目视)。非精密进近没有下滑道,gs_dot应该置空而不是给 1.0——给 1.0 的话,插件读到的偏差恒为 0,报告会显示「下滑道完美」,属于虚假通过。目视进近同理,航道偏差也不该参与打分。
气象条件不建独立 profile,而是让插件按当时的能见度和云底高自动选 1000 英尺门还是 500 英尺门。判定逻辑简单:能见度低于某个值或云底高低于 1000 英尺,就走 IMC 门。这条规则写进配置里,比每次手动切可靠得多。
切 profile 之后一定要飞一次验证。我见过不止一次,配置里 profile 名字改了,但目标速度表没跟着改,结果速度判据用了上一档的数值,整份报告的速度项全错,人还以为是操纵问题。
4.3 批量回放:用脚本把十次飞行拉到一张表上
单份报告只能看一次落地,价值有限。真正的价值在横向对比——同一个机场、同一机型、连续十次,看哪个指标在收敛、哪个在飘。插件输出的 JSON 结构统一,写十几行 Python 就能拉平。
import glob, json, statistics rows = [] for p in glob.glob("reports/*.json"): with open(p, encoding="utf-8") as f: r = json.load(f) rows.append({ "flight": r["meta"]["flight_id"], "sink": r["touchdown"]["sink_fpm"], # 负值,越小越重 "g": r["touchdown"]["g_normal"], "pass500": r["gates"]["vmc_500ft"]["passed"], "speeddev": r["gates"]["vmc_500ft"]["speed_dev"], }) rows.sort(key=lambda x: x["flight"]) for r in rows: print(f'{r["flight"]:>8} sink={r["sink"]:>6.0f} fpm g={r["g"]:.2f} ' f'speed={r["speeddev"]:>+5.1f} kt gate500={r["pass500"]}') sink = [r["sink"] for r in rows] print("接地率 均值 %.0f 标准差 %.0f 最重 %.0f" % ( statistics.mean(sink), statistics.pstdev(sink), min(sink)))脚本本身没什么难度,两个地方要留意。min(sink)取到的是绝对值最大的负值,也就是最重的那次接地——如果你的报告用绝对值存接地率,这里要改成max。speed_dev的正负号约定也要先确认,有的报告是「实际减目标」,有的是「目标减实际」,符号反了会把「进跑道太快」读成「能量不足」。 标准差比均值有用得多:均值从 -250 降到 -200 可能只是运气好,标准差从 120 降到 40 才是真的稳了。
5. 避坑与排查:五类最常见的翻车现场
5.1 数据采集侧的三类问题
现象一:报告里 AGL 一直是 0,或者在地面时疯狂跳变。原因通常是 dataref 读错。y_agl在某些情况下返回的是相对某个参考面的高度,而不是相对当前地面;也有可能是单位没换算,读到的米被当成了英尺。解决方式:把原始值打印到日志里,飞机停在地面时应该是 0 附近,滑行时随地形小幅波动,起飞后单调增长。三个特征有一个不对,就是路径错了,去 dataref 查看工具里找一个替代路径。
现象二:曲线中间出现台阶或者断档。台阶的典型成因是采样循环用了墙钟时间做步进,开加速时相邻两帧的仿真时间差被拉大,画出来就是一段横线接一段陡坡。断档则多半是 sim 卡顿或加载地景时主循环被阻塞,缓冲区里缺了几帧。前者换成仿真时间做步进即可;后者没法完全避免,处理办法是在判定时检查窗口内的帧数,低于阈值就把这次判定标记为「数据不足」而不是「不通过」——把缺数据当成失败,会让你误以为程序有问题。
现象三:连续几份报告的数值一模一样。这是缓冲区没重置。落地后数据落盘了,但环形缓冲的指针没归零,下一次飞行开头读到的还是上一次的尾部数据,判定自然重复。解决方式是在检测到「从地面重新起飞」这个状态变化时清空缓冲,或者更粗暴一点,每次进入飞行界面时清一次。这类问题最坑的地方在于它不会报错,报告看起来完全正常,只是没在更新。
5.2 判据与配置侧的两类问题
现象四:报告全线飘红,连滑行段都在报不稳定。先查符号约定,再查单位,最后查 profile。下降率的负号是最常见的元凶:判据写成「下降率 ≤ 1000」而实际值是 -800,程序按数值比较直接判失败。其次是单位,判据用英尺而数据是米,1000 英尺的门变成了 1000 米,所有采样点都「没到门」,判定逻辑直接跳过或者判异常。最后是 profile,机型档位没切对,目标速度差出几十节,速度项必然全错。排查顺序就按这个来,能省掉大半时间。
现象五:改了阈值但报告毫无变化。三种可能:改错了文件(有的包里有config/和config/default/两份,实际读的是后者);配置有语法错误被静默忽略,回退到内置默认值;配置在飞行开始时只加载一次,飞行中改文件不生效。第二种最隐蔽,JSON 少一个逗号不会让插件崩,只会让它悄悄用默认值。改完配置后把文件过一遍 JSON 校验,再重启 sim 完整飞一次,别在空中改完就指望立刻生效。
提示:每次改配置前把旧文件复制一份带日期的备份。这类工具的配置项多,改坏一项很难靠肉眼找回来,有备份能省一次重装。
6. 进阶:把一堆报告压成一条能看出趋势的曲线
单次报告解决的是「这次落得怎么样」,连续报告解决的是「我到底有没有变好」。后者才是这类工具真正的用法。
具体做法:固定条件——同机场、同跑道、同机型、同载重档位、尽量同气象,连续飞十次,用 4.3 的脚本拉成一张表。然后只盯一个指标,别同时看七个。我一般先盯「500 英尺门通过率」,因为它综合反映程序执行;通过率稳定在八成以上之后,再去看接地率的标准差。
| 观察窗口 | 稳定进近通过率 | 接地率均值 | 接地率标准差 | 结论 |
|---|---|---|---|---|
| 第 1~10 次 | 60% | -280 fpm | 118 | 程序不稳,先改五边建立距离 |
| 第 11~20 次 | 80% | -240 fpm | 96 | 程序改善,但拉平仍偏早 |
| 第 21~30 次 | 90% | -210 fpm | 45 | 拉平节奏固定,进入肌肉记忆 |
这张表里最有信息量的是标准差那一列。均值从 -280 到 -210 只是「轻了一点」,标准差从 118 掉到 45 才是「稳了」——前者可能是运气,后者只能是动作定型。看均值容易自我感觉良好,看标准差不会骗人。
还有一个具体技巧:接地率别单独看,和「接地点距离」配对看。下沉率偏大且接地点偏近,是拉平起步太晚;下沉率偏大但接地点正常偏近不多,是拉平后顶杆太多导致二次下沉;下沉率很小但接地点偏远,是拉平过早、平飘过长。三种组合对应三种完全不同的修正动作,只看接地率一个数永远分不清。
我自己的习惯是,每次想调配置或者换机型之前,先拿旧配置飞三次留底,改完再飞三次对比。不看基线就改参数,改完你根本说不清变化是来自于参数还是来自于自己这几天手感好了。有次我嫌麻烦跳过这一步,把速度窗口从 -5/+10 收到 -3/+5,飞了五次全红,以为是自己退步,回头翻记录才发现是那天换了载重档位、目标速度表没跟着改——白焦虑了两天。
从那以后我每次动配置文件之前,都强制先跑三趟基线,把reports/里的文件名加上日期和机型前缀再存一份。多花十分钟,省的是后面几个小时的自我怀疑。希望帮到你。
本文还有配套的精品资源,点击获取