Prpy
Python utilities used by the Personal Robotics Laboratory.
Install / Use
/learn @personalrobotics/PrpyREADME
PrPy
PrPy is a Python library used by the Personal Robotics Laboratory at Carnegie Mellon University. This library includes robot-agnostic utilities that make it easier to use OpenRAVE in Python scripts. This includes a high-level planning pipeline, helper functions, and visualization tools.
Planning Pipeline
There are a large array of motion planners that have complementary strengths
and weaknesses. PrPy provides a planning pipeline in the prpy.planning
namespace that makes it easy plan with multiple planners in parallel on a
single problem. Additionally, the planning pipeline takes advantage of the
dynamic nature of Python to mix-and-match planners with heterogeneous
capabilities.
Every planner used in the PrPy planning pipeline extends the
prpy.planning.base.Planner class. Typically, a planner will extend one of
two subclasses:
prpy.planning.base.BasePlanner: implements or wraps a motion plannerprpy.planning.base.MetaPlanner: combines the output of multiple motion planners, each of which is aBasePlanneror anotherMetaPlanner
Each planner has one or more planning methods, annotated with either the
@LockedPlanningMethod or @ClonedPlanningMethod decorator, that look like
ordinary functions. Using these decorators makes other PrPy components
aware that these methods exist and follow a particular specification that
allows them to be composed with other PrPy objects automatically. For
example, MetaPlanners will report that they can perform planning methods
that their child motion planners have enumerated via @PlanningMethod
decorators.
@PlanningMethod decorators also make sure that calls to planning code are
executed in a thread-safe manner. In the case of @LockedPlanningMethod,
this is enforced by locking the calling environment until the planning method
has completed. In the case of @ClonedPlanningMethod, this is enforced by
cloning the calling environment, and calling the wrapped method with references
to the cloned environment. The result of the method is then copied back to the
calling environment. @ClonedPlanningMethods can be used to run multiple
planners in parallel and to parallelize planning and execution.
In general, locked planning methods are used for calls that will terminate extremely quickly, while cloned planning methods are used for calls that might take a significant amount of time.
For example, the following code will use OMPL to plan robot's active DOFs
from their current values to to the goal_config configuration:
planner = OMPLPlanner('RRTConnect')
output_path = planner.PlanToConfiguration(robot, goal_config)
As this is a @ClonedPlanningMethod, robot.GetEnv() is cloned into the
the planner.env planning environment. Planning occurs within this cloned
environment. Finally, the output path is cloned back into robot.GetEnv()
and is returned by the planner.
See the following sub-sections for more information about the built-in planners provided with PrPy, information about writing your own planner, and several more complex usage examples.
Built-In Planners
PrPy provides wrappers for several existing planning libraries:
CBiRRTPlanner: Constrained Bi-directional Rapidly-Exploring Random Tree (CBiRRT), requires the CoMPs suiteCHOMPPlanner: Covariant Hamiltonian Optimization for Motion Planning (CHOMP), requires or_cdchompOMPLPlanner: wrapper for randomized planners implemented in the Open Motion Planning Library, requires or_omplOpenRAVEPlanner: wrapper for OpenRAVE planners that implement thePlannerBaseinterfaceSBPLPlanner: wrapper for the Search-Based Planning Library (SBPL), requires or_sbpl
Additionally, PrPy provides several simple planners of its own:
MKPlanner: Jacobian pseudo-inverse controller for executing straight-line workspace trajectoriesSnapPlanner: attempts to execute a straight-line joint-space trajectory to the goalGreedyIKPlanner: follows a workspace path by greedily picking IK solutionsVectorFieldPlanner: follows any custom cspace vector field until a custom termination
Finally, PrPy provides several meta-planners for combining the above planners:
Sequence: sequentially queries a list of planners and returns the result of the first planner in the list that succeeds.Ranked: queries a list of planners in parallel and returns the solution first planner in the list that returns successIKPlanner: plan to an end-effector pose by sequentially planning to a list of ranked IK solutionsNamedPlanner: plan to a named configuration associated with the robot
See the Python docstrings in the above classes for more information.
Common Planning Methods
There is no formal list of @*PlanningMethods or their arguments. However, we
have found these methods to be useful:
PlanToConfiguration(robot, goal_config): plan the robot's active DOFs from the robot's current configuration to thegoal_configconfiguration.PlanToConfigurations(robot, goal_configs): plan the robot's active DOFs from the robot's current configuration to any of the elements in thegoal_configslist.PlanToEndEffectorPose(robot, goal_pose): plan the robot's active manipulator's end-effector togoal_pose.PlanToEndEffectorOffset(robot, direction, min_distance, max_distance): plan the robot's active manipulator in a straight line in the direction specified by thedirectionunit vector for [min_distance,max_distance] meters.PlanToTSR(robot, tsrchains): plan with the start, goal, and/or constraints specified by a list of TSR chains.PlanToBasePose(robot, goal_pose): plan with the robot's affine DOFs in the plane to a desired base pose.
Most planners that implement these methods accept a timelimit parameter, in
seconds, for which to plan before raising a PlanningError. Additionally, many
of these methods accept planner-specific keyword arguments.
Writing a Custom Planner
Implementing a custom planner requires extending the BasePlanner class and
decorating one or more methods with the @LockedPlanningMethod or
@ClonedPlanningMethod decorator.
Extending the BasePlanner class allows PrPy to identify your planner as a
base planner class, as opposed to a meta-planner. The @PlanningMethod
decorators handle environment cloning or locking and allows meta-planners to
query the list of planning methods that the planner supports (e.g. to generate
docstrings).
Each instance of a BasePlanner-derived class constructs a planning
environment self.env. This environment is uniquely associated with each
instance of the planner and is what will be used in @ClonedPlanningMethod
calls. Since this environment is persistent and unique, it can also be used
as a place to cache data or pre-load plugins for planners that have heavyweight
initialization steps. However, because of this, each planning instance can
only execute one @ClonedPlanningMethod at a time. It can still execute
arbitrary @LockedPlanningMethod calls, as long as they are referring to
robots in different environments.
Please obey the following guidelines:
- Assume that the planning environment is locked during the entire call.
- Subclass constructors must call
BasePlanner.__init__. - Each
@PlanningMethodmust accept the first argumentrobot, which is a robot in the environment it should be using to perform planning. - Each
@PlanningMethodmust accept**kwargsto ignore arguments that are not supported by the planner. - Each
@PlanningMethodmust return aTrajectorywhich was created in the same environment as the robot it was passed (e.g.robot.GetEnv()). - When possible, use one of the defacto-standard
@*PlanningMethodnames listed below. - Raise a
PlanningErrorto indicate an expected, but fatal, error (e.g. timeout with no collision-free path). - When applicable, raise a context-specific subclass of
PlanningErrorto indicate the nature of the error (e.g.StartCollisionPlanningError) - Raise a standard Python exception, e.g.
ValueError, to indicate an unexpected error has occurred (e.g. argument out of range). - Raise a
UnsupportedPlanningErrorto indicate that the planning operation is fundamentally not supported (e.g. constraint type is not implemented).
Examples
Trajectory optimizers, like CHOMP, typically produce higher quality paths than
randomized planners. However, these algorithms are not probabilistically
complete and can get stuck in local minima. You can mitigate this by using the
Sequence planner to first call CHOMP, then fall back on RRT-Connect:
planner = Sequence(CHOMPPlanner(), OMPLPlanner('RRTConnect'))
path = planner.PlanToConfiguration(robot, goal)
Unfortunately, this means that RRT-Connect does not start planning until CHOMP
has already failed to find a solution. Instead of using Sequence, we can use
the Ranked meta-planner to plan with both planners in parallel. Just as
before, the meta-planner will immediately return CHOMP's solution if it returns
success. However, RRT-Connect will have a head-start if CHOMP fails:
planner = Ranked(CHOMPPlanner(), OMPLPlanner('RRTConnect'))
path = planner.PlanToConfiguration(robot, goal)`
In other cases, a meta-planner can be used to combine the disparate capabilities of multiple planenrs. For example,
Related Skills
node-connect
339.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.8kCommit, push, and open a PR
