Skip to content

RADU: Control the Robot using a Joystick

By Sebastian Günther

Posted in Robots, Radu, Rasperry_pi, Raspberry_pico, Micropython

A mobile robot offers many interfaces for controlling. In my project RADU, a two-wheel movable robot, I implemented a wrap for movements commands from the Robot Operating System ROS. Commands have two parts in which we are interested: The linear velocity in meter/second, which moves the robot along its x-axis, and the angular velocity on radians/second, which turns the robot around its z-axis, also called pitch. Any controller that sends input which can be converted to these movements messages is usable by the project.

This article is a quick tutorial how to use a game console controller, from a PlayStation or Xbox, to move a robot. You will learn how to connect and read gamepad data, and how to convert it to ROS compatible commands.

The technical context of this article is Ubuntu 20.04 LTS with latest ros-noetic and Python 3.8.10.

Connect and Read Gamepad Data

We need to connect a gamepad and check that your Linux system correctly recognizes it. Use whatever gamepad you have - in my case, it’s a PlayStation 3 controller, and plug it into a USB port

Then, run the Linux command dmesg to show decent Kernel messages about the detected device.

[412373.206747] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[412373.206749] usb 1-2: Product: PLAYSTATION(R)3 Controller
[412373.206751] usb 1-2: Manufacturer: Sony
[412373.562080] input: Sony PLAYSTATION(R)3 Controller Motion Sensors as /devices/pci0000:00/0000:00:15.0/usb1/1-2/1-2:1.0/0003:054C:0268.001C/input/input102
[412373.624511] input: Sony PLAYSTATION(R)3 Controller as /devices/pci0000:00/0000:00:15.0/usb1/1-2/1-2:1.0/0003:054C:0268.001C/input/input101
[412373.625435] sony 0003:054C:0268.001C: input,hiddev1,hidraw3: USB HID v81.11 Joystick [Sony PLAYSTATION(R)3 Controller] on usb-0000:00:15.0-2/input0

Next, we need to figure out where joystick data can be accessed. Execute the command cat /proc/bus/input/devices and look for a text block that mentions the controller that you connected. In my case, I see this:

I: Bus=0003 Vendor=054c Product=0268 Version=8111
N: Name="Sony PLAYSTATION(R)3 Controller"
P: Phys=usb-0000:00:15.0-3.1/input0
S: Sysfs=/devices/pci0000:00/0000:00:15.0/usb1/1-3/1-3.1/1-3.1:1.0/0003:054C:0268.0059/input/input224
U: Uniq=00:21:4f:08:f6:c8
H: Handlers=event17 js0
B: EV=20001b
B: KEY=f00000000 0 0 0 7fdb000000000000 0 0 0 0
B: ABS=3f
B: MSC=10
B: FF=107030000 0

In the line p, we see /input0, and in line H, we see js0. We combine this information to the device file /dev/input/js0. This device file provides a byte stream about controller commands.

Now, we will read this byte stream with ROS.

Reading Gamepad Data with ROS

In order to work with any gamepad, we need to install additional ROS packages. In a terminal, execute this command:

sudo apt-get install ros-noetic-joy-teleop ros-noetic-teleop-twist-joy ros-noetic-joy

These packages provide several ways to interact with a connected joypad. To get started, we will run a ROS node called joy_node with the parameter of the detected device file. Run the following command ...

rosrun joy joy_node dev:=/dev/input/js0

... and you should see this output.

[ INFO] [1627219939.885877257]: Opened joystick: /dev/input/js0 (Sony PLAYSTATION(R)3 Controller). deadzone_: 0.050000.

This node published data at the topic /joy. Let’s subscribe to this topic and see the messages.

$> rostopic echo /joy

  seq: 177
    secs: 1627219968
    nsecs: 437779598
  frame_id: "/dev/input/js0"
axes: [0.04505977779626846, -0.0, 0.0, -0.0, -0.0, 0.0]
buttons: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  seq: 178
    secs: 1627219968
    nsecs: 447620065
  frame_id: "/dev/input/js0"
axes: [-0.0, -0.0, 0.0, -0.0, -0.0, 0.0]
buttons: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Each message contains two vectors. The first vector axes contains the left/right and top/down position of the two control sticks and the cross key as float values. The second vector buttons contains integer values about which buttons are pressed. See the button mapping documentation.

Converting Gamepad Data to Twist Messages

The last step is to translate the messages from the /joy topic to TWIST messages. Another ROS package already performs this translation, we just need to start the teleop_twist_joynode.

Start the node with ...

rosrun teleop_twist_joy teleop_node

... and you should see the following output.

[ INFO] [1627221588.156287628]: Teleop enable button 0.
[ INFO] [1627221588.161486651]: Linear axis x on 1 at scale 0.500000.
[ INFO] [1627221588.161570308]: Angular axis yaw on 0 at scale 0.500000.

Before continuing, be sure to check that all required ROS nodes are running and connected as shown in the following picture:

Subscribe to the topic /cmd_vel. Then, on your gamepad, identify the deadman switch button, a safe guard to prevent sending commands that you did not intent. From the above mentioned message Teleop enable button 0. and the button mapping documentation, I can see that this button is X on my PlayStation 3 controller.

Now comes the big moment: Hold down the X button, move the joystick, and you should see these messages:

  x: 0.036096855998039246
  y: 0.0
  z: 0.0
  x: 0.0
  y: 0.0
  z: -0.5
  x: -0.0
  y: 0.0
  z: 0.0
  x: 0.0
  y: 0.0
  z: -0.45583534240722656

Cool! Now you just need feed these messages to your robot, and you can start moving around, controlled with a gamepad.


This article showed you how to use a gamepad for controlling your robot. We learned how to identify a connected controller on Linux by reading the build-in proc log to identify which device file represents the controller. Then we continued to install the required ROS packages joy-teleop and teleop-twist-joy and started a simple node that shows which game stick or buttons are pressed. And with the help of another node we can transform these messages to movement messages in the Twist format. Now, grab your favourite controller and move your robot in realtime.