Grand Diomande Research · Full HTML Reader

Rust Integration Guide

// Check for Mocopi sensor (base kit has 6) assert!(LimbId::Hip.has_mocopi_sensor()); assert!(!LimbId::LeftElbow.has_mocopi_sensor()); // No sensor on elbows ```

Embodied Trajectory Systems research note experiment writeup candidate score 18 .md

Full Public Reader

Rust Integration Guide

Complete guide for using cc-collection in Rust applications.

Installation

Add to your `Cargo.toml`:

toml
[dependencies]
cc-collection = { path = "path/to/core/cc-collection" }

# Optional: Enable features
# cc-collection = { path = "...", features = ["python", "wasm"] }

Initialization

rust
use cc_collection::{init, VERSION};

fn main() {
    // Initialize library (recommended at startup)
    init();

    println!("cc-collection v{}", VERSION);
}

---

Core Types

LimbId Enum

The `LimbId` enum represents the 14 tracked limbs:

rust
use cc_collection::LimbId;

// All available limbs
let all_limbs: Vec<LimbId> = LimbId::all().collect();
assert_eq!(all_limbs.len(), 14);

// Access specific limb
let hip = LimbId::Hip;
let head = LimbId::Head;

// From index (0-13)
let limb = LimbId::from_index(0).unwrap(); // Hip
assert_eq!(limb, LimbId::Hip);

// To index
assert_eq!(LimbId::Head.to_index(), 1);

// Get name
assert_eq!(LimbId::LeftWrist.name(), "left_wrist");

// Check for Mocopi sensor (base kit has 6)
assert!(LimbId::Hip.has_mocopi_sensor());
assert!(!LimbId::LeftElbow.has_mocopi_sensor()); // No sensor on elbows

LimbState

Per-limb state after fusion:

rust
use cc_collection::{LimbState, LimbId, DataSource};

let state = LimbState {
    limb: LimbId::Hip,
    position: [0.0, 1.0, 0.0],           // World position [x, y, z]
    quaternion: [1.0, 0.0, 0.0, 0.0],    // Rotation [w, x, y, z]
    linear_velocity: [0.0, 0.0, 0.0],    // Velocity [vx, vy, vz]
    angular_velocity: [0.0, 0.0, 0.0],   // Angular velocity [wx, wy, wz]
    orientation_source: DataSource::Fused,
    position_source: DataSource::Fused,
    confidence: 0.95,                     // [0, 1]
};

FusedSkeleton

Complete skeleton with all limb states:

rust
use cc_collection::{FusedSkeleton, LimbId};

// Create new skeleton
let mut skeleton = FusedSkeleton::new("session_001".to_string());

// Access limbs
let hip = skeleton.get_limb(LimbId::Hip);
println!("Hip position: {:?}", hip.position);

// Mutable access
let head = skeleton.get_limb_mut(LimbId::Head);
head.position = [0.0, 1.7, 0.0];

// Iterate all limbs
for (i, limb_state) in skeleton.limbs.iter().enumerate() {
    let limb_id = LimbId::from_index(i).unwrap();
    println!("{}: {:?}", limb_id.name(), limb_state.position);
}

DataSource

Tracks origin of sensor data:

rust
use cc_collection::DataSource;

match data_source {
    DataSource::None => println!("No data"),
    DataSource::Mocopi => println!("From Mocopi IMU"),
    DataSource::MediaPipe => println!("From camera"),
    DataSource::Fused => println!("Combined from both"),
    DataSource::Interpolated => println!("Recovered from occlusion"),
}

FusionMode

Current fusion state:

rust
use cc_collection::FusionMode;

match mode {
    FusionMode::None => println!("No sensors active"),
    FusionMode::MocopiOnly => println!("Mocopi only"),
    FusionMode::MediaPipeOnly => println!("MediaPipe only"),
    FusionMode::Fused => println!("Both sensors fused"),
}

---

Sensor Fusion

FusionConfig

rust
use cc_collection::{FusionConfig, FusionEngine};
use cc_collection::fusion::{KalmanConfig, OcclusionConfig, TemporalConfig};

// Default configuration
let config = FusionConfig::default();

// Custom configuration
let config = FusionConfig {
    session_id: "performance_001".to_string(),
    min_confidence: 0.5,       // Minimum confidence threshold
    enable_smoothing: true,    // Enable temporal smoothing
    kalman: KalmanConfig::default(),
    occlusion: OcclusionConfig::default(),
    temporal: TemporalConfig::default(),
};

let mut engine = FusionEngine::new(config);

FusionEngine

Main sensor fusion processor:

rust
use cc_collection::{FusionEngine, FusionConfig, MocopiFrame, MediaPipeFrame};

// Create engine
let mut engine = FusionEngine::new(FusionConfig::default());

// Process frame with both sensors
let skeleton = engine.process_frame(
    Some(&mocopi_frame),
    Some(&mediapipe_frame),
    1.0 / 60.0,  // dt in seconds
);

// Process with only Mocopi
let skeleton = engine.process_frame(
    Some(&mocopi_frame),
    None,
    1.0 / 60.0,
);

// Process with only MediaPipe
let skeleton = engine.process_frame(
    None,
    Some(&mediapipe_frame),
    1.0 / 60.0,
);

// Check current state
println!("Frame: {}", engine.frame_number());
println!("Mode: {:?}", engine.fusion_mode());

// Get detailed statistics
let stats = engine.stats();
println!("Occluded limbs: {}", stats.occluded_limbs);
println!("Clock offset: {:.2}ms", stats.clock_offset_ms);
println!("Synchronized: {}", stats.is_synchronized);

// Reset for new session
engine.reset();

---

Protocol Parsing

MocopiParser

Parse Sony Mocopi UDP packets:

rust
use cc_collection::MocopiParser;

let mut parser = MocopiParser::new();

// Parse binary UDP packet
let frame = parser.parse(&udp_packet_bytes)?;
println!("Frame {}, {} bones", frame.frame_number, frame.bones.len());

// Access bone data
for bone in &frame.bones {
    println!("Bone {}: pos={:?}, quat={:?}",
        bone.bone_id,
        bone.position,
        bone.quaternion,
    );
}

// Parse from JSON (alternative format)
let json_str = r#"{
    "timestamp_ms": 100.0,
    "frame_number": 42,
    "bones": [
        {"bone_id": 0, "position": [0, 1, 0], "quaternion": [0, 0, 0, 1]}
    ]
}"#;
let frame = parser.parse_json(json_str)?;

MediaPipeParser

Parse MediaPipe Holistic JSON:

rust
use cc_collection::MediaPipeParser;

let mut parser = MediaPipeParser::new();

// Parse JSON from camera processor
let frame = parser.parse_json(&mediapipe_json)?;

// Check available data
if let Some(pose) = &frame.pose_world_landmarks {
    println!("Pose: {} landmarks", pose.len());
}
if frame.face_landmarks.is_some() {
    println!("Face detected");
}
if frame.left_hand_landmarks.is_some() {
    println!("Left hand detected");
}

// Confidence scores
println!("Pose confidence: {:.2}", frame.confidence.pose);
println!("Face confidence: {:.2}", frame.confidence.face);

---

Motion Transforms

To25DTransform

Convert skeleton to 25D ML vector:

rust
use cc_collection::To25DTransform;
use cc_collection::transform::To25DConfig;

// Simple creation
let mut transform = To25DTransform::new(60.0);  // 60 FPS

// With custom config
let config = To25DConfig {
    frame_rate: 60.0,
    smooth_velocity: true,
    smoothing_alpha: 0.3,     // Higher = more smoothing, more lag
    normalize: true,
    scale: 2.0,               // 2m reference space
    clamp_output: true,
    max_position: 5.0,
    max_velocity: 20.0,
};
let mut transform = To25DTransform::with_config(config);

// Transform skeleton
let beat_phase = 0.5;  // [0, 1] - position in beat
let motion: [f32; 25] = transform.transform(&skeleton, beat_phase);

// Access motion components
let hip_position = &motion[0..3];      // [x, y, z]
let hip_velocity = &motion[3..6];      // [vx, vy, vz]
let head_relative = &motion[6..9];     // Head relative to hip
let left_wrist_rel = &motion[9..12];   // Left wrist relative to hip
let right_wrist_rel = &motion[12..15]; // Right wrist relative to hip
let left_ankle_rel = &motion[15..18];  // Left ankle relative to hip
let right_ankle_rel = &motion[18..21]; // Right ankle relative to hip
let orientation = &motion[21..24];     // [yaw, pitch, roll]
let beat = motion[24];                 // Beat phase

// Stats
println!("Frames: {}", transform.frame_count());
println!("Gimbal locks: {}", transform.gimbal_lock_frames());

// Reset for new session
transform.reset();

To63DTransform

Extended transform with face and hand features:

rust
use cc_collection::To63DTransform;
use cc_collection::transform::To63DConfig;

let mut transform = To63DTransform::new(60.0);

let motion: [f32; 63] = transform.transform(&skeleton, beat_phase);

// 63D layout
// 0-24: Core motion (same as 25D)
// 25-34: Face state (10 features)
// 35-48: Left hand (14 features)
// 49-62: Right hand (14 features)

---

Capture Session

Record motion data with beat synchronization:

rust
use cc_collection::{CaptureSession, FusedSkeleton};
use cc_collection::capture::{CaptureConfig, SessionState};

// Create session
let config = CaptureConfig {
    name: "dance_recording_001".to_string(),
    target_fps: 60.0,
    min_quality: 0.7,
    target_bpm: 120.0,
    capacity: 36000,  // ~10 minutes at 60fps
};
let mut session = CaptureSession::new(config);

// Session lifecycle
session.start();
assert_eq!(session.state(), SessionState::Recording);

// Record frames
for (skeleton, beat_phase, quality) in frames {
    if session.capture(&skeleton, beat_phase, quality) {
        // Frame accepted
    } else {
        // Rejected (below min_quality or session not recording)
    }
}

// Pause/resume
session.pause();
session.resume();

// Stop recording
session.stop();

// Access statistics
println!("Frames: {}", session.frame_count());
println!("Duration: {:.1}s", session.duration_secs());
println!("Avg quality: {:.2}", session.average_quality());
println!("Avg FPS: {:.1}", session.average_fps());

---

Validation

MotionValidator

Physics-based validation:

rust
use cc_collection::{MotionValidator, ValidationReport};
use cc_collection::validation::ValidationConfig;

let validator = MotionValidator::new();

// Validate skeleton
let report: ValidationReport = validator.validate(&skeleton);

if report.is_valid {
    println!("Valid! Quality: {:.2}", report.quality);
} else {
    println!("Invalid! Issues:");
    for issue in &report.issues {
        println!("  - {} (limb: {:?})", issue.message, issue.limb);
    }
}

// Validation checks:
// - Joint angle constraints
// - Velocity limits (no teleportation)
// - Anatomical plausibility
// - Sensor confidence

---

Error Handling

cc-collection provides typed errors:

rust
use cc_collection::error::{
    Error, Result, FusionError, CaptureError,
    TransformError, ProtocolError, ValidationError
};

fn process_frame() -> Result<()> {
    let parser = MocopiParser::new();

    // Parse with error handling
    let frame = parser.parse(&data).map_err(|e| {
        match e {
            Error::Protocol(ProtocolError::InvalidHeader) => {
                println!("Bad packet header");
            }
            Error::Protocol(ProtocolError::InvalidData(msg)) => {
                println!("Invalid data: {}", msg);
            }
            _ => println!("Other error: {}", e),
        }
        e
    })?;

    Ok(())
}

---

Complete Example

Real-time fusion pipeline:

rust
use cc_collection::{
    init, FusionEngine, FusionConfig,
    MocopiParser, MediaPipeParser,
    To25DTransform, MotionValidator,
    CaptureSession, CaptureConfig,
};
use cc_collection::error::Result;

fn main() -> Result<()> {
    // Initialize
    init();

    // Create components
    let mut engine = FusionEngine::new(FusionConfig {
        session_id: "live_capture".to_string(),
        min_confidence: 0.5,
        enable_smoothing: true,
        ..Default::default()
    });

    let mut mocopi_parser = MocopiParser::new();
    let mut mediapipe_parser = MediaPipeParser::new();
    let mut transform = To25DTransform::new(60.0);
    let validator = MotionValidator::new();

    let mut session = CaptureSession::new(CaptureConfig {
        name: "session_001".to_string(),
        target_fps: 60.0,
        min_quality: 0.7,
        target_bpm: 120.0,
        capacity: 36000,
    });

    // Start capture
    session.start();

    // Processing loop (pseudo-code)
    let dt = 1.0 / 60.0;
    let mut beat_phase = 0.0;

    loop {
        // Get sensor data (from your data sources)
        let mocopi_data = receive_mocopi_udp();
        let mediapipe_data = receive_mediapipe_ws();

        // Parse
        let mocopi_frame = mocopi_data
            .map(|data| mocopi_parser.parse(&data))
            .transpose()?;
        let mediapipe_frame = mediapipe_data
            .map(|data| mediapipe_parser.parse_json(&data))
            .transpose()?;

        // Fuse
        let skeleton = engine.process_frame(
            mocopi_frame.as_ref(),
            mediapipe_frame.as_ref(),
            dt,
        );

        // Validate
        let report = validator.validate(&skeleton);

        // Transform for ML
        let motion_25d = transform.transform(&skeleton, beat_phase);

        // Record if valid
        if report.is_valid {
            session.capture(&skeleton, beat_phase, report.quality);

            // Send to ML model, visualization, etc.
            send_to_model(&motion_25d);
        }

        // Update beat phase (example: 120 BPM)
        beat_phase = (beat_phase + dt * 2.0) % 1.0;

        // Break condition
        if should_stop() {
            break;
        }
    }

    // Finish
    session.stop();
    println!("Captured {} frames", session.frame_count());

    Ok(())
}

// Placeholder functions
fn receive_mocopi_udp() -> Option<Vec<u8>> { None }
fn receive_mediapipe_ws() -> Option<String> { None }
fn send_to_model(_: &[f32; 25]) {}
fn should_stop() -> bool { true }

---

Performance Tips

Reuse Objects

rust
// Create once, reuse for entire session
let mut engine = FusionEngine::new(config);
let mut transform = To25DTransform::new(60.0);
let validator = MotionValidator::new();

// Process many frames without recreating
for _ in 0..10000 {
    let skeleton = engine.process_frame(...);
    let motion = transform.transform(&skeleton, beat_phase);
    let report = validator.validate(&skeleton);
}

Batch Processing

rust
// For offline processing, batch transforms
let mut transform = To25DTransform::new(60.0);
let mut motions = Vec::with_capacity(skeletons.len());

for (skeleton, phase) in &skeletons {
    motions.push(transform.transform(skeleton, *phase));
}

Feature Flags

toml
# Minimal build (no bindings)
cc-collection = { path = "..." }

# With Python bindings (adds PyO3 dependency)
cc-collection = { path = "...", features = ["python"] }

# With WASM bindings (adds wasm-bindgen)
cc-collection = { path = "...", features = ["wasm"] }

---

Thread Safety

  • `FusionEngine`, `To25DTransform`, `To63DTransform`, and `CaptureSession` are not thread-safe
  • Create one instance per thread if needed
  • Use channels or mutexes for cross-thread communication
rust
use std::sync::mpsc;
use std::thread;

// Producer thread (sensor input)
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
    loop {
        let data = receive_sensor_data();
        tx.send(data).unwrap();
    }
});

// Consumer thread (fusion)
let mut engine = FusionEngine::new(config);
for data in rx {
    let skeleton = engine.process_frame(...);
    // Process skeleton
}

---

Integration with cc-mcs

For server integration, see the cc-mcs fusion bridge:

rust
// In cc-mcs server
use cc_collection::{FusionEngine, FusionConfig};

pub struct FusionBridge {
    engine: FusionEngine,
    transform: To25DTransform,
}

impl FusionBridge {
    pub fn new(session_id: &str) -> Self {
        let config = FusionConfig {
            session_id: session_id.to_string(),
            ..Default::default()
        };
        Self {
            engine: FusionEngine::new(config),
            transform: To25DTransform::new(60.0),
        }
    }

    pub fn process(&mut self, mocopi: Option<&MocopiFrame>,
                   mediapipe: Option<&MediaPipeFrame>,
                   beat_phase: f32) -> [f32; 25] {
        let skeleton = self.engine.process_frame(
            mocopi, mediapipe, 1.0 / 60.0
        );
        self.transform.transform(&skeleton, beat_phase)
    }
}

---

Testing

bash
# Run all tests
cargo test

# Run with Python feature tests
cargo test --features python

# Run benchmarks
cargo bench

# Run specific test
cargo test fusion_engine

# Run with logging
RUST_LOG=debug cargo test -- --nocapture

---

Logging

cc-collection uses the `tracing` crate:

rust
use tracing_subscriber;

fn main() {
    // Enable logging
    tracing_subscriber::fmt()
        .with_env_filter("cc_collection=debug")
        .init();

    // Now cc-collection will log debug info
    let mut engine = FusionEngine::new(config);
}

Environment variable:

bash
RUST_LOG=cc_collection=debug cargo run

Promotion Decision

Attach run IDs, datasets, metrics, and reproduction commands.

Source Anchor

Comp-Core/core/motion/cc-collection/docs/RUST_GUIDE.md

Detected Structure

Evaluation · Architecture