Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
abey79 committed Feb 24, 2025
1 parent eaece8c commit 6a8575b
Show file tree
Hide file tree
Showing 4 changed files with 481 additions and 72 deletions.
37 changes: 37 additions & 0 deletions examples/python/px4_ulog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!--[metadata]
title = "PX4 ulog"
tags = ["2D", "3D", "map", "drone"]
description = "Load and visualize a PX4 ulog drone log file"
-->


<!--
TODO:
thumbnail = "https://static.rerun.io/air_traffic_data/348dd2def3a55fd0bf481a35a0765eeacfa20b6f/480w.png"
thumbnail_dimensions = [480, 480]
#channel = "nightly"
-->

TODO: complete thse

Check warning on line 15 in examples/python/px4_ulog/README.md

View workflow job for this annotation

GitHub Actions / Checks / Spell Check

"thse" should be "the" or "these" or "this".

## Run the code

To run this example, make sure you have Python version at least 3.9, the Rerun repository checked out and the latest SDK installed:
```bash
pip install --upgrade rerun-sdk # install the latest Rerun SDK
git clone [email protected]:rerun-io/rerun.git # Clone the repository
cd rerun
git checkout latest # Check out the commit matching the latest SDK release
```
Install the necessary libraries specified in the requirements file:
```bash
pip install -e examples/python/air_traffic_data
```
To experiment with the provided example, simply execute the main Python script:
```bash
python -m air_traffic_data
```
If you wish to customize it, explore additional features, or save it use the CLI with the `--help` option for guidance:
```bash
python -m air_traffic_data --help
```
200 changes: 200 additions & 0 deletions examples/python/px4_ulog/px4_ulog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
from __future__ import annotations

import io
from argparse import ArgumentParser
from pathlib import Path

import numpy as np
import requests
import rerun as rr
import rerun.blueprint as rrb
from pyulog import ULog
from tqdm import tqdm

DATASET_DIR = Path(__file__).parent / "dataset"
if not DATASET_DIR.exists():
DATASET_DIR.mkdir()

ULOG_FILE_PATH = DATASET_DIR / "log_file.ulg"

ULOG_FILE_URL = "https://github.com/foxglove/ulog/raw/refs/heads/main/tests/log_6_2021-7-20-11-41-56.ulg"


def download_with_progress(url: str, what: str) -> io.BytesIO:
"""Download file with tqdm progress bar."""
chunk_size = 1024 * 1024
resp = requests.get(url, stream=True)
total_size = int(resp.headers.get("content-length", 0))
with tqdm(
desc=f"Downloading {what}…",
total=total_size,
unit="iB",
unit_scale=True,
unit_divisor=1024,
) as progress:
download_file = io.BytesIO()
for data in resp.iter_content(chunk_size):
download_file.write(data)
progress.update(len(data))

download_file.seek(0)
return download_file


def log_initial_parameters(log_file: ULog) -> None:
param_table_content = "\n".join([f"| {param} | {value} |" for param, value in log_file.initial_parameters.items()])

param_text = f"""
## Initial Parameters
| Parameter | Value |
|-----------|-------|
{param_table_content}
"""

rr.log("initial_parameters", rr.TextDocument(text=param_text, media_type="text/markdown"), static=True)


def log_messages(log_file: ULog) -> None:
for msg in log_file.logged_messages:
rr.set_time_seconds("timestamp", msg.timestamp / 1e6)
rr.log("messages", rr.TextLog(text=msg.message, level=msg.log_level_str()))


def log_data(log_file: ULog) -> None:
for data in log_file.data_list:
name = data.name

columns = data.data.keys() - {"timestamp"}

time_columns = [rr.TimeSecondsColumn("timestamp", data.data["timestamp"] / 1e6)]

if "timestamp_sample" in columns:
time_columns.append(rr.TimeSecondsColumn("timestamp_sample", data.data["timestamp_sample"] / 1e6))
columns.remove("timestamp_sample")

all_columns = {}
for column in columns:
try:
col_name, idx = column.split("[")
idx = int(idx.strip("]"))
except ValueError:
col_name = column
idx = None

if idx is None:
all_columns[col_name] = data.data[column]
else:
all_columns.setdefault(col_name, {})[idx] = data.data[column]

for col_name, col_data in all_columns.items():
entity_path = f"{name}/{col_name.replace('.', '/')}"

if isinstance(col_data, dict):
arr = np.vstack([col_data[i] for i in sorted(col_data.keys())]).T
else:
arr = col_data

rr.send_columns(entity_path, indexes=time_columns, columns=rr.Scalar.columns(scalar=arr))

if name == "vehicle_global_position":
latlon = np.vstack([data.data["lat"], data.data["lon"]]).T
rr.send_columns(name, indexes=time_columns, columns=rr.GeoPoints.columns(positions=latlon))
elif name == "vehicle_local_position":
pos3d = np.vstack([data.data["x"], data.data["y"], -data.data["z"]]).T
pos2d = np.vstack([data.data["x"], data.data["y"]]).T
rr.send_columns(
f"{name}/pos",
indexes=time_columns,
columns=[
*rr.Points3D.columns(positions=pos3d),
*rr.Points2D.columns(positions=pos2d),
],
)

v2d = np.vstack([data.data["vx"], data.data["vy"]]).T
v3d = np.vstack([data.data["vx"], data.data["vy"], -data.data["vz"]]).T
rr.send_columns(
f"{name}/vel",
indexes=time_columns,
columns=[
*rr.Arrows3D.columns(origins=pos3d, vectors=v3d),
*rr.Arrows2D.columns(origins=pos2d, vectors=v2d),
],
)
elif name == "position_setpoint_triplet":
latlon = np.vstack([data.data["next.lat"], data.data["next.lon"]]).T
radius = data.data["next.loiter_radius"]

rr.send_columns(
f"{name}/next/latlon",
indexes=time_columns,
columns=rr.GeoPoints.columns(positions=latlon, radii=radius)
)


def main() -> None:
parser = ArgumentParser(description="Loada and visualize a PX4 ulog file")

parser.add_argument("--uuid", type=str, help="UUID of log file on `review.px4.io`")

rr.script_add_args(parser)
args = parser.parse_args()

rr.script_setup(args, "rerun_example_px4_log")

if args.uuid is not None:
# TODO: download
file_path = DATASET_DIR / f"{args.uuid}.ulg"
else:
file_path = ULOG_FILE_PATH
if not file_path.exists():
ulog_file_data = download_with_progress(ULOG_FILE_URL, "ulog file")
file_path.write_bytes(ulog_file_data.read())

log_file = ULog(str(file_path))
log_initial_parameters(log_file)
log_messages(log_file)
log_data(log_file)

blueprint = rrb.Vertical(
rrb.Horizontal(
rrb.Tabs(
rrb.Spatial2DView(
name="2D",
overrides={
"vehicle_local_position/pos": [
# TODO: broken because of #8557
rrb.VisibleTimeRange(
"timestamp",
start=rrb.TimeRangeBoundary.infinite(),
end=rrb.TimeRangeBoundary.infinite(),
)
],
},
),
rrb.Spatial3DView(
name="3D",
overrides={
"vehicle_local_position/pos": [
rrb.VisibleTimeRange(
"timestamp",
start=rrb.TimeRangeBoundary.infinite(),
end=rrb.TimeRangeBoundary.infinite(),
)
],
},
),
rrb.MapView(name="Map"),
),
rrb.TextLogView(),
column_shares=[3, 1],
),
rrb.Tabs(*[rrb.TimeSeriesView(origin=data.name) for data in log_file.data_list]),
)

rr.send_blueprint(blueprint, make_active=False, make_default=True)


if __name__ == "__main__":
main()
19 changes: 19 additions & 0 deletions examples/python/px4_ulog/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[project]
name = "px4_ulog"
version = "0.1.0"
readme = "README.md"
dependencies = [
"numpy",
"pyulog",
"requests",
"rerun-sdk",
"tqdm",
]

[project.scripts]
px4_ulog = "px4_ulog:main"


[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Loading

0 comments on commit 6a8575b

Please sign in to comment.