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.
Creating MoveIt Plugins
This Page gives a detailed explanation of how to add plugins in ROS in general. The two necessary elements are base and plugin classes. The plugin class inherits from the base class and overrides its virtual functions. The main library used for this purpose is pluginlib. This tutorials contains three different types of plugins, namely, motion planner, controller manager and constraint sampler.
Motion Planner Plugin
In this section, we will show how to add a new motion planner to MoveIt as a plugin. The base class in MoveIt is planning_interface
from which any new planner plugin should inherit. For demonstration purposes, a linear interpolation planner (lerp) which plans the motion between two states in joint space is created. This planner could be used as a start point for adding any new planner as it contains the necessary basics. The final source files designed in this tutorial are available here. The following graph shows a brief overall view of the relation between classes for adding a new planner in MoveIt.
First we create the plugin class in the moveit_tutorials
package. To make the plugin class for lerp
, create a file named lerp_planner_manager.cpp
in src folder. In this file, LERPPlanPlannerManager
overrides the functions of PlannerManager
class from planning_interface
. In the end of this file, we need to register LERPPlanPlannerManager
class as a plugin, this is done by CLASS_LOADER_REGISTER_CLASS
macro from class_loader
:
CLASS_LOADER_REGISTER_CLASS(emptyplan_interface::EmptyPlanPlannerManager, planning_interface::PlannerManager);
Next we create the LERPPlanningContext
class that overrides the functions of PlanningContext
. This class will override the solve function where the planner solves the problem and returns the solution. As the solve function implementation may need many classes from the planner code base, it is more readable to make another class called LERPInterface
where the actual implementation of the planner solve method will take place. Basically, this class is the entry point to the new motion planner algorithm. The response in this solve function is prepared in the type of moveit_msgs::MotionPlanDetailedResponse
which is converted to planning_interface::MotionPlanDetailedResponse
in LERPPlanningContext
class.
Moreover, PlannerConfigurationSettings
could be used to pass the planner-specific parameters. Another way to pass these parameters is using ROS param server which reads from a yaml file. In this tutorial, for our lerp planner, we use lerp_planning.yaml
in panda_moveit_config
package that contains only one parameter, num_steps
which gets loaded in lerp_interface
whenever its solve function is called.
Export the plugin
First, we need to make the plugin available to the ROS Toolchain. To this end, a plugin description xml file (emptyplan_interface_plugin_description.xml
) containing the library
tag with the following options should be created:
<library path="libmoveit_emptyplan_planner_plugin">
<class name="emptyplan_interface/EmptyPlanPlanner" type="emptyplan_interface::EmptyPlanPlannerManager" base_class_type="planning_interface::PlannerManager">
<description>
</description>
</class>
</library>
Then, to export the plugin, we use the address of the above xml file and the export
tag in package.xml file:
<export>
<moveit_core plugin="${prefix}/emptyplan_interface_plugin_description.xml"/>
</export>
Note that the name of the tag, moveit_core
, is the same as that of the package where the base class, planning_interface
, lives in.
Check the plugin
With the following command, one can verify if the new plugin is created and exported correctly or not:
rospack plugins --attrib=plugin moveit_core
The result should contain moveit_planners_lerp
with the address of the plugin description xml file:
moveit_tutorials <ros_workspace>/src/moveit_tutorials/creating_moveit_plugins/lerp_motion_planner/lerp_interface_plugin_description.xml
Plugin usage
In this subsection, we explain how to load and use the lerp planner that we have created previously. To this end, a ros node called lerp_example.cpp
is created. The first step is to get the state and group of joints of the robot that are related to the requested planning group as well as the planning scene by the following lines of code:
moveit::core::RobotStatePtr robot_state(new moveit::core::RobotState(robot_model));
const moveit::core::JointModelGroup* joint_model_group = robot_state->getJointModelGroup(PLANNING_GROUP);
const std::vector<std::string>& joint_names = joint_model_group->getVariableNames();
planning_scene::PlanningScenePtr planning_scene(new planning_scene::PlanningScene(robot_model));
The next step is to load the planner using pluginlib and set the parameter planner_plugin_name
to the one that we have created:
boost::scoped_ptr<pluginlib::ClassLoader<planning_interface::PlannerManager>> planner_plugin_loader;
planning_interface::PlannerManagerPtr planner_instance;
std::string planner_plugin_name = "lerp_interface/LERPPlanner";
node_handle.setParam("planning_plugin", planner_plugin_name);
Having the planner loaded, it is time to set the start and goal state for the motion planning problem. The start state is the current state of the robot which is set to req.start_state
. On the other hand, the goal constraint is set by creating a moveit_msgs::Constraints
using the goal state and the joint model group. This constraint is fed to req.goal_constraint
. The following code shows how to do these steps:
// Get the joint values of the start state and set them in request.start_state
std::vector<double> start_joint_values;
robot_state->copyJointGroupPositions(joint_model_group, start_joint_values);
req.start_state.joint_state.position = start_joint_values;
// Goal constraint
moveit::core::RobotState goal_state(robot_model);
std::vector<double> joint_values = { 0.8, 0.7, 1, 1.3, 1.9, 2.2, 3 };
goal_state.setJointGroupPositions(joint_model_group, joint_values);
moveit_msgs::Constraints joint_goal = kinematic_constraints::constructGoalConstraints(goal_state, joint_model_group);
req.goal_constraints.clear();
req.goal_constraints.push_back(joint_goal);
So far, we have loaded the planner and created the start and goal state for the motion planning problem but we have not solved the problem yet. Solving the motion planning problem in the joint state by the given information about the start and goal state is done by creating a PlanningContext
instance and call its solve function. Remember that the response passed to this solve function should be of type planning_interface::MotionPlanResponse
:
planning_interface::PlanningContextPtr context = planner_instance->getPlanningContext(planning_scene, req, res.error_code_);
Finally, to run this node, we need to roslaunch lerp_example.launch in launch folder. This launch file launches the demo.launch
of package panda_moveit_config
by passing lerp
as the name of the planner. Then, lerp_example
gets launched and lerp_planning.yaml
is loaded to set the lerp-specific parameters to ROS Parameter Server.
Example Controller Manager Plugin
MoveIt controller managers, somewhat a misnomer, are the interfaces to your custom low level controllers. A better way to think of them are controller interfaces. For most use cases, the included MoveItSimpleControllerManager is sufficient if your robot controllers already provide ROS actions for FollowJointTrajectory. If you use ros_control, the included MoveItRosControlInterface is also ideal.
However, for some applications you might desire a more custom controller manager. An example template for starting your custom controller manager is provided here.
Example Constraint Sampler Plugin
Create a
ROBOT_moveit_plugins
package and within that a sub-folder for yourROBOT_constraint_sampler
plugin. Modify the template provided byROBOT_moveit_plugins/ROBOT_moveit_constraint_sampler_plugin
In your
ROBOT_moveit_config/launch/move_group.launch
file, within the<node name="move_group">
, add the parameter:<param name="constraint_samplers" value="ROBOT_moveit_constraint_sampler/ROBOTConstraintSamplerAllocator"/>
Now when you launch move_group, it should default to your new constraint sampler.