TheDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/MRRP-lab/arm-demos/llms.txt
Use this file to discover all available pages before exploring further.
colman_motion package contains the motion planning executables for the Colman arm demos. All nodes use the moveit_py Python bindings to interface with MoveIt 2 and plan trajectories for the ur_arm planning group. The package provides three executables covering the full demo pipeline: autonomous pick-and-place, calibration data collection, and hand-eye transform solving.
Executables
pick_and_place
Autonomous vacuum pick-and-place loop that detects objects with AprilTags and moves them to a drop-off location.
eye_in_hand
Interactive calibration data collector. Move the arm to each pose and press Enter to capture arm pose + tag pose sample pairs.
hand_eye_solve
Offline solver that reads a
.npz calibration file and computes the camera-to-gripper transform using five OpenCV hand-eye methods.pick_and_place
The main demo node. It instantiatesMoveItPy, acquires the ur_arm planning component, and runs a continuous pick loop until a /stop service call is received or the process is interrupted. On each iteration it:
- Moves to
LOOK_ONE(a downward-looking pose over the workspace) using joint-space planning - Queries the AprilTag TF frame (
tag_1) relative tobase_linkand validates positional stability (3 mm threshold over 1 s) - Approaches above the tag, descends, activates the vacuum gripper via
/io_and_status_controller/set_io - Retreats, moves to the drop-off location
(0.2, -0.2, 0.0381), releases vacuum, and retreats
Key classes
StopControl
StopControl
A
Node that exposes a /stop service (std_srvs/Trigger). When called it sets a threading.Event and invokes trajectory_execution_manager.stop_execution(auto_clear=True) so any in-progress trajectory is halted immediately. The stop_event is shared with every planning helper so they abort at the next check.SceneManager
SceneManager
A
Node that wraps the MoveIt 2 planning scene monitor. Provides three methods:add_box(obj_id, frame_id, dims, pos, orientation_w)— inserts a boxCollisionObjectinto the sceneremove_object(obj_id)— removes a named collision objectclear_scene()— removes all collision objects
planning_scene_monitor.read_write() and call scene.current_state.update() to ensure the scene is consistent before returning.VacuumControl
VacuumControl
A
Node that calls the UR I/O service (ur_msgs/SetIO) at /io_and_status_controller/set_io to toggle digital output pin 0, which controls the vacuum gripper. Uses a ReentrantCallbackGroup so service calls do not block the executor.TagLookup
TagLookup
A
Node that uses tf2_ros.Buffer to look up AprilTag transforms. get_tag_pose(tag_frame, base_frame) takes two samples 1 second apart and returns the transform only if both lookups succeed and the maximum positional delta is below 3 mm, rejecting stale or noisy readings.Planning helper functions
Moves the arm to a Cartesian pose expressed in
base_link. The pose goal is set on the vacuum_tip_link end-effector link.| Parameter | Type | Description |
|---|---|---|
ur | MoveItPy | MoveIt Python instance |
arm | PlanningComponent | Planning component for the ur_arm group |
logger | rclpy.Logger | ROS logger |
x, y, z | float | Target position in metres, base_link frame |
qx, qy, qz, qw | float | Target orientation as a quaternion (default: identity) |
params | PlanRequestParameters | Optional OMPL planner parameters (velocity/accel scaling) |
stop_event | threading.Event | If set, aborts before planning |
Moves the arm to a named joint configuration by constructing a joint constraint from a
RobotState.| Parameter | Type | Description |
|---|---|---|
ur | MoveItPy | MoveIt Python instance |
arm | PlanningComponent | Planning component for the ur_arm group |
logger | rclpy.Logger | ROS logger |
joint_values | dict[str, float] | Map of joint name to value in radians |
params | PlanRequestParameters | Optional OMPL planner parameters |
stop_event | threading.Event | If set, aborts before planning |
Moves the arm to a named pose defined in the SRDF (e.g.
"Up", "Look1", "Compact").| Parameter | Type | Description |
|---|---|---|
ur | MoveItPy | MoveIt Python instance |
arm | PlanningComponent | Planning component for the ur_arm group |
logger | rclpy.Logger | ROS logger |
pose_name | str | Named state from the SRDF, e.g. "Up" |
Plans and executes a trajectory with automatic retry on planning failure. All higher-level motion helpers delegate to this function.
Returns
| Parameter | Type | Description |
|---|---|---|
robot | MoveItPy | MoveIt Python instance used to execute the trajectory |
planning_component | PlanningComponent | Component with goal state already set |
logger | rclpy.Logger | ROS logger |
single_plan_parameters | PlanRequestParameters | Single-pipeline planner parameters |
multi_plan_parameters | MultiPlanRequestParameters | Multi-pipeline planner parameters |
retries | int | Number of planning attempts before giving up (default: 3) |
stop_event | threading.Event | If set before each attempt, returns False immediately |
True if a plan was found and executed successfully, False otherwise.Pre-defined joint configurations
The following joint dictionaries are defined as module-level constants and used throughout the pick-and-place loop:| Name | Purpose |
|---|---|
UP | Matches the SRDF "Up" named state (home pose) |
LOOK_ONE | Camera looking straight down at quadrant 1 |
LOOK_FOUR | Camera looking straight down at quadrant 4 (rotated −90° pan) |
COMPACT | Low-profile tucked pose for safe transit |
eye_in_hand
An interactive calibration data collection node (EyeInHandCalibration). You move the arm to a sequence of poses that expose the AprilTag (frame tag_20) to the wrist camera, then press Enter at each pose to capture a sample. Press q to save and exit.
Each captured sample records:
arm_rotations/arm_translations—base_link→tool0transform (rotation matrix + translation vector)tag_rotations/tag_translations—oakd_pro_camera_rgb_optical_frame_wrist→tag_20transform
calib_samples.npz in the working directory.
hand_eye_solve
An offline script (not a ROS node) that readscalib_samples.npz and solves the hand-eye calibration problem using five OpenCV methods: TSAI, PARK, HORAUD, ANDREFF, and DANIILIDIS. For each method it prints the resulting <origin xyz="..." rpy="..." /> Xacro fragment that you paste into the camera link definition in colman.urdf.xacro.
−π/2, 0, −π/2 in XYZ Euler angles) before outputting RPY, so the result is in the physical body frame of the camera.
Comparing results across all five methods is intentional: if they diverge significantly it usually means the calibration dataset needs more samples or better pose diversity.