Files
Uni-Lab-OS/unilabos/layout_optimizer/models.py
yexiaozhou 9ef24b7768 feat(layout_optimizer): DE optimizer V2 — custom loop, graduated hard constraints, broad phase
Replace scipy differential_evolution with custom DE loop for per-device
crossover, circular θ wrapping, and configurable mutation strategy
(currenttobest1bin default, best1bin as turbo mode).

Key improvements:
- Graduate ALL hard constraints during DE (proportional penalty instead of
  flat inf), giving DE smooth gradient for reachability, min_spacing, etc.
  Binary inf preserved for final pass/fail reporting.
- 2-axis sweep-and-prune AABB broad phase for collision pair pruning
- Multi-seed injection from multiple seeder presets + Gaussian variants
- snap_theta_safe: collision-check after angle snapping, revert on violation
- Weight normalization (100 distance / 60 angle / 5× hard multiplier)
- Constraint priority field (critical/high/normal/low → weight multiplier)
  with LLM intent interpreter setting priority per constraint type
- Final success field now checks user hard constraints in binary mode
- arm_slider added to mock checker reach table (1.07m)

Tests: 202 passed, 24 new tests added (optimizer 7, constraints 6, broad_phase 11)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 00:32:34 +08:00

98 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""数据模型定义Device, Lab, Placement, Constraint 及 API 请求/响应。"""
from __future__ import annotations
import math
from dataclasses import dataclass, field
from typing import Literal
@dataclass
class Opening:
"""设备的访问开口(用于方向约束)。"""
# 设备局部坐标系中的方向单位向量,如 (0, -1) = 正前方
direction: tuple[float, float] = (0.0, -1.0)
label: str = ""
@dataclass
class Device:
"""设备描述。"""
id: str
name: str
# 碰撞包围盒 (width along X, depth along Y),单位:米
bbox: tuple[float, float] = (0.6, 0.4)
device_type: Literal["static", "articulation", "rigid"] = "static"
# 以下为可选扩展字段(向后兼容)
height: float = 0.4
origin_offset: tuple[float, float] = (0.0, 0.0)
openings: list[Opening] = field(default_factory=list)
source: Literal["registry", "assets", "manual"] = "manual"
model_path: str = ""
model_type: str = ""
thumbnail_url: str = ""
@dataclass
class Obstacle:
"""实验室内固定障碍物(矩形)。"""
x: float
y: float
width: float
depth: float
@dataclass
class Lab:
"""实验室平面图。"""
width: float # X 方向,单位:米
depth: float # Y 方向,单位:米
obstacles: list[Obstacle] = field(default_factory=list)
@dataclass
class Placement:
"""单个设备的布局位姿。"""
device_id: str
x: float
y: float
theta: float # 旋转角,弧度
uuid: str = "" # 前端分配的唯一标识,透传不生成
def rotated_bbox(self, device: Device) -> tuple[float, float]:
"""返回旋转后的 AABB 尺寸 (half_w, half_h)。"""
w, d = device.bbox
cos_t = abs(math.cos(self.theta))
sin_t = abs(math.sin(self.theta))
half_w = (w * cos_t + d * sin_t) / 2
half_h = (w * sin_t + d * cos_t) / 2
return half_w, half_h
@dataclass
class Constraint:
"""约束规则。"""
type: Literal["hard", "soft"]
rule_name: str
# 规则参数,含义因 rule_name 而异
params: dict = field(default_factory=dict)
# 仅 soft 约束使用
weight: float = 1.0
# 优先级等级,影响有效权重的乘数
priority: str | None = None # "critical" | "high" | "normal" | "low"
@dataclass
class Intent:
"""LLM 可生成的语义化意图,由 interpreter 翻译为 Constraint 列表。"""
intent: str # 意图类型,如 "reachable_by", "close_together"
params: dict = field(default_factory=dict)
description: str = "" # 可选的自然语言描述(用于审计/调试)