Skip to content

Add UZH-FPV drone racing dataset (UZHFPV)#331

Open
vlordier wants to merge 1 commit intoneuromorphs:developfrom
vlordier:copilot/add-datasets-from-fpv-ifi-uzh-ch
Open

Add UZH-FPV drone racing dataset (UZHFPV)#331
vlordier wants to merge 1 commit intoneuromorphs:developfrom
vlordier:copilot/add-datasets-from-fpv-ifi-uzh-ch

Conversation

@vlordier
Copy link
Copy Markdown

Co-authored-by: vlordier <5443125+vlordier@users.noreply.github.com>
Agent-Logs-Url: https://github.com/vlordier/tonic/sessions/33daba03-974f-4ca9-a7f3-967a32a6a818
Copilot AI review requested due to automatic review settings March 25, 2026 13:30
@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 41.66667% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.21%. Comparing base (8f95aec) to head (6bdb240).
⚠️ Report is 5 commits behind head on develop.

Files with missing lines Patch % Lines
tonic/datasets/uzh_fpv.py 40.84% 42 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #331      +/-   ##
===========================================
- Coverage    76.50%   74.21%   -2.29%     
===========================================
  Files           48       50       +2     
  Lines         2992     3126     +134     
===========================================
+ Hits          2289     2320      +31     
- Misses         703      806     +103     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for the UZH-FPV drone racing dataset to Tonic, including a new Dataset implementation, package exports, documentation listing, and basic tests.

Changes:

  • Introduce tonic.datasets.UZHFPV to download/read UZH-FPV ROS bag recordings (events/IMU/images + optional GT).
  • Export UZHFPV from tonic.datasets and include it in the datasets docs autosummary.
  • Add unit tests for recording selection and validation.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
tonic/datasets/uzh_fpv.py New dataset implementation for UZH-FPV, including download, existence checks, and ROS bag parsing.
tonic/datasets/__init__.py Exposes UZHFPV in the datasets package namespace and __all__.
test/test_uzh_fpv.py Adds tests for recording selection (single, multiple, all) and validation behavior.
docs/datasets.rst Lists UZHFPV under pose estimation / odometry / SLAM datasets.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tonic/datasets/uzh_fpv.py
Comment on lines +139 to +161
# Normalize timestamps to start at 0 and convert from seconds to microseconds
events = topics["/dvs/events"]
events["ts"] -= events["ts"][0]
events["ts"] *= 1e6
events = make_structured_array(
events["ts"], events["x"], events["y"], events["pol"], dtype=self.dtype
)

# Normalize and convert IMU timestamps to microseconds
imu = topics["/dvs/imu"]
imu["ts"] = ((imu["ts"] - imu["ts"][0]) * 1e6).astype(int)

# Normalize and convert image timestamps to microseconds, stack frames
images = topics["/dvs/image_raw"]
images["frames"] = np.stack(images["frames"])
images["ts"] = ((images["ts"] - images["ts"][0]) * 1e6).astype(int)

data = events, imu, images

if has_gt and "/groundtruth/odometry" in topics:
# Normalize and convert ground truth timestamps to microseconds
target = topics["/groundtruth/odometry"]
target["ts"] = ((target["ts"] - target["ts"][0]) * 1e6).astype(int)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ground-truth timestamps are normalized against the first GT sample, which can desynchronize GT from events/IMU/images if those streams use different t0. Normalize GT timestamps using the same shared reference time as the rest of the modalities so consumers can directly compare timelines.

Suggested change
# Normalize timestamps to start at 0 and convert from seconds to microseconds
events = topics["/dvs/events"]
events["ts"] -= events["ts"][0]
events["ts"] *= 1e6
events = make_structured_array(
events["ts"], events["x"], events["y"], events["pol"], dtype=self.dtype
)
# Normalize and convert IMU timestamps to microseconds
imu = topics["/dvs/imu"]
imu["ts"] = ((imu["ts"] - imu["ts"][0]) * 1e6).astype(int)
# Normalize and convert image timestamps to microseconds, stack frames
images = topics["/dvs/image_raw"]
images["frames"] = np.stack(images["frames"])
images["ts"] = ((images["ts"] - images["ts"][0]) * 1e6).astype(int)
data = events, imu, images
if has_gt and "/groundtruth/odometry" in topics:
# Normalize and convert ground truth timestamps to microseconds
target = topics["/groundtruth/odometry"]
target["ts"] = ((target["ts"] - target["ts"][0]) * 1e6).astype(int)
# Use a shared reference time (t0) based on the first event timestamp
base_ts = topics["/dvs/events"]["ts"][0]
# Normalize event timestamps to start at 0 (shared t0) and convert from seconds to microseconds
events = topics["/dvs/events"]
events["ts"] = (events["ts"] - base_ts) * 1e6
events = make_structured_array(
events["ts"], events["x"], events["y"], events["pol"], dtype=self.dtype
)
# Normalize and convert IMU timestamps to microseconds using the shared reference time
imu = topics["/dvs/imu"]
imu["ts"] = ((imu["ts"] - base_ts) * 1e6).astype(int)
# Normalize and convert image timestamps to microseconds using the shared reference time, stack frames
images = topics["/dvs/image_raw"]
images["frames"] = np.stack(images["frames"])
images["ts"] = ((images["ts"] - base_ts) * 1e6).astype(int)
data = events, imu, images
if has_gt and "/groundtruth/odometry" in topics:
# Normalize and convert ground truth timestamps to microseconds using the shared reference time
target = topics["/groundtruth/odometry"]
target["ts"] = ((target["ts"] - base_ts) * 1e6).astype(int)

Copilot uses AI. Check for mistakes.
Comment thread test/test_uzh_fpv.py
Comment on lines +8 to +14
def test_single_recording():
with patch.object(tonic.datasets.UZHFPV, "_check_exists", return_value=True):
dataset = tonic.datasets.UZHFPV(
save_to="data",
recording="indoor_forward_3",
)
assert len(dataset) == 1
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current tests only validate recording selection/validation and don’t exercise getitem behavior (ROS bag parsing, timestamp conversion/normalization, transform hooks, and GT presence/absence). Add a unit test that patches importRosbag to return a minimal synthetic topics dict and asserts the returned events dtype/ordering, timestamp units, and that data/target transforms are applied as expected.

Copilot uses AI. Check for mistakes.
Comment thread tonic/datasets/uzh_fpv.py
Comment on lines +139 to +161
# Normalize timestamps to start at 0 and convert from seconds to microseconds
events = topics["/dvs/events"]
events["ts"] -= events["ts"][0]
events["ts"] *= 1e6
events = make_structured_array(
events["ts"], events["x"], events["y"], events["pol"], dtype=self.dtype
)

# Normalize and convert IMU timestamps to microseconds
imu = topics["/dvs/imu"]
imu["ts"] = ((imu["ts"] - imu["ts"][0]) * 1e6).astype(int)

# Normalize and convert image timestamps to microseconds, stack frames
images = topics["/dvs/image_raw"]
images["frames"] = np.stack(images["frames"])
images["ts"] = ((images["ts"] - images["ts"][0]) * 1e6).astype(int)

data = events, imu, images

if has_gt and "/groundtruth/odometry" in topics:
# Normalize and convert ground truth timestamps to microseconds
target = topics["/groundtruth/odometry"]
target["ts"] = ((target["ts"] - target["ts"][0]) * 1e6).astype(int)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timestamp normalization uses each stream’s own first timestamp as t0 (events, IMU, images). This breaks cross-sensor alignment (e.g., IMU/image timestamps won’t be comparable to event timestamps). Consider computing a single reference time (e.g., the earliest timestamp across available topics, or the events’ first timestamp) and subtracting that from all modalities before converting to microseconds.

Suggested change
# Normalize timestamps to start at 0 and convert from seconds to microseconds
events = topics["/dvs/events"]
events["ts"] -= events["ts"][0]
events["ts"] *= 1e6
events = make_structured_array(
events["ts"], events["x"], events["y"], events["pol"], dtype=self.dtype
)
# Normalize and convert IMU timestamps to microseconds
imu = topics["/dvs/imu"]
imu["ts"] = ((imu["ts"] - imu["ts"][0]) * 1e6).astype(int)
# Normalize and convert image timestamps to microseconds, stack frames
images = topics["/dvs/image_raw"]
images["frames"] = np.stack(images["frames"])
images["ts"] = ((images["ts"] - images["ts"][0]) * 1e6).astype(int)
data = events, imu, images
if has_gt and "/groundtruth/odometry" in topics:
# Normalize and convert ground truth timestamps to microseconds
target = topics["/groundtruth/odometry"]
target["ts"] = ((target["ts"] - target["ts"][0]) * 1e6).astype(int)
# Extract raw modalities
events = topics["/dvs/events"]
imu = topics["/dvs/imu"]
images = topics["/dvs/image_raw"]
# Compute a common reference time (earliest timestamp across available streams)
t0_candidates = [events["ts"][0], imu["ts"][0], images["ts"][0]]
gt_topic = None
if has_gt and "/groundtruth/odometry" in topics:
gt_topic = topics["/groundtruth/odometry"]
t0_candidates.append(gt_topic["ts"][0])
t0 = min(t0_candidates)
# Normalize event timestamps to common t0 and convert from seconds to microseconds
events_ts = ((events["ts"] - t0) * 1e6).astype(int)
events = make_structured_array(
events_ts, events["x"], events["y"], events["pol"], dtype=self.dtype
)
# Normalize and convert IMU timestamps to microseconds using common t0
imu["ts"] = ((imu["ts"] - t0) * 1e6).astype(int)
# Normalize and convert image timestamps to microseconds using common t0, stack frames
images["frames"] = np.stack(images["frames"])
images["ts"] = ((images["ts"] - t0) * 1e6).astype(int)
data = events, imu, images
if gt_topic is not None:
# Normalize and convert ground truth timestamps to microseconds using common t0
target = gt_topic
target["ts"] = ((target["ts"] - t0) * 1e6).astype(int)

Copilot uses AI. Check for mistakes.
Comment thread tonic/datasets/uzh_fpv.py
Comment on lines +141 to +161
events["ts"] -= events["ts"][0]
events["ts"] *= 1e6
events = make_structured_array(
events["ts"], events["x"], events["y"], events["pol"], dtype=self.dtype
)

# Normalize and convert IMU timestamps to microseconds
imu = topics["/dvs/imu"]
imu["ts"] = ((imu["ts"] - imu["ts"][0]) * 1e6).astype(int)

# Normalize and convert image timestamps to microseconds, stack frames
images = topics["/dvs/image_raw"]
images["frames"] = np.stack(images["frames"])
images["ts"] = ((images["ts"] - images["ts"][0]) * 1e6).astype(int)

data = events, imu, images

if has_gt and "/groundtruth/odometry" in topics:
# Normalize and convert ground truth timestamps to microseconds
target = topics["/groundtruth/odometry"]
target["ts"] = ((target["ts"] - target["ts"][0]) * 1e6).astype(int)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMU timestamps are normalized relative to the first IMU sample rather than the same time origin as events/images. For synchronized sensor fusion use-cases, normalize IMU (and image) timestamps against the same reference time used for events so timelines remain consistent.

Suggested change
events["ts"] -= events["ts"][0]
events["ts"] *= 1e6
events = make_structured_array(
events["ts"], events["x"], events["y"], events["pol"], dtype=self.dtype
)
# Normalize and convert IMU timestamps to microseconds
imu = topics["/dvs/imu"]
imu["ts"] = ((imu["ts"] - imu["ts"][0]) * 1e6).astype(int)
# Normalize and convert image timestamps to microseconds, stack frames
images = topics["/dvs/image_raw"]
images["frames"] = np.stack(images["frames"])
images["ts"] = ((images["ts"] - images["ts"][0]) * 1e6).astype(int)
data = events, imu, images
if has_gt and "/groundtruth/odometry" in topics:
# Normalize and convert ground truth timestamps to microseconds
target = topics["/groundtruth/odometry"]
target["ts"] = ((target["ts"] - target["ts"][0]) * 1e6).astype(int)
reference_time = events["ts"][0]
events["ts"] = (events["ts"] - reference_time) * 1e6
events = make_structured_array(
events["ts"], events["x"], events["y"], events["pol"], dtype=self.dtype
)
# Normalize and convert IMU timestamps to microseconds using the same reference time as events
imu = topics["/dvs/imu"]
imu["ts"] = ((imu["ts"] - reference_time) * 1e6).astype(int)
# Normalize and convert image timestamps to microseconds, stack frames, using the same reference time
images = topics["/dvs/image_raw"]
images["frames"] = np.stack(images["frames"])
images["ts"] = ((images["ts"] - reference_time) * 1e6).astype(int)
data = events, imu, images
if has_gt and "/groundtruth/odometry" in topics:
# Normalize and convert ground truth timestamps to microseconds using the same reference time
target = topics["/groundtruth/odometry"]
target["ts"] = ((target["ts"] - reference_time) * 1e6).astype(int)

Copilot uses AI. Check for mistakes.
@vlordier
Copy link
Copy Markdown
Author

@copilot open a new pull request to apply changes based on the comments in this thread

@vlordier
Copy link
Copy Markdown
Author

@copilot fix coverage : ❌ Patch coverage is 41.66667% with 42 lines in your changes missing coverage. Please review.

@vlordier
Copy link
Copy Markdown
Author

@copilot open a new pull request to apply changes based on the comments in this thread

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants