f3a8a38acd
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (pull_request) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
集成同源 BYOK 会话隔离、精简模型设置、官方订阅入口和 HTTPS 发布运维;保留本地训练/调参与控制能力。同步 npm 版本及 CHANGELOG,记录公网真实 API 验收仍待用户凭据。
94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
"""Dependency-free task registry and bounded PPO settings."""
|
||
|
||
import json
|
||
import math
|
||
import re
|
||
from pathlib import Path
|
||
|
||
CONTRACTS = Path(__file__).resolve().parents[2] / "contracts"
|
||
TASK = json.loads((CONTRACTS / "mobile-manipulator-v2.json").read_text())
|
||
ROBOTS = {r["id"]: r for r in json.loads((CONTRACTS / "mobile-robots-v1.json").read_text())}
|
||
MOBILE_TASKS = {
|
||
"MobileManipulator-LeKiwi-v1": "lekiwi-v1",
|
||
"MobileManipulator-LeKiwi-Bundle": "lekiwi-bundle",
|
||
}
|
||
|
||
|
||
def validate_mobile_params(value):
|
||
if not isinstance(value, dict) or value.keys() - {
|
||
"rolloutSteps",
|
||
"objectPosition",
|
||
"goalPosition",
|
||
"stage",
|
||
"sourceJobId",
|
||
"positionJitter",
|
||
"evaluationEpisodes",
|
||
"navigationBootstrapSteps",
|
||
}:
|
||
raise ValueError("mobileParams 包含未知参数")
|
||
steps = value.get("rolloutSteps", 128)
|
||
if isinstance(steps, bool) or not isinstance(steps, int) or not 8 <= steps <= 4096:
|
||
raise ValueError("rolloutSteps 必须为 8–4096 的整数")
|
||
stage = value.get("stage", "navigate")
|
||
if stage not in ("navigate", "reach", "pick-place"):
|
||
raise ValueError("stage 必须为 navigate / reach / pick-place")
|
||
source = value.get("sourceJobId")
|
||
if source is not None and (
|
||
not isinstance(source, str) or not re.fullmatch(r"[0-9a-f]{32}", source)
|
||
):
|
||
raise ValueError("sourceJobId 必须是服务内的训练作业 ID,不接受 checkpoint 路径")
|
||
jitter = value.get("positionJitter", 0.1)
|
||
if isinstance(jitter, bool) or not isinstance(jitter, (int, float)) or not 0 <= jitter <= 0.3:
|
||
raise ValueError("positionJitter 必须在 0–0.3 m 内")
|
||
episodes = value.get("evaluationEpisodes", 10)
|
||
if isinstance(episodes, bool) or not isinstance(episodes, int) or not 2 <= episodes <= 64:
|
||
raise ValueError("evaluationEpisodes 必须为 2–64 的整数")
|
||
bootstrap = value.get("navigationBootstrapSteps", 4096)
|
||
if isinstance(bootstrap, bool) or not isinstance(bootstrap, int) or not 0 <= bootstrap <= 10000:
|
||
raise ValueError("navigationBootstrapSteps 必须为 0–10000 的整数")
|
||
result = {
|
||
"navigationBootstrapSteps": bootstrap,
|
||
"rolloutSteps": steps,
|
||
"stage": stage,
|
||
"positionJitter": jitter,
|
||
"evaluationEpisodes": episodes,
|
||
}
|
||
if source is not None:
|
||
result["sourceJobId"] = source
|
||
for key, default in (
|
||
("objectPosition", TASK["objectStart"]),
|
||
("goalPosition", TASK["goalStart"]),
|
||
):
|
||
position = value.get(key, default)
|
||
if (
|
||
not isinstance(position, list)
|
||
or len(position) != 3
|
||
or any(
|
||
isinstance(x, bool)
|
||
or not isinstance(x, (float, int))
|
||
or not -TASK["positionScale"] <= x <= TASK["positionScale"]
|
||
or not math.isfinite(x)
|
||
for x in position
|
||
)
|
||
or position[2] < TASK["objectStart"][2]
|
||
):
|
||
raise ValueError(f"{key} 必须是任务范围内的三维坐标,z 不低于物体半高")
|
||
result[key] = position.copy()
|
||
return result
|
||
|
||
|
||
def mobile_metadata(task_id):
|
||
return {
|
||
"id": task_id,
|
||
"name": f"移动操作 · {ROBOTS[MOBILE_TASKS[task_id]]['label']}",
|
||
"family": "mobile-manipulator",
|
||
"robotId": MOBILE_TASKS[task_id],
|
||
"browserCompatible": True,
|
||
"terrainPresets": [],
|
||
"terrainParameters": {},
|
||
"sensorTypes": [],
|
||
"sensorParameters": {},
|
||
"mapSyncScope": "当前移动操作场景快照",
|
||
"controlDt": TASK["controlDt"],
|
||
}
|