Skip to content

6d: continuous rotation representation, bimanual layouts, shared norm helpers - #653

Merged
RyanPCo merged 1 commit into
mainfrom
ryanco/rot6d-core
Oct 19, 2026
Merged

RyanPCo merged 1 commit into
mainfrom
ryanco/rot6d-core

Conversation

@RyanPCo

@RyanPCo RyanPCo commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Branch 1 of 8 in the split of #640, which is the tip of the stack and carries the map of all eight.
Unit suite on this branch: 216 passed, 0 failed.

Groundwork for the 6D action pipeline, with no behavior change anywhere.

pose_utils gains _ypr_to_rot6d / _rot6d_to_ypr (numpy, ZYX euler <-> the first
two columns of the rotation matrix, Gram-Schmidt on the way back so a
non-orthonormal model prediction still yields a proper rotation), matching the
column convention of the torch helpers in action_utils. BIMANUAL_CARTESIAN_LAYOUTS
maps a vector width (12/14 ypr, 18/20 6D) to its xyz / rot / grip channel
indices; the norm-stat bounds check and the eval metrics both consume it to know
which channel is which.

action_chunk_transforms gains CartesianYPRToRot6D / CartesianRot6DToYPR, which
convert a bimanual cartesian vector between xyz+ypr(+gripper) and
xyz+rot6d(+gripper) while preserving the numpy/tensor type of the input.

action_utils extracts the normalization math that was inlined in
MultiDataset._apply_norm_one / ._apply_unnorm_one into module-level
_apply_norm_one / _apply_unnorm_one, over a shared _norm_center_scale. _eps
preserves the historical denominators exactly (std + 1e-6 for zscore,
(hi - lo) + 1e-6 for minmax/quantile, i.e. half_range + 0.5e-6), so the three
norm modes are numerically identical to before; MultiDataset now delegates.

Co-Authored-By: Claude Opus 5 (1M context) [email protected]

🤖 Generated with Claude Code

RyanPCo commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@RyanPCo
RyanPCo marked this pull request as ready for review September 29, 2026 21:27
@RyanPCo

RyanPCo commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@greptile-apps

greptile-apps Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Adds rotation representation and normalization helpers to data pipeline.

The PR should not merge until minmax and quantile normalization retain their previous behavior; the tensor-device concern is non-blocking.

Findings

  1. P1 Normalization changes constant channels ▶
  2. P2 Tensor output loses its device ▶

Summary

The PR adds NumPy YPR/6D rotation conversions, bimanual channel layouts and Cartesian transforms, and extracts dataset normalization into shared helpers.

  • The normalization extraction changes minmax and quantile values, including a substantial change for constant channels.
  • Tensor-based Cartesian transforms return results on CPU.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Bimanual YPR vector] --> B[CartesianYPRToRot6D]
  B --> C[Bimanual 6D vector]
  C --> D[CartesianRot6DToYPR]
  D --> E[Bimanual YPR vector]
  F[MultiDataset tensor and stats] --> G[Shared normalization helpers]
  G --> H[Normalized model input]
Loading

Reviews (1) · Last reviewed commit: "6d: continuous rotation representation, ..."

) -> torch.Tensor:
"""Normalize with per-key stats (zscore, or [-1, 1] for minmax/quantile)."""
center, scale = _norm_center_scale(stats, norm_mode, tensor)
return (tensor - center) / (scale + _eps(norm_mode))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Normalization changes constant channels

For minmax and quantile normalization, the centered formula does not match the previous 2 * (x - lo) / (hi - lo + 1e-6) - 1. When a channel is constant and its sample equals the bound, it now normalizes to 0 instead of -1. The dataset uses this helper for training, including the configured quantile mode, so this refactor changes model inputs. Please preserve the previous offset.

Comment on lines +399 to +400
def _like_input(out: np.ndarray, is_tensor: bool):
return torch.from_numpy(out) if is_tensor else out

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Tensor output loses its device

If either new transform receives a CUDA tensor, _as_array moves it to CPU and this conversion returns a CPU tensor. Code that then combines the result with CUDA tensors will encounter a device mismatch. Preserve the input device on the returned tensor, or make the transforms' CPU-only contract explicit.

RyanPCo commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Merge activity

  • Oct 19, 7:23 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Oct 19, 7:31 PM UTC: Graphite rebased this pull request as part of a merge.
  • Oct 19, 7:31 PM UTC: @RyanPCo merged this pull request with Graphite.

@RyanPCo
RyanPCo changed the base branch from ryanco/phase4-compile-cache-per-job to graphite-base/653 October 19, 2026 19:28
@RyanPCo
RyanPCo changed the base branch from graphite-base/653 to main October 19, 2026 19:29
… helpers

Groundwork for the 6D action pipeline, with no behavior change anywhere.

pose_utils gains _ypr_to_rot6d / _rot6d_to_ypr (numpy, ZYX euler <-> the first
two columns of the rotation matrix, Gram-Schmidt on the way back so a
non-orthonormal model prediction still yields a proper rotation), matching the
column convention of the torch helpers in action_utils. BIMANUAL_CARTESIAN_LAYOUTS
maps a vector width (12/14 ypr, 18/20 6D) to its xyz / rot / grip channel
indices; the norm-stat bounds check and the eval metrics both consume it to know
which channel is which.

action_chunk_transforms gains CartesianYPRToRot6D / CartesianRot6DToYPR, which
convert a bimanual cartesian vector between xyz+ypr(+gripper) and
xyz+rot6d(+gripper) while preserving the numpy/tensor type of the input.

action_utils extracts the normalization math that was inlined in
MultiDataset._apply_norm_one / ._apply_unnorm_one into module-level
_apply_norm_one / _apply_unnorm_one, over a shared _norm_center_scale. _eps
preserves the historical denominators exactly (std + 1e-6 for zscore,
(hi - lo) + 1e-6 for minmax/quantile, i.e. half_range + 0.5e-6), so the three
norm modes are numerically identical to before; MultiDataset now delegates.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
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.

1 participant