Add your description here
Project description
Fly Ruler Protocol Python Bindings
本目录提供 fly_ruler_proto_core 的 Python 绑定,基于 PyO3 与 maturin 构建。它面向飞行模拟的数据发送端,将一架飞行器的完整生命周期封装为高层的 Python API,让 Python 脚本能够以最小网络细节向 Godot 或 Rust 服务端推送飞行器状态与事件。
Python wheels、Linux server 和自带 Web 控制台的 MSFS zip 由同一 tag workflow 发布,详见 ../../RELEASING.md。
1. 定位与架构
Python 脚本
↓
FlyRulerClient / PyClient(本绑定)
↓
fly_ruler_proto_core::transport::AircraftClient
↓
UDP 网络 → Fly Ruler Server / MSFS / Godot
PyClient:Rust 实现的 aircraft-bound 客户端(一个实例对应一架飞行器)。FlyRulerClient:Python 高层包装,提供上下文管理器与最佳实践。
发送到 fly-ruler-server 的状态可以在同仓库 Web 控制台中实时查看、绘制历史曲线、按事件跳转和回放。显式 timestamp 是生产者定义的有限源时间轴秒数,可以使用 Unix wall time,也可以从 0 开始使用仿真时间;后一种情况必须同时设置 spawn_timestamp,并让 state、telemetry、event 与 despawn 使用相同时间基准。Live stale 使用服务端单调接收时间判定,不依赖源时间是否为 Unix 时间。
2. 环境准备
推荐工具链:
2.1 创建虚拟环境并安装构建工具
cd bindings/python
uv venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
uv pip install maturin pytest
2.2 构建并安装本地扩展
maturin develop
这会将 Rust 扩展编译为 _core 并安装到当前虚拟环境,Python 包可直接导入。
3. 数据类型
模块导出了与 protobuf schema 对齐的基础数据类。
3.1 Vector3
from fly_ruler_proto_python import Vector3
v = Vector3(1.0, 2.0, 3.0)
print(v.x, v.y, v.z)
v.x = 9.0
zero = Vector3.zero()
| 属性 | 类型 | 说明 |
|---|---|---|
x |
float |
X 分量 |
y |
float |
Y 分量 |
z |
float |
Z 分量 |
3.2 Attitude
from fly_ruler_proto_python import Attitude
identity = Attitude.identity()
from_quaternion = Attitude.from_quaternion((1.0, 0.0, 0.0, 0.0))
from_matrix = Attitude.from_rotation_matrix((1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0))
from_euler = Attitude.from_euler((0.0, 0.0, 0.0))
| 属性 | 类型 | 说明 |
|---|---|---|
quaternion |
tuple[float, float, float, float] |
Hamilton scalar-first [w, x, y, z] quaternion |
rotation_matrix |
tuple[float, ...] |
BODY-FRD→NED 3×3 row-major matrix |
euler |
tuple[float, float, float] |
按需派生的 Z-Y-X [roll, pitch, yaw] Euler 角,单位 rad |
3.3 DerivedState
from fly_ruler_proto_python import DerivedState
d = DerivedState(
lat=37.7749,
lon=-122.4194,
altitude=500.0,
alpha=0.05,
beta=0.0,
tas=50.0,
eas=48.0,
gamma=0.1,
chi=0.2,
ias=47.5,
cas=47.5,
mach=0.15,
)
| 属性 | 类型 | 说明 |
|---|---|---|
lat |
float |
纬度 |
lon |
float |
经度 |
altitude |
float |
高度 |
alpha |
float |
迎角 |
beta |
float |
侧滑角 |
tas |
float |
真空速 |
eas |
float |
当量空速 |
gamma |
float |
航迹倾斜角 |
chi |
float |
航迹方位角 |
ias |
float | None |
指示空速 |
cas |
float | None |
校准空速 |
mach |
float | None |
马赫数 |
3.4 AircraftState
from fly_ruler_proto_python import (
AircraftState, ControlSurfaceState, DerivedState, PropulsorKind,
PropulsorState, Attitude, Vector3,
)
state = AircraftState(
position=Vector3(100.0, 200.0, -300.0),
velocity=Vector3(1.0, 2.0, 3.0),
attitude=Attitude.identity(),
angular_velocity=Vector3(0.1, 0.2, 0.3),
derived=DerivedState(lat=30.0, lon=120.0, altitude=1000.0),
control_surfaces=ControlSurfaceState(rudder_rad=0.1),
propulsors=[PropulsorState("engine", kind=PropulsorKind.JET, throttle_ratio=0.7, index=1)],
)
# 悬停/默认状态
hover = AircraftState.hover()
| 属性 | 类型 | 说明 |
|---|---|---|
position |
Vector3 |
位置 |
velocity |
Vector3 |
速度 |
attitude |
Attitude |
已校验的 BODY-FRD→NED 姿态,wire 上编码为 quaternion |
angular_velocity |
Vector3 |
角速度 |
derived |
DerivedState | None |
派生气动/导航状态 |
control_surfaces |
ControlSurfaceState | None |
标准舵面状态 |
linear_acceleration_body |
Vector3 | None |
Body-FRD 线加速度,m/s² |
propulsors |
list[PropulsorState] |
jet、propeller 或 rotor 的规范推进状态;可选 index 用于 MSFS 槽位 |
3.5 辅助函数 create_aircraft_state
from fly_ruler_proto_python import create_aircraft_state
state = create_aircraft_state(
position=(1.0, 2.0, 3.0),
velocity=(4.0, 5.0, 6.0),
attitude=(1.0, 0.1, 0.2, 0.3),
angular_velocity=(0.4, 0.5, 0.6),
derived=DerivedState(lat=30.0, lon=120.0, altitude=1000.0),
control_surfaces=ControlSurfaceState(rudder_rad=0.1),
propulsors=[PropulsorState("engine", kind=PropulsorKind.JET, throttle_ratio=0.7, index=1)],
)
提供元组形式的便捷构造,并可传入标准空气数据、舵面、Body-FRD 加速度与推进器状态。模型内部诊断量通过预注册 telemetry schema 发布,不进入 AircraftState。
4. FlyRulerClient — 高层客户端
4.1 构造参数
class FlyRulerClient:
def __init__(
self,
address: str,
aircraft_name: str,
initial_state: AircraftState | None = None,
toml_config: str = "",
heartbeat_interval_secs: float = 1.0,
telemetry_schemas: Sequence[TelemetryStreamSchema] = (),
spawn_timestamp: float | None = None,
) -> None: ...
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
address |
str |
必填 | 服务端地址,例如 "127.0.0.1:18002" |
aircraft_name |
str |
必填 | 飞行器名称 |
initial_state |
AircraftState | None |
None |
初始状态,默认悬停 |
toml_config |
str |
"" |
TOML 格式飞行器配置 |
heartbeat_interval_secs |
float |
1.0 |
心跳间隔(秒) |
telemetry_schemas |
Sequence[TelemetryStreamSchema] |
() |
在 spawn 时注册的不可变 telemetry stream |
spawn_timestamp |
float | None |
None |
spawn 使用的时间戳;使用仿真时间发布样本时应传入初始仿真时间 |
构造函数会自动完成:连接 UDP、发送 Handshake、发送 Spawn 请求。
4.2 属性
client.client_uuid # str,客户端 UUID
client.aircraft_uuid # str,飞行器 UUID
4.3 方法
update_state(state, timestamp=None)
client.update_state(state)
client.update_state(state, timestamp=123.456)
向服务端发送飞行器状态更新。
publish_telemetry(stream_id, values, timestamp=None)
from fly_ruler_proto_python import TelemetryField, TelemetryStreamSchema, TelemetryValueType
schema = TelemetryStreamSchema(
"controller",
[
TelemetryField("controller.pitch.error", "Pitch error", "Pitch", "rad"),
TelemetryField("controller.saturated", value_type=TelemetryValueType.Bool),
],
nominal_rate_hz=100.0,
)
with FlyRulerClient(address, "GTM", telemetry_schemas=[schema], spawn_timestamp=0.0) as client:
client.publish_telemetry("controller", (pitch_error, saturated), timestamp=t)
values 必须按 schema 字段顺序传入,数量和 f64/i64/bool 类型都会在 Python/Rust 边界校验。Schema 在一次 aircraft 生命周期内不可变,server 因而能在首个 sample 到来前提供字段目录。向量与矩阵应展开成稳定标量 field id,例如 sensor.imu.accel.x。
create_event(event_name, timestamp=None)
client.create_event("missile_launch")
client.create_event("missile_launch", timestamp=123.456)
发送自定义事件。
MSFS bridge 识别两个标准起落架事件:
client.create_event("flyruler.control.gear_up")
client.create_event("flyruler.control.gear_down")
despawn(reason=None, timestamp=None)
client.despawn(reason="mission_complete")
发送 Despawn 事件,标记该飞行器生命周期结束。
close()
client.close()
- 若尚未 despawn,会自动发送
Despawn(reason="client_close")。 - 停止后台任务(sender / operation / heartbeat)。
- 关闭网络连接。
4.4 上下文管理器
推荐使用 with 语句,确保退出时自动关闭:
from fly_ruler_proto_python import FlyRulerClient, create_aircraft_state
with FlyRulerClient("127.0.0.1:18002", "F-16") as aircraft:
aircraft.update_state(create_aircraft_state(position=(100.0, 0.0, -1000.0)))
aircraft.create_event("missile_launch")
5. PyClient — 底层 Rust 客户端
FlyRulerClient 内部包装了 PyClient。若需要直接使用 Rust 层暴露的客户端,可从 _core 导入:
from fly_ruler_proto_python._core import PyClient, AircraftState
client = PyClient(
"127.0.0.1:18002",
"F-16",
AircraftState.hover(),
"",
1.0,
)
PyClient 提供的方法与 FlyRulerClient 一致:
client_uuid()aircraft_uuid()update_state(state, timestamp=None)create_event(event_name, timestamp=None)despawn(reason=None, timestamp=None)close()
区别:
PyClient在析构时__del__会尝试自动调用close()。FlyRulerClient提供更符合 Python 习惯的高阶封装(上下文管理器、create_aircraft_state集成等)。
6. MSFS 多机/AI 示例
一个 FlyRulerClient 实例对应一架 aircraft。你可以:
- 在同一个 Python 进程里创建多个
FlyRulerClient。 - 或者启动多个 Python client 进程,分别发送不同 aircraft。
MSFS bridge 会把一个 aircraft 映射到 user aircraft;当 bridge 使用
--enable-ai-aircraft 启动时,其余 spawned aircraft 会作为 AI 视觉飞机渲染。
先启动 bridge:
./fly-ruler-msfs-bridge.exe \
--enable-ai-aircraft \
--ai-aircraft-title "Rafale M" \
--max-ai-aircraft 8
然后从一个进程发送三架飞机:
cd bindings/python
uv run python examples/demo_msfs_ai_client.py --aircraft-count 3
脚本会打印每架飞机的 aircraft_uuid。如果希望固定哪一架是 MSFS user aircraft,
把对应 UUID 传给 bridge:
./fly-ruler-msfs-bridge.exe \
--aircraft-id <printed-aircraft-uuid-without-dashes> \
--enable-ai-aircraft \
--ai-aircraft-title "Rafale M"
也可以启动多个独立进程,每个进程只发送一架:
uv run python examples/demo_msfs_ai_client.py --aircraft-count 3 --aircraft-index 0
uv run python examples/demo_msfs_ai_client.py --aircraft-count 3 --aircraft-index 1
uv run python examples/demo_msfs_ai_client.py --aircraft-count 3 --aircraft-index 2
注意:--ai-aircraft-title 必须是 MSFS 里可用的 aircraft title;复杂第三方飞机在
AI 模式下可能不会完整驱动座舱/HUD/插件动画,建议先用 stock aircraft 验证多机外部视景。
7. 协议版本
from fly_ruler_proto_python import PROTOCOL_VERSION, get_protocol_version
print(PROTOCOL_VERSION) # "0.3.0"
print(get_protocol_version()) # "0.3.0"
版本号来自 fly_ruler_proto_core::PROTOCOL_VERSION,所有绑定共享同一来源。
8. 日志
Python 绑定在首次创建运行时(即构造 PyClient )时,会初始化一次 tracing 订阅器。初始化是幂等的。
默认日志过滤:
warn,
fly_ruler_proto_python.client=info,
fly_ruler_proto_python.server=info,
fly_ruler_proto_core.runtime=warn,
fly_ruler_proto_core.store=warn,
fly_ruler_proto_core.transport=warn
可通过 RUST_LOG 环境变量覆盖:
RUST_LOG=debug python your_script.py
9. 完整示例
import time
from fly_ruler_proto_python import FlyRulerClient, create_aircraft_state
SERVER = "127.0.0.1:18002"
def main():
with FlyRulerClient(SERVER, "F-16", heartbeat_interval_secs=1.0) as aircraft:
print(f"client_uuid={aircraft.client_uuid}")
print(f"aircraft_uuid={aircraft.aircraft_uuid}")
for i in range(100):
state = create_aircraft_state(
position=(float(i), 0.0, -1000.0),
velocity=(10.0, 0.0, 0.0),
)
aircraft.update_state(state, timestamp=time.time())
time.sleep(0.001) # 1000Hz
aircraft.create_event("waypoint_reached", timestamp=time.time())
if __name__ == "__main__":
main()
10. 测试
cd bindings/python
source .venv/bin/activate
# Rust 侧单元测试
cargo test -p fly_ruler_proto_python
# Python 侧测试
pytest tests/
测试覆盖:
- 数据类构造与属性读写(
Vector3、Attitude、DerivedState、AircraftState) create_aircraft_state辅助函数- 协议版本一致性
FlyRulerClient包装器转发与上下文管理器行为
11. 错误处理
绑定层将 Rust 错误统一映射为 Python ConnectionError:
try:
client = FlyRulerClient("127.0.0.1:18002", "F-16")
except ConnectionError as e:
print(f"连接失败: {e}")
常见失败原因:
- 网络不可达
- 服务端未启动
- 协议版本不匹配(此时服务端会返回错误响应,连接仍可能建立,但后续操作可能失败)
12. 构建配置速查
pyproject.toml 中的关键配置:
[build-system]
requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"
[project]
name = "fly_ruler_proto_python"
version = "0.3.0"
requires-python = ">=3.10"
- 包名:
fly_ruler_proto_python - Rust crate:
bindings/python/Cargo.toml中的fly_ruler_proto_python - 入口模块:
src/lib.rs定义_core,Python 包通过__init__.py重新导出。
13. 相关文档
- 内核文档:
../../core/README.md - Godot 绑定:
../godot/README.md - 项目总览:
../../AGENTS.md - Protobuf Schema:
../../core/proto/fly_ruler.proto
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fly_ruler_proto_python-0.3.0-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: fly_ruler_proto_python-0.3.0-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 1.1 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13090fd0ab3f7d803c7c745b3cd30b4d4aecfb5d0614cbdb02a9d4ce66d47627
|
|
| MD5 |
fb8c1b7e8b89bf4f88ebd161f1a29310
|
|
| BLAKE2b-256 |
a258660e4fac2917a019286cc3975277714903a8c89c640cc7f82155b4ae1968
|
Provenance
The following attestation bundles were made for fly_ruler_proto_python-0.3.0-cp39-abi3-win_amd64.whl:
Publisher:
release.yml on WindLX/fly_ruler_proto
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fly_ruler_proto_python-0.3.0-cp39-abi3-win_amd64.whl -
Subject digest:
13090fd0ab3f7d803c7c745b3cd30b4d4aecfb5d0614cbdb02a9d4ce66d47627 - Sigstore transparency entry: 2188300633
- Sigstore integration time:
-
Permalink:
WindLX/fly_ruler_proto@de59fb79dd1f00127d9b88aecfef6e325cd68416 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/WindLX
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@de59fb79dd1f00127d9b88aecfef6e325cd68416 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fly_ruler_proto_python-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: fly_ruler_proto_python-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 1.7 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f07f2af5b4d29daf3571b684dba2326918a3a66767e914f1a53316e9736d2997
|
|
| MD5 |
7eaf1e3bb0b89f51919399b51580d7fa
|
|
| BLAKE2b-256 |
92f7e879a3b25f7a6eeaed1f24a58db2bbac3674e3f48c05b9df57f7ec654e1b
|
Provenance
The following attestation bundles were made for fly_ruler_proto_python-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on WindLX/fly_ruler_proto
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fly_ruler_proto_python-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
f07f2af5b4d29daf3571b684dba2326918a3a66767e914f1a53316e9736d2997 - Sigstore transparency entry: 2188300784
- Sigstore integration time:
-
Permalink:
WindLX/fly_ruler_proto@de59fb79dd1f00127d9b88aecfef6e325cd68416 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/WindLX
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@de59fb79dd1f00127d9b88aecfef6e325cd68416 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fly_ruler_proto_python-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: fly_ruler_proto_python-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f3eb1862845bcd46dd7429c7d3b3d356e09716de18cd43cfa56d4a037f965ab
|
|
| MD5 |
e7267190c1ed38c3f6e669c3a2377178
|
|
| BLAKE2b-256 |
4fccb63094ca72dc2c0016847aa4590492b5150f42f18ea7c8857376a0edb887
|
Provenance
The following attestation bundles were made for fly_ruler_proto_python-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on WindLX/fly_ruler_proto
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fly_ruler_proto_python-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
2f3eb1862845bcd46dd7429c7d3b3d356e09716de18cd43cfa56d4a037f965ab - Sigstore transparency entry: 2188300698
- Sigstore integration time:
-
Permalink:
WindLX/fly_ruler_proto@de59fb79dd1f00127d9b88aecfef6e325cd68416 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/WindLX
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@de59fb79dd1f00127d9b88aecfef6e325cd68416 -
Trigger Event:
push
-
Statement type: