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.
- dhb_xr/__init__.py +61 -0
- dhb_xr/cli.py +206 -0
- dhb_xr/core/__init__.py +28 -0
- dhb_xr/core/geometry.py +167 -0
- dhb_xr/core/geometry_torch.py +77 -0
- dhb_xr/core/types.py +113 -0
- dhb_xr/database/__init__.py +10 -0
- dhb_xr/database/motion_db.py +79 -0
- dhb_xr/database/retrieval.py +6 -0
- dhb_xr/database/similarity.py +71 -0
- dhb_xr/decoder/__init__.py +13 -0
- dhb_xr/decoder/decoder_torch.py +52 -0
- dhb_xr/decoder/dhb_dr.py +261 -0
- dhb_xr/decoder/dhb_qr.py +89 -0
- dhb_xr/encoder/__init__.py +27 -0
- dhb_xr/encoder/dhb_dr.py +418 -0
- dhb_xr/encoder/dhb_qr.py +129 -0
- dhb_xr/encoder/dhb_ti.py +204 -0
- dhb_xr/encoder/encoder_torch.py +54 -0
- dhb_xr/encoder/padding.py +82 -0
- dhb_xr/generative/__init__.py +78 -0
- dhb_xr/generative/flow_matching.py +705 -0
- dhb_xr/generative/latent_encoder.py +536 -0
- dhb_xr/generative/sampling.py +203 -0
- dhb_xr/generative/training.py +475 -0
- dhb_xr/generative/vfm_tokenizer.py +485 -0
- dhb_xr/integration/__init__.py +13 -0
- dhb_xr/integration/vla/__init__.py +11 -0
- dhb_xr/integration/vla/libero.py +132 -0
- dhb_xr/integration/vla/pipeline.py +85 -0
- dhb_xr/integration/vla/robocasa.py +85 -0
- dhb_xr/losses/__init__.py +16 -0
- dhb_xr/losses/geodesic_loss.py +91 -0
- dhb_xr/losses/hybrid_loss.py +36 -0
- dhb_xr/losses/invariant_loss.py +73 -0
- dhb_xr/optimization/__init__.py +72 -0
- dhb_xr/optimization/casadi_solver.py +342 -0
- dhb_xr/optimization/constraints.py +32 -0
- dhb_xr/optimization/cusadi_solver.py +311 -0
- dhb_xr/optimization/export_casadi_decode.py +111 -0
- dhb_xr/optimization/fatrop_solver.py +477 -0
- dhb_xr/optimization/torch_solver.py +85 -0
- dhb_xr/preprocessing/__init__.py +42 -0
- dhb_xr/preprocessing/diagnostics.py +330 -0
- dhb_xr/preprocessing/trajectory_cleaner.py +485 -0
- dhb_xr/tokenization/__init__.py +56 -0
- dhb_xr/tokenization/causal_encoder.py +54 -0
- dhb_xr/tokenization/compression.py +749 -0
- dhb_xr/tokenization/hierarchical.py +359 -0
- dhb_xr/tokenization/rvq.py +178 -0
- dhb_xr/tokenization/vqvae.py +155 -0
- dhb_xr/utils/__init__.py +24 -0
- dhb_xr/utils/io.py +59 -0
- dhb_xr/utils/resampling.py +66 -0
- dhb_xr/utils/xdof_loader.py +89 -0
- dhb_xr/visualization/__init__.py +5 -0
- dhb_xr/visualization/plot.py +242 -0
- dhb_xr-0.2.1.dist-info/METADATA +784 -0
- dhb_xr-0.2.1.dist-info/RECORD +82 -0
- dhb_xr-0.2.1.dist-info/WHEEL +5 -0
- dhb_xr-0.2.1.dist-info/entry_points.txt +2 -0
- dhb_xr-0.2.1.dist-info/top_level.txt +3 -0
- examples/__init__.py +54 -0
- examples/basic_encoding.py +82 -0
- examples/benchmark_backends.py +37 -0
- examples/dhb_qr_comparison.py +79 -0
- examples/dhb_ti_time_invariant.py +72 -0
- examples/gpu_batch_optimization.py +102 -0
- examples/imitation_learning.py +53 -0
- examples/integration/__init__.py +19 -0
- examples/integration/libero_full_demo.py +692 -0
- examples/integration/libero_pro_dhb_demo.py +1063 -0
- examples/integration/libero_simulation_demo.py +286 -0
- examples/integration/libero_swap_demo.py +534 -0
- examples/integration/robocasa_libero_dhb_pipeline.py +56 -0
- examples/integration/test_libero_adapter.py +47 -0
- examples/integration/test_libero_encoding.py +75 -0
- examples/integration/test_libero_retrieval.py +105 -0
- examples/motion_database.py +88 -0
- examples/trajectory_adaptation.py +85 -0
- examples/vla_tokenization.py +107 -0
- 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
|