Utilities#

Helper functions and utilities for GenesisLab.

Overview#

Utility modules provide commonly used functions and tools:

  • ConfigClass: Configuration system decorator

  • Math: Mathematical operations and transformations

  • Timing: Performance profiling and timing

  • I/O: File loading and saving

  • Typing: Type definitions and hints

ConfigClass#

The configuration system using @configclass decorator.

Usage#

from genesislab.utils.configclass import configclass
from dataclasses import field

@configclass
class MyConfig:
    # Basic types
    name: str = "default"
    value: float = 1.0
    count: int = 10
    enabled: bool = True
    
    # Collections
    values: list[float] = field(default_factory=lambda: [1.0, 2.0])
    mapping: dict[str, float] = field(default_factory=dict)
    
    # Nested configs
    sub_config: SubConfig = SubConfig()

Critical Rule#

ALWAYS use @configclass, NEVER use @dataclass.

# ✅ Correct
from genesislab.utils.configclass import configclass

@configclass
class MyCfg:
    value: float = 1.0

# ❌ Incorrect - Will cause errors!
from dataclasses import dataclass

@dataclass
class MyCfg:
    value: float = 1.0

Math Utilities#

Quaternion Operations#

from genesislab.utils.math import (
    quat_mul,
    quat_conjugate,
    quat_rotate,
    quat_to_euler,
    euler_to_quat
)

# Quaternion multiplication (WXYZ format)
q1 = torch.tensor([1.0, 0.0, 0.0, 0.0])
q2 = torch.tensor([0.707, 0.0, 0.707, 0.0])
q_result = quat_mul(q1, q2)

# Rotate vector by quaternion
vec = torch.tensor([1.0, 0.0, 0.0])
rotated = quat_rotate(q1, vec)

# Convert to Euler angles (roll, pitch, yaw)
euler = quat_to_euler(q1)  # [roll, pitch, yaw]

# Convert from Euler angles
quat = euler_to_quat(euler)

Note: GenesisLab uses WXYZ quaternion format throughout.

Transformations#

from genesislab.utils.math import (
    transform_points,
    inverse_transform,
    homogeneous_transform
)

# Transform points
points = torch.randn(100, 3)  # [N, 3]
position = torch.tensor([1.0, 2.0, 3.0])
orientation = torch.tensor([1.0, 0.0, 0.0, 0.0])  # WXYZ quat

transformed = transform_points(points, position, orientation)

Sampling#

from genesislab.utils.math import (
    sample_uniform,
    sample_gaussian,
    sample_uniform_sphere
)

# Uniform sampling in range
samples = sample_uniform(low=-1.0, high=1.0, size=(1000, 3))

# Gaussian sampling
samples = sample_gaussian(mean=0.0, std=1.0, size=(1000, 3))

# Uniform sampling on sphere
directions = sample_uniform_sphere(size=1000)  # [1000, 3]

Timing Utilities#

Timer#

from genesislab.utils.timing import Timer

# Context manager
with Timer("simulation_step"):
    env.step(actions)

# Manual timing
timer = Timer("my_operation")
timer.start()
# ... operation ...
elapsed = timer.stop()
print(f"Elapsed: {elapsed:.3f}s")

Performance Profiling#

from genesislab.utils.timing import profile_function

@profile_function
def my_expensive_function():
    # ... computation ...
    pass

# Call function - timing info is automatically logged
result = my_expensive_function()

FPS Counter#

from genesislab.utils.timing import FPSCounter

fps_counter = FPSCounter(window_size=100)

for _ in range(1000):
    # ... simulation step ...
    
    fps_counter.tick()
    if fps_counter.frame_count % 100 == 0:
        print(f"FPS: {fps_counter.fps:.0f}")

I/O Utilities#

Loading Assets#

from genesislab.utils.io import load_urdf, load_mesh

# Load URDF
robot = load_urdf("path/to/robot.urdf")

# Load mesh
mesh = load_mesh("path/to/mesh.obj")

Saving/Loading Checkpoints#

from genesislab.utils.io import save_checkpoint, load_checkpoint

# Save
checkpoint = {
    "policy": policy.state_dict(),
    "optimizer": optimizer.state_dict(),
    "episode": episode
}
save_checkpoint(checkpoint, "checkpoint.pt")

# Load
checkpoint = load_checkpoint("checkpoint.pt")
policy.load_state_dict(checkpoint["policy"])

Configuration Files#

from genesislab.utils.io import save_config, load_config

# Save configuration
cfg = MyTaskCfg()
save_config(cfg, "config.yaml")

# Load configuration
cfg = load_config("config.yaml", MyTaskCfg)

Typing#

Common type definitions used throughout GenesisLab.

from genesislab.utils.typing import (
    TensorType,
    ObsDict,
    InfoDict,
    ConfigType
)

# Type hints
def compute_reward(
    observations: ObsDict,
    actions: TensorType
) -> TensorType:
    """
    Compute reward.
    
    Args:
        observations: Dictionary of observation tensors
        actions: Action tensor [num_envs, action_dim]
        
    Returns:
        Reward tensor [num_envs]
    """
    pass

Logging#

Logger Setup#

from genesislab.utils.logging import setup_logger, get_logger

# Setup logger
setup_logger(
    log_dir="logs/",
    log_level="INFO",
    log_to_file=True
)

# Get logger
logger = get_logger(__name__)
logger.info("Training started")
logger.warning("Learning rate adjusted")

TensorBoard Integration#

from genesislab.utils.logging import TensorBoardLogger

# Create logger
tb_logger = TensorBoardLogger(log_dir="logs/tensorboard")

# Log scalars
tb_logger.log_scalar("reward/mean", mean_reward, step)
tb_logger.log_scalar("policy/lr", learning_rate, step)

# Log histogram
tb_logger.log_histogram("actions", actions, step)

# Log image
tb_logger.log_image("observation/camera", image, step)

WandB Integration#

from genesislab.utils.logging import WandbLogger

# Create logger
wandb_logger = WandbLogger(
    project="genesislab",
    name="go2_flat",
    config=vars(cfg)
)

# Log metrics
wandb_logger.log({
    "reward": mean_reward,
    "episode_length": mean_length
}, step=iteration)

Random Seed#

Setting Seeds#

from genesislab.utils.random import set_seed

# Set all random seeds (Python, NumPy, PyTorch, Genesis)
set_seed(42)

Device Management#

Device Utilities#

from genesislab.utils.device import get_device, to_device

# Get default device
device = get_device()  # Returns cuda if available

# Move data to device
data = to_device(data, device)

Debugging Utilities#

Tensor Checks#

from genesislab.utils.debug import check_tensor, print_tensor_stats

# Check for NaN/Inf
check_tensor(tensor, name="my_tensor")  # Raises if invalid

# Print statistics
print_tensor_stats(tensor, name="my_tensor")
# Output:
# my_tensor: shape=(4096, 48), min=-1.23, max=2.45, mean=0.12, std=0.89

Visualization Helpers#

from genesislab.utils.vis import (
    plot_observations,
    plot_rewards,
    plot_trajectories
)

# Plot observation distribution
plot_observations(observations, save_path="obs.png")

# Plot reward components
plot_rewards(reward_terms, save_path="rewards.png")

# Plot robot trajectories
plot_trajectories(positions, save_path="trajectories.png")

Common Patterns#

Noise Application#

from genesislab.utils.noise import add_noise

# Add noise to observations
obs_noisy = add_noise(
    obs,
    noise_type="gaussian",
    noise_params={"mean": 0.0, "std": 0.1}
)

Clamping and Scaling#

from genesislab.utils.math import scale_transform, saturate

# Scale from [-1, 1] to [a, b]
scaled = scale_transform(
    actions,
    input_range=(-1, 1),
    output_range=(-0.5, 0.5)
)

# Clamp values
clamped = saturate(values, min_val=-1.0, max_val=1.0)

Example: Complete Utility Usage#

from genesislab.utils.configclass import configclass
from genesislab.utils.timing import Timer, FPSCounter
from genesislab.utils.logging import setup_logger, get_logger
from genesislab.utils.random import set_seed
from genesislab.utils.math import quat_to_euler
import torch

# Setup
set_seed(42)
setup_logger(log_dir="logs/")
logger = get_logger(__name__)

# Configuration
@configclass
class TrainCfg:
    num_envs: int = 4096
    learning_rate: float = 3e-4
    max_iterations: int = 1000

cfg = TrainCfg()
logger.info(f"Starting training with {cfg.num_envs} environments")

# Training loop
fps_counter = FPSCounter()

for iteration in range(cfg.max_iterations):
    with Timer("iteration"):
        # ... training step ...
        pass
    
    fps_counter.tick()
    
    if iteration % 100 == 0:
        logger.info(f"Iteration {iteration}, FPS: {fps_counter.fps:.0f}")

Next Steps#