Skip to content

Commit fefe90d

Browse files
committed
Add collision and button-press integration tests for domino and coffee
Move debug_motion_planning.py and debug_coffee_place.py into test_skill_factories_integration.py as proper pytest functions. - test_domino_pick_place_no_collisions: picks domino_1 and places it between others, verifying no non-held dominoes move during BiRRT. - test_coffee_place_no_button_press: picks jug and places in machine, verifying machine doesn't turn on (xfail: button detection zone overlaps dispense area approach path).
1 parent d7b4b57 commit fefe90d

1 file changed

Lines changed: 201 additions & 0 deletions

File tree

tests/test_skill_factories_integration.py

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,207 @@ class _ExposedDominoEnv(_ExposedEnvMixin, PyBulletDominoEnv):
11861186
f"is_held={is_held}")
11871187

11881188

1189+
def test_domino_pick_place_no_collisions():
1190+
"""Pick domino_1 and place it between others — no non-held domino moves.
1191+
1192+
Uses position mode with motion planning so BiRRT plans collision-free
1193+
paths. Verifies that non-held dominoes remain stationary throughout
1194+
the pick and place sequences (i.e., no arm–domino collisions).
1195+
"""
1196+
try:
1197+
from predicators.envs.pybullet_domino import PyBulletDominoEnv
1198+
except ImportError:
1199+
pytest.skip("pybullet_domino not available")
1200+
1201+
utils.reset_config({
1202+
"env": "pybullet_domino",
1203+
"use_gui": False,
1204+
"pybullet_control_mode": "position",
1205+
"pybullet_robot": "fetch",
1206+
"domino_use_skill_factories": True,
1207+
"skill_phase_use_motion_planning": True,
1208+
"pybullet_ik_validate": False,
1209+
"domino_initialize_at_finished_state": False,
1210+
"domino_use_domino_blocks_as_target": True,
1211+
"domino_use_grid": True,
1212+
"domino_include_connected_predicate": False,
1213+
"domino_use_continuous_place": True,
1214+
"domino_restricted_push": True,
1215+
"domino_prune_actions": False,
1216+
"domino_has_glued_dominos": False,
1217+
"num_train_tasks": 1,
1218+
"num_test_tasks": 1,
1219+
})
1220+
1221+
class _ExposedDominoEnv(_ExposedEnvMixin, PyBulletDominoEnv):
1222+
pass
1223+
1224+
env = _ExposedDominoEnv(use_gui=False)
1225+
Pick = env._options["Pick"]
1226+
Place = env._options["Place"]
1227+
1228+
domino_type = env._domino_component.domino_type
1229+
robot_type = next(t for t in env.types if t.name == "robot")
1230+
1231+
# Use test task 0 (matches debug_motion_planning.py setup)
1232+
obs = env.reset("test", 0)
1233+
state = obs
1234+
1235+
robot = state.get_objects(robot_type)[0]
1236+
dominos = state.get_objects(domino_type)
1237+
pick_target = next(d for d in dominos if d.name == "domino_1")
1238+
1239+
pos_tol = 1e-3
1240+
1241+
def _get_positions(st):
1242+
return {o.name: (st.get(o, "x"), st.get(o, "y"), st.get(o, "z"))
1243+
for o in st.get_objects(domino_type)}
1244+
1245+
def _check_moved(before, st, skip_names=()):
1246+
moved = []
1247+
cur = _get_positions(st)
1248+
for name, (bx, by, bz) in before.items():
1249+
if name in skip_names:
1250+
continue
1251+
cx, cy, cz = cur[name]
1252+
disp = np.sqrt((cx - bx)**2 + (cy - by)**2 + (cz - bz)**2)
1253+
if disp > pos_tol:
1254+
moved.append((name, disp))
1255+
return moved
1256+
1257+
# ---- Pick domino_1 ----
1258+
pos_before_pick = _get_positions(state)
1259+
option = Pick.ground([robot, pick_target],
1260+
np.array([0.01], dtype=np.float32))
1261+
assert option.initiable(state)
1262+
1263+
pick_collisions = []
1264+
for _ in range(300):
1265+
if option.terminal(state):
1266+
break
1267+
action = option.policy(state)
1268+
state = env.simulate(state, action)
1269+
moved = _check_moved(pos_before_pick, state,
1270+
skip_names={pick_target.name})
1271+
if moved:
1272+
pick_collisions = moved
1273+
1274+
assert state.get(pick_target, "is_held") > 0.5, \
1275+
"domino_1 should be held after pick"
1276+
assert not pick_collisions, \
1277+
f"Non-held dominoes moved during Pick: {pick_collisions}"
1278+
1279+
# ---- Place at (0.75, 1.26) between existing dominoes ----
1280+
pos_before_place = _get_positions(state)
1281+
target_x, target_y, target_yaw = 0.75, 1.26, 0.0
1282+
release_z = env.table_height + env.domino_height * 1.13
1283+
1284+
option = Place.ground(
1285+
[robot],
1286+
np.array([target_x, target_y, target_yaw, release_z],
1287+
dtype=np.float32))
1288+
assert option.initiable(state)
1289+
1290+
place_collisions = []
1291+
for _ in range(300):
1292+
if option.terminal(state):
1293+
break
1294+
action = option.policy(state)
1295+
state = env.simulate(state, action)
1296+
moved = _check_moved(pos_before_place, state)
1297+
if moved:
1298+
place_collisions = moved
1299+
1300+
assert not place_collisions, \
1301+
f"Non-held dominoes moved during Place: {place_collisions}"
1302+
1303+
1304+
@pytest.mark.xfail(reason="Button detection zone overlaps dispense area "
1305+
"approach path — robot arm triggers button during place")
1306+
def test_coffee_place_no_button_press():
1307+
"""PickJug then PlaceJugInMachine without turning machine on.
1308+
1309+
The jug should be placed on the dispense area without hitting the
1310+
machine's top overhang or accidentally pressing the button.
1311+
"""
1312+
utils.reset_config({
1313+
"env": "pybullet_coffee",
1314+
"use_gui": False,
1315+
"pybullet_control_mode": "position",
1316+
"pybullet_robot": "fetch",
1317+
"pybullet_ik_validate": False,
1318+
"coffee_use_skill_factories": True,
1319+
"coffee_rotated_jug_ratio": 0,
1320+
"coffee_num_cups_train": [1],
1321+
"coffee_num_cups_test": [1],
1322+
"coffee_machine_have_light_bar": False,
1323+
"coffee_move_back_after_place_and_push": True,
1324+
"coffee_machine_has_plug": False,
1325+
"coffee_combined_move_and_twist_policy": True,
1326+
"coffee_use_pixelated_jug": True,
1327+
"coffee_fill_jug_gradually": True,
1328+
"skill_phase_use_motion_planning": True,
1329+
"max_num_steps_option_rollout": 100,
1330+
"num_train_tasks": 1,
1331+
"num_test_tasks": 1,
1332+
})
1333+
1334+
env = _ExposedCoffeeEnv(use_gui=False)
1335+
1336+
robot_type = next(t for t in env.types if t.name == "robot")
1337+
jug_type = next(t for t in env.types if t.name == "jug")
1338+
machine_type = next(t for t in env.types if t.name == "coffee_machine")
1339+
1340+
obs = env.reset("test", 0)
1341+
state = obs
1342+
1343+
robot = state.get_objects(robot_type)[0]
1344+
jug = state.get_objects(jug_type)[0]
1345+
machine = state.get_objects(machine_type)[0]
1346+
1347+
assert state.get(machine, "is_on") < 0.5, "Machine should start OFF"
1348+
1349+
# ---- Pick the jug ----
1350+
pick_option = env.PickJug.ground(
1351+
[robot, jug], np.array([0.01], dtype=np.float32))
1352+
assert pick_option.initiable(state)
1353+
1354+
for _ in range(300):
1355+
if pick_option.terminal(state):
1356+
break
1357+
action = pick_option.policy(state)
1358+
state = env.simulate(state, action)
1359+
1360+
assert state.get(jug, "is_held") > 0.5, "Jug should be held after pick"
1361+
assert state.get(machine, "is_on") < 0.5, "Machine turned on during pick!"
1362+
1363+
# ---- Place jug in machine ----
1364+
target_x = PyBulletCoffeeEnv.dispense_area_x
1365+
target_y = PyBulletCoffeeEnv.dispense_area_y
1366+
target_yaw = PyBulletCoffeeEnv.robot_init_wrist
1367+
release_z = PyBulletCoffeeEnv.z_lb + env.jug_handle_height()
1368+
1369+
place_option = env.PlaceJugInMachine.ground(
1370+
[robot, jug, machine],
1371+
np.array([target_x, target_y, target_yaw, release_z],
1372+
dtype=np.float32))
1373+
assert place_option.initiable(state)
1374+
1375+
machine_turned_on_step = None
1376+
for step in range(300):
1377+
if place_option.terminal(state):
1378+
break
1379+
action = place_option.policy(state)
1380+
state = env.simulate(state, action)
1381+
1382+
if state.get(machine, "is_on") > 0.5 and machine_turned_on_step is None:
1383+
machine_turned_on_step = step
1384+
1385+
assert machine_turned_on_step is None, (
1386+
f"Machine was turned on at step {machine_turned_on_step} during "
1387+
f"PlaceJugInMachine — robot arm likely triggered the button.")
1388+
1389+
11891390
def test_human_interaction_scripted_domino_solves_task():
11901391
"""Full pipeline: human_interaction approach with scripted option plan
11911392
(domino2.txt) solves the 1st test task in pybullet_domino."""

0 commit comments

Comments
 (0)