Skip to main content

dsfb_robotics/datasets/
mod.rs

1//! Per-dataset residual adapters.
2//!
3//! Each adapter is a `no_std` + `no_alloc` pure function that
4//! converts a dataset-specific raw-sample type into a scalar residual
5//! norm `‖r(k)‖`. The scalar stream is then fed into the canonical
6//! [`crate::engine::DsfbRoboticsEngine`] / [`crate::observe`] pipeline
7//! unchanged.
8//!
9//! ## Dataset index (companion paper §10)
10//!
11//! | Module | Family | Residual form | Notes |
12//! |---|---|---|---|
13//! | `cwru` | PHM (bearing) | `\|E_{BPFI}(k) − μ_healthy\|` | Spectral-envelope deviation |
14//! | `ims` | PHM (bearing) | `\|HI(k) − HI_nominal\|` | Health-index trajectory |
15//! | `kuka_lwr` | Kinematics | `‖ddq − ddq_nominal‖` | Kinematic-residual variant, Simionato 7R |
16//! | `femto_st` | PHM (bearing) | `\|vib-HI(k) − HI_calib\|` | PRONOSTIA |
17//! | `panda_gaz` | Kinematics | `‖τ_meas − τ_pred(θ̂_panda)‖` | Literal Gaz-cpp model, Gaz 2019 |
18//! | `dlr_justin` | Kinematics | `‖τ_meas − τ_interp‖` | Literal Giacomuzzo Zenodo τ_interp |
19//! | `ur10_kufieta` | Kinematics | `‖τ_meas − τ_RNEA(URSim)‖` | Literal pinocchio RNEA, Polydoros 2015 |
20//! | `cheetah3` | Balancing | `combine(r_F, r_ξ)` | Quadruped MPC + CoM |
21//! | `icub_pushrecovery` | Balancing | `combine(r_W, r_ξ)` | Humanoid WBC + centroidal |
22
23pub mod cwru;
24pub mod ims;
25pub mod kuka_lwr;
26pub mod femto_st;
27pub mod panda_gaz;
28pub mod dlr_justin;
29pub mod ur10_kufieta;
30pub mod cheetah3;
31pub mod icub_pushrecovery;
32pub mod droid;
33pub mod openx;
34pub mod anymal_parkour;
35pub mod unitree_g1;
36pub mod aloha_static;
37pub mod icub3_sorrentino;
38pub mod mobile_aloha;
39pub mod so100;
40pub mod aloha_static_tape;
41pub mod aloha_static_screw_driver;
42pub mod aloha_static_pingpong_test;
43
44/// Stable dataset identifier, used in `paper-lock` subcommand dispatch
45/// and per-dataset audit artefacts.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub enum DatasetId {
49    /// Case Western Reserve University bearing dataset.
50    Cwru,
51    /// IMS run-to-failure bearing dataset (NASA PCoE).
52    Ims,
53    /// KUKA LWR-IV+ joint-space identification (Simionato 7R / Jubien 2014 lineage).
54    KukaLwr,
55    /// FEMTO-ST PRONOSTIA accelerated bearing degradation (IEEE PHM 2012).
56    FemtoSt,
57    /// Franka Emika Panda dynamic identification (Gaz et al. 2019).
58    PandaGaz,
59    /// DLR-class 7-DoF Panda measurement-vs-model torque corpus (Giacomuzzo et al. 2024, Zenodo 12516500).
60    DlrJustin,
61    /// Universal Robots UR10 pick-and-place torque identification (Polydoros et al. IROS 2015).
62    Ur10Kufieta,
63    /// MIT Mini-Cheetah locomotion open logs (Katz–Di Carlo–Kim 2019; UMich-CURLY dataset).
64    Cheetah3,
65    /// ergoCub humanoid push-recovery experiment (Romualdi–Viceconte 2024, ami-iit).
66    IcubPushRecovery,
67    /// DROID distributed robot manipulation dataset (Khazatsky et al. 2024, Stanford/TRI).
68    Droid,
69    /// Open X-Embodiment cross-robot manipulation corpus (RT-X 2024 collaboration).
70    Openx,
71    /// ANYmal-C parkour locomotion in the wild (Miki et al., Science Robotics 2022).
72    AnymalParkour,
73    /// Unitree G1 humanoid teleoperation (Makolon0321 / `unitree_g1_block_stack`).
74    UnitreeG1,
75    /// ALOHA bimanual static teleoperation (Zhao et al. 2023, LeRobot
76    /// aloha_static_coffee corpus) — real physical ALOHA hardware.
77    AlohaStatic,
78    /// ergoCub Sorrentino balancing-torque-control (ami-iit RAL 2025).
79    Icub3Sorrentino,
80    /// Mobile ALOHA wipe-wine (Fu, Zhao, Finn 2024, Stanford).
81    MobileAloha,
82    /// SO-ARM100 pick-and-place (HuggingFace LeRobot, Apache-2.0).
83    So100,
84    /// ALOHA static tape attachment (LeRobot real bimanual).
85    AlohaStaticTape,
86    /// ALOHA static screw-driver tool-use (LeRobot real bimanual).
87    AlohaStaticScrewDriver,
88    /// ALOHA static ping-pong rhythmic transfer (LeRobot real bimanual).
89    AlohaStaticPingpongTest,
90}
91
92impl DatasetId {
93    /// Stable short identifier (kebab-style) for CLI + filesystem use.
94    #[inline]
95    #[must_use]
96    pub const fn slug(self) -> &'static str {
97        match self {
98            Self::Cwru => "cwru",
99            Self::Ims => "ims",
100            Self::KukaLwr => "kuka_lwr",
101            Self::FemtoSt => "femto_st",
102            Self::PandaGaz => "panda_gaz",
103            Self::DlrJustin => "dlr_justin",
104            Self::Ur10Kufieta => "ur10_kufieta",
105            Self::Cheetah3 => "cheetah3",
106            Self::IcubPushRecovery => "icub_pushrecovery",
107            Self::Droid => "droid",
108            Self::Openx => "openx",
109            Self::AnymalParkour => "anymal_parkour",
110            Self::UnitreeG1 => "unitree_g1",
111            Self::AlohaStatic => "aloha_static",
112            Self::Icub3Sorrentino => "icub3_sorrentino",
113            Self::MobileAloha => "mobile_aloha",
114            Self::So100 => "so100",
115            Self::AlohaStaticTape => "aloha_static_tape",
116            Self::AlohaStaticScrewDriver => "aloha_static_screw_driver",
117            Self::AlohaStaticPingpongTest => "aloha_static_pingpong_test",
118        }
119    }
120
121    /// Parse a slug back into a `DatasetId`. Returns `None` for unknowns.
122    ///
123    /// Safe-state policy: the `_ => None` arm is the explicit,
124    /// documented fallback state. Unknown slugs are treated as "not a
125    /// supported dataset" rather than coerced to a default or panicking.
126    /// Callers surface the `None` to the user as an EX_USAGE CLI error
127    /// (see the `paper-lock` CLI binary in `src/main.rs` and the
128    /// integration tests in `tests/paper_lock_binary.rs`), which is the
129    /// intended safe behaviour.
130    #[must_use]
131    pub fn from_slug(s: &str) -> Option<Self> {
132        debug_assert!(s.len() < 64, "slug unreasonably long");
133        match s {
134            "cwru" => Some(Self::Cwru),
135            "ims" => Some(Self::Ims),
136            "kuka_lwr" => Some(Self::KukaLwr),
137            "femto_st" => Some(Self::FemtoSt),
138            "panda_gaz" => Some(Self::PandaGaz),
139            "dlr_justin" => Some(Self::DlrJustin),
140            "ur10_kufieta" => Some(Self::Ur10Kufieta),
141            "cheetah3" => Some(Self::Cheetah3),
142            "icub_pushrecovery" => Some(Self::IcubPushRecovery),
143            "droid" => Some(Self::Droid),
144            "openx" => Some(Self::Openx),
145            "anymal_parkour" => Some(Self::AnymalParkour),
146            "unitree_g1" => Some(Self::UnitreeG1),
147            "aloha_static" => Some(Self::AlohaStatic),
148            "icub3_sorrentino" => Some(Self::Icub3Sorrentino),
149            "mobile_aloha" => Some(Self::MobileAloha),
150            "so100" => Some(Self::So100),
151            "aloha_static_tape" => Some(Self::AlohaStaticTape),
152            "aloha_static_screw_driver" => Some(Self::AlohaStaticScrewDriver),
153            "aloha_static_pingpong_test" => Some(Self::AlohaStaticPingpongTest),
154            // SAFE-STATE: unknown slug is the explicitly-named fallback
155            // state. Bind it to `unknown` so the arm is named (no
156            // wildcard catch-all), assert the input shape, and return
157            // None. Callers surface the None as EX_USAGE at the CLI
158            // boundary (see `crate::main` and `tests/paper_lock_binary.rs`).
159            unknown => {
160                debug_assert!(
161                    unknown.len() < 64,
162                    "slug input bound by callsite preconditions"
163                );
164                None
165            }
166        }
167    }
168
169    /// Residual-family tag, for table-of-contents emission.
170    #[inline]
171    #[must_use]
172    pub const fn family(self) -> DatasetFamily {
173        match self {
174            Self::Cwru | Self::Ims | Self::FemtoSt => DatasetFamily::Phm,
175            Self::KukaLwr
176            | Self::PandaGaz
177            | Self::DlrJustin
178            | Self::Ur10Kufieta
179            | Self::Droid
180            | Self::Openx
181            | Self::AlohaStatic
182            | Self::MobileAloha
183            | Self::So100
184            | Self::AlohaStaticTape
185            | Self::AlohaStaticScrewDriver
186            | Self::AlohaStaticPingpongTest => DatasetFamily::Kinematics,
187            Self::Cheetah3
188            | Self::IcubPushRecovery
189            | Self::AnymalParkour
190            | Self::UnitreeG1
191            | Self::Icub3Sorrentino => DatasetFamily::Balancing,
192        }
193    }
194}
195
196/// Family classification matching the companion paper's §10 grouping.
197#[derive(Debug, Clone, Copy, PartialEq, Eq)]
198#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
199pub enum DatasetFamily {
200    /// Prognostics / health monitoring (bearing degradation).
201    Phm,
202    /// Kinematic identification / manipulation residuals (arms, cobots).
203    Kinematics,
204    /// Balancing / whole-body control residuals (legged platforms).
205    Balancing,
206}
207
208impl DatasetFamily {
209    /// Stable label for table emission.
210    #[inline]
211    #[must_use]
212    pub const fn label(self) -> &'static str {
213        match self {
214            Self::Phm => "PHM",
215            Self::Kinematics => "Kinematics",
216            Self::Balancing => "Balancing",
217        }
218    }
219}
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224
225    #[test]
226    fn slug_roundtrips_for_every_dataset() {
227        for id in [
228            DatasetId::Cwru,
229            DatasetId::Ims,
230            DatasetId::KukaLwr,
231            DatasetId::FemtoSt,
232            DatasetId::PandaGaz,
233            DatasetId::DlrJustin,
234            DatasetId::Ur10Kufieta,
235            DatasetId::Cheetah3,
236            DatasetId::IcubPushRecovery,
237            DatasetId::Droid,
238            DatasetId::Openx,
239            DatasetId::AnymalParkour,
240            DatasetId::UnitreeG1,
241            DatasetId::AlohaStatic,
242        ] {
243            let slug = id.slug();
244            assert_eq!(DatasetId::from_slug(slug), Some(id), "roundtrip failed for {slug}");
245        }
246    }
247
248    #[test]
249    fn unknown_slug_is_none() {
250        assert_eq!(DatasetId::from_slug("nope"), None);
251        assert_eq!(DatasetId::from_slug(""), None);
252    }
253
254    #[test]
255    fn kinematics_family_covers_four_arms() {
256        let arms = [DatasetId::KukaLwr, DatasetId::PandaGaz, DatasetId::DlrJustin, DatasetId::Ur10Kufieta];
257        for a in arms {
258            assert_eq!(a.family(), DatasetFamily::Kinematics);
259        }
260    }
261
262    #[test]
263    fn balancing_family_covers_two_platforms() {
264        assert_eq!(DatasetId::Cheetah3.family(), DatasetFamily::Balancing);
265        assert_eq!(DatasetId::IcubPushRecovery.family(), DatasetFamily::Balancing);
266    }
267
268    #[test]
269    fn phm_family_covers_three_bearing_datasets() {
270        assert_eq!(DatasetId::Cwru.family(), DatasetFamily::Phm);
271        assert_eq!(DatasetId::Ims.family(), DatasetFamily::Phm);
272        assert_eq!(DatasetId::FemtoSt.family(), DatasetFamily::Phm);
273    }
274}