Skip to content

Tutorial: Tool Change

Many users will want to work with tools other than the default Stretch Gripper that ships with the robot. In this tutorial you will learn how to configure the Stretch software interfaces to support other tools.

Changing Tool Interfaces in Stretch Body

Stretch Body supports a plug-in based architecture for tools. A tool is an extension of the EndOfArm class that supports additional degrees of freedom.

Standard Tools

Stretch Body supports two tool interfaces by default: The ToolNone & ToolStretchGripper. We will explore swapping between these default tools.


Stretch is configured to load the ToolStretchGripper interface by default. This tool is loaded according to the robot.tool parameter:

>>$ | grep robot.tool
stretch_body.robot_params.nominal_params     param.robot.tool               tool_stretch_gripper

We can interact with this tool from iPython

In [1]: import stretch_body.robot as robot

In [2]: r=robot.Robot()

In [3]: r.startup()

In [4]: r.end_of_arm
Out[4]: <stretch_body.end_of_arm_tools.ToolStretchGripper instance at 0x7f99109155a0>

In [5]: r.end_of_arm.motors
{'stretch_gripper': <stretch_body.stretch_gripper.StretchGripper instance at 0x7f99109159b0>,
 'wrist_yaw': <stretch_body.wrist_yaw.WristYaw instance at 0x7f9910915820>}

In [6]: r.end_of_arm.stow()
--------- Stowing Wrist Yaw ----
--------- Stowing Gripper ----
In [7]: r.stop()


The ToolNone interface can be loaded when no tool is attached to the Wrist Yaw joint. To switch to this interface, simply update the field in your stretch_re1_user_params.yaml to:

  tool: tool_none

After updating the YAML we can interact with the ToolNone via iPython

In [1]: import stretch_body.robot as robot

In [2]: r=robot.Robot()

In [3]: r.startup()

In [4]: r.end_of_arm
Out[4]: <stretch_body.end_of_arm_tools.ToolNone instance at 0x7f245f786fa0>

In [5]: r.end_of_arm.motors
Out[5]: {'wrist_yaw': <stretch_body.wrist_yaw.WristYaw instance at 0x7f245e69e410>}

In [6]: r.end_of_arm.stow()
--------- Stowing Wrist Yaw ----
In [7]: r.stop()

Loading Tool Interfaces from the Stretch Tool Share

The Stretch Tool Share is an open Git repository for non-standard Stretch tools. It hosts the CAD, URDF, and Python files needed to integrate these tools onto your robot.

To use Stretch Tool Share tools, first update your installation:

$ pip install -U hello-robot-stretch-tool-share

As an example, we see on the Tool Share that there is a tool, the ToolDryEraseToolHolderV1 which extends the EndOfArm class. In order to load this tool interface , modify your stretch_user_params.yaml to load the tool as before. We will also need to tell it where to find the tool's parameter file:

  tool: tool_dry_erase_holder_v1
- stretch_tool_share.dry_erase_holder_v1.params

We can now interact with the tool in iPython:

In [1]: import stretch_body.robot as robot

In [2]: r=robot.Robot()

In [3]: r.startup()

In [4]: r.end_of_arm
Out[4]: <stretch_tool_share.dry_erase_holder_v1.tool.ToolDryEraseHolderV1 instance at 0x7f3b61c17f00>

In [5]: r.end_of_arm.motors
Out[5]: {'wrist_yaw': <stretch_body.wrist_yaw.WristYaw instance at 0x7f3b61c59280>}

In [6]: r.end_of_arm.stow()
--------- Stowing Wrist Yaw ----

Changing Tool Interfaces in Stretch ROS

Next we'll show how to change the ROS interface for a tool. Here we will continue with the ToolDryEraseHolderV1 example. First, configure Stretch Body to use the tool as in the previous exercise.

Next, ensure your ROS is up to date:

$ cd ~/catkin_ws/src/stretch_ros/
$ git pull

To access the URDF data for the ToolDryEraseHolderV1 we'll need to clone the Tool Share repository:

$ cd ~/repos
$ git clone

Copy in the tool's URDF data into the Stretch ROS repository:

$ cd ~/repos/stretch_tool_share/tool_share/dry_erase_holder_v1
$ cp stretch_description/urdf/*.xacro ~/catkin_ws/src/stretch_ros/stretch_description/urdf/
$ cp stretch_description/meshes/*.STL ~/catkin_ws/src/stretch_ros/stretch_description/meshes/

Now we will update the tool Xacro for Stretch. Open the file ~/catkin_ws/src/stretch_ros/stretch_description/urdf/stretch_description.xacro in an editor. Comment out the current tool Xacro and include the Xacro for the dry erase holder.

<?xml version="1.0"?>
<robot xmlns:xacro="" name="stretch_description">

  <!--<xacro:include filename="stretch_gripper.xacro" />-->
  <xacro:include filename="stretch_dry_erase_marker.xacro" />

  <xacro:include filename="stretch_main.xacro" />
  <xacro:include filename="stretch_aruco.xacro" />
  <xacro:include filename="stretch_d435i.xacro" />
  <xacro:include filename="stretch_laser_range_finder.xacro" />
  <xacro:include filename="stretch_respeaker.xacro" />

Finally, we'll update our already calibrated URDF to use this new tool:

$ cd ~/catkin_ws/src/stretch_ros/stretch_description/urdf
$ cp stretch.urdf stretch.urdf.bak
$ rosrun stretch_calibration

Ctrl-C when the rosrun command terminates and you're ready to visualize the tool in RViz:

$ roslaunch stretch_calibration simple_test_head_calibration.launch

Advanced Topics

Understanding How the Tool Plug-In Works

For users looking to create their own custom tools it can be useful to understand how the tool plug-in architecture works. Here we will walk through the basics of the system for both Stretch Body and Stretch ROS

Stretch Body

The Robot class expects an instance of EndOfArm tool to be present. The EndOfArm tool is an extension of the DynamixelXChain class, which manages a chain of Dynamixel servos.

A tool is defined via its parameters (either in user YAML or Python). For example, the ToolStretchGripper is defined in These parameters tell the plug-in which DynamixelHelloXL430 instances to load and manage. Here we see:

"tool_stretch_gripper": {
    'use_group_sync_read': 1,
    'retry_on_comm_failure': 1,
    'py_class_name': 'ToolStretchGripper',
    'py_module_name': 'stretch_body.end_of_arm_tools',
    'stow': {'stretch_gripper': 0, 'wrist_yaw': 3.4},
    'devices': {
        'stretch_gripper': {
            'py_class_name': 'StretchGripper',
            'py_module_name': 'stretch_body.stretch_gripper'
        'wrist_yaw': {
            'py_class_name': 'WristYaw',
            'py_module_name': 'stretch_body.wrist_yaw'

This dictionary defines a tool of class ToolStretchGripper with two DynamixelHelloXL430 devices on its bus (StretchGripper and WristYaw).

We see that the ToolStretchGripper class extends the EndOfArm class and provides its own stowing behavior:

class ToolStretchGripper(EndOfArm):
    def __init__(self, name='tool_stretch_gripper'):

    def stow(self):
        # Fold in wrist and gripper
        print('--------- Stowing Wrist Yaw ----')
        self.move_to('wrist_yaw', self.params['stow']['wrist_yaw'])
        print('--------- Stowing Gripper ----')
        self.move_to('stretch_gripper', self.params['stow']['stretch_gripper'])

For tools that are not a part of Stretch Body, such as from the Tool Share, you must include the tool parameters as well in your stretch_user_params.yaml. A robot that must support many tools may have user YAML that looks like:

- stretch_tool_share.usbcam_wrist_v1.params
- stretch_tool_share.stretch_dex_wrist_beta.params
- stretch_tool_share.dry_erase_holder_v1.params
  tool: tool_dry_erase_holder_v1
  #tool: tool_none
  #tool: tool_stretch_gripper
  #tool: tool_usbcam_wrist_v1
  #tool: tool_stretch_dex_wrist_beta

For a more complex implementation of a tool we recommend reviewing the Stretch Dex Wrist implementation on the Stretch Tool Share.

Stretch ROS

Stretch ROS also supports the tool plug-in architecture. Under ROS this is managed by extending the SimpleCommandGroup.

More coming soon.

All materials are Copyright 2022 by Hello Robot Inc. Hello Robot and Stretch are registered trademarks.