MAVPose reads ArduPilot and PX4 logs into pandas DataFrames: every message type on one time axis, values in real units, and "value not provided" markers removed. Optional add-ons let AI agents read your logs and turn plain-English questions into plots. Like the Log Pose — it locks onto your data and charts the course.
Guarantees
Raw flight logs mix clocks, store scaled integers and hide "no reading" sentinels among real values. MAVPose fixes all of that, the same way for every format.
Receive time, TimeUS since boot, epoch microseconds, ULog timestamps: every message type lands on one shared time_s axis, so series from different messages line up.
Millimetres, degE7 and millivolts become metres, degrees and volts, using the autopilot's own unit definitions. Every column reports its unit.
Sentinels such as current_battery = -1 or eph = 65535 become NaN. The markers come from the official MAVLink (441 fields) and PX4 (31 fields) definitions, not guesses.
Power loss and bad transfers happen. Corrupt packets are skipped and counted in log.stats instead of ending the parse.
pip install mavpose pulls in 10 packages and no AI libraries. The assistant and the MCP server are opt-in extras.
A small, documented public API with a written stability policy, so other tools can build on it.
Log formats
mavpose.load() detects the format. The same methods, units and guarantees apply to each.
| Format | Extension | Autopilot |
|---|---|---|
| MAVLink telemetry log | .tlog | ArduPilot, PX4, any MAVLink vehicle |
| DataFlash binary / text log | .bin, .log | ArduPilot |
| ULog | .ulg | PX4 |
Optional add-ons
Both add-ons sit on top of the same data layer, so they inherit its guarantees. Neither ever sends a raw log to a model.
mavpose[mcp] — mavpose-mcp
--root folders. Symlinked logs and .. escapes are refused.mavpose[chat] — mavpose
plot.json; --spec redraws it on any log, no model or key needed.MCP tools
time_s axis, with unitsTech stack
The core depends only on the standard log readers and pandas. Everything AI-related lives in extras.
| Layer | Library / Service |
|---|---|
| Log readers | pymavlink ≥ 2.4.48 · pyulog ≥ 1.1 |
| Data | pandas ≥ 2.0 + pyarrow ≥ 14.0 |
| Units & markers | Generated from MAVLink XML and PX4 msg definitions |
| MCP server | mcp ≥ 2.3 (stdio) |
| Plot assistant | LangChain · any provider, incl. local models |
| Plotting | matplotlib ≥ 3.8 |
| Plots | JSON specs drawn by MAVPose (code mode: restricted runner) |
| Lint / CI | ruff + pytest, Python 3.10–3.13 |
Installation
Python 3.10 or newer. Pick only the parts you need.
# Data layer only: 10 packages, no AI dependencies pip install mavpose # Add the MCP server for AI agents pip install "mavpose[mcp]" # Add the plain-English plot assistant pip install "mavpose[chat]"
Need a sample log? Download here →
Connect an MCP client — most use an entry like this; file and key names vary by client
{
"mcpServers": {
"mavpose": {
"command": "mavpose-mcp",
"args": ["--root", "/home/you/flight-logs"]
}
}
}
Plot assistant CLI — needs [chat] and a key for your AI provider (or a local model)
mavpose log_file [options] log_file MAVLink .tlog, ArduPilot .bin/.log, or PX4 .ulg -p, --prompt TEXT Plot request (omit for interactive mode) -m, --model P:M provider:model, e.g. anthropic:<model>, ollama:llama3.1:8b --spec FILE Redraw a saved plot.json; no model needed -o, --out-dir DIR Save outputs to another folder -f, --format FMT png (default), svg or pdf --dpi N Resolution (default 150; 400 with --code) --code Have the model write a Python script instead -r, --retries N Attempts per request (default: 3) -v, --verbose Enable debug logging # Writes plot.png, plot.json and telemetry.parquet next to the log (or in --out-dir)
Plot assistant models
Set your provider's key and choose a model with --model provider:model or MAVPOSE_MODEL. Also works with OpenRouter, Groq, DeepSeek, Mistral, Together, LM Studio and any OpenAI-compatible server. Local models keep everything on your machine.
openai
OPENAI_API_KEYopenai:<model>
anthropic
ANTHROPIC_API_KEYanthropic:<model>
gemini
GEMINI_API_KEYgemini:<model>
ollama
no key neededollama:llama3.1:8b
One line to load a log. Real units, one clock, no fake readings.