From each sensor's 9D action [Δp(3), Rot6D(ΔR)(6)] you can rasterize a dense per-pixel 3D displacement field — the apparent motion of a world-static contact:
r6D → ΔR_t R^app = ΔR_tᵀ p^app = −ΔR_tᵀ Δp_t M_t(u,v) = (R^app − I) P_t(u,v) + p^app P_t = flat gel plane, z = 0
episode_001 · 1524 frames @ 6 fps · left + right sensors · 40×30 mm gel
Left pane: the measured AllTracker flow overlay. Right pane: the action-derived field, drawn at its own true scale. The prior's arrows saturate the frame because the sensor really is moving that far — the gel simply does not show it.
10 s from the highest-motion window (t = 68.7–78.7 s). Three panes: measured flow, the action prior divided by its mean ratio, and the prior at true scale. Even after removing the scale difference, the directions do not track: the prior is a smooth global sweep, the measurement is sparse and localized to where contact actually occurs.
| Sensor | measured mean | prior mean | ratio | corr | EPE vs baseline |
|---|---|---|---|---|---|
| left (+x,−y) | 0.260 px | 164.5 px | 632× | +0.22 | 164.4 vs 0.26 |
| right (+y,−x) | 0.178 px | 96.4 px | 542× | +0.36 | 96.4 vs 0.18 |
The formula assumes a world-static contact that the gel slides freely beneath. In reality the gel is bonded to the sensor and friction makes the contact patch stick: it travels with the sensor, so the image barely changes. Measured 0.26 px ≈ 0.04 mm, while the sensor genuinely moves tens of millimetres per 1/6 s frame. The prior predicts sensor motion; the tactile image records the residual slip.
That makes it a valid, cheap spatial encoding of the action — a per-pixel layout of the 6-DoF command, decomposed below into a uniform translation term and a rotation term that vanishes at the image centre. It is not a predictor of tactile optical flow, and should not be used as a flow supervision target.
# build the maps at the latent frame rate (all 36 episodes)
python data_preprocessing/build_tactile_motion_maps.py --action-file sensor_actions_fps6.pt
→ <episode>/tactile_motion_maps_fps6.npz motion_{left,right}: (N,60,80,3) float16, metres
# score against measured flow, testing the axis convention rather than assuming it
python data_preprocessing/compare_motion_map_to_flow.py --episode motherboard_0510_episode_001
One episode (1524 frames, both sensors). The same comparison on episode_004 gives ratios of 158–184× and correlations of +0.06/+0.12 — same conclusion, different magnitude. The Δz channel has no optical-flow counterpart and remains unvalidated.