UAV Systems Engineering · M7-L2 · Hands-on lab (runs on your machine)
Analyze timestamp synchronization
Record the data, then ask what the timestamps really say.
What you will do
Record a bag with an IMU (at least 100 Hz) and a camera while rotating the sensor rig by hand (all three axes, a few minutes). You will measure how regular each stream’s timestamps are, estimate the rate and drift of each clock relative to the other, estimate the camera-IMU time offset from the angular rates, and report the numbers you would put in an interface control document. The reference script below runs on synthetic data so you can check your pipeline before using a real recording; replace the loader with your bag reader (rosbag2 and the Python rosbag2_py API, or an export to CSV).
Before you start
- Lessons M7-T8 (synchronisation), M7-T10 (software timestamping), M7-T16 (extrinsic calibration).
- A recording with IMU and camera, or use the synthetic data.
- Python 3 with NumPy.
Analysis script (synthetic data)
import numpy as np
rng = np.random.default_rng(21)
# ---- replace this block with a loader of your bag: arrays t_imu, w_imu (n x 3), t_cam (arrival stamps), w_cam (n x 3 angular rate from VO or optical flow)
T = 120.0; t_imu = np.arange(0, T, 1 / 200.0)
f = rng.uniform(0.3, 1.2, (3, 3)); p = rng.uniform(0, 6.28, (3, 3))
omega = lambda t: sum(0.9 * np.sin(2 * np.pi * f[:, i] * t[:, None] + p[:, i]) for i in range(3))
w_imu = omega(t_imu) + 0.01 * rng.standard_normal((t_imu.size, 3))
true_cam_period = (1 / 30.0) * (1 + 60e-6); t_cam_true = 2.0 + np.arange(int((T - 4) / true_cam_period)) * true_cam_period
delay_fixed, td_true = 0.018, 0.030 # fixed transfer delay of the stamps, and true camera-IMU offset
t_cam = t_cam_true + delay_fixed + rng.gamma(1.5, 0.003, t_cam_true.size) # arrival-time stamps (software)
w_cam = omega(t_cam_true - td_true) + 0.02 * rng.standard_normal((t_cam.size, 3)) # camera angular rate sampled at the true sample times (shifted by the offset)
# ---- end of replaceable block
def stream_stats(t, name, nominal):
dt = np.diff(t); print(f"{name}: mean rate {1 / dt.mean():.3f} Hz (nominal {1 / nominal:.1f}); period std {dt.std() * 1e3:.3f} ms, p99.9 |error| {np.percentile(np.abs(dt - nominal), 99.9) * 1e3:.2f} ms, max gap {dt.max() * 1e3:.1f} ms")
stream_stats(t_imu, 'imu', 1 / 200.0); stream_stats(t_cam, 'cam (arrival stamps)', 1 / 30.0)
# 1) smooth the camera stamps by a lower-envelope line (see M7-T10): the smoothed stamps estimate the schedule up to the fixed delay
k = np.arange(t_cam.size, dtype=float)
hull = []
for pt in zip(k, t_cam):
while len(hull) >= 2 and (hull[-1][0] - hull[-2][0]) * (pt[1] - hull[-2][1]) - (hull[-1][1] - hull[-2][1]) * (pt[0] - hull[-2][0]) <= 0: hull.pop()
hull.append(pt)
best = max(((t_cam.size * (y0 - (y1 - y0) / (x1 - x0) * x0) + (y1 - y0) / (x1 - x0) * k.sum(), (y1 - y0) / (x1 - x0), y0 - (y1 - y0) / (x1 - x0) * x0) for (x0, y0), (x1, y1) in zip(hull[:-1], hull[1:])))
period_hat, t0_hat = best[1], best[2]; t_cam_s = t0_hat + period_hat * k
print(f"estimated camera period {period_hat * 1e3:.4f} ms ({1 / period_hat:.4f} Hz), drift vs nominal {(period_hat * 30.0 - 1) * 1e6:.1f} ppm")
# 2) camera-IMU time offset (including the fixed delay) from the correlation of angular-rate magnitudes
n_cam = np.linalg.norm(w_cam, axis=1); n_imu = np.linalg.norm(w_imu, axis=1); cs = []
taus = np.arange(-0.1, 0.1, 0.0005)
for tau in taus: cs.append(np.corrcoef(np.interp(t_cam_s - tau, t_imu, n_imu), n_cam)[0, 1])
tau_hat = taus[int(np.argmax(cs))]
print(f"offset of the smoothed camera stamps relative to the IMU clock: tau = {tau_hat * 1e3:.1f} ms (fixed stamp delay + true offset = {(delay_fixed + td_true) * 1e3:.1f} ms)")
# convention: the IMU signal at (camera stamp - tau) matches the camera signal, so a camera stamp that is late by 48 ms gives tau = +48 ms
assert abs(tau_hat - (delay_fixed + td_true)) < 0.003Procedure on real data
- Record: at least five minutes, IMU ≥ 100 Hz, camera 20–30 Hz, rotating the rig through all axes with a mix of slow and fast motion. Keep exposure fixed if possible.
- Run the stream statistics: report the mean rate, period standard deviation, p99.9 period error and the maximum gap for each sensor.
- Estimate the clock rate difference between the camera schedule and the IMU (the smoothed period against the nominal one) and state it in ppm.
- Estimate the offset with the angular-rate correlation, then repeat on a second recording and report the difference between the two as the repeatability.
- Write the interface entry (rate, jitter, stamp semantics, offset ± repeatability) for the ICD.
The sign of the offset must be fixed by a convention: here tau is the amount by which the camera stamps are late, i.e. the IMU signal at (camera stamp − tau) matches the camera signal. Test the convention with a known shift: delay the camera stamps by 20 ms in software and check that tau increases by 20 ms.
How to know it worked
- The script runs on the synthetic data and its assertion passes.
- You have stream statistics, a drift estimate and an offset with a repeatability for your real recording.
- A deliberate 20 ms delay added to the camera stamps increases the estimated tau by about 20 ms.
If it does not work
| Symptom | Likely cause | What to check |
|---|---|---|
| Correlation peak is flat or double-peaked | Too little or too periodic rotation | Excite all axes with irregular motion; use a longer recording |
| Offset differs between recordings by several ms | Rate drift not removed or poor excitation | Smooth the stamps first; check the rate estimate |
| Camera rate looks irregular | Arrival stamps and USB bursts | Use device or hardware stamps where available |
| Angular rate from vision is noisy | Poor features or motion blur | Slow down, improve lighting, use a gyro-aided estimate |
References
Record your progress
Saved in this browser. This lab runs on your own machine, so the site cannot check it for you.