feat: 更新右臂和左臂配置,禁用奇异性避免;增强ACT数据采集功能,添加日志记录

This commit is contained in:
2026-08-13 16:58:54 +08:00
parent 2d89fe1820
commit 94e1bf9467
7 changed files with 102 additions and 14 deletions
+26 -7
View File
@@ -616,14 +616,19 @@ class _StatusPublisher:
class _Logger:
def info(self, *_args, **_kwargs):
pass
def __init__(self):
self.infos = []
self.warnings = []
self.errors = []
def warn(self, *_args, **_kwargs):
pass
def info(self, message, *_args, **_kwargs):
self.infos.append(message)
def error(self, *_args, **_kwargs):
pass
def warn(self, message, *_args, **_kwargs):
self.warnings.append(message)
def error(self, message, *_args, **_kwargs):
self.errors.append(message)
def _control_message(seq, control_ns, *, grip=True):
@@ -705,7 +710,8 @@ def _recorder_for_test(tmp_path):
recorder._status_pub = _StatusPublisher()
recorder._now_ns = lambda: now_ns
recorder._disk_usage = lambda _path: SimpleNamespace(free=5 * 1024**3)
recorder.get_logger = lambda: _Logger()
recorder._logger = _Logger()
recorder.get_logger = lambda: recorder._logger
return recorder
@@ -776,6 +782,14 @@ def test_end_to_end_fake_episode_saves_and_returns_idle(tmp_path):
assert recorder.state is RecordingState.IDLE
assert "SAVING" in recorder._status_pub.messages
assert recorder._status_pub.messages[-2:] == ["SAVED", "IDLE"]
assert any(
"ACT录制状态:RECORDINGepisode_0" in message
for message in recorder._logger.infos
)
assert any(
"episode_0.hdf53 samples" in message
for message in recorder._logger.infos
)
@requires_h5py
@@ -791,6 +805,11 @@ def test_final_publish_rejects_allocated_episode_number_conflict(tmp_path):
assert not (recorder._task_dir / "episode_1.hdf5").exists()
rejected = list((recorder._task_dir / "rejected").glob("*.hdf5"))
assert len(rejected) == 1
assert any(
str(rejected[0]) in message
and "原因:episode_number_conflict" in message
for message in recorder._logger.warnings
)
with h5py.File(rejected[0], "r") as root:
assert root.attrs["reject_reason"] == "episode_number_conflict"
@@ -1142,6 +1142,13 @@ class ActEpisodeRecorder(Node):
self._on_left_controller,
10,
)
self.get_logger().info(
f"ACT录制器已启动,输出目录:{self._task_dir}"
)
self.get_logger().info(
"操作提示:右手B准备/结束录制;准备后握住右手Grip开始采样;"
"左手Y长按1秒丢弃;录制中不要按右手A"
)
self._publish_state(RecordingState.IDLE)
@staticmethod
@@ -1182,6 +1189,12 @@ class ActEpisodeRecorder(Node):
message = String()
message.data = state.value if not reason else f"{state.value}:{reason}"
self._status_pub.publish(message)
episode = (
f"episode_{self._episode_index}"
if self._episode_index is not None
else ""
)
self.get_logger().info(f"ACT录制状态:{message.data}{episode}")
def _run_preflight(self) -> str | None:
now_ns = self._now_ns()
@@ -1557,6 +1570,10 @@ class ActEpisodeRecorder(Node):
except FileExistsError:
self._reject_closed_partial("episode_number_conflict")
return
self.get_logger().info(
f"ACT数据已保存:{destination}"
f"{report.metrics['sample_count']} samples"
)
self._finish_result(RecordingState.SAVED)
def _reject_closed_partial(
@@ -1570,6 +1587,7 @@ class ActEpisodeRecorder(Node):
root.attrs["episode_status"] = "rejected"
root.attrs["reject_reason"] = reason
root.attrs["interrupted"] = np.bool_(interrupted)
sample_count = int(root["action"].shape[0])
rejected = self._task_dir / "rejected"
rejected.mkdir(exist_ok=True)
safe_reason = re.sub(r"[^a-zA-Z0-9_-]", "_", reason)
@@ -1581,6 +1599,10 @@ class ActEpisodeRecorder(Node):
/ f"episode_{episode_index}_{safe_reason}_{timestamp}.hdf5"
)
publish_without_overwrite(self._partial_path, destination)
self.get_logger().warn(
f"ACT数据已拒绝:{destination}{sample_count} samples"
f"原因:{reason}"
)
self._finish_result(RecordingState.REJECTED, reason)
def _reject_current(
@@ -1609,11 +1631,16 @@ class ActEpisodeRecorder(Node):
def _discard_current(self) -> None:
if self._partial_path is None:
return
partial = self._partial_path
if self._writer is not None:
self._writer.finish()
sample_count = self._store.count if self._store is not None else 0
if self._store is not None:
self._store.close()
discard_partial(self._partial_path)
discard_partial(partial)
self.get_logger().info(
f"ACT数据已丢弃:{partial}{sample_count} samples"
)
self._finish_result(RecordingState.DISCARDED)
def _finish_result(