Documentation Version
You're reading the documentation for a stable version of MoveIt that is not being developed further. For information on the recommended version, please have a look at Main.
Using OMPL Constrained Planning
This tutorial shows you how to use OMPL’s Constrained planning capabilities from MoveIt. To illustrate the capabilities of this planner, four planning problems are solved with different types of path constraints. It will be easier to follow if you have read through the example on how to use the Move Group interface.
When to use this Planner
The interface currently only supports a single position or orientation constraint on any link of the robot. The planning approach provides an alternative for the enforce_joint_model_state_space option. It is expected to be most valuable for constraints regions that have a small (or zero) volume in Cartesian space, where rejection sampling does not always work. For example, this planner can be used to constrain the robot end-effector on a plane or along a line.
How to Configure OMPL to use Constrained Planning
OMPL reads configurations parameters from a file called ompl_planning.yaml
. This guide adds the required parameters directly in the launch file, but they can also be set in the yaml file. This guide uses the Panda robot, for which this file can be found here. Add a parameter to your robot’s ompl_planning.yaml
to tell OMPL to plan in a constrained state space by setting enforce_constrained_state_space
to true
. In addition, if the parameter projection_evaluator was not yet specified, we also need to add it. The projection evaluator is used to help with discretizing the state space with high dimensionality by using projections from the state space to a low dimensional Euclidean space. You can read more about it here. In general, using the first couple joints works pretty well.
panda_arm:
enforce_constrained_state_space: true
projection_evaluator: joints(panda_joint1,panda_joint2)
You can see an example here.
This tutorial adds the parameters in the launch file. The launch file uses moveit_configs_utils
to simplify the launch file. The moveit_config
is configured with the moveit_resources Panda MoveIt config. We add our OMPL parameters to the planning_pipelines
with the following lines:
moveit_config.planning_pipelines["ompl"]["panda_arm"]["enforce_constrained_state_space"] = True
moveit_config.planning_pipelines["ompl"]["panda_arm"]["projection_evaluator"] = "joints(panda_joint1,panda_joint2)"
moveit_config.planning_pipelines["ompl"]["panda_arm_hand"]["enforce_constrained_state_space"] = True
moveit_config.planning_pipelines["ompl"]["panda_arm_hand"]["projection_evaluator"] = "joints(panda_joint1,panda_joint2)"
Running the Example
Run the following command to launch the example:
ros2 launch moveit_tutorials ompl_constrained_planning.launch.py
The Panda robot should appear, with the RViz Visual Tools and the Trajectory Slider panel in the bottom-left. You should also see this text in your terminal:
Waiting to continue: Press 'Next' in the RvizVisualToolsGui window to start with the box constraint example
To start the first example, click the Next button.
The first example shows a plan with box constraints. A red and green sphere should appear in Rviz to show the start and goal states respectively. In addition, a grey box should appear that represents the position constraint on the link panda_link8
. If planning succeeds, you should see a preview of the trajectory that was planned. You can use the Trajectory Slider panel to inspect the trajectory.
The following message appears in the terminal:
Waiting to continue: Press 'Next' in the RvizVisualToolsGui window to continue to the planar constraint example
After pressing Next, the next planning problem is solved. This example uses equality constraints to plan a trajectory with the end effector constrained in a plane.
Again, if planning succeeds, the trajectory is animated in Rviz. Press Next again to move on to the third planning problem, using equality constraints again to plan along a line.
You can see the trajectory animated if planning succeeds. Finally, press Next to move to the orientation constraint example.
This example may take longer to plan. Press Next to remove all markers and end the example. If planning fails, you can start at the beginning of the section to try again.
How to Set Constraints
This section will cover how to use position, equality, and orientation constraints using the Move Group interface. This section walks through the example code. Some lines have been omitted for brevity.
Be sure to add enforce_constrained_state_space
and projection_evaluator
to your ompl_planning.yaml
!
Initial Setup
First, we start off by setting up the MoveGroupInterface
similarly to the Move Group interface example page. This assumes you’ve already set up your node.
moveit::planning_interface::MoveGroupInterface move_group_interface(node, "panda_arm");
Next, we create a lambda to help us create a pose at a given relative position from the current pose.
auto current_pose = move_group_interface.getCurrentPose();
// Creates a pose at a given positional offset from the current pose
auto get_relative_pose = [current_pose, &moveit_visual_tools](double x, double y, double z) {
auto target_pose = current_pose;
target_pose.pose.position.x += x;
target_pose.pose.position.y += y;
target_pose.pose.position.z += z;
moveit_visual_tools.publishSphere(current_pose.pose, rviz_visual_tools::RED, 0.05);
moveit_visual_tools.publishSphere(target_pose.pose, rviz_visual_tools::GREEN, 0.05);
moveit_visual_tools.trigger();
return target_pose;
};
Now, we’re ready to set up some constraints, starting with a box constraint.
Box Constraints
We start by using the lambda to create a target pose offset from the current pose. This pose should be within the box that we make. In this example, we make a box of size (0.1, 0.4, 0.4)
, so the target pose should be within our constraint region.
auto target_pose = get_relative_pose(0.0, 0.3, -0.3);
Now, we set up the constraints. A box is a PositionConstraint
- see the full message definition :here. We set the frame_id
in the header, as well as the link_name
of the link to be constrained (in this case, the end effector). We then create a box using shape_msgs
and set its dimensiions. We then place that box into box_constraint
.
// Let's try the simple box constraints first!
moveit_msgs::msg::PositionConstraint box_constraint;
box_constraint.header.frame_id = move_group_interface.getPoseReferenceFrame();
box_constraint.link_name = move_group_interface.getEndEffectorLink();
shape_msgs::msg::SolidPrimitive box;
box.type = shape_msgs::msg::SolidPrimitive::BOX;
box.dimensions = { 0.1, 0.4, 0.4 };
box_constraint.constraint_region.primitives.emplace_back(box);
Next, we set the pose of our box constraint. This is done by using geometry_msgs
. We set the position and orientation of the box, and add the pose to box_constraint
.
geometry_msgs::msg::Pose box_pose;
box_pose.position.x = current_pose.pose.position.x;
box_pose.position.y = 0.15;
box_pose.position.z = 0.45;
box_pose.orientation.w = 1.0;
box_constraint.constraint_region.primitive_poses.emplace_back(box_pose);
box_constraint.weight = 1.0;
Finally, we create a generic Constraints
message and add our box_constraint
to the position_constraints
.
moveit_msgs::msg::Constraints box_constraints;
box_constraints.position_constraints.push_bemplace_backack(box_constraint);
Now that we’ve created our constraint, set the path constraints via the Move Group interface and plan. It’s helpful to increase the default planning time, as planning with constraints can be slower.
moveit::planning_interface::MoveGroupInterface::Plan plan;
move_group_interface.setPathConstraints(box_constraints);
move_group_interface.setPoseTarget(target_pose);
move_group_interface.setPlanningTime(10.0);
move_group_interface.plan(plan);
Equality Constraints
We can plan with the end-effector constrained to a plane or a line using equality constraints. First, we’ll cover the plane case.
We need to create a pose goal that lies in this plane. The plane is tilted by 45 degrees, so moving an equal amount in the y and z direction will be on the plane. Be sure that both the goal and the start state satisfy the path constraints, or planning will always fail.
target_pose = get_relative_pose(0.0, 0.3, -0.3);
We create a plane perpendicular to the y-axis and tilt it by 45 degrees. We create a plane by making a box and setting one dimension 0.0005
. This is an important numvber that we will cover shortly.
moveit_msgs::msg::PositionConstraint plane_constraint;
plane_constraint.header.frame_id = move_group_interface.getPoseReferenceFrame();
plane_constraint.link_name = move_group_interface.getEndEffectorLink();
shape_msgs::msg::SolidPrimitive plane;
plane.type = shape_msgs::msg::SolidPrimitive::BOX;
plane.dimensions = { 1.0, 0.0005, 1.0 };
plane_constraint.constraint_region.primitives.emplace_back(plane);
geometry_msgs::msg::Pose plane_pose;
plane_pose.position.x = current_pose.pose.position.x;
plane_pose.position.y = current_pose.pose.position.y;
plane_pose.position.z = current_pose.pose.position.z;
plane_pose.orientation.x = sin(M_PI_4 / 2);
plane_pose.orientation.y = 0.0;
plane_pose.orientation.z = 0.0;
plane_pose.orientation.w = cos(M_PI_4 / 2);
plane_constraint.constraint_region.primitive_poses.emplace_back(plane_pose);
plane_constraint.weight = 1.0;
Solving the problem using equality constraints is a bit more complicated. We need to tell the planner explicitly that we want to use equality constraints for the small dimensions. This is achieved by setting the name of the constraint to "use_equality_constraints"
. In addition, any dimension of the box below a threshold of 0.001
will be considered an equality constraint. However, if we make it too small, the box will be thinner that the tolerance used by OMPL to evaluate constraints (1e-4
by default). MoveIt will use the stricter tolerance (the box width) to check the constraints, and many states will appear invalid. That’s where the magic number 0.0005
comes from, it is between 0.00001
and 0.001
.
moveit_msgs::msg::Constraints plane_constraints;
plane_constraints.position_constraints.emplace_back(plane_constraint);
plane_constraints.name = "use_equality_constraints";
move_group_interface.setPathConstraints(plane_constraints);
As before, set the target and plan.
move_group_interface.setPoseTarget(target_pose);
move_group_interface.setPlanningTime(10.0);
move_group_interface.plan(plan);
Building on the previous constraint, we can make it a line by also reducing the dimension of the box in the x-direction.
moveit_msgs::msg::PositionConstraint line_constraint;
line_constraint.header.frame_id = move_group_interface.getPoseReferenceFrame();
line_constraint.link_name = move_group_interface.getEndEffectorLink();
shape_msgs::msg::SolidPrimitive line;
line.type = shape_msgs::msg::SolidPrimitive::BOX;
line.dimensions = { 0.0005, 0.0005, 1.0 };
line_constraint.constraint_region.primitives.emplace_back(line);
Orientation Constraints
Finally, we can place constraints on orientation. We set the target pose to be the other side of the robot for a more drastic move as we are no longer constrained by position.
target_pose = get_relative_pose(-0.6, 0.1, 0);
We create an OrientationConstraint
instead of a Position Constraint
, but similarly to before, we set the header.frame_id
and the link_name
.
moveit_msgs::msg::OrientationConstraint orientation_constraint;
orientation_constraint.header.frame_id = move_group_interface.getPoseReferenceFrame();
orientation_constraint.link_name = move_group_interface.getEndEffectorLink();
We then can use a geometry_msgs::msg::Quaternion
to set the orientation constraint, or in this case, we just constrain the orientation to not change from current_pose
.
orientation_constraint.orientation = current_pose.pose.orientation;
orientation_constraint.absolute_x_axis_tolerance = 0.4;
orientation_constraint.absolute_y_axis_tolerance = 0.4;
orientation_constraint.absolute_z_axis_tolerance = 0.4;
orientation_constraint.weight = 1.0;
Similarly to the position constraints, we need to use a generic Constraints
message, but this time we add it to the orientation_constraints
.
moveit_msgs::msg::Constraints orientation_constraints;
orientation_constraints.orientation_constraints.emplace_back(orientation_constraint);
Set up the planning problem as before and plan.
move_group_interface.setPathConstraints(orientation_constraints);
move_group_interface.setPoseTarget(target_pose);
move_group_interface.setPlanningTime(10.0);
move_group_interface.plan(plan);