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

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.pybash
python add_tcp.py --prim_path /World/ur10ebash
python add_tcp.py --ip 192.168.1.100For all options:
bash
python add_tcp.py --helpParameter Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
name | str | required | Unique name for the new TCP frame. |
transform | list[float] | np.ndarray | required | 6-element pose offset [x, y, z, rx, ry, rz] from default_tcp. Position in meters, orientation as Euler XYZ in degrees. |
set_active | bool | True | If True, immediately assigns the new TCP via active_tcp, pushing it to the controller when connected. |
Returns
| Type | Description |
|---|---|
None | add_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
| Exception | Condition |
|---|---|
TypeError | name is not a str, or transform is not a list/np.ndarray (or contains non-numeric values) |
ValueError | transform does not have exactly 6 elements, contains non-finite values, or a TCP named name already exists |
RuntimeError | The kinematic model has not been built yet |
ConnectionError / OSError | The 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.