Note: This tutorial assumes that you have completed the previous tutorials: How to Pair the PS3 Joystick with a Bluetooth Dongle.
(!) Please ask about problems and questions regarding this tutorial on Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Writing a Teleoperation Node for the PS3 Joystick

Description: This tutorial covers how to write a teleoperation node and use it to drive the turtle in the turtlesim.

Keywords: teleoperation, joystick, PS3

Tutorial Level: BEGINNER

Workspace Setup

If you have not yet created a workspace in which to complete the tutorials, click here for some brief instructions .

  Show EOL distros: 

Create a file named ~/tutorials.rosinstall with the following content:

- other: { local-name: workspace }

To overlay on the ROS distro you are using:

rosinstall ~/tutorials /opt/ros/$ROS_DISTRO>> ~/tutorials.rosinstall

To use this workspace whenever you open a new terminal setup your ROS environment by typing:

source ~/tutorials/setup.bash

Sourcing this file adds ~/tutorials/workspace to your ROS_PACKAGE_PATH.

Any packages you create in that directory will be found by rospack.

An alternative to source your script file is to add it to your .bashrc, but remember that this will persist in your .bashrc into the future, and you can only have one environment setup. For more on what this is doing see this page

Create a catkin workspace like so:

$ source /opt/ros/$ROS_DISTRO/setup.bash
$ mkdir -p ~/tutorial_ws/src
$ cd ~/tutorial_ws
$ catkin_init_workspace src
$ catkin_make

And now source the setup file from the result-space, so that packages you add to this workspace's src folder will be findable by rospack, and the built binaries by rosrun and roslaunch:

$ source devel/setup.bash

Create a Scratch Package

Before starting this tutorial, take the time to create a scratch package to work in and manipulate the example code. See creating a ROS package to learn more about creating a package. In the tutorials folder, create a package called learning_ps3joy, with dependencies on roscpp, turtlesim and ps3joy:

$ roscd tutorials
$ roscreate-pkg learning_ps3joy roscpp turtlesim ps3joy

Uncomment the rosbuild_genmsg() line in the CMakeLists.txt file of the learning_ps3joy package and run rosmake.

Writing a Simple Teleoperation Node

First, create learning_ps3joy/src/turtle_teleop_ps3joy.cpp in your favorite editor, and place the following inside it:

The Code

   1 #include <ros/ros.h>
   2 #include <turtlesim/Velocity.h>
   3 #include <sensor_msgs/Joy.h>
   6 class TeleopTurtle
   7 {
   8 public:
   9   TeleopTurtle();
  11 private:
  12   void joyCallback(const sensor_msgs::Joy::ConstPtr& joy);
  14   ros::NodeHandle nh_;
  16   int linear_, angular_;
  17   double l_scale_, a_scale_;
  18   ros::Publisher vel_pub_;
  19   ros::Subscriber joy_sub_;
  21 };
  24 TeleopTurtle::TeleopTurtle():
  25   linear_(1),
  26   angular_(2)
  27 {
  29   nh_.param("axis_linear", linear_, linear_);
  30   nh_.param("axis_angular", angular_, angular_);
  31   nh_.param("scale_angular", a_scale_, a_scale_);
  32   nh_.param("scale_linear", l_scale_, l_scale_);
  35   vel_pub_ = nh_.advertise<turtlesim::Velocity>("turtle1/command_velocity", 1);
  38   joy_sub_ = nh_.subscribe<sensor_msgs::Joy>("joy", 10, &TeleopTurtle::joyCallback, this);
  40 }
  42 void TeleopTurtle::joyCallback(const sensor_msgs::Joy::ConstPtr& joy)
  43 {
  44   turtlesim::Velocity vel;
  45   vel.angular = a_scale_*joy->axes[angular_];
  46   vel.linear = l_scale_*joy->axes[linear_];
  47   vel_pub_.publish(vel);
  48 }
  51 int main(int argc, char** argv)
  52 {
  53   ros::init(argc, argv, "teleop_turtle");
  54   TeleopTurtle teleop_turtle;
  56   ros::spin();
  57 }

The Code Explained

Now, let's break down the code piece by piece.

   2 #include <ros/ros.h>
   3 #include <turtlesim/Velocity.h>
   4 #include <sensor_msgs/Joy.h>

  • turtlesim/Velocity.h includes the turtle velocity msg so that we can publish velocity commands to the turtle
  • joy/Joy.h includes the joystick msg so that we can listen to the joy topic

   7 class TeleopTurtle
   8 {
   9 public:
  10   TeleopTurtle();
  12 private:
  13   void joyCallback(const sensor_msgs::Joy::ConstPtr& joy);
  15   ros::NodeHandle nh_;
  17   int linear_, angular_;
  18   double l_scale_, a_scale_;
  19   ros::Publisher vel_pub_;
  20   ros::Subscriber joy_sub_;
  22 };

Here we create the TeleopTurtle class and define the joyCallback function that will take a joy msg. We also create a node handle, publisher, and subscriber for later use.

  25 TeleopTurtle::TeleopTurtle():
  26   linear_(1),
  27   angular_(2)
  28 {
  30   nh_.param("axis_linear", linear_, linear_);
  31   nh_.param("axis_angular", angular_, angular_);
  32   nh_.param("scale_angular", a_scale_, a_scale_);
  33   nh_.param("scale_linear", l_scale_, l_scale_);

Here we initialize some parameters: the linear_ and angular_ variables are used to define which axes of the joystick will control our turtle. We also check the parameter server for new scalar values for driving the turtle.

  36   vel_pub_ = nh_.advertise<turtlesim::Velocity>("turtle1/command_velocity", 1);

Here we create a publisher that will advertise on the command_velocity topic of the turtle.

  39   joy_sub_ = nh_.subscribe<sensor_msgs::Joy>("joy", 10, &TeleopTurtle::joyCallback, this);

Here we subscribe to the joystick topic for the input to drive the turtle. If our node is slow in processing incoming messages on the joystick topic, up to 10 messages will be buffered before any are lost.

  43 void TeleopTurtle::joyCallback(const sensor_msgs::Joy::ConstPtr& joy)
  44 {
  45   turtlesim::Velocity vel;
  46   vel.angular = a_scale_*joy->axes[angular_];
  47   vel.linear = l_scale_*joy->axes[linear_];
  48   vel_pub_.publish(vel);
  49 }

Here we take the data from the joystick and manipulate it by scaling it and using independent axes to control the linear and angular velocities of the turtle. Finally we publish the prepared message.

  52 int main(int argc, char** argv)
  53 {
  54   ros::init(argc, argv, "teleop_turtle");
  55   TeleopTurtle teleop_turtle;
  57   ros::spin();
  58 }

Lastly we initialize our ROS node, create a teleop_turtle, and spin our node until Ctrl-C is pressed.

Compiling and Running Turtle Teleop

Add the following line to your CMakeLists.txt file:

rosbuild_add_executable(turtle_teleop_ps3joy src/turtle_teleop_ps3joy.cpp)

Run rosmake on your package.

Setting up the PS3 Joystick

Before we can run the joystick and teleop together we need to make the joystick accessible. Connect your PS3 joystick as we did in the previous tutorial.

$ sudo bash
$ rosrun ps3joy

Now list the permissions of the joystick:

$ ls -l /dev/input/js0

You will see something similar to:

  • crw-rw-XX- 1 root dialout 188, 0 2009-08-14 12:04 /dev/input/js0

If XX is rw: the js device is configured properly.

If XX is --: the js device is not configured properly and you need to:

$ sudo chmod a+rw /dev/input/js0

Writing a Launch File to Start all of the Nodes

Now let's make a launch file to start all of the nodes we need to start: create the file launch/turtle_ps3joy.launch and paste the following into it:

   1 <launch>
   3  <!-- Turtlesim Node-->
   4   <node pkg="turtlesim" type="turtlesim_node" name="sim"/>
   7  <!-- PS3 joy node -->
   8   <node respawn="true" pkg="ps3joy"
   9         type="ps3_joy_node" name="PS3_turtle_joy" >
  10     <param name="dev" type="string" value="/dev/input/js0" />
  11     <param name="deadzone" value="0.12" />
  12   </node>
  14  <!-- Axes -->
  15   <param name="axis_linear" value="1" type="int"/>
  16   <param name="axis_angular" value="0" type="int"/>
  17   <param name="scale_linear" value="2" type="double"/>
  18   <param name="scale_angular" value="2" type="double"/>
  20   <node pkg="turtle_teleop" type="turtle_teleop_joy" name="teleop"/>
  22 </launch>

Use the left joystick of the PS3 controller to drive.

Wiki: ps3joy/Tutorials/WritingTeleopNode (last edited 2017-06-16 23:09:53 by TullyFoote)