Instructions to use ZibinDong/ActionCodec2 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ZibinDong/ActionCodec2 with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("ZibinDong/ActionCodec2", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download runtime/backends/actioncodec2/batch_semantics.py from ZibinDong/ActionCodec2: direct link, hf CLI and curl.
- Browser
- Download file 10.6 kB
-
https://huggingface.co/ZibinDong/ActionCodec2/resolve/main/runtime/backends/actioncodec2/batch_semantics.py
- Command line
-
hf download hf://ZibinDong/ActionCodec2/runtime/backends/actioncodec2/batch_semantics.py
-
curl -L -o batch_semantics.py https://huggingface.co/ZibinDong/ActionCodec2/resolve/main/runtime/backends/actioncodec2/batch_semantics.py
10.6 kB
| """Canonical adapters shared by episode fitting and batched inference.""" | |
| from __future__ import annotations | |
| from typing import TYPE_CHECKING | |
| import numpy as np | |
| from ._so3 import ( | |
| conjugate_quaternion, | |
| multiply_quaternions, | |
| normalize_quaternion, | |
| quaternion_to_rotvec, | |
| ) | |
| from .native_backend import native_kernel | |
| from .semantic import ( | |
| difference_additive, | |
| eef_pose_absolute, | |
| eef_pose_components, | |
| eef_pose_deltas, | |
| eef_pose_from_components, | |
| integrate_additive, | |
| integrate_eef_deltas, | |
| normalize_gripper_actions, | |
| resample_uniform, | |
| wrap_to_pi, | |
| ) | |
| if TYPE_CHECKING: | |
| from .presets import ComponentSpec, PresetConfig | |
| def _resample(values, source_hz, target_hz, method, *, kind="linear", target_length=None): | |
| source_hz, target_hz = float(source_hz), float(target_hz) | |
| if source_hz == target_hz and target_length in (None, values.shape[1]): | |
| return values | |
| kernel = native_kernel("resample_uniform_batch") | |
| if kernel is not None: | |
| return kernel( | |
| values, | |
| source_hz, | |
| target_hz, | |
| kind, | |
| -1 if target_length is None else target_length, | |
| method, | |
| ) | |
| return np.stack( | |
| [ | |
| resample_uniform( | |
| row, | |
| source_hz=source_hz, | |
| target_hz=target_hz, | |
| kind=kind, | |
| target_length=target_length, | |
| method=method, | |
| ) | |
| for row in values | |
| ] | |
| ) | |
| def _pose_map(values, kernel_name, reference): | |
| kernel = native_kernel(kernel_name) | |
| return kernel(values) if kernel is not None else np.stack([reference(row) for row in values]) | |
| def _rotvec_poses(values): | |
| kernel = native_kernel("eef_pose_from_rotvec_batch") | |
| if kernel is not None: | |
| return kernel(values) | |
| flat = values.reshape(-1, 6) | |
| return eef_pose_from_components(flat[:, :3], flat[:, 3:], "rotation_vector").reshape( | |
| *values.shape[:2], 7 | |
| ) | |
| def _poses(values: np.ndarray, component: ComponentSpec) -> np.ndarray: | |
| position = np.take(values, component.position_dims, axis=-1) | |
| rotation = np.take(values, component.rotation_dims, axis=-1) | |
| if component.rotation_format == "rotation_vector": | |
| return _rotvec_poses(np.concatenate((position, rotation), axis=-1)) | |
| return eef_pose_from_components( | |
| position.reshape(-1, 3), rotation.reshape(-1, rotation.shape[-1]), component.rotation_format | |
| ).reshape(*values.shape[:2], 7) | |
| def _local(poses, anchor): | |
| quaternion = normalize_quaternion(poses[..., 3:7]) | |
| inverse = conjugate_quaternion(normalize_quaternion(anchor[..., 3:7])) | |
| relative = multiply_quaternions(inverse, quaternion) | |
| return np.concatenate( | |
| (poses[..., :3].astype(np.float64) - anchor[..., :3], quaternion_to_rotvec(relative)), | |
| axis=-1, | |
| ).astype(np.float32) | |
| def _joint(preset, values, component, source_hz, current_state): | |
| source = np.take(values, component.joint_dims, axis=-1) | |
| if preset.input == "delta": | |
| source = integrate_additive(source, 1) | |
| anchor = np.zeros((len(source), 1, source.shape[-1]), np.float32) | |
| else: | |
| anchor = ( | |
| source[:, :1] | |
| if current_state is None | |
| else np.take(current_state, component.joint_dims, axis=-1)[:, None] | |
| ) | |
| joined = np.concatenate((anchor, source), axis=1) | |
| if component.joint_wrap == "pi": | |
| outside = np.abs(joined) > np.pi + 1e-5 | |
| if outside.any(): | |
| row = int(np.flatnonzero(outside.any(axis=(1, 2)))[0]) | |
| column = component.joint_dims[int(np.argmax(outside[row].any(axis=0)))] | |
| raise ValueError( | |
| f"action space {preset.name!r} declares joint_wrap='pi' for component " | |
| f"{component.name!r}, so its absolute joint columns must lie in (-pi, pi]; " | |
| f"source column {column} reaches {joined[row][outside[row]][0]:.6g} rad. Wrap the " | |
| "inputs, or register a layout with joint_wrap='none' for coordinates that " | |
| "are continuous past one turn, such as a multi-turn or prismatic axis." | |
| ) | |
| batch, steps, width = joined.shape | |
| kernel = native_kernel("unwrap_angles") | |
| unwrapped = ( | |
| ( | |
| kernel(joined.transpose(1, 0, 2).reshape(steps, batch * width), True) | |
| .reshape(steps, batch, width) | |
| .transpose(1, 0, 2) | |
| ) | |
| if kernel is not None | |
| else np.unwrap(joined, axis=1) | |
| ) | |
| anchor, source = unwrapped[:, :1], unwrapped[:, 1:] | |
| return np.asarray( | |
| _resample(source, source_hz, preset.target_hz, preset.interpolation) - anchor, | |
| dtype=np.float32, | |
| ) | |
| def _eef(preset, values, component, source_hz, current_state): | |
| if preset.input == "delta": | |
| delta = np.concatenate( | |
| ( | |
| np.take(values, component.position_dims, axis=-1), | |
| np.take(values, component.rotation_dims, axis=-1), | |
| ), | |
| axis=-1, | |
| ) | |
| poses = _pose_map(delta, "integrate_eef_deltas_batch", integrate_eef_deltas) | |
| sampled = _resample( | |
| poses, source_hz, preset.target_hz, preset.interpolation, kind="pose_xyzw" | |
| ) | |
| return _pose_map(sampled, "eef_pose_absolute_batch", eef_pose_absolute) | |
| poses = _poses(values, component) | |
| anchor = poses[:, :1] if current_state is None else _poses(current_state[:, None], component) | |
| sampled = _resample(poses, source_hz, preset.target_hz, preset.interpolation, kind="pose_xyzw") | |
| return _local(sampled, anchor) | |
| def canonicalize_batch(preset: PresetConfig, values, *, source_hz, current_state): | |
| """Return component ``(B,T_codec,D_active)`` arrays from validated input.""" | |
| return { | |
| component.name: canonicalize_component_batch( | |
| preset, values, component, source_hz=source_hz, current_state=current_state | |
| ) | |
| for component in preset.components | |
| } | |
| def canonicalize_component_batch(preset, values, component, *, source_hz, current_state): | |
| """Convert one component of validated ``(B,T,D)`` source actions.""" | |
| motion = (_joint if component.space == "joint" else _eef)( | |
| preset, values, component, source_hz, current_state | |
| ) | |
| if component.gripper_dim is None: | |
| return motion | |
| gripper = values[..., component.gripper_dim : component.gripper_dim + 1] | |
| if component.gripper_threshold is not None: | |
| gripper = ((gripper >= component.gripper_threshold) if component.gripper_open_is_high | |
| else (gripper <= component.gripper_threshold)).astype(np.float32) | |
| else: | |
| gripper = normalize_gripper_actions( | |
| gripper.reshape(-1, 1), | |
| closed_value=component.gripper_closed_value, | |
| open_value=component.gripper_open_value, | |
| ).reshape(*values.shape[:2], 1) | |
| gripper = _resample(gripper, source_hz, preset.target_hz, preset.interpolation, kind="hold") | |
| return np.concatenate((motion, gripper), axis=-1) | |
| def _external_eef(motion, component, current_state): | |
| poses = _rotvec_poses(motion) | |
| if current_state is None: | |
| return _pose_map(poses, "eef_pose_deltas_batch", eef_pose_deltas) | |
| anchor = _poses(current_state[:, None], component) | |
| quaternion = multiply_quaternions( | |
| normalize_quaternion(anchor[..., 3:7]), normalize_quaternion(poses[..., 3:7]) | |
| ) | |
| attached = np.concatenate( | |
| (poses[..., :3].astype(np.float64) + anchor[..., :3], quaternion), axis=-1 | |
| ).astype(np.float32) | |
| return eef_pose_components(attached.reshape(-1, 7), component.rotation_format).reshape( | |
| *attached.shape[:2], 3 + len(component.rotation_dims) | |
| ) | |
| def externalize_batch( | |
| values, component: ComponentSpec, *, source_hz, target_hz, target_length, method, current_state | |
| ): | |
| """Resample decoded paths and return ``(B,T_output,D_component)`` arrays.""" | |
| sampled = resample_canonical_batch( | |
| values, | |
| component, | |
| source_hz=source_hz, | |
| target_hz=target_hz, | |
| target_length=target_length, | |
| method=method, | |
| ) | |
| return externalize_canonical_batch(sampled, component, current_state=current_state) | |
| def resample_canonical_batch( | |
| values, component: ComponentSpec, *, source_hz, target_hz, target_length, method | |
| ): | |
| """Rate-convert local ``(B,T,D_active)`` paths, holding gripper commands.""" | |
| motion_width = len(component.joint_dims or ()) if component.space == "joint" else 6 | |
| motion = values[..., :motion_width] | |
| if component.space == "eef": | |
| poses = _rotvec_poses(motion) | |
| poses = _resample( | |
| poses, source_hz, target_hz, method, kind="pose_xyzw", target_length=target_length | |
| ) | |
| motion = _pose_map(poses, "eef_pose_absolute_batch", eef_pose_absolute) | |
| else: | |
| motion = _resample(motion, source_hz, target_hz, method, target_length=target_length) | |
| if component.gripper_dim is None: | |
| return np.asarray(motion, dtype=np.float32) | |
| gripper = _resample( | |
| values[..., motion_width : motion_width + 1], | |
| source_hz, | |
| target_hz, | |
| method, | |
| kind="hold", | |
| target_length=target_length, | |
| ) | |
| return np.asarray(np.concatenate((motion, gripper), axis=-1), dtype=np.float32) | |
| def externalize_canonical_batch(values, component: ComponentSpec, *, current_state): | |
| """Restore source coordinates or deltas from ``(B,T,D_active)`` local paths.""" | |
| motion_width = len(component.joint_dims or ()) if component.space == "joint" else 6 | |
| motion = values[..., :motion_width] | |
| if component.space == "eef": | |
| motion = _external_eef(motion, component, current_state) | |
| elif current_state is None: | |
| motion = difference_additive(motion, 1) | |
| else: | |
| motion = motion + np.take(current_state, component.joint_dims, axis=-1)[:, None] | |
| if component.joint_wrap == "pi": | |
| motion = wrap_to_pi(motion) | |
| if component.gripper_dim is None: | |
| return np.asarray(motion, dtype=np.float32) | |
| gripper = values[..., motion_width : motion_width + 1] | |
| if component.gripper_threshold is not None: | |
| gripper = gripper if component.gripper_open_is_high else 1.0 - gripper | |
| return np.asarray(np.concatenate((motion, gripper), axis=-1), dtype=np.float32) | |
| gripper = component.gripper_closed_value + gripper * ( | |
| component.gripper_open_value - component.gripper_closed_value | |
| ) | |
| return np.asarray(np.concatenate((motion, gripper), axis=-1), dtype=np.float32) | |