Files
Mujoco_WASM/training_server/mobile_manipulator/config.py
T
chenlin 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
feat: release v1.0.1 CADWorld 网站与 LeKiwi 智能抓放
集成同源 BYOK 会话隔离、精简模型设置、官方订阅入口和 HTTPS 发布运维;保留本地训练/调参与控制能力。同步 npm 版本及 CHANGELOG,记录公网真实 API 验收仍待用户凭据。
2026-09-24 09:57:41 +08:00

94 lines
3.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.
"""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"],
}