Implementing a Joint Space Controller
In this exercise you will use standard ROS libraries and interfaces to write a position-resolved joint-space controller for the PR2. This will be genuine low-level controller running in hard realtime on the robot.
For the next couple of tasks, make sure to have understood this tutorial.
Creating the ROS Package
Create a new package for our controllers called iai_seminar_manipulation_controllers. Make sure it depends on the necessary pr2-interface packages.
Copy IAISeminarMultiJointPositionController.h for the controller in a suitable include-sub-directory.
Create the corresponding cpp-file in src, and add empty implementations for the functions declared in the h-file.
Add our still empty controller as a regular library (NOT a controller plugin library) to the CMakeLists, and try compiling.
Exporting the Controller as a Controller Plugin
In order to tell the pr2 controller manager that our controller can be loaded and run as a realtime controller we need to register it as a controller plugin. Adapt the example from here to do so, and test if the plugin is visible with
rospack plugins --attrib=plugin pr2_controller_interface
iai_seminar_manipulation_controllers should now be in the list of controllers. Tipp: In case you wonder what the real name of your library is: Look for it after in your /lib dirrectory…
Loading, starting and stopping our Controller Plugin
Just to make sure we have set up everything correctly, let's use the pr2_controller_manager to load, start and stop our (still empty) controller.
$ rosrun pr2_controller_manager pr2_controller_manager list-types
...some other controllers...
iai_seminar_manipulation_controllers/<YourControllerClassName>
...some other controllers...
In order to actual load an instance of our controller class we need to choose a name for it, and let the pr2_controller_manager know that the controller so-named is associated with this type of controller. We do this by setting the parameter 'type' within the namespace of our controller-instance to the type of our controller. This might sound a bit complicated bit, but it isn't. Let's assume we wanna call our controller iai_joint_position_controller. Then we set the parameter like this:
$ rosparam set rosparam set iai_joint_position_controller/type iai_seminar_manipulation_controllers/<YourControllerClassName>
$ rosrun pr2_controller_manager pr2_controller_manager load iai_joint_position_controller
$ rosrun pr2_controller_manager pr2_controller_manager list
$ rosrun pr2_controller_manager pr2_controller_manager start iai_joint_position_controller
$ rosrun pr2_controller_manager pr2_controller_manager list
NOTE: Our controller code is now running on the simulated PR2. Right now, it does not do anything because you haven't filed any code in init(), start(), update(), stop(), yet.
$ rosrun pr2_controller_manager pr2_controller_manager stop iai_joint_position_controller
More examples of how to use the pr2_controller_manager can be found here.
Adding a launch-file
We will now add a launch-file to start our controllers automatically:
From the materials-directory, copy the file pr2_multi_joint_position_controllers.yaml into the config directory of our controllers package.
Create a new launch-file with a name sounding like it starts the joint position controllers on the pr2.
In this launch-file, Load the parameters given in the yaml-file.
Also in the launch-file, use the spawner of the pr2_controller_manager to load both controllers in a stopped state.
For hints, look again here and here.
Implementing the basic init-function
Now, we start implementing the actual controller functionalities. By setting up our controller in the init-phase:
Use our parameter server utilities to load the names of the joints from the parameter server. Note, look into the yaml-file of our controller to look up the name of the parameter holding the joint names.
Get a pointer to each of these joints and save all of them in a vector.
Check if all joints have been calibrated.
If anything goes wrong during init, throw a meaningful error-message and abort init by returning false.
We want our controller to publish its current state information during every 10th update-call. For that we will use a realtime-publisher from the realtime_tools package because the regular publisher is not thread-safe.
At the end of the init-function, init the realtime_publisher_ to publish on topic state in the local namespace. Also resize the message in the publisher by resizing all its internal points to the number of joints we want to control. It also makes sense to copy the name of the joints we want to control into the message. NOTE: Only in the init() function of a controller should you call init()- or resize- operations on any of the objects in the controller. Such operations usually result in memory allocation of a size unknown at compile time. Such allocations typically breake realtime-safety.
Hint: The documentation of the realtime publisher is here, and we want to use this message type. Also have a look at API of the realtime publisher.
For testing, launch our controller launch-file and start the controllers via the console. Afterwards you can listen to the messages on the topic from the console:
$ rostopic echo r_arm_iai_joint_position_controller/state
Getting a command inside
Our controller should be able to react to external commands. Therefore it shall listen to a topic 'command' in its own namespace. Since we are implementing a realtime process, we need to protect our internal command data structure 'position_command_' from read-write conflicts. A good way to do this is to use a double buffer, guarded by a boost::mutex. A very thorough introduction on double buffers can be found here. Perform your own websearch to find out how to use boost::scoped_locks with a boost::mutex.
implement the command_callback to copy the new command from the msg inside our controller (using a double buffer and the mutex defined in the h-file)
in the update, always read the current command from the double buffer (also guarded by the mutex), and copy the buffered command into the internal position_command_. Hint: Implement and use the method copy_from_command_buffer for this. It is supposed to be atomic…
You can test your code by again echoing the state-topic of the controller and sending a new command from the console:
$ rostopic pub /l_arm_iai_joint_position_controller/command std_msgs/Float64MultiArray '{data: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6]}'
Using PIDs
For doing the actual control we will use the PID-controller implementation provided by Willow Garage. The h-file already declares a vector of such controllers.
Filling in start
Now, we will fill the start-hook:
Closing the loop
Finally, we will calculate the actual feedback control:
get the current time from the robot, calculate some delta-time. and remember the current time for the next control cycle
still: copy in the command from the double buffer
for every joint: calculate the error between actual and commanded position
for every joint: feed the error to the pid-controllers, and command their efforts to the joints of the robot
-
As a last step: keep on publishing the current state of the controller.
Using our controller from