Skip to content

Contact Detection

SUMMARY

Contact detection lets you interrupt an asynchronous motion the moment the robot touches something - useful for assembly insertion, surface probing, and collision-aware free-space moves.

start_contact_detection arms the controller's contact detection subsystem.

read_contact_detection polls the current state without disarming - call it in a loop while the robot is moving.

stop_contact_detection disarms detection and returns the definitive final result.

SUPPORTED ROBOTS

Currently supported only on Universal Robots.

UNITS

direction is an optional 3-element Cartesian vector [dx, dy, dz] (unitless - only the direction matters, not magnitude). Returns booleans only.

The Skill

python
# Arm contact detection (optionally constrained to a direction)
robot.start_contact_detection(direction=[])

# Poll the detection state during motion
contact = robot.read_contact_detection()

# Disarm and retrieve the final result
contact = robot.stop_contact_detection()

The Code

Safety first!

A real robot will faithfully do whatever you ask of it - so please take a moment to clear the workspace, keep an E-Stop within reach, and be ready to disconnect.

Operating real hardware is at your own risk.

Example: Move and Detect Contact

Start an asynchronous Cartesian move, arm contact detection, poll at 20 Hz, then disarm.

python
"""
Contact Detection example for the Synapse SDK.

Shows the full lifecycle: ``start_contact_detection`` arms the watcher,
``read_contact_detection`` polls during motion, and ``stop_contact_detection``
disarms it and returns the final result.

Currently supported only for Universal Robots (UR10e).

Usage:
    python contact_detection.py --ip <ROBOT_IP>
"""

import argparse
import time

from loguru import logger

from telekinesis.synapse.robots.manipulators import universal_robots


def main(ip: str):
    """Run an async +Z move with contact detection armed, poll, then stop."""

    # Create and connect to the robot
    robot = universal_robots.UniversalRobotsUR10E()
    robot.connect(ip=ip)

    try:
        # Get current pose and define a target slightly above in Z
        current = robot.get_cartesian_pose()
        target = list(current)
        target[2] += 0.002
        logger.info(f"Will move async toward {target} and watch for contact")

        # Start an asynchronous move and arm contact detection
        robot.set_cartesian_pose(
            cartesian_pose=target,
            speed=0.05,
            acceleration=0.1,
            asynchronous=True,
        )
        robot.start_contact_detection()

        # Poll for contact for up to 1 s
        for _ in range(20):
            if robot.read_contact_detection():
                logger.info("Contact detected!")
                break
            time.sleep(0.05)

        # Disarm contact detection and report the final result
        contact = robot.stop_contact_detection()
        logger.success(f"Final contact: {contact}")

    finally:
        robot.disconnect()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Contact Detection Synapse example")
    parser.add_argument("--ip", type=str, required=True, help="UR robot IP address")
    args = parser.parse_args()

    main(ip=args.ip)

The Explanation of the Code

start_contact_detection arms the contact detection subsystem on the controller. The optional direction parameter constrains detection to a specific Cartesian direction (3-element vector [dx, dy, dz]); passing an empty list (default) enables omnidirectional detection. This skill is typically paired with an asynchronous move so the Python process can poll while motion continues.

read_contact_detection queries whether contact has been detected since detection was armed. It does not stop detection - the robot continues moving and the subsystem remains armed after the call. Call it in a polling loop at the desired rate.

stop_contact_detection disarms the subsystem and returns the definitive boolean: True if any contact event was recorded during the detection window, False if the move completed without contact. Always call it to cleanly finalize a detection session, even if contact was already observed via read_contact_detection.

How to Tune the Parameters

start_contact_detection

ParameterTypeDefaultDescription
directionlist[float][]Optional 3-element Cartesian direction [dx, dy, dz] to limit detection. Empty list enables omnidirectional detection.

read_contact_detection

This skill takes no input parameters.

stop_contact_detection

This skill takes no input parameters.

Return Value

MethodTypeDescription
start_contact_detectionboolTrue if contact detection was successfully armed, False otherwise.
read_contact_detectionboolTrue if contact has been detected, False if no contact has occurred yet.
stop_contact_detectionboolTrue if contact was detected during the detection window, False otherwise.

Where to Use the Skill

  • Assembly insertion - Detect the moment a part makes contact with a mating feature.
  • Surface probing - Stop motion when the TCP touches a surface.
  • Collision avoidance - React to unexpected contact during free-space moves.
  • High-frequency polling loops - Check for contact at 20-125 Hz during asynchronous motion.

When Not to Use the Skill

  • You need force-torque data after contact - use Get TCP Force to read wrench values.
  • The move is synchronous - contact detection is intended for use with asynchronous moves so the Python process can poll while motion continues.
  • You only need contact during a guarded approach - use Move Until Contact for a single guarded move with a built-in stop condition.