(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Teleop PR2 arm in simulation

Description: This tutorial will give you keyboard teleop control of the PR2 arm

Tutorial Level: BEGINNER

Start Simulator

The first step is to start the simulator. We'll use the standard pr2_gazebo simulator, so launch:

$ roscore
$ roslaunch pr2_gazebo pr2_empty_world.launch

and the simulator with a pr2 in it should appear

Cartesian Controller

We'll use the JTTeleop controller, available in the pr2_cockpit stack. This controller uses multiple parameters, which we can set in a configuration file and then load with rosparam. However, we'll also need to stop the currently running trajectory controllers, so make a launch file jtteleop.launch:

<launch>
 <node name="stop_r_arm"
    pkg="pr2_controller_manager" type="pr2_controller_manager"
    args="stop r_arm_controller" />

 <rosparam ns="r_cart">
   type: JTTeleopController
   root_name: torso_lift_link
   tip_name: r_gripper_tool_frame
   k_posture: 25.0
   jacobian_inverse_damping: 0.01
   pose_command_filter: 0.01
   cart_gains:
     trans:
       p: 800.0
       d: 15.0
     rot:
       p: 80.0
       d: 1.2
   joint_feedforward:
     r_shoulder_pan_joint: 3.33
     r_shoulder_lift_joint: 1.16
     r_upper_arm_roll_joint: 0.1
     r_elbow_flex_joint: 0.25
     r_forearm_roll_joint: 0.133
     r_wrist_flex_joint: 0.0727
     r_wrist_roll_joint: 0.0727
   joint_max_effort:
     r_shoulder_pan_joint: 11.88
     r_shoulder_lift_joint: 11.64
     r_upper_arm_roll_joint: 6.143
     r_elbow_flex_joint: 6.804
     r_forearm_roll_joint: 8.376
     r_wrist_flex_joint: 5.568
     r_wrist_roll_joint: 5.568

   vel_saturation_trans: 2.0
   vel_saturation_rot: 4.0
 </rosparam>
<node name="spawn_cart"
       pkg="pr2_controller_manager" type="spawner"
       args="r_cart" />
</launch>

Note: For Electric onwards, the controller has moved to a different package.
Change the controller type to:
type: robot_mechanism_controllers/JTCartesianController

This file stops the current right-arm controller, loads in the parameters for our cartesian controller, and then spawns it.

Save this file and launch it:

$ roslaunch jtteleop.launch

Control

The JTTeleop controller uses geometry_msgs/PoseStamped messages to control the end effector of the right arm. We'll whip up a small keyboard teleop client to send this out, a modified version of the standard base teleop client.

First, make a package with the necessary dependencies

$ roscreate-pkg teleop_arms geometry_msgs roscpp

Next, edit the CMakeLists.txt to make the executable we'll be writing:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
rosbuild_add_executable(teleop_pr2_arms_keyboard src/teleop_pr2_arms_keyboard.cpp)

Now, open up src/teleop_pr2_arms_keyboard.cpp in your favorite editor, and paste

   1 #include <termios.h>
   2 #include <signal.h>
   3 #include <stdio.h>
   4 #include <stdlib.h>
   5 
   6 #include <ros/ros.h>
   7 #include <geometry_msgs/PoseStamped.h>
   8 
   9 #define KEYCODE_A 0x61
  10 #define KEYCODE_D 0x64
  11 #define KEYCODE_S 0x73
  12 #define KEYCODE_W 0x77
  13 #define KEYCODE_Q 0x71
  14 #define KEYCODE_E 0x65
  15 
  16 class TeleopPR2Keyboard
  17 {
  18   private:
  19   geometry_msgs::PoseStamped cmd;
  20 
  21   ros::NodeHandle n_;
  22   ros::Publisher pose_pub_;
  23 
  24   public:
  25   void init()
  26   {
  27     //header - this is impt
  28     cmd.header.frame_id = "/torso_lift_link";
  29 
  30     //Clear out our cmd - these values are roundabout initials
  31     cmd.pose.position.x=0.9;
  32     cmd.pose.position.y=0.2;
  33     cmd.pose.position.z=-0.3;
  34     cmd.pose.orientation.x=-0.00244781865415;
  35     cmd.pose.orientation.y=-0.548220284495;
  36     cmd.pose.orientation.z=0.00145617884538;
  37     cmd.pose.orientation.w=0.836329126239;
  38 
  39     pose_pub_ = n_.advertise<geometry_msgs::PoseStamped>("r_cart/command_pose", 1);
  40 
  41     ros::NodeHandle n_private("~");
  42   }
  43 
  44   ~TeleopPR2Keyboard()   { }
  45   void keyboardLoop();
  46 };
  47 
  48 int kfd = 0;
  49 struct termios cooked, raw;
  50 
  51 void quit(int sig)
  52 {
  53   tcsetattr(kfd, TCSANOW, &cooked);
  54   exit(0);
  55 }
  56 
  57 int main(int argc, char** argv)
  58 {
  59   ros::init(argc, argv, "pr2_arms_keyboard");
  60 
  61   TeleopPR2Keyboard tpk;
  62   tpk.init();
  63 
  64   signal(SIGINT,quit);
  65 
  66   tpk.keyboardLoop();
  67 
  68   return(0);
  69 }
  70 
  71 void TeleopPR2Keyboard::keyboardLoop()
  72 {
  73   char c;
  74   bool dirty=false;
  75 
  76   // get the console in raw mode
  77   tcgetattr(kfd, &cooked);
  78   memcpy(&raw, &cooked, sizeof(struct termios));
  79   raw.c_lflag &=~ (ICANON | ECHO);
  80   // Setting a new line, then end of file
  81   raw.c_cc[VEOL] = 1;
  82   raw.c_cc[VEOF] = 2;
  83   tcsetattr(kfd, TCSANOW, &raw);
  84 
  85   puts("Reading from keyboard");
  86   puts("---------------------------");
  87   puts("Use 'WS' to forward/back");
  88   puts("Use 'AD' to left/right");
  89   puts("Use 'QE' to up/down");
  90 
  91   for(;;)
  92   {
  93     // get the next event from the keyboard
  94     if(read(kfd, &c, 1) < 0)
  95     {
  96       perror("read():");
  97       exit(-1);
  98     }
  99 
 100     switch(c)
 101     {
 102       // Walking
 103     case KEYCODE_W:
 104       cmd.pose.position.x = cmd.pose.position.x+0.1;
 105       dirty = true;
 106       break;
 107     case KEYCODE_S:
 108       cmd.pose.position.x = cmd.pose.position.x-0.1;
 109       dirty = true;
 110       break;
 111     case KEYCODE_A:
 112       cmd.pose.position.y = cmd.pose.position.y+0.1;
 113       dirty = true;
 114       break;
 115     case KEYCODE_D:
 116       cmd.pose.position.y = cmd.pose.position.y-0.1;
 117       dirty = true;
 118       break;
 119     case KEYCODE_Q:
 120       cmd.pose.position.z = cmd.pose.position.z+0.1;
 121       dirty = true;
 122       break;
 123     case KEYCODE_E:
 124       cmd.pose.position.z = cmd.pose.position.z-0.1;
 125       dirty = true;
 126       break;
 127 
 128     }
 129 
 130 
 131     if (dirty == true)
 132     {
 133       pose_pub_.publish(cmd);
 134     }
 135 
 136 
 137   }
 138 }

Make the program with

$ rosmake teleop_arms

and then run

$ rosrun teleop_arms teleop_pr2_arms_keyboard

for cool keyboard control of the endpoint of the robot's right arm.

Explained

This code builds a PoseStamped message for sending to the JTTeleop controller on /r_cart/command_pose. For each keypress, the commanded x,y,or z positions of the robot's gripper (tip_name in the parameters) is updated. It is vitally necessary to set the frame_id in the header of the message to match the root_name of the JTTeleop parameters.

Wiki: pr2_simulator/Tutorials/Teleop PR2 arm in simulation (last edited 2013-01-02 12:28:54 by DavidButterworth)