mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-05-24 23:40:00 +00:00
chore(DE): add debug mode and detailed log regarding cost changes
This commit is contained in:
@@ -20,6 +20,8 @@ from .obb import (
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any
|
||||
|
||||
from .interfaces import CollisionChecker, ReachabilityChecker
|
||||
|
||||
# 归一化默认权重 — 1cm距离违规 ≈ 5°角度违规 的惩罚量级
|
||||
@@ -482,6 +484,98 @@ def _opening_surface_center(
|
||||
return (world_x, world_y)
|
||||
|
||||
|
||||
def evaluate_default_hard_constraints_breakdown(
|
||||
devices: list[Device],
|
||||
placements: list[Placement],
|
||||
lab: Lab,
|
||||
collision_checker: CollisionChecker,
|
||||
*,
|
||||
collision_weight: float = DEFAULT_WEIGHT_DISTANCE * HARD_MULTIPLIER,
|
||||
boundary_weight: float = DEFAULT_WEIGHT_DISTANCE * HARD_MULTIPLIER,
|
||||
) -> dict[str, float]:
|
||||
"""与 evaluate_default_hard_constraints 逻辑相同,但返回分项明细。"""
|
||||
device_map = {d.id: d for d in devices}
|
||||
collision_cost = 0.0
|
||||
boundary_cost = 0.0
|
||||
|
||||
candidate_pairs = sweep_and_prune_pairs(devices, placements)
|
||||
for i, j in candidate_pairs:
|
||||
di, dj = device_map[placements[i].device_id], device_map[placements[j].device_id]
|
||||
ci = obb_corners(placements[i].x, placements[i].y,
|
||||
di.bbox[0], di.bbox[1], placements[i].theta)
|
||||
cj = obb_corners(placements[j].x, placements[j].y,
|
||||
dj.bbox[0], dj.bbox[1], placements[j].theta)
|
||||
depth = obb_penetration_depth(ci, cj)
|
||||
if depth > 0:
|
||||
collision_cost += collision_weight * depth
|
||||
|
||||
for p in placements:
|
||||
dev = device_map[p.device_id]
|
||||
hw, hd = p.rotated_bbox(dev)
|
||||
overshoot = 0.0
|
||||
overshoot += max(0.0, hw - p.x)
|
||||
overshoot += max(0.0, (p.x + hw) - lab.width)
|
||||
overshoot += max(0.0, hd - p.y)
|
||||
overshoot += max(0.0, (p.y + hd) - lab.depth)
|
||||
boundary_cost += boundary_weight * overshoot
|
||||
|
||||
return {
|
||||
"collision": collision_cost,
|
||||
"boundary": boundary_cost,
|
||||
"total": collision_cost + boundary_cost,
|
||||
"collision_weight": collision_weight,
|
||||
"boundary_weight": boundary_weight,
|
||||
}
|
||||
|
||||
|
||||
def evaluate_constraints_breakdown(
|
||||
devices: list[Device],
|
||||
placements: list[Placement],
|
||||
lab: Lab,
|
||||
constraints: list[Constraint],
|
||||
collision_checker: CollisionChecker,
|
||||
reachability_checker: ReachabilityChecker | None = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""与 evaluate_constraints 逻辑相同,但返回每条约束的分项明细。"""
|
||||
device_map = {d.id: d for d in devices}
|
||||
placement_map = {p.device_id: p for p in placements}
|
||||
|
||||
results = []
|
||||
for c in constraints:
|
||||
cost = _evaluate_single(
|
||||
c, device_map, placement_map, lab, collision_checker, reachability_checker,
|
||||
graduated=True,
|
||||
)
|
||||
ew = c.weight
|
||||
if c.priority and c.priority in PRIORITY_MULTIPLIERS:
|
||||
ew *= PRIORITY_MULTIPLIERS[c.priority]
|
||||
results.append({
|
||||
"name": _constraint_display_name(c),
|
||||
"rule": c.rule_name,
|
||||
"type": c.type,
|
||||
"cost": cost,
|
||||
"weight": ew,
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
def _constraint_display_name(c: Constraint) -> str:
|
||||
"""为约束生成可读的显示名称。"""
|
||||
params = c.params
|
||||
if c.rule_name in (
|
||||
"distance_less_than", "distance_greater_than",
|
||||
"minimize_distance", "maximize_distance",
|
||||
):
|
||||
return f"{c.rule_name}({params.get('device_a', '?')}, {params.get('device_b', '?')})"
|
||||
if c.rule_name == "reachability":
|
||||
return f"reachability({params.get('arm_id', '?')}, {params.get('target_device_id', '?')})"
|
||||
if c.rule_name == "min_spacing":
|
||||
return f"min_spacing(gap={params.get('min_gap', '?')})"
|
||||
if c.rule_name == "prefer_orientation_mode":
|
||||
return f"prefer_orientation_mode({params.get('mode', '?')})"
|
||||
return c.rule_name
|
||||
|
||||
|
||||
def _line_of_sight_penalty(
|
||||
arm_id: str,
|
||||
arm_p: Placement,
|
||||
|
||||
Reference in New Issue
Block a user