mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-05-23 04:39:59 +00:00
139 lines
5.5 KiB
Python
139 lines
5.5 KiB
Python
"""MockCollisionChecker 和 MockReachabilityChecker 测试。"""
|
||
|
||
import math
|
||
|
||
from ..mock_checkers import MockCollisionChecker, MockReachabilityChecker
|
||
|
||
|
||
class TestMockCollisionChecker:
|
||
def setup_method(self):
|
||
self.checker = MockCollisionChecker()
|
||
|
||
def test_no_collision_far_apart(self):
|
||
"""两个设备距离足够远,不碰撞。"""
|
||
placements = [
|
||
{"id": "a", "bbox": (0.5, 0.5), "pos": (1.0, 1.0, 0.0)},
|
||
{"id": "b", "bbox": (0.5, 0.5), "pos": (3.0, 3.0, 0.0)},
|
||
]
|
||
assert self.checker.check(placements) == []
|
||
|
||
def test_collision_overlapping(self):
|
||
"""两个设备重叠,应检测到碰撞。"""
|
||
placements = [
|
||
{"id": "a", "bbox": (1.0, 1.0), "pos": (1.0, 1.0, 0.0)},
|
||
{"id": "b", "bbox": (1.0, 1.0), "pos": (1.5, 1.0, 0.0)},
|
||
]
|
||
collisions = self.checker.check(placements)
|
||
assert ("a", "b") in collisions
|
||
|
||
def test_collision_touching_edges(self):
|
||
"""两设备恰好边缘接触,不算碰撞(< 而非 <=)。"""
|
||
placements = [
|
||
{"id": "a", "bbox": (1.0, 1.0), "pos": (0.5, 0.5, 0.0)},
|
||
{"id": "b", "bbox": (1.0, 1.0), "pos": (1.5, 0.5, 0.0)},
|
||
]
|
||
collisions = self.checker.check(placements)
|
||
assert collisions == []
|
||
|
||
def test_collision_with_rotation(self):
|
||
"""旋转后的设备 OBB 可能导致碰撞。"""
|
||
placements = [
|
||
{"id": "a", "bbox": (1.0, 0.2), "pos": (1.0, 1.0, math.pi / 4)},
|
||
{"id": "b", "bbox": (0.5, 0.5), "pos": (1.4, 1.0, 0.0)}, # closer: OBB overlap
|
||
]
|
||
collisions = self.checker.check(placements)
|
||
assert ("a", "b") in collisions
|
||
|
||
def test_no_collision_with_rotation(self):
|
||
"""旋转后仍不碰撞。"""
|
||
placements = [
|
||
{"id": "a", "bbox": (1.0, 0.2), "pos": (1.0, 1.0, math.pi / 4)},
|
||
{"id": "b", "bbox": (0.5, 0.5), "pos": (2.0, 1.0, 0.0)},
|
||
]
|
||
collisions = self.checker.check(placements)
|
||
assert collisions == []
|
||
|
||
def test_check_bounds_within(self):
|
||
"""设备在边界内。"""
|
||
placements = [
|
||
{"id": "a", "bbox": (0.5, 0.5), "pos": (1.0, 1.0, 0.0)},
|
||
]
|
||
assert self.checker.check_bounds(placements, 5.0, 5.0) == []
|
||
|
||
def test_check_bounds_outside(self):
|
||
"""设备超出边界。"""
|
||
placements = [
|
||
{"id": "a", "bbox": (1.0, 1.0), "pos": (0.2, 0.2, 0.0)},
|
||
]
|
||
oob = self.checker.check_bounds(placements, 5.0, 5.0)
|
||
assert "a" in oob
|
||
|
||
def test_three_devices_multiple_collisions(self):
|
||
"""三个设备,两两碰撞。"""
|
||
placements = [
|
||
{"id": "a", "bbox": (1.0, 1.0), "pos": (1.0, 1.0, 0.0)},
|
||
{"id": "b", "bbox": (1.0, 1.0), "pos": (1.3, 1.0, 0.0)},
|
||
{"id": "c", "bbox": (1.0, 1.0), "pos": (1.6, 1.0, 0.0)},
|
||
]
|
||
collisions = self.checker.check(placements)
|
||
assert ("a", "b") in collisions
|
||
assert ("b", "c") in collisions
|
||
|
||
|
||
def test_obb_collision_rotated_no_false_positive():
|
||
"""A rotated narrow device should NOT collide with a nearby device
|
||
that the old AABB method would have flagged as colliding.
|
||
|
||
Old AABB expands footprint; OBB is precise.
|
||
"""
|
||
checker = MockCollisionChecker()
|
||
# Narrow device (2.0 x 0.5) rotated 45°:
|
||
# AABB would be ~1.77 x 1.77, OBB is the actual narrow rectangle
|
||
placements = [
|
||
{"id": "narrow", "bbox": (2.0, 0.5), "pos": (3.0, 3.0, math.pi / 4)},
|
||
{"id": "nearby", "bbox": (0.5, 0.5), "pos": (4.5, 3.0, 0.0)},
|
||
]
|
||
collisions = checker.check(placements)
|
||
# With OBB: no collision (the narrow rotated box doesn't reach)
|
||
assert ("narrow", "nearby") not in collisions and ("nearby", "narrow") not in collisions
|
||
|
||
|
||
class TestMockReachabilityChecker:
|
||
def setup_method(self):
|
||
self.checker = MockReachabilityChecker()
|
||
|
||
def test_reachable_within_radius(self):
|
||
"""目标在臂展半径内。"""
|
||
arm_pose = {"x": 0.0, "y": 0.0, "theta": 0.0}
|
||
target = {"x": 0.5, "y": 0.5, "z": 0.0}
|
||
assert self.checker.is_reachable("elite_cs66", arm_pose, target)
|
||
|
||
def test_not_reachable_outside_radius(self):
|
||
"""目标超出臂展半径。"""
|
||
arm_pose = {"x": 0.0, "y": 0.0, "theta": 0.0}
|
||
target = {"x": 2.0, "y": 2.0, "z": 0.0}
|
||
assert not self.checker.is_reachable("elite_cs66", arm_pose, target)
|
||
|
||
def test_reachable_at_boundary(self):
|
||
"""目标恰好在臂展边界上(应可达)。"""
|
||
arm_pose = {"x": 0.0, "y": 0.0, "theta": 0.0}
|
||
target = {"x": 0.914, "y": 0.0, "z": 0.0}
|
||
assert self.checker.is_reachable("elite_cs66", arm_pose, target)
|
||
|
||
def test_unknown_arm_uses_default(self):
|
||
"""未知型号使用 1.0m 回退臂展(realistic lab-scale default)。"""
|
||
arm_pose = {"x": 0.0, "y": 0.0, "theta": 0.0}
|
||
# Within 1.0m fallback reach
|
||
target_near = {"x": 0.8, "y": 0.0, "z": 0.0}
|
||
assert self.checker.is_reachable("unknown_arm", arm_pose, target_near)
|
||
# Beyond 1.0m fallback reach
|
||
target_far = {"x": 1.5, "y": 0.0, "z": 0.0}
|
||
assert not self.checker.is_reachable("unknown_arm", arm_pose, target_far)
|
||
|
||
def test_custom_arm_reach(self):
|
||
"""自定义臂展参数。"""
|
||
checker = MockReachabilityChecker(arm_reach={"custom_arm": 1.5})
|
||
arm_pose = {"x": 0.0, "y": 0.0, "theta": 0.0}
|
||
target = {"x": 1.4, "y": 0.0, "z": 0.0}
|
||
assert checker.is_reachable("custom_arm", arm_pose, target)
|