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.
UNITS
direction is an optional 3-element Cartesian vector [dx, dy, dz] (unitless - only the direction matters, not magnitude). Returns booleans only.
The Skill
# 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.
"""
Drives the TCP slowly downward while polling contact detection, and stops as soon as the tool touches a surface.
Supports Universal Robots (UR).
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 | None) -> None:
"""Probe downward until contact is detected, then stop and report."""
#===================== Create Robot ==========================================
robot = universal_robots.UniversalRobotsUR10E(name='UR10e')
try:
#===================== Connect Robot ==========================================
if ip:
robot.connect(ip=ip)
#===================== Prepare Target ==========================================
target_pose = robot.get_cartesian_pose()
target_pose[2] -= 0.15 # Move 15 cm down in Z
# ==================== Run Skill ============================================
robot.start_contact_detection()
robot.set_cartesian_pose(cartesian_pose=target_pose, speed=0.05,
acceleration=0.25, asynchronous=True)
# Poll until contact, or for up to 5 s if nothing is hit.
contact = False
deadline = time.time() + 5.0
while not contact and time.time() < deadline:
contact = robot.read_contact_detection()
time.sleep(0.02)
robot.stop_cartesian_motion(stopping_speed=0.25)
robot.stop_contact_detection()
logger.success(f"Contact: {contact}")
except (ConnectionError, OSError) as e:
logger.error(f"Error occurred: {e}")
finally:
robot.disconnect()
robot.shutdown()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Probe downward with contact detection polling (start/read/stop)")
parser.add_argument("--ip", type=str, default=None,
help="UR robot IP address for real hardware, e.g. 192.168.1.100")
args = parser.parse_args()
main(ip=args.ip)Running the Example
python contact_detection.py --ip 192.168.1.100For all options:
python contact_detection.py --helpParameter Configuration
start_contact_detection
| Parameter | Type | Default | Description |
|---|---|---|---|
direction | list[float] | [] | Optional 3-element Cartesian direction [dx, dy, dz] to limit detection. Empty list (or None) enables omnidirectional detection. |
read_contact_detection
This skill takes no input parameters.
stop_contact_detection
This skill takes no input parameters.
Returns
| Method | Type | Description |
|---|---|---|
start_contact_detection | bool | True if contact detection was successfully armed, False otherwise. |
read_contact_detection | bool | True if contact has been detected, False if no contact has occurred yet. |
stop_contact_detection | bool | True if contact was detected during the detection window, False otherwise. |
Raises
start_contact_detection
| Exception | Condition |
|---|---|
RuntimeError | The robot is not connected |
ConnectionError / OSError | The connection to the controller fails or drops during the surrounding hardware session |
read_contact_detection
| Exception | Condition |
|---|---|
RuntimeError | The robot is not connected |
ConnectionError / OSError | The connection to the controller fails or drops during the surrounding hardware session |
stop_contact_detection
| Exception | Condition |
|---|---|
RuntimeError | The robot is not connected |
ConnectionError / OSError | The connection to the controller fails or drops during the surrounding hardware session |
How to Tune the Parameters
start_contact_detection's direction narrows detection to a single Cartesian direction (e.g. straight down for a vertical probing move) so lateral contact with a fixture doesn't trigger a false stop; leave it as the default [] for a free-space move where contact could come from any direction. Poll read_contact_detection at 20-125 Hz - fast enough to catch contact promptly without saturating the RTDE channel with requests - and always pair start_contact_detection with a matching stop_contact_detection to disarm the subsystem, even on the path where contact was already observed via read_contact_detection.
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.