Domino Hands is a ROS2 manipulation system that uses a Franka Emika FER arm and an Intel RealSense camera to detect loose dominoes, pick them up, reorient them, place them into configurable patterns, and then topple the first domino. The project was built for Northwestern ME450 by Gregory Aiosa, Daniel Augustin, Michael Jenz, and Chenyu Zhu.
The system is organized as three ROS2 packages: a high-level domino task package, a reusable MoveIt motion-planning package, and a custom service-interface package. The main launch file brings up MoveIt/RViz, RealSense, OpenCV domino detection, AprilTag/easy-handeye calibration, and the placement orchestrator.
RealSense RGB-D camera
|
v
find_dominoes
| HSV + depth + contour orientation
v
domino TFs in base frame
|
v
place_dominoes
| pattern YAML + current domino poses
v
MotionPlanningInterface
|
v
MoveIt2 + PlanningScene + Franka gripper
|
v
pick -> stage upright -> place -> toppleThe same launch file supports simulation and real hardware. In demo mode it starts the Franka MoveIt demo stack. On the real robot it assumes the station is running and launches RViz, RealSense, calibration publishers, perception, and task control.
# Real robot workflow
ros2 launch place_dominoes domino.launch.xml \
open_cv:=true demo:=false pattern:=straight_line.yaml
ros2 service call /place_dominoes std_srvs/srv/Empty
ros2 service call /topple_dominoes std_srvs/srv/Empty
# Simulation workflow
ros2 launch place_dominoes domino.launch.xml \
demo:=true scene:=jig_laying.yaml pattern:=straight_line.yaml
ros2 service call /place_dominoes std_srvs/srv/EmptyThe perception node is intentionally fast and inspectable. It uses depth slicing to isolate the table region, HSV filtering to segment domino-colored pixels, contour geometry to reject non-domino shapes, and RealSense intrinsics to convert the center pixel into a 3D point.
RGB image + aligned depth image + camera intrinsics
|
v
depth slice: keep points within table workspace
|
v
HSV threshold + blur + morphology
|
v
find contours and min-area rectangles
|
v
filter by area and long/short side ratio
|
v
center pixel + depth -> 3D camera-frame point
|
v
rectangle angle -> yaw quaternion
|
v
camera TF -> base TF -> domino0, domino1, ...OpenCV was the right tool for this project because dominoes are small, close to the camera, and visually distinct from the table under controlled lighting. The detector is simple enough to debug from intermediate image topics.
Advantages
Limitations
The camera pose is published through easy_handeye2 calibration, with AprilTags available during calibration. Accurate extrinsics are critical because the manipulation node consumes domino TFs in the robot base frame, not image coordinates.
Advantages
Limitations
Goal patterns are loaded from YAML files as named domino poses. The project includes straight-line, circle, squiggle, spiral, and dense generated patterns. Each goal domino has a size, position, and quaternion orientation, which allows the planner to treat the future layout as a concrete set of target poses.
pattern YAML
|
v
goal_domino_0 ... goal_domino_n
|
v
publish translucent RViz markers
|
v
pair start_domino_i with goal_domino_i
|
v
move each physical domino into the selected patternThe core challenge is that a domino is too small to simply pick up flat and place into a tight final pattern. The gripper can collide with neighboring dominoes, and a flat grasp does not give the arm a good final placement posture. The solution is a staged regrasp pipeline.
For each domino:
1. Move above the table and collect current domino TFs.
2. Move to the next detected domino and align the gripper to its long axis.
3. Pick the domino from its start pose.
4. If laying down, move to a staging area and stand it upright.
5. Regrasp the upright domino with a placement-friendly top-down grip.
6. Move above the matching goal pose.
7. Lower through a Cartesian path when possible.
8. Detach, force-seat on the table, respawn the collision object, and open the gripper.
9. Return to ready and repeat.Pose goals are used for most large moves: picture pose, ready pose, above-start pose, staging pose, and above-goal pose. Each plan is retried repeatedly because manipulation around a dense table scene can fail due to IK, orientation constraints, or collision geometry.
Advantages
Limitations
For short vertical moves, the system prefers Cartesian waypoints. This is especially important when lowering a domino into a goal pose because the end-effector should descend predictably instead of approaching from a strange IK path.
Advantages
Limitations
Hard-coded table heights were not reliable because the real workspace surface was not perfectly flat. The fix was a force-limited descent loop: the arm lowers in 1 mm increments at slow velocity while monitoring Franka controller effort. When the effort crosses a threshold, the robot treats that as table contact.
target placement pose
|
v
temporarily remove table collision object
|
v
lower 1 mm at a time
|
v
monitor fer_joint2 effort
|
+-- effort below threshold --> continue lowering
|
+-- effort above threshold --> contact confirmed
|
v
restore table collision objectAdvantages
Limitations
The planning scene is not static. Dominoes are added as boxes, attached to the gripper during transport, detached at the goal, removed during force placement, and respawned afterward. This keeps MoveIt aware of carried objects while still allowing intentional table contact.
After placement, the /topple_dominoes service removes the first domino collision object, computes a point behind the first goal domino based on its orientation, moves the gripper behind it, and pushes through the domino to start the chain reaction.
first goal domino pose
|
v
compute domino local axis in world frame
|
v
choose approach point behind domino
|
v
move gripper to hover point
|
v
move into domino to topple pattern# Real robot demo with camera-based detection
ros2 launch place_dominoes domino.launch.xml \
open_cv:=true demo:=false pattern:=straight_line.yaml
ros2 service call /place_dominoes std_srvs/srv/Empty
ros2 service call /topple_dominoes std_srvs/srv/Empty
# Simulation demo with a predefined starting scene
ros2 launch place_dominoes domino.launch.xml \
demo:=true scene:=jig_laying.yaml pattern:=straight_line.yaml
ros2 service call /place_dominoes std_srvs/srv/Empty