Skip to content

Add TCP

SUMMARY

add_tcp registers a new named TCP frame at a fixed offset [x, y, z, rx, ry, rz] (m, deg) from the robot's default TCP. Pass set_active=True to immediately assign it as the active TCP as well.

UNITS

transform is [x, y, z, rx, ry, rz] - translation in meters, orientation as Euler XYZ degrees, relative to the default TCP frame.

The Skill

python
robot.add_tcp(name="new_tool", transform=[0.0, 0.0, 0.1, 0.0, 0.0, 0.0], set_active=True)

The Code

A UR10e manipulator with a new TCP frame registered via add_tcp
python
"""
Registers a custom TCP frame and inspects the active TCP before and after.

Supports Universal Robots (UR), Epson, virtual, and Isaac Sim.

Usage:
    python add_tcp.py [--ip <ROBOT_IP>] [--prim_path <PRIM_PATH>]
"""

import argparse

from loguru import logger

from telekinesis.synapse.robots.manipulators import universal_robots


def main(ip: str | None, prim_path: str | None) -> None:
    """Observe the active TCP and its transform before and after add_tcp()."""

    #===================== Create Robot ==========================================
    robot = universal_robots.UniversalRobotsUR10E(name='UR10e')

    # ==================== Visualization (Optional) =============================
    robot.visualize_rerun(live=True)

    try:
        #===================== Connect Robot ==========================================
        if ip:
            robot.connect(ip=ip)
        elif prim_path:
            robot.connect(simulation_prim_path=prim_path)
            robot.set_joint_positions(robot.default_joint_configuration)

        # ==================== Run Skill ============================================
        logger.info(f"Active TCP before add_tcp(): {robot.active_tcp}"
                    f" \nActive TCP transform: {robot.get_active_tcp_transform()}"
                    f" \n TCP pose: {robot.get_cartesian_pose()}")

        new_tcp_pose_in_default_tcp_frame = [0.0, 0.0, 0.1, 0.0, 0.0, 0.0]  # 100 mm along Z-axis
        robot.add_tcp(name="new_tool",
                      transform=new_tcp_pose_in_default_tcp_frame,
                      set_active=True)

        logger.info(f"Active TCP after add_tcp(): {robot.active_tcp}"
                    f" \nActive TCP transform: {robot.get_active_tcp_transform()}"
                    f" \n TCP pose: {robot.get_cartesian_pose()}")
    except (ConnectionError, OSError) as e:
        logger.error(f"Error occurred: {e}")
    finally:
        robot.disconnect()
        robot.shutdown()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Add a custom TCP to the robot and inspect the active TCP.")
    parser.add_argument("--ip", type=str, default=None,
                         help="UR robot IP address for real hardware, e.g. 192.168.1.100")
    parser.add_argument("--prim_path", type=str, default=None,
                         help='Isaac Sim articulation prim path, e.g. "/World/ur10e"')
    args = parser.parse_args()

    main(ip=args.ip, prim_path=args.prim_path)

Running the Example

bash
python add_tcp.py
bash
python add_tcp.py --prim_path /World/ur10e
bash
python add_tcp.py --ip 192.168.1.100

For all options:

bash
python add_tcp.py --help

Parameter Configuration

ParameterTypeDefaultDescription
namestrrequiredUnique name for the new TCP frame.
transformlist[float] | np.ndarrayrequired6-element pose offset [x, y, z, rx, ry, rz] from default_tcp. Position in meters, orientation as Euler XYZ in degrees.
set_activeboolTrueIf True, immediately assigns the new TCP via active_tcp, pushing it to the controller when connected.

Returns

TypeDescription
Noneadd_tcp registers the TCP as a side effect. Read robot.active_tcp (name) or robot.get_active_tcp_transform() (transform) to confirm the result.

Raises

ExceptionCondition
TypeErrorname is not a str, or transform is not a list/np.ndarray (or contains non-numeric values)
ValueErrortransform does not have exactly 6 elements, contains non-finite values, or a TCP named name already exists
RuntimeErrorThe kinematic model has not been built yet
ConnectionError / OSErrorThe connection to the physical controller fails or drops while pushing the new active TCP (Universal Robots only)

How to Tune the Parameters

Pass set_active=False when registering several TCPs up front (e.g. "camera_tcp", "gripper_tcp") so each add_tcp call only registers the frame without pushing a TCP change to the controller; switch between them afterward with Change Active TCP.

Where to Use the Skill

  • Tool changes - Register a TCP for each tool (gripper, sensor, custom end-effector) you plan to use.
  • Multi-frame workflows - Register several TCPs up front (e.g. "camera_tcp", "gripper_tcp") and switch between them with Change Active TCP.

When Not to Use the Skill

  • The TCP already exists - use Update TCP to change an existing TCP's transform instead of adding a duplicate.
  • You just need to switch between existing TCPs - use Change Active TCP, which doesn't re-register anything.