Files
rustytorch/demos/embodied-shared/src/lib.rs
T
osobhandClaude Opus 4.6 02d382d5f6 style: apply rustfmt across all crates and demos
Consistent formatting pass: line wrapping, import sorting, trailing
whitespace removal, let-chain indentation, merged derive attributes,
and unsafe block reformatting.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-04-12 07:01:58 -07:00

794 lines
22 KiB
Rust

//! Shared types for EmbodiedSim - Robotics World Model Trainer.
//!
//! This crate defines the IPC types for sim-to-real robotics world models.
use serde::{Deserialize, Serialize};
// ============================================================================
// Observation Types
// ============================================================================
/// Robot observation (state).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Observation {
/// Joint positions (radians).
pub joint_positions: Vec<f32>,
/// Joint velocities (rad/s).
pub joint_velocities: Vec<f32>,
/// End-effector pose (x, y, z, qw, qx, qy, qz).
pub end_effector_pose: [f32; 7],
/// Camera image (flattened RGB).
pub camera_image: Option<Vec<u8>>,
/// Image dimensions (width, height).
pub image_dims: Option<(usize, usize)>,
/// Timestamp.
pub timestamp: f64,
}
impl Default for Observation {
fn default() -> Self {
Self {
joint_positions: vec![0.0; 7],
joint_velocities: vec![0.0; 7],
end_effector_pose: [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],
camera_image: None,
image_dims: None,
timestamp: 0.0,
}
}
}
/// Action command to robot.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Action {
/// Joint position targets (radians).
pub joint_positions: Option<Vec<f32>>,
/// Joint velocity targets (rad/s).
pub joint_velocities: Option<Vec<f32>>,
/// Joint torques (Nm).
pub joint_torques: Option<Vec<f32>>,
/// End-effector delta pose (dx, dy, dz, droll, dpitch, dyaw).
pub ee_delta: Option<[f32; 6]>,
/// Gripper command (0 = open, 1 = closed).
pub gripper: Option<f32>,
}
/// Transition tuple for training.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Transition {
/// Current observation.
pub observation: Observation,
/// Action taken.
pub action: Action,
/// Reward received.
pub reward: f32,
/// Next observation.
pub next_observation: Observation,
/// Whether episode terminated.
pub done: bool,
/// Whether episode was truncated.
pub truncated: bool,
/// Additional info.
pub info: TransitionInfo,
}
/// Additional transition information.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TransitionInfo {
/// Task success.
pub success: Option<bool>,
/// Distance to goal.
pub distance_to_goal: Option<f32>,
/// Contact forces.
pub contact_forces: Option<Vec<f32>>,
}
// ============================================================================
// World Model Types
// ============================================================================
/// World model type.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum WorldModelType {
/// RSSM (Recurrent State Space Model) - DreamerV3 style.
RSSM,
/// JEPA (Joint Embedding Predictive Architecture).
JEPA,
/// Transformer-based world model.
Transformer,
/// Latent Dynamics Model.
LatentDynamics,
/// Variational World Model.
VWM,
}
/// World model configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorldModelConfig {
/// Model type.
pub model_type: WorldModelType,
/// Observation encoding dimension.
pub obs_embed_dim: usize,
/// Action encoding dimension.
pub action_embed_dim: usize,
/// Deterministic state dimension (RSSM).
pub deter_dim: usize,
/// Stochastic state dimension (RSSM).
pub stoch_dim: usize,
/// Number of stochastic classes (discrete).
pub stoch_classes: usize,
/// Hidden dimension for MLPs.
pub hidden_dim: usize,
/// Number of transformer layers (if applicable).
pub num_layers: usize,
/// Image encoder type.
pub image_encoder: ImageEncoderType,
/// Whether to predict rewards.
pub predict_reward: bool,
/// Whether to predict termination.
pub predict_done: bool,
/// Imagination horizon.
pub imagination_horizon: usize,
}
impl Default for WorldModelConfig {
fn default() -> Self {
Self {
model_type: WorldModelType::RSSM,
obs_embed_dim: 256,
action_embed_dim: 64,
deter_dim: 512,
stoch_dim: 32,
stoch_classes: 32,
hidden_dim: 512,
num_layers: 2,
image_encoder: ImageEncoderType::CNN,
predict_reward: true,
predict_done: true,
imagination_horizon: 15,
}
}
}
/// Image encoder type.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum ImageEncoderType {
/// Convolutional neural network.
CNN,
/// Vision Transformer.
ViT,
/// ResNet-style.
ResNet,
/// Simple MLP (for vector observations).
MLP,
}
// ============================================================================
// Policy Types
// ============================================================================
/// Policy type.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PolicyType {
/// Actor-Critic.
ActorCritic,
/// Model Predictive Control.
MPC,
/// Cross-Entropy Method.
CEM,
/// Behavioral Cloning.
BehavioralCloning,
/// Diffusion Policy.
DiffusionPolicy,
}
/// Policy configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PolicyConfig {
/// Policy type.
pub policy_type: PolicyType,
/// State dimension (from world model).
pub state_dim: usize,
/// Action dimension.
pub action_dim: usize,
/// Hidden dimensions for actor.
pub actor_hidden: Vec<usize>,
/// Hidden dimensions for critic.
pub critic_hidden: Vec<usize>,
/// Discount factor.
pub gamma: f32,
/// GAE lambda.
pub gae_lambda: f32,
/// Entropy coefficient.
pub entropy_coef: f32,
/// Use continuous actions.
pub continuous_actions: bool,
/// Action distribution type.
pub action_dist: ActionDistribution,
}
impl Default for PolicyConfig {
fn default() -> Self {
Self {
policy_type: PolicyType::ActorCritic,
state_dim: 512,
action_dim: 7,
actor_hidden: vec![256, 256],
critic_hidden: vec![256, 256],
gamma: 0.99,
gae_lambda: 0.95,
entropy_coef: 0.001,
continuous_actions: true,
action_dist: ActionDistribution::Normal,
}
}
}
/// Action distribution type.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum ActionDistribution {
/// Normal (Gaussian) distribution.
Normal,
/// Truncated normal distribution.
TruncatedNormal,
/// Categorical distribution.
Categorical,
/// Mixture of Gaussians.
MixtureOfGaussians,
}
// ============================================================================
// Training Types
// ============================================================================
/// Training configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrainingConfig {
/// Batch size.
pub batch_size: usize,
/// Sequence length for world model.
pub sequence_length: usize,
/// Learning rate for world model.
pub wm_learning_rate: f64,
/// Learning rate for actor.
pub actor_learning_rate: f64,
/// Learning rate for critic.
pub critic_learning_rate: f64,
/// Number of training epochs.
pub epochs: usize,
/// Buffer size for replay.
pub buffer_size: usize,
/// Prefill steps.
pub prefill_steps: usize,
/// Train every N steps.
pub train_every: usize,
/// Train ratio (train steps per env step).
pub train_ratio: f32,
/// Use mixed precision.
pub mixed_precision: bool,
/// Gradient clipping.
pub gradient_clip: f32,
/// Random seed.
pub seed: Option<u64>,
}
impl Default for TrainingConfig {
fn default() -> Self {
Self {
batch_size: 64,
sequence_length: 50,
wm_learning_rate: 1e-4,
actor_learning_rate: 3e-5,
critic_learning_rate: 3e-5,
epochs: 100,
buffer_size: 1_000_000,
prefill_steps: 5000,
train_every: 5,
train_ratio: 512.0,
mixed_precision: true,
gradient_clip: 100.0,
seed: Some(42),
}
}
}
/// Training progress.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrainingProgress {
/// Current epoch.
pub epoch: usize,
/// Total epochs.
pub total_epochs: usize,
/// Current step.
pub step: usize,
/// Total steps.
pub total_steps: usize,
/// World model loss.
pub wm_loss: f64,
/// Image reconstruction loss.
pub image_loss: Option<f64>,
/// Reward prediction loss.
pub reward_loss: Option<f64>,
/// KL divergence loss.
pub kl_loss: Option<f64>,
/// Actor loss.
pub actor_loss: Option<f64>,
/// Critic loss.
pub critic_loss: Option<f64>,
/// Entropy.
pub entropy: Option<f64>,
/// Learning rate.
pub learning_rate: f64,
/// Environment return (evaluation).
pub env_return: Option<f64>,
/// Episode length (evaluation).
pub episode_length: Option<f64>,
/// Elapsed time in seconds.
pub elapsed_seconds: f64,
}
// ============================================================================
// Environment Types
// ============================================================================
/// Robot environment type.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum EnvironmentType {
/// Panda Franka robot.
Panda,
/// UR5 robot.
UR5,
/// Sawyer robot.
Sawyer,
/// Kuka robot.
Kuka,
/// Custom robot.
Custom,
}
/// Task type.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum TaskType {
/// Reach a target position.
Reach,
/// Push an object.
Push,
/// Pick and place.
PickPlace,
/// Stack objects.
Stack,
/// Insert peg into hole.
PegInsertion,
/// Open a door.
DoorOpen,
/// General manipulation.
Manipulation,
}
/// Environment configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvironmentConfig {
/// Environment type.
pub env_type: EnvironmentType,
/// Task type.
pub task_type: TaskType,
/// Number of joints.
pub num_joints: usize,
/// Action dimension.
pub action_dim: usize,
/// Observation dimension (non-image).
pub obs_dim: usize,
/// Use camera observations.
pub use_camera: bool,
/// Camera image size.
pub camera_size: (usize, usize),
/// Maximum episode length.
pub max_episode_length: usize,
/// Action repeat.
pub action_repeat: usize,
/// Control frequency (Hz).
pub control_freq: f32,
/// Reward type.
pub reward_type: RewardType,
}
impl Default for EnvironmentConfig {
fn default() -> Self {
Self {
env_type: EnvironmentType::Panda,
task_type: TaskType::Reach,
num_joints: 7,
action_dim: 7,
obs_dim: 21, // 7 pos + 7 vel + 7 ee pose
use_camera: true,
camera_size: (64, 64),
max_episode_length: 500,
action_repeat: 2,
control_freq: 20.0,
reward_type: RewardType::Dense,
}
}
}
/// Reward type.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum RewardType {
/// Dense reward (continuous feedback).
Dense,
/// Sparse reward (only on success).
Sparse,
/// Shaped reward (curriculum).
Shaped,
}
// ============================================================================
// Evaluation Types
// ============================================================================
/// Evaluation request.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvaluationRequest {
/// Number of episodes.
pub num_episodes: usize,
/// Whether to render.
pub render: bool,
/// Use deterministic policy.
pub deterministic: bool,
/// Record video.
pub record_video: bool,
/// Environment config.
pub env_config: EnvironmentConfig,
}
impl Default for EvaluationRequest {
fn default() -> Self {
Self {
num_episodes: 10,
render: false,
deterministic: true,
record_video: false,
env_config: EnvironmentConfig::default(),
}
}
}
/// Evaluation result.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvaluationResult {
/// Mean return.
pub mean_return: f64,
/// Std return.
pub std_return: f64,
/// Mean episode length.
pub mean_length: f64,
/// Success rate.
pub success_rate: f64,
/// Individual episode returns.
pub episode_returns: Vec<f64>,
/// Individual episode lengths.
pub episode_lengths: Vec<usize>,
/// Total evaluation time.
pub eval_time: f64,
}
// ============================================================================
// Imagination Types
// ============================================================================
/// Imagined trajectory.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImaginedTrajectory {
/// States (world model latent states).
pub states: Vec<Vec<f64>>,
/// Actions taken.
pub actions: Vec<Vec<f64>>,
/// Predicted rewards.
pub rewards: Vec<f64>,
/// Predicted values.
pub values: Vec<f64>,
/// Predicted termination probabilities.
pub dones: Vec<f64>,
/// Decoded observations (optional).
pub decoded_observations: Option<Vec<Vec<u8>>>,
}
/// Imagination request.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImaginationRequest {
/// Starting observation.
pub initial_observation: Observation,
/// Horizon length.
pub horizon: usize,
/// Number of trajectories.
pub num_trajectories: usize,
/// Use policy for actions.
pub use_policy: bool,
/// Decode observations.
pub decode: bool,
}
// ============================================================================
// Sim-to-Real Types
// ============================================================================
/// Domain randomization config.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DomainRandomization {
/// Randomize dynamics.
pub dynamics: bool,
/// Randomize visuals.
pub visuals: bool,
/// Randomize lighting.
pub lighting: bool,
/// Mass range (multiplier).
pub mass_range: (f32, f32),
/// Friction range.
pub friction_range: (f32, f32),
/// Damping range.
pub damping_range: (f32, f32),
/// Action noise std.
pub action_noise: f32,
/// Observation noise std.
pub observation_noise: f32,
}
impl Default for DomainRandomization {
fn default() -> Self {
Self {
dynamics: true,
visuals: true,
lighting: true,
mass_range: (0.8, 1.2),
friction_range: (0.5, 1.5),
damping_range: (0.8, 1.2),
action_noise: 0.01,
observation_noise: 0.01,
}
}
}
/// Sim-to-real transfer config.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimToRealConfig {
/// Domain randomization settings.
pub domain_randomization: DomainRandomization,
/// Use real robot calibration.
pub use_calibration: bool,
/// Action scaling factor.
pub action_scale: f32,
/// Safety limits.
pub safety_limits: SafetyLimits,
}
/// Safety limits for real robot.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SafetyLimits {
/// Maximum joint velocity (rad/s).
pub max_joint_velocity: f32,
/// Maximum joint acceleration (rad/s²).
pub max_joint_acceleration: f32,
/// Maximum end-effector velocity (m/s).
pub max_ee_velocity: f32,
/// Maximum force (N).
pub max_force: f32,
/// Workspace limits (min, max) for x, y, z.
pub workspace: [[f32; 2]; 3],
}
impl Default for SafetyLimits {
fn default() -> Self {
Self {
max_joint_velocity: 1.5,
max_joint_acceleration: 5.0,
max_ee_velocity: 0.5,
max_force: 50.0,
workspace: [
[-0.5, 0.5], // x
[-0.5, 0.5], // y
[0.0, 0.8], // z
],
}
}
}
// ============================================================================
// Sample Functions
// ============================================================================
/// Create a sample observation.
#[must_use]
pub fn sample_observation() -> Observation {
Observation {
joint_positions: vec![0.0, -0.785, 0.0, -2.356, 0.0, 1.571, 0.785],
joint_velocities: vec![0.0; 7],
end_effector_pose: [0.3, 0.0, 0.5, 1.0, 0.0, 0.0, 0.0],
camera_image: None,
image_dims: None,
timestamp: 0.0,
}
}
/// Create a sample action.
#[must_use]
pub fn sample_action() -> Action {
Action {
joint_positions: Some(vec![0.1, -0.7, 0.0, -2.3, 0.0, 1.5, 0.8]),
joint_velocities: None,
joint_torques: None,
ee_delta: None,
gripper: Some(0.0),
}
}
/// Create a sample transition.
#[must_use]
pub fn sample_transition() -> Transition {
Transition {
observation: sample_observation(),
action: sample_action(),
reward: 1.0,
next_observation: sample_observation(),
done: false,
truncated: false,
info: TransitionInfo {
success: Some(false),
distance_to_goal: Some(0.15),
contact_forces: None,
},
}
}
/// Create a sample world model config.
#[must_use]
pub fn sample_world_model_config() -> WorldModelConfig {
WorldModelConfig {
model_type: WorldModelType::RSSM,
obs_embed_dim: 256,
action_embed_dim: 64,
deter_dim: 512,
stoch_dim: 32,
stoch_classes: 32,
hidden_dim: 512,
num_layers: 2,
image_encoder: ImageEncoderType::CNN,
predict_reward: true,
predict_done: true,
imagination_horizon: 15,
}
}
/// Create a sample policy config.
#[must_use]
pub fn sample_policy_config() -> PolicyConfig {
PolicyConfig {
policy_type: PolicyType::ActorCritic,
state_dim: 512 + 32 * 32, // deter + stoch * classes
action_dim: 7,
actor_hidden: vec![256, 256],
critic_hidden: vec![256, 256],
gamma: 0.997,
gae_lambda: 0.95,
entropy_coef: 3e-4,
continuous_actions: true,
action_dist: ActionDistribution::Normal,
}
}
/// Create a sample training config.
#[must_use]
pub fn sample_training_config() -> TrainingConfig {
TrainingConfig {
batch_size: 16,
sequence_length: 64,
wm_learning_rate: 1e-4,
actor_learning_rate: 3e-5,
critic_learning_rate: 3e-5,
epochs: 50,
buffer_size: 100_000,
prefill_steps: 1000,
train_every: 5,
train_ratio: 256.0,
mixed_precision: false,
gradient_clip: 100.0,
seed: Some(42),
}
}
/// Create a sample environment config.
#[must_use]
pub fn sample_env_config() -> EnvironmentConfig {
EnvironmentConfig {
env_type: EnvironmentType::Panda,
task_type: TaskType::Reach,
num_joints: 7,
action_dim: 7,
obs_dim: 21,
use_camera: false,
camera_size: (64, 64),
max_episode_length: 100,
action_repeat: 2,
control_freq: 20.0,
reward_type: RewardType::Dense,
}
}
// ============================================================================
// Tests
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_observation() {
let obs = sample_observation();
assert_eq!(obs.joint_positions.len(), 7);
assert_eq!(obs.joint_velocities.len(), 7);
}
#[test]
fn test_action() {
let action = sample_action();
assert!(action.joint_positions.is_some());
assert!(action.gripper.is_some());
}
#[test]
fn test_transition() {
let trans = sample_transition();
assert!(!trans.done);
assert!(trans.info.distance_to_goal.is_some());
}
#[test]
fn test_world_model_config() {
let config = sample_world_model_config();
assert_eq!(config.model_type, WorldModelType::RSSM);
assert!(config.predict_reward);
}
#[test]
fn test_policy_config() {
let config = sample_policy_config();
assert_eq!(config.policy_type, PolicyType::ActorCritic);
assert!(config.continuous_actions);
}
#[test]
fn test_training_config() {
let config = sample_training_config();
assert!(config.batch_size > 0);
assert!(config.sequence_length > 0);
}
#[test]
fn test_env_config() {
let config = sample_env_config();
assert_eq!(config.env_type, EnvironmentType::Panda);
assert_eq!(config.task_type, TaskType::Reach);
}
#[test]
fn test_domain_randomization() {
let dr = DomainRandomization::default();
assert!(dr.dynamics);
assert!(dr.mass_range.0 < dr.mass_range.1);
}
#[test]
fn test_safety_limits() {
let limits = SafetyLimits::default();
assert!(limits.max_joint_velocity > 0.0);
assert!(limits.max_force > 0.0);
}
#[test]
fn test_serialization() {
let config = sample_world_model_config();
let json = serde_json::to_string(&config).unwrap();
let parsed: WorldModelConfig = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.model_type, config.model_type);
}
}