Setting our Executive
We will now create a package to hold our executive trigging the various arm motion controllers.
Creating the Package
First, you need to create an empty package called iai_seminar_manipulation_executive. Note: As we'll be using roscpp our new executive has to depend on roscpp. Don't forget to also add proper links to your documentation.
Setting up the ROS node
Before adding any functionality, you should set up an empty ROS node.
In our executive package, create a sub-directory src.
In src, create a main.cpp, include the basic ROS header-file <ros/ros.h>, and add a typical c/c++ main function.
In the main itself, init ros and call your node manipulation_executive, get a NodeHandle, and spin.
In our executive package, add an entry to the CMakeLists to build an executable. You can also call it manipulation_executive.
Now you should be able to rosmake and rosrun the Node. It will not do anything but spin silently. However, you can already use various of the ROS commandline tools, e.g. rosnode list, rosnode info, etc., to analyze its behavior.
Note: Almost all information necessary to complete this step can be found here.
Adding a Launch-File
As a next step, add a launch-file to start our executive once in a launch sub-dir of the package. Please note that it is considered good-practice to provide users with launch-files for your nodes. This way, you can make sure that people know how to run your applications.
Moving the Arms (a first glance)
In this sub-task you will use existing ROS functionality to move both arms of our PR2. The interface used for this is action. Please re-visit the actionlib documentation and tutorials as actionlib is a very essential and powerful interface in ROS. Afterwards, have a look at the following tutorial – the solution to this sub-task looks similar to it.
Step-by-step instructions:
In our executive package, add a sub-directory include with a nested sub-directory called like our package, i.e. iai_seminar_manipulation_executive –we will put our header files there. It is typical ROS practice to have header files in the include/<package-name> sub-directory of your package.
Copy the header file RobotArm.h from the materials provided in your repository into our sub-directory for header-files.
Within our existing sub-dir src, create the empty equivalent of RobotArm.h, RobotArm.cpp
In RobotArm.cpp, include RobotArm.h and implement the minimally necessary function stubs of the functions that you still need to implement for RobotArm.h, i.e. return some valid value here.
In the CMakeLists, add RobotArm.cpp as a necessary source file to build our executive executable.
Add the necessary dependencies introduced by the includes of RobotArm.h. Reminder: in ROS the package names are usually called ros-<distro>-<package>, with us using fuerte as distribution.
Try to compile.
If everything compiles, you are ready to get your hands dirty:
Implement waitForActionServer(), initGoal(const std::vector<std::string>& …), and startTrajectory(pr2_controller_msgs::JointTrajectoryGoal& …)
Include RobotArm.h in your main.cpp, instantiate an object of the class RobotArm, and use it to send a goal request to move the left arm of the robot to the action server. As testing data we provided the file left_arm_goals.yaml in the directory materials of the your repository. Use the data of the first goal configuration.
Make sure that the name of the action client you are using resides in the namespace of your controller. Example: Let's consider when started your node has the name my_node (this is the name you provide in the launch-file). Let's further consider you wanna call your action arm_action. After starting your node you could then make the following test in the console:
$rostopic list
< some topics>
/my_node/arm_action/goal
/my_node/arm_action/feedback
/my_node/arm_action/status
/my_node/arm_action/cancel
< some other topics>
$
As a last step, update your launch-file for your users:
In your launch-file, add a topic-remap from your relative topic arm_action to the global name of the joint trajectory action of the left arm.
If you now launch our environment, and then launch your executive the left arm of the robot should move to the desired configuration.
Hints: You can peek here to know how to use the controller, and this page shows how to remap topics in a launch-file.
Moving the Arms (revisited)
Now that we have the code to make one arm move, let's try to make it more convenient and reusable:
In our executive package, create a sub-directory config.
Copy left_arm_goals.yaml and right_arm_goals.yaml from the materials directory to config
In the launch-file, load the params given in left_arm_goals.yaml in the namespace of our executive. Look
here for an example of how to do it.
After starting your launch-file, a test could be this:
$ rosparam list | grep goal
<bla>
/your_executive/first_goal_configuration/execution_time
/your_executive/first_goal_configuration/joints
/your_executive/first_goal_configuration/positions
/your_executive/first_goal_configuration/velocities
<blub>
$ rosparam get /your_executive/first_goal_configuration/joints
[l_shoulder_pan_joint, l_shoulder_lift_joint, l_upper_arm_roll_joint, l_elbow_flex_joint, l_forearm_roll_joint, l_wrist_flex_joint, l_wrist_roll_joint]
Your next assignment is to load and use these parameters from the parameter server. We will do this in a new library package. This package will not contain any executable but just library code to be used by other packages.
NOTE: ROS libraries are a great thing that basically offer two advantages. (1) They help you re-use code in various of your ROS application. (2) If you provide some generic algorithm as library as a pure c/c++ library, you even allow non-ROS-users to use your algorithm in their work. This is hard to achieve but the ultimate design goal: A clean cut between functionality/algorithm and meta-OS, i.e. ROS.
Now, implement all functionality described in ParameterServerUtils.h to load values from the parameter server. Consider using the data already one the server to test your functions. (
This should give you some good examples.)
Afterwards, let the executive package depend on the library package. Use the parameter-server-functions (and our original initGoal and startTrajectory) in the executive package to implement the second version of initGoal and startTrajectory of the RobotArm class – it should be quick!
Now, use the new initGoal(ros::NodeHandle&) and startTrajectory() in main.cpp. This should remove any need for hard-coded goals, and instead use the ones from the parameter server.
Using parameters from the server instead of hard-coded values the way we just did has several tremendous advantages:
It shows users which kind of parameters your node needs to produce useful results.
It also provides them with a set of _specific_ example parameters that should work (always nice for demonstrations)
It allows you to change your current choice of parameters without recompiling the node. This will save you a lot of time in the future.
Moving the Arms (re-using code)
Finally, we will re-use our code to move both arms:
In the launch-file, give our executive a name that indicates that it takes care of the left arm.
In the launch-file, start another second node for the right arm. It should be just like the left one, just map the action to the right arm controllers and load the parameters from right_goal_configuration.yaml
That's it. We have just completely re-used our code to control another arm! Imagine how much time you could save on a spider… ;)