feat(actpred): 固定范围归一化 + 逐列加权损失,附 pybot*actpred 审计修复
新增 action_norm.py 固定物理范围 min-max 归一化,无 stats 文件零状态部署;train.py 引入 _weighted_mse 逐列加权(舵机 1.0/spk 0.2/保留位 0.0)。审计修复: P0-1 pybot 读 normalize_mode, R2-1 NaN 阻断 best, P2-a 后端大小写不敏感, P1-3 utf-8-sig BOM, R1-1 data_dir 覆盖, R2-7 _run.sh 报错, R2-3 空 val 存 best.pt, P2-a2 误报警, R1-2 合并 parse_action_history, P2-b 合并 numpy import。评估修正 oob 物理界+error_growth 按 episode 中位数。推理部署全链支持 fixed_range。
This commit is contained in:
1 parent
adfa61aa3c
commit
85bf68f505
19 files changed
+874
-167
No files matched your search
+11
-2
@@ -141,7 +141,8 @@ def _load_predictor(checkpoint):
|
||||
"""
|
||||
import os
|
||||
try:
|
||||
if checkpoint.endswith(".hef"):
|
||||
lower = checkpoint.lower()
|
||||
if lower.endswith(".hef"):
|
||||
from actpred.hailo_infer import HailoPredictor as _P
|
||||
hef_dir = os.path.dirname(os.path.abspath(checkpoint))
|
||||
|
||||
@@ -160,6 +161,7 @@ def _load_predictor(checkpoint):
|
||||
|
||||
# predict_delta:从同目录 checkpoint 读取训练配置
|
||||
predict_delta = False
|
||||
normalize_mode = "zscore"
|
||||
for name in ("pretrain_best_inference.pt",
|
||||
"best_inference.pt"):
|
||||
ckpt_path = os.path.join(hef_dir, name)
|
||||
@@ -171,9 +173,15 @@ def _load_predictor(checkpoint):
|
||||
cfg = ck.get("config", {})
|
||||
predict_delta = cfg.get("train", {}).get(
|
||||
"predict_delta", False)
|
||||
normalize_mode = cfg.get("data", {}).get(
|
||||
"normalize_mode", "zscore")
|
||||
if predict_delta:
|
||||
logging.info(
|
||||
"predict_delta=True from %s", ckpt_path)
|
||||
if normalize_mode != "zscore":
|
||||
logging.info(
|
||||
"normalize_mode=%s from %s",
|
||||
normalize_mode, ckpt_path)
|
||||
except Exception:
|
||||
pass
|
||||
break
|
||||
@@ -181,7 +189,8 @@ def _load_predictor(checkpoint):
|
||||
try:
|
||||
return _P(checkpoint, stats_path=stats_path,
|
||||
predict_delta=predict_delta,
|
||||
pred_token=pred_token)
|
||||
pred_token=pred_token,
|
||||
normalize_mode=normalize_mode)
|
||||
except Exception:
|
||||
logging.exception("加载 HEF 推理器失败: %s", checkpoint)
|
||||
return None
|
||||
|
||||
@@ -14,6 +14,8 @@ import logging
|
||||
import time
|
||||
from collections import deque
|
||||
|
||||
import numpy as np
|
||||
|
||||
from . import plugin
|
||||
from .hardvars import (AliasVar, CameraVar, I2CVar, MPU6050Var, ServoVar,
|
||||
SpeakerVar)
|
||||
@@ -238,7 +240,6 @@ class RobotStruct:
|
||||
(会阻塞 ~1/framerate 秒,把 hz 锁死在相机帧率之下)。dry_run
|
||||
或队列空时回退 _grab_frame 合成帧。
|
||||
"""
|
||||
import numpy as np
|
||||
camera_var = self._find_camera()
|
||||
if camera_var is None and not self.dry_run:
|
||||
raise ValueError("闭环需要 @Camera 块(或使用 --dry-run 生成合成帧)")
|
||||
@@ -357,7 +358,6 @@ class RobotStruct:
|
||||
帧源与主循环一致(_loop_recent_frames),避免冷启动与运行期帧源
|
||||
不一致。history 用零向量填满是保守初值,避免随机姿态跳变损坏舵机。
|
||||
"""
|
||||
import numpy as np
|
||||
frames.extend(self._loop_recent_frames(camera_var, frames.maxlen))
|
||||
for _ in range(history.maxlen):
|
||||
history.append(np.zeros(action_dim, dtype=np.float32))
|
||||
@@ -368,7 +368,6 @@ class RobotStruct:
|
||||
SpeakerVar.play 强制要求 delay 键;闭环已剔除 delay 列(避免发声
|
||||
阻塞卡死节拍),此处为其补 delay=0 —— 发声但不阻塞,节拍由 hz 掌控。
|
||||
"""
|
||||
import numpy as np
|
||||
if not np.all(np.isfinite(act)):
|
||||
log.warning("action vector contains NaN/Inf, skipping step")
|
||||
return
|
||||
@@ -442,7 +441,6 @@ class RobotStruct:
|
||||
"""
|
||||
if not imu_vars:
|
||||
return None
|
||||
import numpy as np
|
||||
nk = len(IMU_KEYS)
|
||||
out = np.zeros(nk * len(imu_vars), dtype=np.float32)
|
||||
for k, var in enumerate(imu_vars):
|
||||
@@ -458,7 +456,6 @@ class RobotStruct:
|
||||
@staticmethod
|
||||
def _pad_imu(vec, imu_dim):
|
||||
"""拼接姿态向量 → pad/truncate 到 predictor 的 imu_dim(None → 全零)"""
|
||||
import numpy as np
|
||||
out = np.zeros(imu_dim, dtype=np.float32)
|
||||
if vec is not None:
|
||||
src = np.asarray(vec, dtype=np.float32).ravel()
|
||||
@@ -468,7 +465,6 @@ class RobotStruct:
|
||||
|
||||
def _build_action_vec(self, vals, action_dim):
|
||||
"""从值列表构建动作向量,pad/truncate 到 action_dim"""
|
||||
import numpy as np
|
||||
vec = np.zeros(action_dim, dtype=np.float32)
|
||||
n = min(len(vals), action_dim)
|
||||
vec[:n] = vals[:n]
|
||||
@@ -476,7 +472,6 @@ class RobotStruct:
|
||||
|
||||
def _grab_frame(self, camera_var):
|
||||
"""从摄像头同步抓一帧;dry_run 或无帧时返回随机噪声帧"""
|
||||
import numpy as np
|
||||
if camera_var is not None:
|
||||
frame = camera_var.camera.capture()
|
||||
if frame is not None:
|
||||
|
||||
Reference in new issue
Block a user