Package Item in Honeycomb
SUMMARY
Pick and place honeycomb cardboard inserts into product boxes
Code
INFO
The following code was generated by the Tzara agent.
python
'''
Prompt: I have a UR10e and an RG6 gripper, I want to do a repackaging task where
the parts are placed in a rectangular grid and need to be placed into another
grid where there is a fixed offset on the x axis and the y axis and every other
row is offset from the previous row. The first row has n slots, second m third n
etc. Every other row is identical. When picking up the parts do not open the
gripper all the way as the parts are close. start and end the program at a
home position
'''
# Telekinesis pipeline: UR10e + OnRobot RG6 repackaging pick-and-place
# Picks from a regular rows x cols source grid and places into a staggered destination
# grid with alternating row slot counts (n, m, n, m, ...) offset from the source.
#
# Key constraint: gripper uses a PARTIAL open width during picks (source parts are
# closely packed); normal open is fine between moves / after place.
import numpy as np
from loguru import logger
from telekinesis.synapse.robots.manipulators.universal_robots import UniversalRobotsUR10E
from telekinesis.synapse.tools.parallel_grippers.onrobot import OnRobotRG6
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
ROBOT_IP = "192.168.1.2"
GRIPPER_IP = "192.168.1.1"
# Home pose [x, y, z, rx, ry, rz] in meters + degrees (tool pointing down).
HOME_POSE = [0.00, 0.8000, 0.400, 180.0, 0.0, 90.0]
# Common pick/place orientation (tool pointing down).
TOOL_RX, TOOL_RY, TOOL_RZ = 180.0, 0.0, 45.0
# Z heights (meters)
Z_APPROACH = 0.350 # safe travel height above grids
Z_PICK = 0.21 # contact height at source
Z_PLACE = 0.23 # contact height at destination
# --- Source grid (regular rows x cols) ---
SRC_ORIGIN_XY = (0.0235, 0.86715) # (x0, y0) of source slot (0,0) in meters
SRC_ROWS = 1
SRC_COLS = 1
SRC_DX = 0.045 # spacing along x between columns
SRC_DY = -0.045 # spacing along y between rows
# --- Destination grid (staggered, alternating n/m row slot counts) ---
# Destination origin is offset from source origin by a fixed (x, y) offset. # (dx, dy) from source origin to destination origin
DST_ORIGIN_XY = (-0.3825, 0.87011)
DST_N = 5 # slots in odd rows (row index 0, 2, 4, ...)
DST_M = 4 # slots in even rows (row index 1, 3, 5, ...)
DST_DX = 0.0297 # spacing along x between slots within a row
DST_DY = -0.023275 # spacing along y between rows
DST_STAGGER_X = DST_DX / 2.0 # zigzag x-shift applied to even rows
# --- Gripper widths (mm; RG6 stroke 0..160 mm) ---
GRIP_OPEN_FULL_MM = 120.0 # normal open between moves / after place
GRIP_OPEN_PARTIAL_MM = 60.0 # PARTIAL open used when approaching/releasing at source
GRIP_CLOSE_MM = 0.0 # close on the part (hardware will stop on contact)
GRIP_FORCE_N = 40.0 # RG6 max is 120 N
# Motion params
MOVE_SPEED = 0.3
MOVE_ACCEL = 0.5
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def source_slot_pose(row: int, col: int, z: float) -> list[float]:
x = SRC_ORIGIN_XY[0] + col * SRC_DX
y = SRC_ORIGIN_XY[1] + row * SRC_DY
return [x, y, z, TOOL_RX, TOOL_RY, TOOL_RZ]
def destination_slot_pose(flat_index: int, z: float) -> list[float]:
"""
Map a flat slot index to an (x, y) pose in the staggered destination grid
with alternating row slot counts (N, M, N, M, ...).
Odd rows (0, 2, ...) have DST_N slots; even rows (1, 3, ...) have DST_M slots
and are x-staggered by DST_STAGGER_X.
"""
remaining = flat_index
row = 0
while True:
row_slots = DST_N if (row % 2 == 0) else DST_M
if remaining < row_slots:
col = remaining
break
remaining -= row_slots
row += 1
x_offset = 0.0 if (row % 2 == 0) else DST_STAGGER_X
x = DST_ORIGIN_XY[0] + col * DST_DX + x_offset
y = DST_ORIGIN_XY[1] + row * DST_DY
return [x, y, z, TOOL_RX, TOOL_RY, TOOL_RZ]
def above(pose: list[float], z: float) -> list[float]:
p = list(pose)
p[2] = z
return p
# ---------------------------------------------------------------------------
# Connect hardware
# ---------------------------------------------------------------------------
robot = UniversalRobotsUR10E()
robot.connect(ROBOT_IP)
gripper = OnRobotRG6()
# OnRobot RG6 uses Modbus TCP per synapse.tools.parallel_grippers.onrobot
gripper.connect(ip=GRIPPER_IP, protocol="MODBUS_TCP")
gripper.set_unit("position", "mm")
gripper.set_unit("force", "N")
# ---------------------------------------------------------------------------
# Pipeline
# ---------------------------------------------------------------------------
try:
# 1) Start at home with gripper at normal open
gripper.move(position=GRIP_OPEN_FULL_MM, force=GRIP_FORCE_N, asynchronous=False)
robot.set_cartesian_pose(HOME_POSE, speed=MOVE_SPEED, acceleration=MOVE_ACCEL)
# 2) Iterate source grid row-major; place into staggered destination by flat index
flat_index = 0
for r in range(SRC_ROWS):
for c in range(SRC_COLS):
pick_pose = source_slot_pose(r, c, Z_PICK)
pick_above = above(pick_pose, Z_APPROACH)
place_pose = destination_slot_pose(flat_index, Z_PLACE)
place_above = above(place_pose, Z_APPROACH)
logger.info(f"[{flat_index}] pick src(r={r},c={c}) -> place flat={flat_index}")
# --- PICK ---
# Partial open BEFORE entering the tight source region
gripper.move(position=GRIP_OPEN_PARTIAL_MM,
force=GRIP_FORCE_N, asynchronous=False)
robot.set_cartesian_pose(pick_above, speed=MOVE_SPEED, acceleration=MOVE_ACCEL)
robot.set_cartesian_pose(pick_pose, speed=MOVE_SPEED, acceleration=MOVE_ACCEL)
# Grasp
gripper.move(position=GRIP_CLOSE_MM, force=GRIP_FORCE_N, asynchronous=False)
# Retreat while still at partial width (avoid hitting neighbors on exit)
robot.set_cartesian_pose(pick_above, speed=MOVE_SPEED, acceleration=MOVE_ACCEL)
# --- PLACE ---
robot.set_cartesian_pose(place_above, speed=MOVE_SPEED, acceleration=MOVE_ACCEL)
robot.set_cartesian_pose(place_pose, speed=MOVE_SPEED, acceleration=MOVE_ACCEL)
# Normal open on release (destination has room due to stagger/offset)
gripper.move(position=GRIP_OPEN_FULL_MM,
force=GRIP_FORCE_N, asynchronous=False)
robot.set_cartesian_pose(place_above, speed=MOVE_SPEED, acceleration=MOVE_ACCEL)
flat_index += 1
# 3) Return to home
robot.set_cartesian_pose(HOME_POSE, speed=MOVE_SPEED, acceleration=MOVE_ACCEL)
finally:
# Clean disconnect
try:
gripper.disconnect()
except Exception as e:
logger.warning(f"Gripper disconnect failed: {e}")
try:
robot.disconnect()
except Exception as e:
logger.warning(f"Robot disconnect failed: {e}")
