Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Compound State Planning: Python

This example demonstrates how to use CompoundStateSpace in Python to plan for a system with both position and orientation.

import math
import random
from oxmpl_py.base import (
    CompoundState,
    CompoundStateSpace,
    RealVectorStateSpace,
    SO2StateSpace,
    ProblemDefinition,
    RealVectorState,
    SO2State,
    PlannerConfig,
)
from oxmpl_py.geometric import RRT

class CompoundCircularGoal:
    def __init__(self, space: CompoundStateSpace, x: float, y: float, radius: float):
        self.space = space
        # Target: Position (x,y) and Orientation 0.0 (though orientation is ignored for goal satisfaction here)
        self.target = CompoundState([RealVectorState([x, y]), SO2State(0.0)])
        self.radius = radius
        self.rng = random.Random(123)

    def is_satisfied(self, state: CompoundState) -> bool:
        return self.space.distance(self.target, state) <= self.radius

    def sample_goal(self) -> CompoundState:
        angle = self.rng.uniform(0, 2 * math.pi)
        radius = self.radius * math.sqrt(self.rng.uniform(0, 1))

        x = self.target.components[0].values[0] + radius * math.cos(angle)
        y = self.target.components[0].values[1] + radius * math.sin(angle)

        # Random orientation for the goal sample
        so2_value = self.rng.uniform(0, 2 * math.pi)

        return CompoundState([RealVectorState([x, y]), SO2State(so2_value)])

def is_state_valid(state: CompoundState) -> bool:
    """
    Checks if the state is valid.
    Invalid if the position is inside a box obstacle centered at (0, 0)
    with width 1.0 and height 4.0.
    """
    rv_state = state.components[0]
    x, y = rv_state.values

    # Obstacle definition
    x_min, x_max = -0.5, 0.5
    y_min, y_max = -2.0, 2.0

    is_in_wall = (
        x >= x_min and x <= x_max and
        y >= y_min and y <= y_max
    )

    return not is_in_wall

def main():
    # 1. Define State Space: R^2 x SO(2)
    rv_space = RealVectorStateSpace(dimension=2, bounds=[(-5.0, 5.0), (-5.0, 5.0)])
    so2_space = SO2StateSpace()

    # Combined space with weights
    space = CompoundStateSpace([rv_space, so2_space], weights=[1.0, 0.5])

    # 2. Define Start and Goal
    start_state = CompoundState([RealVectorState([-2.0, 0.0]), SO2State(0.0)])

    # Goal region around (2.0, 0.0) with radius 0.5
    goal_region = CompoundCircularGoal(space, x=2.0, y=0.0, radius=0.5)

    # 3. Create Problem Definition
    problem_def = ProblemDefinition.from_compound(space, start_state, goal_region)
    planner_config = PlannerConfig(seed=123)

    # 4. Setup Planner (RRT)
    planner = RRT(
        max_distance=0.5,
        goal_bias=0.1,
        problem_definition=problem_def,
        planner_config=planner_config,
    )

    planner.setup(is_state_valid)

    # 5. Solve
    print("Solving Compound State planning problem (Python)...")
    try:
        path = planner.solve(timeout_secs=5.0)
        print(f"Solution found with {len(path.states)} states.")

        if len(path.states) > 0:
            start = path.states[0]
            end = path.states[-1]
            s_rv = start.components[0].values
            s_so2 = start.components[1].value
            e_rv = end.components[0].values
            e_so2 = end.components[1].value

            print(f"Start: ({s_rv[0]:.2f}, {s_rv[1]:.2f}, {s_so2:.2f})")
            print(f"End:   ({e_rv[0]:.2f}, {e_rv[1]:.2f}, {e_so2:.2f})")

    except Exception as e:
        print(f"Planning failed: {e}")

if __name__ == "__main__":
    main()