feat(training): release V0.9.1 避障训练与基础策略迁移
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled

This commit is contained in:
2026-09-08 10:50:13 +08:00
parent fa5485049a
commit 438e56bcc8
113 changed files with 15027 additions and 539 deletions
+72 -2
View File
@@ -10,6 +10,7 @@ from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from server import ( # noqa: E402
DEFAULT_TASKS,
MAX_JOBS,
ApiError,
TrainingJob,
@@ -79,6 +80,70 @@ out.write_bytes(b'onnx')
with self.assertRaises(ApiError):
self.manager.parse_config(self.payload(wandbMode="login"))
def test_custom_task_metadata_and_validated_deployment(self):
self.manager.tasks = DEFAULT_TASKS
health = self.manager.health()
task = next(
item for item in health["taskMetadata"] if item["id"] == "Unitree-Go2-ObstacleAvoidance"
)
self.assertEqual(task["sensorTypes"], ["raycast"])
config = self.manager.parse_config(
self.payload(
taskId=task["id"],
terrainPreset="discrete_obstacles",
terrainParams={"obstacle_count": 8, "friction": 0.9},
sensorCfg={"type": "raycast", "fov": 100, "maxDistance": 5},
)
)
self.assertEqual(config.deployment["observationSize"], 81)
self.assertEqual(config.deployment["terrain"]["actualObstacleCount"], 8)
self.assertEqual(config.deployment["sensorCfg"]["rayCount"], 32)
self.assertEqual(
TrainingJob(id="a" * 32, config=config).public()["deployment"], config.deployment
)
rough = self.manager.parse_config(self.payload(taskId="Unitree-Go2-Rough"))
self.assertFalse(rough.deployment["browserCompatible"])
self.assertEqual(rough.deployment["observationSize"], 234)
def test_custom_config_rejects_unknown_nonfinite_and_incompatible_fields(self):
self.manager.tasks = DEFAULT_TASKS
for fields in (
{"terrainPreset": "plane;touch /tmp/injected"},
{"terrainPreset": ["plane"]},
{"terrainParams": {"friction": float("nan")}},
{"terrainParams": {"size": float("inf")}},
{"terrainParams": {"size": 10**400}},
{"terrainParams": {"obstacle_count": True}},
{"terrainParams": {"obstacle_count": 1.5}},
{"terrainParams": {"mjcf": "<include/>"}},
{"terrainParams": {"obstacle_height_min": 1, "obstacle_height_max": 0.1}},
{"sensorCfg": {"rayCount": 1_000_000}},
{"sensorCfg": {"maxDistance": 1, "safetyDistance": 1}},
{"sensorCfg": {"fov": float("nan")}},
{"sensorType": "camera_depth"},
{"rewardPresetId": "a" * 32},
):
with self.subTest(fields=fields), self.assertRaises(ApiError):
self.manager.parse_config(
self.payload(taskId="Unitree-Go2-ObstacleAvoidance", **fields)
)
with self.assertRaises(ApiError):
self.manager.parse_config(self.payload(sensorCfg={"fov": 90}))
def test_custom_job_passes_server_owned_json_file_without_shell(self):
self.manager.tasks = DEFAULT_TASKS
config = self.manager.parse_config(self.payload(taskId="Unitree-Go2-ObstacleAvoidance"))
job = TrainingJob(id="b" * 32, config=config)
self.manager.jobs[job.id] = job
with patch("server.subprocess.Popen", wraps=subprocess.Popen) as popen:
self.manager._run(job)
args = popen.call_args.args[0]
self.assertNotIn("shell", popen.call_args.kwargs)
path = Path(args[args.index("--task-config") + 1])
self.assertTrue(path.is_relative_to(self.root))
self.assertEqual(json.loads(path.read_text()), config.task_config)
self.assertEqual(job.state, "succeeded")
def test_builds_argument_array_without_shell(self):
config = self.manager.parse_config(self.payload(device="gpu", gpuIds=[0, 2]))
command = self.manager.command_for(config)
@@ -89,8 +154,13 @@ out.write_bytes(b'onnx')
def test_resolves_reward_preset_to_inline_validated_trainer_argument(self):
preset_id = "f" * 32
reward_config = {"weights": {"pose": 1.2}, "params": {}}
self.manager.preset_resolver = lambda value: reward_config if value == preset_id else None
from tuning.schema import base_configuration
reward_config = base_configuration()
reward_config["weights"]["pose"] = 1.2
self.manager.preset_resolver = lambda value, task: (
reward_config if value == preset_id and task == "Unitree-Go2-Flat" else None
)
config = self.manager.parse_config(self.payload(rewardPresetId=preset_id))
command = self.manager.command_for(config)
index = command.index("--reward-config-json")