dhb-xr 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. dhb_xr/__init__.py +61 -0
  2. dhb_xr/cli.py +206 -0
  3. dhb_xr/core/__init__.py +28 -0
  4. dhb_xr/core/geometry.py +167 -0
  5. dhb_xr/core/geometry_torch.py +77 -0
  6. dhb_xr/core/types.py +113 -0
  7. dhb_xr/database/__init__.py +10 -0
  8. dhb_xr/database/motion_db.py +79 -0
  9. dhb_xr/database/retrieval.py +6 -0
  10. dhb_xr/database/similarity.py +71 -0
  11. dhb_xr/decoder/__init__.py +13 -0
  12. dhb_xr/decoder/decoder_torch.py +52 -0
  13. dhb_xr/decoder/dhb_dr.py +261 -0
  14. dhb_xr/decoder/dhb_qr.py +89 -0
  15. dhb_xr/encoder/__init__.py +27 -0
  16. dhb_xr/encoder/dhb_dr.py +418 -0
  17. dhb_xr/encoder/dhb_qr.py +129 -0
  18. dhb_xr/encoder/dhb_ti.py +204 -0
  19. dhb_xr/encoder/encoder_torch.py +54 -0
  20. dhb_xr/encoder/padding.py +82 -0
  21. dhb_xr/generative/__init__.py +78 -0
  22. dhb_xr/generative/flow_matching.py +705 -0
  23. dhb_xr/generative/latent_encoder.py +536 -0
  24. dhb_xr/generative/sampling.py +203 -0
  25. dhb_xr/generative/training.py +475 -0
  26. dhb_xr/generative/vfm_tokenizer.py +485 -0
  27. dhb_xr/integration/__init__.py +13 -0
  28. dhb_xr/integration/vla/__init__.py +11 -0
  29. dhb_xr/integration/vla/libero.py +132 -0
  30. dhb_xr/integration/vla/pipeline.py +85 -0
  31. dhb_xr/integration/vla/robocasa.py +85 -0
  32. dhb_xr/losses/__init__.py +16 -0
  33. dhb_xr/losses/geodesic_loss.py +91 -0
  34. dhb_xr/losses/hybrid_loss.py +36 -0
  35. dhb_xr/losses/invariant_loss.py +73 -0
  36. dhb_xr/optimization/__init__.py +72 -0
  37. dhb_xr/optimization/casadi_solver.py +342 -0
  38. dhb_xr/optimization/constraints.py +32 -0
  39. dhb_xr/optimization/cusadi_solver.py +311 -0
  40. dhb_xr/optimization/export_casadi_decode.py +111 -0
  41. dhb_xr/optimization/fatrop_solver.py +477 -0
  42. dhb_xr/optimization/torch_solver.py +85 -0
  43. dhb_xr/preprocessing/__init__.py +42 -0
  44. dhb_xr/preprocessing/diagnostics.py +330 -0
  45. dhb_xr/preprocessing/trajectory_cleaner.py +485 -0
  46. dhb_xr/tokenization/__init__.py +56 -0
  47. dhb_xr/tokenization/causal_encoder.py +54 -0
  48. dhb_xr/tokenization/compression.py +749 -0
  49. dhb_xr/tokenization/hierarchical.py +359 -0
  50. dhb_xr/tokenization/rvq.py +178 -0
  51. dhb_xr/tokenization/vqvae.py +155 -0
  52. dhb_xr/utils/__init__.py +24 -0
  53. dhb_xr/utils/io.py +59 -0
  54. dhb_xr/utils/resampling.py +66 -0
  55. dhb_xr/utils/xdof_loader.py +89 -0
  56. dhb_xr/visualization/__init__.py +5 -0
  57. dhb_xr/visualization/plot.py +242 -0
  58. dhb_xr-0.2.1.dist-info/METADATA +784 -0
  59. dhb_xr-0.2.1.dist-info/RECORD +82 -0
  60. dhb_xr-0.2.1.dist-info/WHEEL +5 -0
  61. dhb_xr-0.2.1.dist-info/entry_points.txt +2 -0
  62. dhb_xr-0.2.1.dist-info/top_level.txt +3 -0
  63. examples/__init__.py +54 -0
  64. examples/basic_encoding.py +82 -0
  65. examples/benchmark_backends.py +37 -0
  66. examples/dhb_qr_comparison.py +79 -0
  67. examples/dhb_ti_time_invariant.py +72 -0
  68. examples/gpu_batch_optimization.py +102 -0
  69. examples/imitation_learning.py +53 -0
  70. examples/integration/__init__.py +19 -0
  71. examples/integration/libero_full_demo.py +692 -0
  72. examples/integration/libero_pro_dhb_demo.py +1063 -0
  73. examples/integration/libero_simulation_demo.py +286 -0
  74. examples/integration/libero_swap_demo.py +534 -0
  75. examples/integration/robocasa_libero_dhb_pipeline.py +56 -0
  76. examples/integration/test_libero_adapter.py +47 -0
  77. examples/integration/test_libero_encoding.py +75 -0
  78. examples/integration/test_libero_retrieval.py +105 -0
  79. examples/motion_database.py +88 -0
  80. examples/trajectory_adaptation.py +85 -0
  81. examples/vla_tokenization.py +107 -0
  82. notebooks/__init__.py +24 -0
@@ -0,0 +1,42 @@
1
+ """
2
+ Preprocessing module for DHB-XR trajectory data.
3
+
4
+ Provides utilities for preparing trajectories before DHB encoding:
5
+ - TrajectoryPreprocessor: Full preprocessing pipeline
6
+ - Diagnostics: Trajectory quality analysis
7
+ - Cleaning utilities: Smoothing, alignment, zero-motion handling
8
+
9
+ Example:
10
+ >>> from dhb_xr.preprocessing import TrajectoryPreprocessor, analyze_trajectory
11
+ >>>
12
+ >>> # Analyze trajectory quality
13
+ >>> diagnostics = analyze_trajectory(positions, quaternions)
14
+ >>> print(f"Detected {diagnostics.num_reversals} reversals")
15
+ >>>
16
+ >>> # Preprocess for encoding
17
+ >>> preprocessor = TrajectoryPreprocessor(align_to_x=True, smooth_reversals=True)
18
+ >>> result = preprocessor.process(positions, quaternions)
19
+ >>> clean_positions, clean_quaternions = result.positions, result.quaternions
20
+ """
21
+
22
+ from .trajectory_cleaner import (
23
+ TrajectoryPreprocessor,
24
+ ProcessedTrajectory,
25
+ )
26
+ from .diagnostics import (
27
+ TrajectoryDiagnostics,
28
+ analyze_trajectory,
29
+ detect_reversals,
30
+ detect_zero_motion,
31
+ )
32
+
33
+ __all__ = [
34
+ # Main preprocessor
35
+ "TrajectoryPreprocessor",
36
+ "ProcessedTrajectory",
37
+ # Diagnostics
38
+ "TrajectoryDiagnostics",
39
+ "analyze_trajectory",
40
+ "detect_reversals",
41
+ "detect_zero_motion",
42
+ ]
@@ -0,0 +1,330 @@
1
+ """
2
+ Trajectory quality diagnostics for DHB encoding.
3
+
4
+ Provides analysis tools to detect potential encoding issues:
5
+ - 180-degree direction reversals
6
+ - Zero/near-zero motion segments
7
+ - Initial direction alignment
8
+ - Overall trajectory quality assessment
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import numpy as np
14
+ from dataclasses import dataclass, field
15
+ from typing import List, Tuple, Optional
16
+ from enum import Enum
17
+
18
+
19
+ class PreprocessingRecommendation(Enum):
20
+ """Recommended preprocessing actions."""
21
+ NONE = "none"
22
+ ALIGN_TO_X = "align_to_x"
23
+ SMOOTH_TRAJECTORY = "smooth_trajectory"
24
+ REMOVE_ZERO_MOTION = "remove_zero_motion"
25
+ INTERPOLATE_REVERSALS = "interpolate_reversals"
26
+
27
+
28
+ @dataclass
29
+ class TrajectoryDiagnostics:
30
+ """Comprehensive trajectory quality report."""
31
+
32
+ # Basic info
33
+ num_samples: int = 0
34
+ trajectory_length: float = 0.0
35
+
36
+ # Reversal detection
37
+ num_reversals: int = 0
38
+ reversal_indices: List[int] = field(default_factory=list)
39
+ min_tangent_dot_product: float = 1.0 # -1 indicates 180° reversal
40
+
41
+ # Zero-motion detection
42
+ num_zero_motion_segments: int = 0
43
+ zero_motion_segments: List[Tuple[int, int]] = field(default_factory=list)
44
+ total_zero_motion_frames: int = 0
45
+
46
+ # Initial direction analysis
47
+ initial_direction: np.ndarray = field(default_factory=lambda: np.array([1.0, 0.0, 0.0]))
48
+ initial_direction_magnitude: float = 0.0
49
+ alignment_angle_to_x: float = 0.0 # Angle between initial direction and +x
50
+
51
+ # Rotation analysis
52
+ max_angular_velocity: float = 0.0
53
+ total_rotation_angle: float = 0.0
54
+
55
+ # Quality metrics
56
+ is_well_conditioned: bool = True
57
+ recommended_preprocessing: List[PreprocessingRecommendation] = field(default_factory=list)
58
+
59
+ def summary(self) -> str:
60
+ """Return human-readable summary."""
61
+ lines = [
62
+ f"Trajectory Diagnostics ({self.num_samples} samples, {self.trajectory_length:.3f}m total)",
63
+ f" Reversals: {self.num_reversals} (min dot: {self.min_tangent_dot_product:.3f})",
64
+ f" Zero-motion segments: {self.num_zero_motion_segments} ({self.total_zero_motion_frames} frames)",
65
+ f" Initial direction: {self.initial_direction} (angle to +x: {np.degrees(self.alignment_angle_to_x):.1f}°)",
66
+ f" Well-conditioned: {self.is_well_conditioned}",
67
+ ]
68
+ if self.recommended_preprocessing:
69
+ lines.append(f" Recommended: {[r.value for r in self.recommended_preprocessing]}")
70
+ return "\n".join(lines)
71
+
72
+
73
+ def detect_reversals(
74
+ positions: np.ndarray,
75
+ threshold: float = -0.5,
76
+ ) -> Tuple[List[int], float]:
77
+ """
78
+ Detect indices where motion direction reverses significantly.
79
+
80
+ A reversal is detected when consecutive tangent vectors have a
81
+ dot product below the threshold (e.g., -0.5 means > 120° turn).
82
+
83
+ Args:
84
+ positions: (N, 3) position trajectory.
85
+ threshold: Dot product threshold for reversal detection.
86
+ -1.0 = exact 180° reversal only
87
+ -0.5 = 120° or larger turn
88
+ 0.0 = 90° or larger turn
89
+
90
+ Returns:
91
+ Tuple of (reversal_indices, min_dot_product).
92
+ """
93
+ positions = np.asarray(positions)
94
+ if len(positions) < 3:
95
+ return [], 1.0
96
+
97
+ # Compute tangent vectors
98
+ tangents = np.diff(positions, axis=0)
99
+ norms = np.linalg.norm(tangents, axis=1, keepdims=True)
100
+
101
+ # Normalize tangents (avoid division by zero)
102
+ eps = 1e-10
103
+ tangents_normalized = tangents / (norms + eps)
104
+
105
+ # Compute consecutive dot products
106
+ reversal_indices = []
107
+ min_dot = 1.0
108
+
109
+ for i in range(len(tangents_normalized) - 1):
110
+ # Skip if either tangent is near-zero
111
+ if norms[i] < eps or norms[i + 1] < eps:
112
+ continue
113
+
114
+ dot = np.dot(tangents_normalized[i], tangents_normalized[i + 1])
115
+ min_dot = min(min_dot, dot)
116
+
117
+ if dot < threshold:
118
+ reversal_indices.append(i + 1) # Index in original positions
119
+
120
+ return reversal_indices, min_dot
121
+
122
+
123
+ def detect_zero_motion(
124
+ positions: np.ndarray,
125
+ threshold: float = 1e-6,
126
+ min_segment_length: int = 1,
127
+ ) -> List[Tuple[int, int]]:
128
+ """
129
+ Detect contiguous segments with zero or near-zero motion.
130
+
131
+ Args:
132
+ positions: (N, 3) position trajectory.
133
+ threshold: Distance threshold for zero motion.
134
+ min_segment_length: Minimum number of consecutive frames to report.
135
+
136
+ Returns:
137
+ List of (start_index, end_index) tuples for zero-motion segments.
138
+ """
139
+ positions = np.asarray(positions)
140
+ if len(positions) < 2:
141
+ return []
142
+
143
+ # Compute step distances
144
+ steps = np.diff(positions, axis=0)
145
+ distances = np.linalg.norm(steps, axis=1)
146
+
147
+ # Find zero-motion frames
148
+ is_zero = distances < threshold
149
+
150
+ # Group into contiguous segments
151
+ segments = []
152
+ in_segment = False
153
+ segment_start = 0
154
+
155
+ for i, zero in enumerate(is_zero):
156
+ if zero and not in_segment:
157
+ in_segment = True
158
+ segment_start = i
159
+ elif not zero and in_segment:
160
+ in_segment = False
161
+ segment_length = i - segment_start
162
+ if segment_length >= min_segment_length:
163
+ segments.append((segment_start, i))
164
+
165
+ # Handle segment at end
166
+ if in_segment:
167
+ segment_length = len(is_zero) - segment_start
168
+ if segment_length >= min_segment_length:
169
+ segments.append((segment_start, len(is_zero)))
170
+
171
+ return segments
172
+
173
+
174
+ def compute_initial_direction(
175
+ positions: np.ndarray,
176
+ num_samples: int = 5,
177
+ ) -> Tuple[np.ndarray, float]:
178
+ """
179
+ Compute the initial motion direction.
180
+
181
+ Uses first few samples to robustly estimate initial direction,
182
+ skipping zero-motion initial frames if present.
183
+
184
+ Args:
185
+ positions: (N, 3) position trajectory.
186
+ num_samples: Number of initial samples to consider.
187
+
188
+ Returns:
189
+ Tuple of (direction_unit_vector, magnitude).
190
+ """
191
+ positions = np.asarray(positions)
192
+ eps = 1e-10
193
+
194
+ # Try successive differences until we find non-zero motion
195
+ for i in range(min(num_samples, len(positions) - 1)):
196
+ diff = positions[i + 1] - positions[i]
197
+ magnitude = np.linalg.norm(diff)
198
+ if magnitude > eps:
199
+ return diff / magnitude, magnitude
200
+
201
+ # If all initial motion is zero, try overall direction
202
+ if len(positions) > 1:
203
+ diff = positions[-1] - positions[0]
204
+ magnitude = np.linalg.norm(diff)
205
+ if magnitude > eps:
206
+ return diff / magnitude, magnitude
207
+
208
+ # Fallback to +x
209
+ return np.array([1.0, 0.0, 0.0]), 0.0
210
+
211
+
212
+ def compute_alignment_angle(direction: np.ndarray, target: np.ndarray = None) -> float:
213
+ """
214
+ Compute angle between direction and target (default +x).
215
+
216
+ Returns angle in radians.
217
+ """
218
+ if target is None:
219
+ target = np.array([1.0, 0.0, 0.0])
220
+
221
+ direction = np.asarray(direction)
222
+ target = np.asarray(target)
223
+
224
+ # Normalize
225
+ d_norm = np.linalg.norm(direction)
226
+ t_norm = np.linalg.norm(target)
227
+
228
+ if d_norm < 1e-10 or t_norm < 1e-10:
229
+ return 0.0
230
+
231
+ direction = direction / d_norm
232
+ target = target / t_norm
233
+
234
+ # Compute angle
235
+ dot = np.clip(np.dot(direction, target), -1.0, 1.0)
236
+ return np.arccos(dot)
237
+
238
+
239
+ def analyze_trajectory(
240
+ positions: np.ndarray,
241
+ quaternions: Optional[np.ndarray] = None,
242
+ reversal_threshold: float = -0.5,
243
+ zero_motion_threshold: float = 1e-6,
244
+ ) -> TrajectoryDiagnostics:
245
+ """
246
+ Comprehensive trajectory quality analysis.
247
+
248
+ Detects potential issues that could cause DHB encoding problems:
249
+ - 180° direction reversals (cause Euler singularities)
250
+ - Zero-motion segments (cause undefined tangent frames)
251
+ - Misaligned initial direction (reconstruction error with default frames)
252
+
253
+ Args:
254
+ positions: (N, 3) position trajectory.
255
+ quaternions: Optional (N, 4) wxyz quaternion trajectory.
256
+ reversal_threshold: Dot product threshold for reversal detection.
257
+ zero_motion_threshold: Distance threshold for zero motion.
258
+
259
+ Returns:
260
+ TrajectoryDiagnostics with analysis results and recommendations.
261
+ """
262
+ positions = np.asarray(positions)
263
+ diagnostics = TrajectoryDiagnostics()
264
+
265
+ # Basic info
266
+ diagnostics.num_samples = len(positions)
267
+ if len(positions) > 1:
268
+ steps = np.diff(positions, axis=0)
269
+ diagnostics.trajectory_length = np.sum(np.linalg.norm(steps, axis=1))
270
+
271
+ # Reversal detection
272
+ reversal_indices, min_dot = detect_reversals(positions, reversal_threshold)
273
+ diagnostics.num_reversals = len(reversal_indices)
274
+ diagnostics.reversal_indices = reversal_indices
275
+ diagnostics.min_tangent_dot_product = min_dot
276
+
277
+ # Zero-motion detection
278
+ zero_segments = detect_zero_motion(positions, zero_motion_threshold)
279
+ diagnostics.num_zero_motion_segments = len(zero_segments)
280
+ diagnostics.zero_motion_segments = zero_segments
281
+ diagnostics.total_zero_motion_frames = sum(end - start for start, end in zero_segments)
282
+
283
+ # Initial direction
284
+ init_dir, init_mag = compute_initial_direction(positions)
285
+ diagnostics.initial_direction = init_dir
286
+ diagnostics.initial_direction_magnitude = init_mag
287
+ diagnostics.alignment_angle_to_x = compute_alignment_angle(init_dir)
288
+
289
+ # Rotation analysis (if quaternions provided)
290
+ if quaternions is not None:
291
+ quaternions = np.asarray(quaternions)
292
+ if len(quaternions) > 1:
293
+ # Compute angular velocities
294
+ from dhb_xr.core import geometry as geom
295
+ angular_velocities = []
296
+ for i in range(len(quaternions) - 1):
297
+ R_prev = geom.quat_to_rot(quaternions[i])
298
+ R_curr = geom.quat_to_rot(quaternions[i + 1])
299
+ R_rel = R_curr @ R_prev.T
300
+ axis_angle = geom.rot_to_axis_angle(R_rel)
301
+ angular_velocities.append(np.linalg.norm(axis_angle))
302
+
303
+ if angular_velocities:
304
+ diagnostics.max_angular_velocity = max(angular_velocities)
305
+ diagnostics.total_rotation_angle = sum(angular_velocities)
306
+
307
+ # Determine if well-conditioned
308
+ diagnostics.is_well_conditioned = (
309
+ diagnostics.num_reversals == 0 and
310
+ diagnostics.num_zero_motion_segments == 0 and
311
+ diagnostics.alignment_angle_to_x < np.radians(30) # Within 30° of +x
312
+ )
313
+
314
+ # Generate recommendations
315
+ recommendations = []
316
+
317
+ if diagnostics.alignment_angle_to_x > np.radians(10):
318
+ recommendations.append(PreprocessingRecommendation.ALIGN_TO_X)
319
+
320
+ if diagnostics.num_reversals > 0:
321
+ recommendations.append(PreprocessingRecommendation.SMOOTH_TRAJECTORY)
322
+ if diagnostics.min_tangent_dot_product < -0.8:
323
+ recommendations.append(PreprocessingRecommendation.INTERPOLATE_REVERSALS)
324
+
325
+ if diagnostics.num_zero_motion_segments > 0:
326
+ recommendations.append(PreprocessingRecommendation.REMOVE_ZERO_MOTION)
327
+
328
+ diagnostics.recommended_preprocessing = recommendations
329
+
330
+ return diagnostics