# Motion Planning for Realistic Robot Arms with VAMP {#vampPlanningTutorial} [TOC] This tutorial shows how to use [VAMP](https://github.com/KavrakiLab/vamp) (Vector-Accelerated Motion Planning) with OMPL for robot motion planning. VAMP provides SIMD-accelerated collision checking and forward kinematics, processing multiple robot configurations simultaneously using vectorized instructions. By plugging VAMP into OMPL's planning framework, you get the flexibility of OMPL's planner library with the speed of VAMP's collision checking. VAMP ships with built-in support for several robots including the Franka Panda, UR5, and Fetch. In this tutorial we will plan for a 7-DOF Panda arm navigating around sphere obstacles. In this tutorial we use the Python bindings for OMPL. There is also a pure C++ version of this demo; see [VAMPPlanning.cpp](VAMPPlanning_8cpp_source.html).

The Panda arm inside a cage of 14 sphere obstacles.
## Prerequisites Install the required Python packages: ~~~{.sh} pip install -r demos/vamp/requirements.txt ~~~ You will also need the OMPL Python bindings installed. See the [installation instructions](installation.html) for details. ## Bridging VAMP and OMPL OMPL is designed to be agnostic to the robot and environment representation. To use VAMP's collision checking with OMPL, we need to implement three bridge classes that translate between OMPL's state representation and VAMP's configuration representation. ### State Space First, we create a state space that uses VAMP's robot module to define the joint limits. This is a simple wrapper around ompl::base::RealVectorStateSpace that reads bounds directly from the VAMP robot: ~~~{.py} from ompl import base as ob import vamp class VampStateSpace(ob.RealVectorStateSpace): def __init__(self, robot: vamp.robot): super().__init__(robot.dimension()) self.robot = robot self.dimension = robot.dimension() bounds = ob.RealVectorBounds(self.dimension) upper_bounds = robot.upper_bounds() lower_bounds = robot.lower_bounds() for i in range(self.dimension): bounds.setLow(i, lower_bounds[i]) bounds.setHigh(i, upper_bounds[i]) self.setBounds(bounds) ~~~ The `robot.dimension()` call returns the number of joints (7 for the Panda), and `robot.lower_bounds()` / `robot.upper_bounds()` return the joint limits. This is all OMPL needs to sample and interpolate in the configuration space. ### State Validity Checker Next, we implement a state validity checker that delegates to VAMP's `robot.validate()` function. This checks whether a single configuration is collision-free against the environment: ~~~{.py} class VampStateValidityChecker(ob.StateValidityChecker): def __init__(self, si: ob.SpaceInformation, env: vamp.Environment, robot: vamp.robot): super().__init__(si) self.env = env self.robot = robot self.dimension = robot.dimension() def isValid(self, state: ob.State) -> bool: config = state[0:self.dimension] return self.robot.validate(config, self.env) ~~~ The key line is `self.robot.validate(config, self.env)`, which uses VAMP's vectorized collision checking to test the configuration against all obstacles in the environment. ### Motion Validator Finally, we implement a motion validator that checks whether an entire straight-line motion between two configurations is collision-free. VAMP provides `robot.validate_motion()` for this purpose, which internally checks configurations along the path at an appropriate resolution: ~~~{.py} class VampMotionValidator(ob.MotionValidator): def __init__(self, si: ob.SpaceInformation, env: vamp.Environment, robot: vamp.robot): super().__init__(si) self.env = env self.robot = robot self.dimension = robot.dimension() def checkMotion(self, s1: ob.State, s2: ob.State) -> bool: config1 = s1[0:self.dimension] config2 = s2[0:self.dimension] return self.robot.validate_motion(config1, config2, self.env) ~~~ > **Note:** By providing a custom ompl::base::MotionValidator, we bypass OMPL's default ompl::base::DiscreteMotionValidator, which would otherwise call `isValid()` at discrete steps along the motion. VAMP's `validate_motion()` handles the entire edge check internally in a single call. These three classes are provided in the `vamp_state_space.py` module included with the demo. ## Setting Up the Planning Problem With the bridge classes in place, we can set up a complete planning problem. We will plan for a Panda arm navigating through a cage of sphere obstacles. ### Creating the Environment First, define the obstacle environment. VAMP represents obstacles as geometric primitives (spheres, boxes, cylinders). Here we create a cage of 14 spheres: ~~~{.py} import vamp import numpy as np # Sphere obstacle centers problem = [ [0.55, 0, 0.25], [0.35, 0.35, 0.25], [0, 0.55, 0.25], [-0.55, 0, 0.25], [-0.35, -0.35, 0.25], [0, -0.55, 0.25], [0.35, -0.35, 0.25], [0.35, 0.35, 0.8], [0, 0.55, 0.8], [-0.35, 0.35, 0.8], [-0.55, 0, 0.8], [-0.35, -0.35, 0.8], [0, -0.55, 0.8], [0.35, -0.35, 0.8], ] radius = 0.2 env = vamp.Environment() for sphere in problem: env.add_sphere(vamp.Sphere(sphere, radius)) ~~~ ### Configuring OMPL Now we wire everything together using OMPL's ompl::geometric::SimpleSetup: ~~~{.py} from ompl import base as ob from ompl import geometric as og from vamp_state_space import VampMotionValidator, VampStateValidityChecker, VampStateSpace # Select the VAMP robot module robot = vamp.panda dimension = robot.dimension() # Create the state space from the VAMP robot space = VampStateSpace(robot=robot) si = ob.SpaceInformation(space) # Set VAMP-based validators motion_validator = VampMotionValidator(si=si, env=env, robot=robot) state_validity_checker = VampStateValidityChecker(si=si, env=env, robot=robot) si.setMotionValidator(motion_validator) si.setStateValidityChecker(state_validity_checker) # Choose a planner planner = og.RRTConnect(si) # Build SimpleSetup ss = og.SimpleSetup(si) ss.setPlanner(planner) ~~~ ### Setting Start and Goal States Define start and goal configurations as joint angles for the 7-DOF Panda arm: ~~~{.py} # Start configuration a = [0., -0.785, 0., -2.356, 0., 1.571, 0.785] # Goal configuration b = [2.35, 1., 0., -0.8, 0, 2.5, 0.785] start = si.allocState() start[0:dimension] = a goal = si.allocState() goal[0:dimension] = b ss.setStartAndGoalStates(start, goal) ~~~ ### Solving With everything configured, solving is a single call: ~~~{.py} import time planning_start = time.time() result = ss.solve(5.0) # 5 second time limit if result: path = ss.getSolutionPath() print(f"Planning time: {time.time() - planning_start}") print(f"Generated path with {path.getStateCount()} states") ~~~ The planner will typically find a solution in milliseconds thanks to VAMP's fast collision checking. ## Benchmarking Planners OMPL's ompl::tools::Benchmark class makes it easy to compare multiple planners on the same problem. Here we benchmark RRTConnect, RRT, KPIECE1, and LBKPIECE1: ~~~{.py} from ompl import tools as ot # Set up SimpleSetup as before (without setting a specific planner) ss = og.SimpleSetup(si) start = si.allocState() start[0:dimension] = a goal = si.allocState() goal[0:dimension] = b ss.setStartAndGoalStates(start, goal) # Add planners to benchmark planners = [ og.RRTConnect(si), og.RRT(si), og.KPIECE1(si), og.LBKPIECE1(si), ] benchmark = ot.Benchmark(ss, "Vamp Cage Planning Benchmark") for planner in planners: benchmark.addPlanner(planner) # Configure and run req = ot.Request() req.maxTime = 1.0 # 1 second per run req.maxMem = 100.0 # 100 MB memory limit req.runCount = 100 # 100 runs per planner req.displayProgress = True benchmark.benchmark(req) # Save results benchmark.saveResultsToFile("benchmark.log") ~~~ The log file can be visualized with [Planner Arena](http://plannerarena.org) (see the [benchmarking tutorial](benchmark.html) for details). You can also convert it to a SQLite database for custom analysis: ~~~{.py} db_path = "benchmark.db" ot.readBenchmarkLog(db_path, ["benchmark.log"], moveitformat=False) ~~~ ## Running the Demo The complete demo script supports both single planning and benchmarking via command-line flags: ~~~{.sh} # Single planning run python3 vamp/vamp_planning.py # With 3D visualization (opens at localhost:8080) python3 vamp/vamp_planning.py --visualize # Benchmark multiple planners python3 vamp/vamp_planning.py --benchmark --n_trials 100 ~~~ ## Extending to Other Robots VAMP supports several robots out of the box. To plan for a different robot, simply change the robot module: ~~~{.py} robot = vamp.ur5 # UR5 (6-DOF) robot = vamp.fetch # Fetch (8-DOF) robot = vamp.baxter # Baxter (7-DOF per arm) ~~~ The `VampStateSpace`, `VampStateValidityChecker`, and `VampMotionValidator` classes work with any VAMP robot module, since they read the dimension and bounds from the robot object. For a more comprehensive example that loads problems from the [MotionBenchMaker](https://github.com/KavrakiLab/motion_bench_maker) dataset and supports multiple robots, see the `motion_benchmaker_demo.py` script in the demos directory.