feat(train): freeze canonical V5 training release

Publish the unified scratch-training contract, compact best-policy bundles, calibrated targets, tests, and presentation-ready retained evidence while excluding archived and intermediate run payloads.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Frank14f
2026-08-04 14:12:27 +08:00
co-authored by Cursor
parent 01645f8d1c
commit b702fb7300
164 changed files with 4217 additions and 1091 deletions
+12 -8
View File
@@ -1,4 +1,8 @@
# Cross-Re Transfer Learning — 分析报告
# Cross-Re Transfer Learning — 历史归档分析
> **历史归档 / ARCHIVED TRANSFER ANALYSIS**
> 本文记录 2026-07-07 的 `_tr` 迁移实验及当时诊断,保留其科学内容作为历史记录;它不描述当前 active scratch workflow,不是模型发布或当前验证声明。当前规范见 `README.md`、`TRAIN_PIPELINE.md` 与 `DESIGN_DECISIONS.md`。
> 当前归档位置:迁移脚本在 `archive/scripts_deprecated/crossre_transfer.sh`;保留的 `_tr` 日志、校准副本和 metadata 在 `archive/transfer_instability/kar_re*_tr_seed*/`;旧校准在 `archive/calibrations_deprecated/`。active case ID 不带 `_sc``_tr` 仅用于这些历史归档。
> 日期: 2026-07-07
> 实验: 改变上游来流雷诺数 (Re60 / Re200 / Re400),从 re100 baseline 进行 transfer learning
@@ -372,17 +376,17 @@ Case: d075 re100 re200 re60 re400
| Base 训练脚本 | `train_karman.py` |
| 环境 | `env_karman.py` |
| 校准脚本 | `calibrate.py`SIM_BP generic 兜底逻辑在第 526527 行) |
| Base transfer 启动 | `crossre_transfer.sh`lr=3e-4, n_epochs=10 |
| Base transfer 启动 | `archive/scripts_deprecated/crossre_transfer.sh`lr=3e-4, n_epochs=10 |
| Extend transfer 启动 | `extend_transfer.sh`lr=1e-4, n_epochs=5, 保守超参) |
| Ext2 transfer 启动 | `ext2_transfer.sh`(同上,重新 fine-tune |
| Multi-GPU launcher | `launch_multi.sh`lr=3e-4, n_epochs=10 |
| 对比分析 | `VARDIST_ANALYSIS.md` |
| re60 校准 | `calibrations/re60/calibration.json`SIM_BP gap=0.20, K_CD=12 |
| re60 校准 | `archive/transfer_instability/kar_re60_tr_seed43/calibration.json`SIM_BP gap=0.20, K_CD=12 |
| re100 校准 | `calibrations/re100/calibration.json`SIM_BP gap=0.50, K_CD=50 |
| re200 校准 | `calibrations/re200/calibration.json`SIM_BP gap=0.32, K_CD=12 |
| re400 校准 | `calibrations/re400/calibration.json`SIM_BP gap=0.17, K_CD=12 |
| re60 base 日志 | `output/transfer_re60_seed43/train.log` |
| re200 base 日志 | `output/transfer_re200_seed43/train.log` |
| re200 校准 | `archive/transfer_instability/kar_re200_tr_seed43/calibration.json`SIM_BP gap=0.32, K_CD=12 |
| re400 校准 | `archive/transfer_instability/kar_re400_tr_seed43/calibration.json`SIM_BP gap=0.17, K_CD=12 |
| re60 base 日志 | `archive/transfer_instability/kar_re60_tr_seed43/train.log` |
| re200 base 日志 | `archive/transfer_instability/kar_re200_tr_seed43/train.log` |
| re200 ext 日志 | `output/transfer_re200ext_seed43/train.log`0.535→0.244 退化) |
| re400 base 日志 | `output/transfer_re400_seed43/train.log`Ep28 0.553→0.264 退化) |
| re400 base 日志 | `archive/transfer_instability/kar_re400_tr_seed43/train.log`Ep28 0.553→0.264 退化) |
| re400 ext2 日志 | `output/transfer_re400ext2_seed43/train.log` |
+19
View File
@@ -0,0 +1,19 @@
# Training design decisions and compatibility notes
These are current contracts and historical caveats for `src/drl_pinball/train`.
- **Shared train/eval environment is intentional.** Each outer iteration evaluates on the same wrapped CFD environment used for learning. Reset/rollout changes environment state and updates `VecNormalize` observation statistics while it remains in training mode. Evaluation does not update policy weights.
- **Outer “episode” means PPO chunk.** One logged episode is normally `learn(2048)` plus a 360-step evaluation. The Gym environments do not terminate; this is not a Gym episode.
- **Symmetry is default-off.** The wrapper remains available for explicit experiments. Historical runs configured with 0.5 were effectively unaugmented because each learn/eval reset restarted the rollout counter before mirrored training transitions could occur; do not label those models symmetry-augmented.
- **Force aggregation differs by scene.** Karman averages the three controlled-cylinder drag/lift values and targets zero. Illusion sums all three and matches one target cylinder.
- **Calibration stages are sorted by quality.** Stage0 is zero rotation and Stage1 is reference open loop. Their DTW scores are sorted into worst/better anchors; stage number does not imply quality.
- **Reward constants differ.** Karman uses `K_CD/K_CL=50/100`; Illusion uses `12/25` because summed three-cylinder forces are compared with one target-cylinder force.
- **`target_diam` is a historical misnomer.** The value is passed to CelerisLab as `radius = target_diam * L0`. With `L0=20`, ratios 0.75/1/1.5/2 mean radii 15/20/30/40 and physical diameters 30/40/60/80 lattice units.
- **Normalization has three layers.** CelerisLab first area/time-averages sensor observations, the env divides force/sensor channels by calibrated `FORCE_SCALE`/`SENS_SCALE`, and `VecNormalize` learns running observation statistics online. Schema-less legacy artifacts inserted `SENSOR_CC=78` before pre-scaling; active native-v2 artifacts do not. The six-channel `legacy-policy-v1` path remains only to load an existing legacy model with its matching normalizer.
- **`VecNormalize` is online, not calibration.** Phase 0 produces fixed physical/reward scales. SB3 running moments evolve through both learning and shared-env evaluation.
- **Best policy requires best normalizer.** `best_model.zip` is incomplete without `best_vecnormalize.pkl` saved at that same best-selection point. Freeze it for inference.
- **Resume is continuation, not exact replay.** It restores a policy checkpoint and matching normalizer, then constructs a new process/environment. It does not promise exact optimizer, RNG, CFD, wrapper, or trajectory state recovery.
- **Seeds distinguish runs, not deterministic replay.** GPU CFD, CUDA/PyTorch behavior, PPO sampling, and process state prevent that stronger guarantee.
- **Targets must be calibrated artifacts.** Keep `target.npy` (and Illusion harmonics) matched to case, config, geometry, SI, force convention, and `calibration.json`.
- **Result retention is historical.** Checked-in summaries/figures support prior writing. They are not model-release artifacts and do not establish full validation or retraining after code changes.
- **Release boundary.** The release provides code, configs, canonical targets, and compact retained best-policy bundles. Each policy is paired with its same-selection normalizer and provenance; intermediate/final checkpoints, logs, TensorBoard streams, and archived failed runs are excluded.
+47 -197
View File
@@ -1,221 +1,71 @@
# DynamisLab — 训练目录
# DRL pinball training quickstart
> `src/drl_pinball/train/`
> 更新: 2026-07-12
This directory contains the source workflow for calibrating and training the modern 2000×600 CelerisLab PPO environments. The release reproduces the workflow and includes compact best-policy bundles for the retained active runs. Existing `results/` material remains historical support for prior writing and is not a claim that current code has been fully retrained or validated.
---
## Setup
## 命名规则
```
{domain}_{variant}_{method}_seed{seed}
domain: kar = Karman | ill = Illusion
variant: re{60,100,200,400} = 雷诺数 | d{075,15,2} = 扰动圆柱直径(L) | {1L,15L,075L,2L} = 目标圆柱尺寸
method: sc = from scratch | tr = transfer from re100 baseline
seed: seed{41..45}
Examples:
kar_re100_sc_seed45 — Karman Re100 scratch, seed 45
kar_d075_sc_seed44 — Karman 0.75L dist-cyl scratch
kar_d15_tr_seed45 — Karman 1.5L dist-cyl transfer
kar_re200_sc_seed43 — Karman Re200 scratch
ill_2L_sc_seed43 — Illusion 2L target scratch
```
---
## 目录结构
```
train/
├── train_karman.py # Karman PPO 训练 (核心)
├── train_illusion.py # Illusion PPO 训练
├── calibrate.py # Phase 0 校准
├── env_karman.py # Karman 环境
├── env_illusion.py # Illusion 环境
├── symmetry_wrapper.py # G-symmetry 数据增强
├── scripts/ # 启动脚本
│ ├── train_baseline.sh # kar_re100_sc multi-seed
│ ├── train_illusion.sh # ill_*_sc 4 个尺寸
│ ├── vardist_scratch.sh # kar_d*_sc (推荐)
│ ├── vardist_transfer.sh # kar_d*_tr (参考)
│ ├── crossre_scratch.sh # kar_re*_sc
│ ├── crossre_transfer.sh # kar_re*_tr (参考)
│ └── resume.sh # 中断恢复
├── calibrations/ # 校准文件
│ ├── kar_re100/ # Re100 基准
│ ├── kar_d*_sc/ # 变直径 scratch (generic SIM_BP)
│ ├── kar_d*_tr/ # 变直径 transfer (实测 SIM_BP)
│ ├── kar_re*_sc/ # 变雷诺数 scratch (generic SIM_BP)
│ ├── kar_re*/ # 变雷诺数 transfer (实测 SIM_BP)
│ └── ill_*/ # Illusion
├── output/ # 训练输出 (21 dirs)
│ ├── kar_re100_sc_seed{41..45}/
│ ├── kar_d*_sc_seed{44,45}/
│ ├── kar_d*_tr_seed{44,45}/
│ ├── kar_re*_sc_seed43/
│ ├── kar_re*_tr_seed43/
│ └── ill_*_sc_seed43/
├── archive/ # 旧版本 (保留)
├── README.md # 当前文件
├── VARDIST_ANALYSIS.md
├── CROSSRE_ANALYSIS.md
└── SERVER_DEPLOY.md
```
---
## 快速开始
### 基准训练
From the repository root:
```bash
cd scripts
bash train_baseline.sh --gpu 0 --episodes 500
# → output/kar_re100_sc_seed{41..45}/
git submodule update --init --recursive
conda create -n pycuda_3_10 python=3.10 -y
conda activate pycuda_3_10
pip install pycuda
pip install -e CelerisLab
pip install -e .
```
### 变直径 scratch (推荐)
Run on Linux with an NVIDIA GPU. Commands below assume the repository's `pycuda_3_10` environment.
## Calibrate the target
Training requires a matched, calibrated artifact set: `calibration.json` and `target.npy`, plus `target_harmonics.json` for Illusion. A JSON alone is not a complete target artifact. Generate the target on the training server before training; do not substitute an uncalibrated target. For example:
```bash
cd scripts
bash vardist_scratch.sh --gpu 0
bash vardist_scratch.sh --only d075
# → output/kar_d075_sc_seed44/ etc.
```
cd src/drl_pinball/train
conda run --no-capture-output -n pycuda_3_10 python -u calibrate.py \
--case kar_re100 --device-id 0 --si 800 \
--config ../../../configs/config_lbm_karman_2000x600.json \
--out-dir calibrations/kar_re100
``` Canonical active case IDs omit method suffixes:
### 变雷诺数 scratch
- Karman: `kar_re100`, `kar_re60`, `kar_re200`, `kar_re400`, `kar_d075`, `kar_d15`, `kar_d2`
- Illusion: `ill_075L`, `ill_1L`, `ill_15L`, `ill_2L`
`_tr` identifies historical archived transfer runs only. Older `_sc` names may still appear inside retained artifacts and history; they are not canonical active IDs.
## Train
Launchers run cases serially on one GPU:
```bash
cd scripts
bash crossre_scratch.sh --gpu 0
bash crossre_scratch.sh --only re200
# → output/kar_re200_sc_seed43/ etc.
cd src/drl_pinball/train
bash scripts/train_baseline.sh --gpu 0 --seeds 41,42 --episodes 500
bash scripts/crossre_scratch.sh --gpu 0 --only re200 --episodes 500
bash scripts/vardist_scratch.sh --gpu 0 --only d15 --episodes 500
bash scripts/train_illusion.sh --gpu 0 --only 1L --episodes 500
```
### Illusion
For an explicit single run, use the Python entry point and the matching config/calibration:
```bash
cd scripts
bash train_illusion.sh --gpu 0
# → output/ill_2L_sc_seed43/ etc.
conda run --no-capture-output -n pycuda_3_10 python -u train_karman.py \
--case-name kar_re100 --device-id 0 --seed 42 \
--config ../../../configs/config_lbm_karman_2000x600.json \
--calibration calibrations/kar_re100/calibration.json \
--total-episodes 500 --symmetry-prob 0
```
### 中断恢复
Illusion uses `train_illusion.py` and an `ill_*` calibration. Symmetry is intentionally default-off; see `DESIGN_DECISIONS.md`.
Resume requires the matching policy and normalizer checkpoint:
```bash
cd scripts
bash resume.sh --case kar_re60_sc --seed 43 --resume 460 --episodes 500
bash scripts/resume.sh --case kar_re200 --seed 43 --resume 460 --episodes 500 --gpu 0
```
---
A training “episode” in these interfaces is one PPO learning chunk (normally 2048 environment steps) followed by a 360-step deterministic evaluation. It is not a Gym episode.
## 核心训练脚本
Outputs are written below `output/{case}_seed{seed}/`. The repository retains only each active runs compact inference bundle: `models/best_model.zip`, `best_vecnormalize.pkl`, `meta.json`, `calibration.json`, and a run-local `target.npy` when available. Canonical calibration targets provide the documented fallback for retained legacy bundles. Intermediate/final checkpoints, TensorBoard streams, and logs remain local. Resume is policy-plus-normalizer continuation, not exact process replay.
### `train_karman.py`
```bash
conda run -n pycuda_3_10 python -u train_karman.py \
--case-name kar_re100_sc --device-id 0 --seed 42 \
--config config.json --calibration calibrations/kar_re100/calibration.json \
--total-episodes 500 [--transfer-model PATH] [--resume-from N]
```
| 参数 | 默认值 | 说明 |
|------|:-----:|------|
| n_steps | 2048 | PPO 每轮收集步数 |
| batch_size | 64 | minibatch |
| n_epochs | 10 | 经验重用次数 |
| lr | 3e-4 | 学习率 |
| gamma | 0.995 | 折扣因子 |
| net_arch | [64, 64] | MLP 两层 sin 激活 |
---
## 最终结果
### Re100 基准 — `kar_re100_sc`
| Seed | r_sim | r_cd | r_cl |
|:----:|:-----:|:----:|:----:|
| 45 | 0.883 | 0.977 | 0.982 |
| Avg | 0.866 | 0.971 | 0.965 |
### Vardist 变直径
| Case | 方式 | r_sim | r_cd | r_cl |
|------|:----:|:-----:|:-----:|:-----:|
| **kar_d075_sc** | scratch | **0.918** | 0.954 | 0.879 |
| kar_d075_tr | transfer | 0.450 | 0.585 | 0.236 |
| **kar_d15_sc** | scratch | **0.904** | 0.991 | 0.992 |
| kar_d15_tr | transfer | 0.791 | 0.918 | 0.470 |
| **kar_d2_sc** | scratch | **0.793** | 0.967 | 0.961 |
| kar_d2_tr | transfer | 0.673 | 0.537 | 0.192 |
> Scratch 全面领先 transfer。
### Cross-Re 变雷诺数
| Case | 方式 | r_sim | r_cd | r_cl |
|------|:----:|:-----:|:-----:|:-----:|
| kar_re60_tr | transfer | 0.446 | **0.882** | 0.936 |
| kar_re60_sc | scratch | 0.261 | **0.987** | 0.981 |
| **kar_re200_sc** | scratch | **0.664** | 0.667 | 0.281 |
| kar_re200_tr | transfer | 0.374 | 0.749 | 0.427 |
| **kar_re400_sc** | scratch | **0.509** | 0.714 | 0.420 |
| kar_re400_tr | transfer | 0.293 | 0.883 | 0.571 |
> Scratch sim 最优,transfer CD 最优。两者均保留。
### Illusion
| Case | r_sim | r_cd | r_cl |
|------|:-----:|:-----:|:-----:|
| ill_1L_sc | 0.725 | 0.774 | 0.449 |
| ill_15L_sc | 0.810 | 0.825 | 0.384 |
| ill_075L_sc | 0.794 | 0.840 | 0.486 |
| **ill_2L_sc** | **0.896** | 0.863 | 0.570 |
---
## Calibration 参考
| Calibration | 物理 | SI | SIM_BP | K_CD/K_CL | 对应 output |
|------------|------|:--:|--------|:---------:|------------|
| `kar_re100` | Re=100, 1.0L | 800 | 实测 | 50/100 | `kar_re100_sc` |
| `kar_d075_sc` | Re=100, 0.75L | 800 | generic | 50/100 | `kar_d075_sc` |
| `kar_d075_tr` | Re=100, 0.75L | 800 | 实测 | 50/100 | `kar_d075_tr` |
| `kar_d15_sc` | Re=100, 1.5L | 800 | generic | 50/100 | `kar_d15_sc` |
| `kar_d15_tr` | Re=100, 1.5L | 800 | 实测 | 50/100 | `kar_d15_tr` |
| `kar_d2_sc` | Re=100, 2.0L | 800 | generic | 50/100 | `kar_d2_sc` |
| `kar_d2_tr` | Re=100, 2.0L | 800 | 实测 | 50/100 | `kar_d2_tr` |
| `kar_re60` | Re=60 | 800 | 实测 | 12/25 | `kar_re60_tr` |
| `kar_re60_sc` | Re=60 | 800 | generic | 50/100 | `kar_re60_sc` |
| `kar_re200` | Re=200 | 500 | 实测 | 12/25 | `kar_re200_tr` |
| `kar_re200_sc` | Re=200 | 500 | generic | 50/100 | `kar_re200_sc` |
| `kar_re400` | Re=400 | 400 | 实测 | 12/25 | `kar_re400_tr` |
| `kar_re400_sc` | Re=400 | 400 | generic | 50/100 | `kar_re400_sc` |
| `ill_*` | Illusion | — | 实测 | 12/25 | `ill_*_sc` |
---
## Key Findings
1. **SIM_BP 映射是跨场景训练的最关键变量。** 实测 gap ≠ 0.35 会导致 reward 梯度异常。
- Generic SIM_BP `[0, 0.30, 0.65, 0.79, 0.89, 1.0]` (gap=0.35) → 稳定学习
2. **变直径: scratch > transfer.** Generic SIM_BP + K_CD/CL=50/100 + lr=3e-4 + 500ep 是最优配方。
3. **变雷诺数: scratch 和 transfer 各有侧重。** 两者均保留。
4. **变雷诺数存在 CD/CL ↔ r_sim 根本性 trade-off。** 训练越久 r_cd 越高但 r_sim 越低(re60_sc: Ep60 r_sim=0.48 → Ep390 r_sim=0.26, r_cd=0.99)。CD/CL reward 分量主导了优化方向。
5. **降低 W_drag/W_lift (0.1) 有害。** CD/CL reward 是稳定器而非瓶颈。
6. **Ep1 r_sim ≥ 0.5 可预测 transfer 成功。**
See `TRAIN_PIPELINE.md` for the full data path, `SERVER_DEPLOY.md` for server commands, and `DESIGN_DECISIONS.md` for conventions and limitations.
+70 -151
View File
@@ -1,189 +1,108 @@
# Server Deployment Guide
# Server deployment
## Prerequisites
This guide uses only scripts and configs currently present in the repository. The release reproduces the training workflow and distributes compact best-policy bundles for retained active runs.
- Linux with NVIDIA GPU(s) and CUDA toolkit installed
- Python 3.10+
- CelerisLab submodule
## One-Time Setup
## Install
```bash
# 1. Clone with submodules
git clone --recurse-submodules <repo-url>
git clone --recurse-submodules <repo-url> DynamisLab
cd DynamisLab
# 2. Create conda environment
conda create -n pycuda_3_10 python=3.10
conda create -n pycuda_3_10 python=3.10 -y
conda activate pycuda_3_10
# 3. Install PyCUDA (match your CUDA version)
pip install pycuda
# 4. Install CelerisLab
pip install -e CelerisLab
# 5. Install DynamisLab
pip install -e .
# 6. Verify
python -c "from CelerisLab import Simulation; print('OK')"
python -c "from CelerisLab import Simulation; print('CelerisLab import OK')"
```
## Calibration (one per case, before training)
Requirements: Linux, NVIDIA GPU/driver and a compatible CUDA toolkit. Run commands from `src/drl_pinball/train` unless stated otherwise.
Every case MUST be calibrated once. This produces calibration.json + target files.
## Canonical cases and configs
### Karman Cloak (Re100, SI=800)
```bash
cd src/drl_pinball/train
conda run -n pycuda_3_10 python calibrate.py \
--case re100 --device-id 0 \
--config ../../../configs/config_lbm_karman_2000x600.json
```
Active IDs have no `_sc` suffix:
### Karman Cloak (Re200, SI=500)
```bash
conda run -n pycuda_3_10 python calibrate.py \
--case re200 --device-id 0 --si 500 \
--config ../../../configs/config_lbm_karman_2000x600_re200.json
```
- `kar_re100`, `kar_d075`, `kar_d15`, `kar_d2`, and all `ill_*` use `../../../configs/config_lbm_karman_2000x600.json`.
- `kar_re60`, `kar_re200`, `kar_re400` use the matching `..._re60.json`, `..._re200.json`, `..._re400.json`.
- `_tr` appears only in archived transfer experiments.
### Karman Cloak (Re50, SI=1600 / Re400, SI=400)
```bash
conda run -n pycuda_3_10 python calibrate.py \
--case re50 --device-id 0 --si 1600 \
--config ../../../configs/config_lbm_karman_2000x600_re50.json
## Calibrate a new artifact set
conda run -n pycuda_3_10 python calibrate.py \
--case re400 --device-id 0 --si 400 \
--config ../../../configs/config_lbm_karman_2000x600_re400.json
```
### Illusion (1L target, SI=600)
```bash
conda run -n pycuda_3_10 python calibrate.py \
--case illusion_1L --device-id 0 --si 600 --scene illusion \
--config ../../../configs/config_lbm_karman_2000x600.json
```
## Training
### CRITICAL: Sequential GPU Startup
Each GPU needs ~7 minutes between starts because CelerisLab compiles CUDA kernels
during the first Simulation() constructor. If two start simultaneously, the kernel
cache gets corrupted → reward=0.000 forever.
### Single-GPU Training (for testing)
Generate a matched calibrated target set on the training server before training. To create the Karman baseline set used by the active launcher:
```bash
# Karman Re100, 20 episodes
conda run -n pycuda_3_10 python -u train_karman.py \
--case-name re100_test --device-id 0 --seed 42 \
--config ../../../configs/config_lbm_karman_2000x600.json \
--calibration calibrations/re100/calibration.json \
--total-episodes 20
# Illusion 1L, 20 episodes
conda run -n pycuda_3_10 python -u train_illusion.py \
--case-name illusion_test --device-id 0 --seed 42 \
--config ../../../configs/config_lbm_karman_2000x600.json \
--calibration calibrations/illusion_1L/calibration.json \
--total-episodes 20
conda run --no-capture-output -n pycuda_3_10 python -u calibrate.py \
--case kar_re100 --device-id 0 --si 800 \
--config ../../../configs/config_lbm_karman_2000x600.json \
--out-dir calibrations/kar_re100
```
### Multi-GPU Training (6 GPUs, using launch script)
For Illusion, remember that the historical `--target-diam` option is passed as a radius ratio: `1.0` produces radius `1.0*L0=20` lattice units, hence physical diameter 40.
```bash
# Re100 Karman, 6 seeds
bash launch_multi.sh \
--case-name re100_karman --seeds 42,43,44,45,46,47 \
--gpus 0,1,2,3,4,5 --episodes 500 \
--config ../../../configs/config_lbm_karman_2000x600.json \
--calibration calibrations/re100/calibration.json
# Re200 Karman, transfer learning from Re100
bash launch_multi.sh \
--case-name re200_karman --seeds 42,43,44,45,46,47 \
--gpus 0,1,2,3,4,5 --episodes 500 \
--config ../../../configs/config_lbm_karman_2000x600_re200.json \
--calibration calibrations/re200/calibration.json \
--transfer output/re100_karman_seed42/models/best_model.zip
conda run --no-capture-output -n pycuda_3_10 python -u calibrate.py \
--case ill_1L --scene illusion --target-diam 1.0 \
--device-id 0 --si 1200 \
--config ../../../configs/config_lbm_karman_2000x600.json \
--out-dir calibrations/ill_1L
```
### Manual Multi-GPU (tmux/screen)
Keep each generated `calibration.json` with its `target.npy` and, for Illusion, `target_harmonics.json`. Targets are calibrated artifacts, not interchangeable input samples.
If launch_multi.sh doesn't work, start manually with delays:
## Train
Real launchers in `scripts/` run serially on one GPU:
```bash
# GPU 0
nohup conda run -n pycuda_3_10 python -u train_karman.py \
--case-name re100_karman --device-id 0 --seed 42 \
--config ../../../configs/config_lbm_karman_2000x600.json \
--calibration calibrations/re100/calibration.json \
--total-episodes 500 > output/re100_karman_seed42/nohup.log 2>&1 &
# WAIT 7 minutes (until "Env ready" appears in log)
sleep 420
# GPU 1
nohup conda run -n pycuda_3_10 python -u train_karman.py \
--case-name re100_karman --device-id 1 --seed 43 \
--config ../../../configs/config_lbm_karman_2000x600.json \
--calibration calibrations/re100/calibration.json \
--total-episodes 500 > output/re100_karman_seed43/nohup.log 2>&1 &
# Repeat for GPU 2,3,4,5 with seeds 44,45,46,47
bash scripts/train_baseline.sh --gpu 0 --seeds 41,42,43 --episodes 500
bash scripts/crossre_scratch.sh --gpu 1 --only re200 --episodes 500
bash scripts/vardist_scratch.sh --gpu 2 --only d15 --episodes 500
bash scripts/train_illusion.sh --gpu 3 --only 1L --episodes 500
```
## Monitoring
There is no active `launch_multi.sh`. For multiple GPUs, start one existing launcher per GPU and give each a disjoint case/seed selection. Stagger heavy CelerisLab initialization if the shared server or CUDA compilation cache requires it; no universal seven-minute delay is guaranteed by this repository.
Explicit single-run example:
```bash
# Check training progress
tail -f output/re100_karman_seed42/train.log
# TensorBoard
tensorboard --logdir output/re100_karman_seed42/tb --port 6006 --bind_all
# Monitor GPU usage
watch -n 1 nvidia-smi
conda run --no-capture-output -n pycuda_3_10 python -u train_karman.py \
--case-name kar_re100 --device-id 0 --seed 42 \
--config ../../../configs/config_lbm_karman_2000x600.json \
--calibration calibrations/kar_re100/calibration.json \
--total-episodes 500 --symmetry-prob 0
```
## Output Structure
```
output/
└── re100_karman_seed42/
├── models/
│ ├── best_model.zip # Best reward model
│ ├── final_model.zip # Final iteration model
│ └── chkpt_ep*.zip # Checkpoints every 10 episodes
├── tb/ # TensorBoard logs
├── train.log # Training log
├── calibration.json # Copy of calibration used
├── vec_normalize.pkl # VecNormalize statistics
└── meta.json # Run metadata
```
## Stopping
Resume with both checkpoint members present:
```bash
# Stop all training
pkill -f train_karman
pkill -f train_illusion
# Or by PID
ps aux | grep train_karman | grep -v grep | awk '{print $2}' | xargs kill
bash scripts/resume.sh --case kar_re200 --seed 43 --resume 460 --episodes 500 --gpu 0
```
## Troubleshooting
Resume continues policy and `VecNormalize`; it is not exact deterministic replay. Seeds distinguish runs but do not guarantee replay.
| Symptom | Cause | Fix |
|---------|-------|-----|
| reward=0.000 forever | Kernel compilation race (two inits at once) | Kill both, `rm -f ~/CelerisLab/src/CelerisLab/lbm/kernels/config/config_objects.h ~/CelerisLab/src/CelerisLab/lbm/kernels/kernel.ptx`, restart sequentially |
| reward=NaN | FORCE_SCALE too small | Check calibration.json, re-run calibrate.py |
| reward flat at Stage0 | Action not being applied | Check `_action_to_omega` sign, `set_body` call |
| CUDA OOM | PyTorch+PyCUDA memory conflict | Reduce `--batch-size 32` |
| ImportError: CelerisLab | Not installed | Run `pip install -e CelerisLab` in conda env |
| Conda env not found | Wrong environment | Use `conda env list` to verify `pycuda_3_10` exists |
## Monitor and retain
```bash
tail -f output/kar_re100_seed42/train.log
tensorboard --logdir output/kar_re100_seed42/tb --port 6006 --bind_all
nvidia-smi
```
Expected run layout:
```text
output/kar_re100_seed42/
├── calibration.json
├── meta.json
├── train.log
├── tb/
├── best_vecnormalize.pkl
├── final_vecnormalize.pkl
├── vec_normalize.pkl # legacy final-state alias
└── models/
├── best_model.zip
├── final_model.zip
├── epNNNN_model.zip
└── epNNNN_vecnormalize.pkl
```
Treat `models/best_model.zip` and root `best_vecnormalize.pkl` as one artifact. Only the allowlisted best-policy inference bundle is released from each active `output/` run. Intermediate/final checkpoints, logs, and TensorBoard streams remain local. Retained repository results are historical writing basis rather than evidence of complete post-fix retraining.
+132
View File
@@ -0,0 +1,132 @@
# Current DRL training pipeline
This document describes the executable modern training path in this directory. It is a workflow specification, not evidence that the current source has been fully retrained or validated after later fixes. The release includes compact best-policy bundles for retained active runs; retained result summaries remain historical writing inputs rather than post-fix retraining evidence.
## 1. Scope and cases
The active path uses CelerisLab on a 2000×600 D2Q9 grid, uniform regularized inlet, free-slip y walls, MRT collision, double buffering, zero action bias, Gymnasium environments, Stable-Baselines3 PPO, and observation-only `VecNormalize`.
Canonical active IDs omit `_sc`:
| Family | Cases | Config | SI |
|---|---|---|---:|
| Karman baseline | `kar_re100` | `configs/config_lbm_karman_2000x600.json` | 800 |
| Karman cross-Re | `kar_re60`, `kar_re200`, `kar_re400` | matching `_re60`, `_re200`, `_re400` JSON | 800, 500, 400 |
| Karman variable disturbance radius | `kar_d075`, `kar_d15`, `kar_d2` | base JSON | 800 |
| Illusion | `ill_075L`, `ill_1L`, `ill_15L`, `ill_2L` | base JSON | 1100, 1200, 1200, 1200 |
Code-level Re uses reference length `2L0=40`, so code `re100` corresponds to physical cylinder-diameter `Re_D=50`. `_tr` is reserved for archived transfer experiments. `_sc` survives only in historical paths/artifacts and must not be introduced into active prose or new case IDs.
## 2. End-to-end flow
```text
calibrate.py
├─ record calibrated target signal
├─ warm training geometry and snapshot it
├─ measure Stage0 (zero rotation)
├─ measure Stage1 (reference open loop)
└─ write calibration.json + target artifacts
train_karman.py / train_illusion.py
├─ load the matched calibration and target artifacts
├─ build physical env → symmetry wrapper → DummyVecEnv → VecNormalize
├─ repeat: PPO learn chunk → same-env deterministic evaluation → checkpoint
└─ write policy, paired normalizer, log, TensorBoard, metadata
../eval/ (separate inference/evaluation workflow)
```
Training and evaluation intentionally share one wrapped environment. This preserves CFD data continuity and avoids constructing a second GPU simulation, but evaluation is not side-effect-free: resetting/stepping changes environment state and, while `VecNormalize.training` remains true, updates observation running statistics. The policy weights do not update during evaluation.
## 3. Phase 0: calibration
`calibrate.py` performs the expensive target and baseline measurements. For Karman it records the wake of one upstream disturbance cylinder at three sensors. For Illusion it records a standalone target cylinder at three sensors plus target drag/lift, then stores a five-harmonic reconstruction of all eight channels.
Artifacts are a contract:
- `calibration.json`: geometry metadata, SI, scales, DTW mapping, reward constants, action mapping.
- `target.npy`: calibrated six-channel sensor target, shape `(150, 6)`.
- `target_harmonics.json`: Illusion only; calibrated sensor/force harmonic model.
- `calibrate.log`: provenance log when calibration is run locally.
Target artifacts must come from the same calibrated case, config, geometry, SI, and force convention as `calibration.json`. Training fallback target recording is convenience behavior, not a substitute for producing and preserving a calibrated artifact set.
Calibration warms the target simulation for `4*NX/U0 = 800000` lattice steps. It then warms the training geometry, snapshots it, and measures:
1. **Stage0** — zero rotation.
2. **Stage1** — reference open-loop rotation (`[0, 0.004, -0.004]` Karman; `[0, 0.005, -0.005]` Illusion).
Stage numbers describe acquisition order, not quality. The code sorts their mean DTW similarities into `worst_sim` and `better_sim` before constructing `SIM_BP`; Stage1 is not assumed better. Karman falls back to the generic `[0, .30, .65, .79, .89, 1]` mapping when the measured spread is below 0.10 or the better baseline is below 0.5. Illusion uses its scene-specific sorted mapping and `SIM_VAL=[0, .1, .35, .7, .85, 1]`.
Reward force sensitivities are:
- Karman: `K_CD=50`, `K_CL=100`.
- Illusion: `K_CD=12`, `K_CL=25`.
## 4. Geometry, observations, and actions
Karman contains disturbance cylinder + three sensors + three controlled cylinders. Its 12-vector is six controlled-cylinder forces followed by six sensor velocities. Drag and lift rewards use the **average** across the three controlled cylinders and target zero force.
Illusion contains three sensors + three controlled cylinders; the target cylinder exists only during target acquisition. Its 14-vector appends reconstructed target drag and lift to the same 12 channels. Its force reward compares the **sum** of all three controlled-cylinder forces with the single target-cylinder force. This wider mismatch motivates the lower `12/25` Gaussian constants.
The historical field name `target_diam` is misleading. The implementation passes `target_diam * L0` to CelerisLab as the circle **radius**. Therefore values `0.75, 1.0, 1.5, 2.0` are radius ratios relative to `L0=20`, giving physical lattice radii `15, 20, 30, 40` and diameters `30, 40, 60, 80`. Preserve this interpretation when reading or regenerating artifacts.
Actions are three normalized rotations:
```text
omega = -(action * ACTION_SCALE) * U0 / RADIUS
ACTION_SCALE=12, U0=0.01, RADIUS=10
```
A 0.1 EMA smooths commanded omega. There is no legacy action bias.
## 5. Three normalization layers
The observation path has three distinct layers; they must not be collapsed conceptually:
1. **Solver observation averaging** — CelerisLab `read_sensor(..., normalize=True)` returns a sensor-area and sampling-time average; force reads are sampling-time averages.
2. **Calibration pre-scaling** — force channels divide by `FORCE_SCALE`; sensor channels divide by `SENS_SCALE`. Illusion target-force channels divide by `FORCE_SCALE` before entering the 14-vector. Current `drl-pinball-calibration-v2` artifacts store native CelerisLab sensor units.
3. **Online `VecNormalize`** — running mean/variance whiten the complete observation (`norm_obs=True`, `norm_reward=False`, `clip_obs=10`, `gamma=0.99`). This is learned online during training; it is not the Phase 0 calibration.
Legacy schema-less calibrations instead used `SENSOR_CC=78` to convert native sensor values into the old policy/DTW unit convention before pre-scaling. `normalization.py` keeps that compatibility path for an existing legacy policy plus its matching `VecNormalize`; the active v2 schema does not apply 78.
Inference must load the model's matching `VecNormalize` and freeze it (`training=False`). A “best model” without the normalizer state saved at the same best-selection point is incomplete.
## 6. PPO loop and shared evaluation
Defaults: sinusoidal MLP `[64,64]`, `n_steps=2048`, `learn_timesteps=2048`, batch 64, 10 epochs, learning rate `3e-4`, PPO gamma `0.995`.
One outer `total_episodes` iteration is:
1. `model.learn(learn_timesteps, reset_num_timesteps=False)`.
2. Disable symmetry probability on the same wrapped env.
3. Reset and run up to 360 policy steps with `model.predict(..., deterministic=True)`.
4. Score mean reward over the last 180 steps.
5. If improved, save `best_model.zip` and root `best_vecnormalize.pkl` at the same point.
6. Save per-iteration model and normalizer checkpoints; finally save `final_model.zip`.
The environments never terminate a Gym episode themselves. “Episode” in logs and CLI means this outer PPO chunk, not a Gym episode.
Because evaluation uses the training `VecNormalize`, its observations contribute to running statistics. This is intentional and must be reproduced when continuing this workflow.
## 7. Symmetry status
`SymmetryAugmentWrapper` implements the up/down G transform. The supported training default is off (`--symmetry-prob 0`); enable it only as an explicit experiment.
History matters: legacy runs were configured with probability 0.5, but repeated `model.learn(...)` and evaluation resets reset the wrapper counter at each chunk. The mirror coin flip occurred only at the chunk boundary, after the last transition, so those models were effectively trained without augmented transitions. They must not be described as symmetry-augmented evidence.
## 8. Checkpoints, resume, and seeds
Each iteration pairs `epNNNN_model.zip` with `epNNNN_vecnormalize.pkl`. Resume loads both, reconstructs a new CFD environment, parses historical best reward from `train.log`, and continues at `N+1`.
Resume is policy-and-normalizer continuation. It is **not** exact state restoration: optimizer/RNG details, CFD process/context, environment trajectory, wrapper state, TensorBoard state, and wall-clock scheduling are not guaranteed to replay identically. Likewise, `--seed` differentiates stochastic runs; it is not a deterministic replay guarantee for GPU CFD + PyTorch + PPO.
Transfer loading remains in the Python entry points for historical compatibility, but active launchers train from scratch. Historical transfer scripts, logs, calibrations, and metadata live below `archive/`; see the warnings in `CROSSRE_ANALYSIS.md` and `VARDIST_ANALYSIS.md`.
## 9. Output contract and release status
A run directory contains `calibration.json`, `train.log`, `tb/`, `meta.json`, `models/`, and normalizer files. `best_model.zip` must travel with `best_vecnormalize.pkl`; `final_model.zip` uses `final_vecnormalize.pkl`, and per-iteration policies use the matching `epNNNN_vecnormalize.pkl`. The legacy root `vec_normalize.pkl` remains a final-state compatibility alias.
The repository publishes the source and canonical targets needed to reproduce the workflow, plus compact retained best-policy bundles (`best_model.zip` with the same-selection `best_vecnormalize.pkl` and provenance files). It does not publish intermediate/final checkpoints, full training logs, or TensorBoard streams. Any retained `results/` summaries and figures are historical writing basis only. Do not infer current post-fix validation, complete retraining, reproducibility across hardware, or release qualification from them.
+12 -8
View File
@@ -1,4 +1,8 @@
# Vardist Transfer Learning — 分析报告
# Vardist Transfer Learning — 历史归档分析
> **历史归档 / ARCHIVED TRANSFER ANALYSIS**
> 本文记录 2026-07-07 的 `_tr` 迁移实验及当时诊断,保留其科学内容作为历史记录;它不描述当前 active scratch workflow,不是模型发布或当前验证声明。当前规范见 `README.md`、`TRAIN_PIPELINE.md` 与 `DESIGN_DECISIONS.md`。
> 当前归档位置:迁移脚本在 `archive/scripts_deprecated/vardist_transfer.sh`;保留的 `_tr` 日志、校准副本和 metadata 在 `archive/transfer_instability/kar_d*_tr_seed*/`;旧校准在 `archive/calibrations_deprecated/kar_d*_tr/`。active case ID 不带 `_sc``_tr` 仅用于这些历史归档。
> 日期: 2026-07-07
> 实验: 改变上游扰动圆柱尺寸 (0.75L / 1.5L / 2.0L),从 re100 baseline 进行 transfer learning
@@ -167,11 +171,11 @@ Ep 200: 0.246
| 训练脚本 | `train_karman.py` |
| 环境 | `env_karman.py` |
| 校准脚本 | `calibrate.py` (generic SIM_BP 兜底逻辑在第 526-535 行) |
| Transfer 启动脚本 | `vardist_transfer.sh` |
| d075 校准 | `calibrations/karman_d075/calibration.json` |
| d15 校准 | `calibrations/karman_d15/calibration.json` |
| d2 校准 | `calibrations/karman_d2/calibration.json` |
| Transfer 启动脚本 | `archive/scripts_deprecated/vardist_transfer.sh` |
| d075 校准 | `archive/transfer_instability/kar_d075_tr_seed44/calibration.json` |
| d15 校准 | `archive/transfer_instability/kar_d15_tr_seed45/calibration.json` |
| d2 校准 | `archive/transfer_instability/kar_d2_tr_seed45/calibration.json` |
| re100 基线校准 | `calibrations/re100/calibration.json` |
| d075 训练日志 | `output/transfer_karman_d075_seed44/train.log` |
| d15 训练日志 | `output/transfer_karman_d15_seed45/train.log` |
| d2 训练日志 | `output/transfer_karman_d2_seed45/train.log` |
| d075 训练日志 | `archive/transfer_instability/kar_d075_tr_seed44/train.log` |
| d15 训练日志 | `archive/transfer_instability/kar_d15_tr_seed45/train.log` |
| d2 训练日志 | `archive/transfer_instability/kar_d2_tr_seed45/train.log` |
+30 -30
View File
@@ -2,7 +2,7 @@
"""Phase 0 calibration: measure baselines and produce calibration.json for any case.
Runs on new CelerisLab solver with 2000x600 config. Produces:
- target.npy (FIFO_LEN, 6) sensor signals (legacy-equiv)
- target.npy (FIFO_LEN, 6) sensor signals (CelerisLab native units)
- calibration.json FORCE_SCALE, SENS_SCALE, dtw_norm_scale, SIM_BP, SIM_VAL
Workflow:
@@ -34,6 +34,7 @@ if str(_REPO) not in sys.path:
sys.path.insert(0, str(_REPO))
from CelerisLab import Simulation
from normalization import CALIBRATION_SCHEMA_NATIVE_V2, SENSOR_UNITS_NATIVE
# ---------------------------------------------------------------------------
# Physics / geometry constants
@@ -52,7 +53,6 @@ SENSOR_X = 1200.0
FIFO_LEN = 150
CONV_LEN = 30
SENSOR_CC = 78.0
N_MEASURE = 100
@@ -160,7 +160,6 @@ def run_stage(sim, dist_id, sensor_ids, pinball_ids, target_states,
gpu_block(sim, lambda: sim.run(si, zero_obs=True))
obs = read_obs(sim, dist_id, sensor_ids, pinball_ids)
sl = obs[2:14].copy()
sl[0:6] *= SENSOR_CC
fifo.append(sl)
obs_slices, sims = [], []
@@ -168,7 +167,6 @@ def run_stage(sim, dist_id, sensor_ids, pinball_ids, target_states,
gpu_block(sim, lambda: sim.run(si, zero_obs=True))
obs = read_obs(sim, dist_id, sensor_ids, pinball_ids)
sl = obs[2:14].copy()
sl[0:6] *= SENSOR_CC
fifo.append(sl)
obs_slices.append(sl.copy())
sim_val = compute_similarity(target_states, np.array(list(fifo)),
@@ -231,12 +229,12 @@ def _calibrate_illusion(case, config_path, device_id, si, out_dir, log, warmup,
for i in range(FIFO_LEN):
gpu_block(sim, lambda: sim.run(si, zero_obs=True))
target_states[i] = [
sim.read_sensor(s0, normalize=True)[0] * SENSOR_CC,
sim.read_sensor(s0, normalize=True)[1] * SENSOR_CC,
sim.read_sensor(s1, normalize=True)[0] * SENSOR_CC,
sim.read_sensor(s1, normalize=True)[1] * SENSOR_CC,
sim.read_sensor(s2, normalize=True)[0] * SENSOR_CC,
sim.read_sensor(s2, normalize=True)[1] * SENSOR_CC,
sim.read_sensor(s0, normalize=True)[0],
sim.read_sensor(s0, normalize=True)[1],
sim.read_sensor(s1, normalize=True)[0],
sim.read_sensor(s1, normalize=True)[1],
sim.read_sensor(s2, normalize=True)[0],
sim.read_sensor(s2, normalize=True)[1],
sim.read_force(0, normalize=True)[0],
sim.read_force(0, normalize=True)[1],
]
@@ -252,7 +250,7 @@ def _calibrate_illusion(case, config_path, device_id, si, out_dir, log, warmup,
target_std = np.std(target_sensor, axis=0)
uy_std_avg = float(np.mean([target_std[1], target_std[3], target_std[5]]))
dtw_norm_scale = max(uy_std_avg, 0.01)
dtw_norm_scale = max(uy_std_avg, np.finfo(np.float32).eps)
log(f" Target recorded. DTW norm_scale: {dtw_norm_scale:.4f}")
# ---- Step 2: Training sim (3 sensors + 3 pinball) ----
@@ -300,7 +298,7 @@ def _calibrate_illusion(case, config_path, device_id, si, out_dir, log, warmup,
all_sens = np.concatenate([stage0["obs_slices"][:, 0:6],
stage1["obs_slices"][:, 0:6]], axis=0)
sens_scale = float(np.max(np.abs(all_sens)))
sens_scale = max(sens_scale, 0.01)
sens_scale = max(sens_scale, np.finfo(np.float32).eps)
s0_sim = float(np.mean(stage0["sims"]))
s1_sim = float(np.mean(stage1["sims"]))
@@ -332,32 +330,33 @@ def _calibrate_illusion(case, config_path, device_id, si, out_dir, log, warmup,
# Rounding
force_scale = round(force_scale, 4)
sens_scale = round(sens_scale, 2)
dtw_norm_scale = round(dtw_norm_scale, 3)
sens_scale = round(sens_scale, 6)
dtw_norm_scale = round(dtw_norm_scale, 6)
sim_bp = [round(x, 2) for x in sim_bp]
for i in range(1, len(sim_bp)):
if sim_bp[i] <= sim_bp[i - 1]:
sim_bp[i] = sim_bp[i - 1] + 0.01
sim_bp[-1] = 1.0
log(f" FORCE_SCALE = {force_scale:.4f}, SENS_SCALE = {sens_scale:.2f}")
log(f" FORCE_SCALE = {force_scale:.4f}, SENS_SCALE = {sens_scale:.6f}")
log(f" Stage0 sim = {s0_sim:.4f}, Stage1 sim = {s1_sim:.4f}")
log(f" SIM_BP = {[f'{x:.2f}' for x in sim_bp]}")
# ---- Step 6: Write calibration.json ----
calibration = {
"schema_version": CALIBRATION_SCHEMA_NATIVE_V2,
"sensor_units": SENSOR_UNITS_NATIVE,
"case": case, "scene": "illusion",
"target_diam": target_diam,
"grid": {"nx": NX, "ny": NY},
"config_path": config_path,
"SI": si,
"FIFO_LEN": FIFO_LEN, "CONV_LEN": CONV_LEN,
"SENSOR_CC": SENSOR_CC,
"FORCE_SCALE": force_scale, "SENS_SCALE": sens_scale,
"dtw_norm_scale": float(dtw_norm_scale),
"SIM_BP": [float(x) for x in sim_bp],
"SIM_VAL": [float(x) for x in sim_val],
"K_CD": K_CD, "K_CL": K_CL,
"K_CD": K_CD_ILLUSION, "K_CL": K_CL_ILLUSION,
"W_CD": W_CD, "W_CL": W_CL, "W_SIM": W_SIM,
"FLOOR_CD": FLOOR_CD, "FLOOR_CL": FLOOR_CL, "FLOOR_SIM": FLOOR_SIM,
"FLOOR_PENALTY": FLOOR_PENALTY,
@@ -438,18 +437,18 @@ def main() -> int:
for i in range(FIFO_LEN):
gpu_block(sim, lambda: sim.run(si, zero_obs=True))
target[i] = [
sim.read_sensor(s0_t, normalize=True)[0] * SENSOR_CC,
sim.read_sensor(s0_t, normalize=True)[1] * SENSOR_CC,
sim.read_sensor(s1_t, normalize=True)[0] * SENSOR_CC,
sim.read_sensor(s1_t, normalize=True)[1] * SENSOR_CC,
sim.read_sensor(s2_t, normalize=True)[0] * SENSOR_CC,
sim.read_sensor(s2_t, normalize=True)[1] * SENSOR_CC,
sim.read_sensor(s0_t, normalize=True)[0],
sim.read_sensor(s0_t, normalize=True)[1],
sim.read_sensor(s1_t, normalize=True)[0],
sim.read_sensor(s1_t, normalize=True)[1],
sim.read_sensor(s2_t, normalize=True)[0],
sim.read_sensor(s2_t, normalize=True)[1],
]
sim.close()
np.save(str(out_dir / "target.npy"), target)
target_std = np.std(target, axis=0)
uy_std_avg = float(np.mean([target_std[1], target_std[3], target_std[5]]))
dtw_norm_scale = max(uy_std_avg, 0.01)
dtw_norm_scale = max(uy_std_avg, np.finfo(np.float32).eps)
log(f" Target recorded. s1_uy std={target[:, 3].std():.4f}")
log(f" DTW norm_scale: {dtw_norm_scale:.4f}")
@@ -499,7 +498,7 @@ def main() -> int:
all_sens = np.concatenate([stage0["obs_slices"][:, 0:6],
stage1["obs_slices"][:, 0:6]], axis=0)
sens_scale = float(np.max(np.abs(all_sens)))
sens_scale = max(sens_scale, 0.01)
sens_scale = max(sens_scale, np.finfo(np.float32).eps)
log(f" FORCE_SCALE = {force_scale:.6f}")
log(f" SENS_SCALE = {sens_scale:.4f}")
@@ -545,8 +544,8 @@ def main() -> int:
# Round measured values to reasonable precision so calibration files
# are human-readable and robust to small measurement noise.
force_scale = round(force_scale, 4) # 0.002429 -> 0.0024
sens_scale = round(sens_scale, 2) # 0.7543 -> 0.75
dtw_norm_scale = round(dtw_norm_scale, 3) # 0.2043 -> 0.204
sens_scale = round(sens_scale, 6) # 0.7543 -> 0.75
dtw_norm_scale = round(dtw_norm_scale, 6) # 0.2043 -> 0.204
sim_bp = [round(x, 2) for x in sim_bp] # 0.3166 -> 0.32 etc.
# Ensure sim_bp monotonicity preserved after rounding
for i in range(1, len(sim_bp)):
@@ -556,12 +555,14 @@ def main() -> int:
sim_bp[-1] = 1.0
log(f" FORCE_SCALE (rounded) = {force_scale:.4f}")
log(f" SENS_SCALE (rounded) = {sens_scale:.2f}")
log(f" dtw_norm_scale (rounded) = {dtw_norm_scale:.3f}")
log(f" SENS_SCALE (rounded) = {sens_scale:.6f}")
log(f" dtw_norm_scale (rounded) = {dtw_norm_scale:.6f}")
log(f" SIM_BP (rounded) = {[f'{x:.2f}' for x in sim_bp]}")
# ---- Step 6: Write calibration.json ----
calibration = {
"schema_version": CALIBRATION_SCHEMA_NATIVE_V2,
"sensor_units": SENSOR_UNITS_NATIVE,
"case": case,
"dist_radius": args.dist_radius,
"grid": {"nx": NX, "ny": NY},
@@ -569,7 +570,6 @@ def main() -> int:
"SI": si,
"FIFO_LEN": FIFO_LEN,
"CONV_LEN": CONV_LEN,
"SENSOR_CC": SENSOR_CC,
"FORCE_SCALE": force_scale,
"SENS_SCALE": sens_scale,
"dtw_norm_scale": float(dtw_norm_scale),
@@ -10,10 +10,9 @@
"SI": 1100,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0027,
"SENS_SCALE": 0.93,
"dtw_norm_scale": 0.186,
"SENS_SCALE": 0.011923076923076923,
"dtw_norm_scale": 0.0023846153846153848,
"SIM_BP": [
0.0,
0.23,
@@ -30,8 +29,8 @@
0.85,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"K_CD": 12.0,
"K_CL": 25.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
@@ -47,5 +46,7 @@
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
@@ -1,12 +1,12 @@
[
{
"dc": 0.7088785445690156,
"dc": 0.009088186382228492,
"amps": [
0.11475430680633039,
0.09430037920896242,
0.03785335654012012,
0.036681363239733915,
0.03288602952216585
0.0014712091979745966,
0.0012089794014262208,
0.0004852994315191922,
0.00047027385375095166,
0.0004216160103361111
],
"freqs": [
0.05333333333333334,
@@ -16,21 +16,21 @@
0.06666666666666667
],
"phases": [
2.6132855445583396,
-0.5789901787454609,
0.8258268724793085,
2.6668932105480825,
-0.6277135419212965
2.613285564653542,
-0.5789903549409586,
0.8258264822665325,
2.6668929594105673,
-0.6277137721461957
]
},
{
"dc": -0.018542804992078648,
"dc": -0.00023772825889685392,
"amps": [
0.14149483059420287,
0.11188103536632915,
0.06452488062802345,
0.04686946222498845,
0.037418557603405504
0.0018140363060154814,
0.0014343722327366626,
0.0008272420402038255,
0.00060089054400642,
0.00047972508467335407
],
"freqs": [
0.05333333333333334,
@@ -40,21 +40,21 @@
0.06666666666666667
],
"phases": [
0.516970687037909,
-2.566893168767107,
-1.2609177447586661,
0.45668472348998385,
-2.5126762596162355
0.5169706675945374,
-2.5668931920978584,
-1.2609177711751982,
0.4566847221498629,
-2.5126761488737994
]
},
{
"dc": 0.5725846501191457,
"dc": 0.007340828847681355,
"amps": [
0.05764520035629576,
0.005899803483735611,
0.0054205372806156865,
0.004581067312849431,
0.002914845994913773
0.00073904103020892,
7.56385062017386e-05,
6.949406770020111e-05,
5.8731632216018344e-05,
3.736982044761247e-05
],
"freqs": [
0.11333333333333334,
@@ -72,13 +72,13 @@
]
},
{
"dc": 0.0112522135147204,
"dc": 0.00014425914889822403,
"amps": [
0.25595997006863674,
0.20892759048230508,
0.08230396158472242,
0.07230005719685514,
0.05046403572810587
0.003281538078215719,
0.0026785588523531027,
0.0010551789938645613,
0.0009269238105851669,
0.0006469748182269073
],
"freqs": [
0.05333333333333334,
@@ -88,21 +88,21 @@
0.04
],
"phases": [
0.737858091105846,
-2.3310043425633937,
0.6601851975187616,
-2.263148437154622,
0.5776398163807126
0.7378580903398775,
-2.331004343514337,
0.6601851998070257,
-2.263148434435599,
0.5776398129078066
]
},
{
"dc": 0.7183246092001597,
"dc": 0.00920928986154051,
"amps": [
0.1142342759424687,
0.09477386576602029,
0.039999549760355566,
0.0361173949295317,
0.033309522129018866
0.0014645418427236946,
0.0012150497277476015,
0.0005128146895743391,
0.0004630435034955362,
0.0004270450024366168
],
"freqs": [
0.05333333333333334,
@@ -112,21 +112,21 @@
0.06666666666666667
],
"phases": [
-0.5356446835968586,
2.572852381448959,
1.1249071992889565,
-0.49462960371639564,
2.54773901735503
-0.5356447051921064,
2.572852488600483,
1.1249074874330494,
-0.494629857878323,
2.5477396481423766
]
},
{
"dc": 0.03294325330294669,
"dc": 0.0004223494148916668,
"amps": [
0.14125404035009642,
0.11221966979698215,
0.05738605376381633,
0.04670339431628726,
0.03788596596235472
0.0018109492567327406,
0.001438713773975289,
0.0007357185931237228,
0.0005987614417660785,
0.00048571744742215996
],
"freqs": [
0.05333333333333334,
@@ -136,21 +136,21 @@
0.06666666666666667
],
"phases": [
0.5115801035482338,
-2.558745487651608,
2.0032061483623718,
0.44313339382107086,
-2.4836080305538353
0.5115801341045253,
-2.558745481973567,
2.0032061539153947,
0.44313339555376424,
-2.483608042896834
]
},
{
"dc": 0.0021232300360376636,
"dc": 0.002123213455391427,
"amps": [
4.857447003344866e-06,
4.692486838309072e-07,
4.25574507402414e-07,
2.214141534606793e-07,
2.135457380991351e-07
4.881182406944959e-06,
4.64207874535886e-07,
4.2991218787600116e-07,
2.1476436487057784e-07,
2.0345852246869883e-07
],
"freqs": [
0.11333333333333334,
@@ -160,21 +160,21 @@
0.1
],
"phases": [
-1.2322896349743138,
1.8869478333532295,
-1.192041653547773,
-1.1139837007307842,
1.8083568237434107
-1.2297210709636235,
1.8387927036937368,
-1.1880140519319704,
-1.1166642329064165,
1.891792473030483
]
},
{
"dc": -4.034008507005637e-06,
"dc": -4.03449388613808e-06,
"amps": [
0.00022175983298265806,
0.00019412298497325598,
7.13773466399049e-05,
6.573336547379603e-05,
4.496282384180768e-05
0.00022175969504500717,
0.00019412209599268325,
7.137768872188961e-05,
6.573323897561949e-05,
4.496354600204898e-05
],
"freqs": [
0.05333333333333334,
@@ -184,11 +184,11 @@
0.07333333333333333
],
"phases": [
-1.7563139947857938,
1.4434754734643556,
1.494965900469088,
-1.8235811215946953,
1.5413793492101098
-1.756313893414596,
1.4434737977480265,
1.4949686639333424,
-1.823578135966167,
1.5413904324220307
]
}
]
]
@@ -10,10 +10,9 @@
"SI": 1200,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0027,
"SENS_SCALE": 0.93,
"dtw_norm_scale": 0.355,
"SENS_SCALE": 0.011923076923076923,
"dtw_norm_scale": 0.004551282051282051,
"SIM_BP": [
0.0,
0.21,
@@ -30,8 +29,8 @@
0.85,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"K_CD": 12.0,
"K_CL": 25.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
@@ -47,5 +46,7 @@
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
@@ -1,12 +1,12 @@
[
{
"dc": 0.6393717394272487,
"dc": 0.008197073444851443,
"amps": [
0.26397499489221904,
0.03693626193592989,
0.03464043162884017,
0.032094292621327965,
0.02660998202074348
0.003384294725463689,
0.0004735415677137682,
0.000444108123074602,
0.0004114650735564253,
0.0003411534092189
],
"freqs": [
0.04,
@@ -16,21 +16,21 @@
0.08
],
"phases": [
-1.6564124362566746,
0.47538888508181093,
1.4273551493900074,
-1.603488924581116,
-2.92943366573134
-1.6564123840424922,
0.4753893835927066,
1.4273561147886427,
-1.6034890004851452,
-2.9294339155988105
]
},
{
"dc": -0.051619164294873676,
"dc": -0.0006617840408132627,
"amps": [
0.4172458605608075,
0.09803703370682554,
0.05739720972114841,
0.050867319019127596,
0.04493433644769158
0.005349305839112549,
0.0012568852204159961,
0.000735861702597037,
0.0006521452634752281,
0.0005760812144873168
],
"freqs": [
0.04,
@@ -40,21 +40,21 @@
0.07333333333333333
],
"phases": [
2.6165737590157754,
-0.40945517817609217,
-0.4706927988361595,
2.5775068579589413,
2.6220842496000643
2.6165737492640235,
-0.4094552198542029,
-0.47069275860313814,
2.5775070384067775,
2.622084395211405
]
},
{
"dc": 0.552529529929161,
"dc": 0.007083711924715939,
"amps": [
0.08403701703961244,
0.027486096578928348,
0.01744191426898051,
0.011353947053282725,
0.01109829213640094
0.0010773976518604574,
0.0003523858577092583,
0.00022361428193052584,
0.00014556342756051445,
0.0001422857950891452
],
"freqs": [
0.08,
@@ -64,21 +64,21 @@
0.16
],
"phases": [
-1.8227867545251144,
1.2862003185097757,
-1.7972626219792263,
1.246149938164889,
-0.21151202059464996
-1.8227867656243208,
1.2862003602555783,
-1.7972626414660602,
1.2461500153317588,
-0.21151212576819925
]
},
{
"dc": 0.010842311040420705,
"dc": 0.00013900397687207142,
"amps": [
0.5943308009504777,
0.08877439867850001,
0.08691429679466427,
0.06563808069193275,
0.05978492368560289
0.007619625642294911,
0.0011381333231880646,
0.0011142858604923952,
0.0008415138469899309,
0.000766473377462724
],
"freqs": [
0.04,
@@ -88,21 +88,21 @@
0.11333333333333334
],
"phases": [
2.703756522909848,
-0.36646754373961843,
-1.2543578596028404,
2.633262384913735,
1.9585690166152028
2.7037565236172956,
-0.366467535468687,
-1.2543578473672652,
2.633262379823474,
1.9585690000549598
]
},
{
"dc": 0.6386356908082962,
"dc": 0.008187637163533105,
"amps": [
0.26431188860383287,
0.03673130028679621,
0.034307996033570334,
0.032458112823718396,
0.025233785999350247
0.003388614153647068,
0.0004709143072201187,
0.00043984600852931016,
0.00041612935020591836,
0.00032351014847826316
],
"freqs": [
0.04,
@@ -112,21 +112,21 @@
0.11333333333333334
],
"phases": [
1.4719480406268972,
-2.7012019612996783,
-1.6212990075981681,
1.4163428238670492,
0.7391902924102435
1.4719481032982067,
-2.701201752954105,
-1.6212988121851675,
1.4163424133157365,
0.7391904060167546
]
},
{
"dc": 0.06721407459272692,
"dc": 0.000861718871503368,
"amps": [
0.4063272578239082,
0.11205511912567469,
0.06737336811041239,
0.03853575023411404,
0.0356513806128155
0.005209323808828321,
0.0014366041070790069,
0.000863761150834127,
0.0004940481317028024,
0.0004570687905458198
],
"freqs": [
0.04,
@@ -136,21 +136,21 @@
0.02666666666666667
],
"phases": [
2.6071524073546244,
2.638430170197671,
-0.42252259968403594,
2.4619405777962924,
-0.326703708621943
2.607152407579822,
2.638430150624339,
-0.422522740774566,
2.4619403762108343,
-0.32670380566107676
]
},
{
"dc": 0.004316972050194939,
"dc": 0.004316989940901597,
"amps": [
7.792624418406872e-05,
2.649820426895165e-05,
1.5679181128164903e-05,
1.1454315139134813e-05,
8.735853307059759e-06
7.795356046161481e-05,
2.6496761508385513e-05,
1.568389958595757e-05,
1.144380116726034e-05,
8.718164895308449e-06
],
"freqs": [
0.08,
@@ -160,21 +160,21 @@
0.09333333333333334
],
"phases": [
-0.4847035254693422,
2.6784244787377918,
-0.5077154053619933,
2.70688939048309,
-0.5282930751629346
-0.48435056342716537,
2.678749890666711,
-0.5079233374296729,
2.706864645609396,
-0.5281751178643231
]
},
{
"dc": 3.652239890319227e-05,
"dc": 3.652287718068692e-05,
"amps": [
0.0016787860570611811,
0.0002649747945744754,
0.0001734345822777016,
0.00013614422915343285,
9.88009892069796e-05
0.0016787853093318894,
0.000264974341796682,
0.00017343643445143914,
0.00013614475076224097,
9.879987270803766e-05
],
"freqs": [
0.04,
@@ -184,11 +184,11 @@
0.02
],
"phases": [
-3.082075177261505,
0.0499441279892221,
-3.072969546929839,
0.040161756095339736,
0.030358149244656192
-3.0820752123815973,
0.0499456525053739,
-3.072978092744599,
0.04017383012652507,
0.030365220287297087
]
}
]
]
@@ -10,10 +10,9 @@
"SI": 1200,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0027,
"SENS_SCALE": 0.93,
"dtw_norm_scale": 0.252,
"SENS_SCALE": 0.011923076923076923,
"dtw_norm_scale": 0.0032307692307692306,
"SIM_BP": [
0.0,
0.21,
@@ -30,8 +29,8 @@
0.85,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"K_CD": 12.0,
"K_CL": 25.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
@@ -47,5 +46,7 @@
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
Binary file not shown.
@@ -1,12 +1,12 @@
[
{
"dc": 0.6871295102437337,
"dc": 0.008809352519675196,
"amps": [
0.18319639782938002,
0.06610945366616479,
0.04311754473414352,
0.03030542180621809,
0.027272312380937323
0.0023486717451016467,
0.0008475572997023936,
0.0005527892157108422,
0.00038853096776261196,
0.00034964489187612246
],
"freqs": [
0.05333333333333334,
@@ -16,21 +16,21 @@
0.15333333333333335
],
"phases": [
-1.5859054738810505,
1.5005459218330446,
-1.534723086539976,
-0.9755852396186818,
-2.400861875184656
-1.585905345022505,
1.5005460959271122,
-1.534723047518141,
-0.9755863118580124,
-2.400862304033253
]
},
{
"dc": -0.02839548134400199,
"dc": -0.0003640446137899581,
"amps": [
0.25230287832767934,
0.09539999608074776,
0.07242769038107988,
0.057915290763028225,
0.04339958293014957
0.0032346521490483984,
0.001223076837162871,
0.000928560102758218,
0.0007425037479272481,
0.0005564049377962642
],
"freqs": [
0.05333333333333334,
@@ -40,21 +40,21 @@
0.10666666666666667
],
"phases": [
2.6842705453730904,
-0.4329499897570072,
2.9316995795051146,
2.670843645654777,
-0.05044820463468416
2.6842705358614727,
-0.4329499704530815,
2.9316995034379096,
2.6708436544501115,
-0.05044802385514657
]
},
{
"dc": 0.5692154564460119,
"dc": 0.007297634102849879,
"amps": [
0.05111823061431893,
0.04128396624646554,
0.015265422980567578,
0.015090006639850726,
0.009557711148572965
0.0006553619123178633,
0.0005292816319405183,
0.0001957105429863926,
0.00019346160862069542,
0.00012253468626713745
],
"freqs": [
0.1,
@@ -64,21 +64,21 @@
0.12000000000000001
],
"phases": [
1.6693519932886194,
-1.4324779931410057,
-1.3940831649835272,
1.6278315583167478,
-1.3566560767853084
1.669352077197208,
-1.4324779140862236,
-1.3940830731486458,
1.6278311988042837,
-1.356655867596017
]
},
{
"dc": 0.013092265091836452,
"dc": 0.00016784953526579417,
"amps": [
0.40124785195509555,
0.16234186402338177,
0.08524230800710621,
0.08108585800028799,
0.07109298715174087
0.005144203252605854,
0.0020813059431478423,
0.0010928500855496533,
0.0010395622982109472,
0.00091144856065585
],
"freqs": [
0.05333333333333334,
@@ -88,21 +88,21 @@
0.04
],
"phases": [
2.807113574648348,
-0.29757223337333294,
2.7719663781063204,
2.1871266999200056,
-0.2589322655847287
2.807113579737809,
-0.29757222637005815,
2.7719663779834143,
2.1871266917002354,
-0.25893229515432825
]
},
{
"dc": 0.684990275700887,
"dc": 0.0087819265988138,
"amps": [
0.18185260409359108,
0.06713403479140795,
0.04138782185879297,
0.02695905744770475,
0.026141859041267534
0.002331443960080372,
0.0008606927859714298,
0.0005306130993882779,
0.0003456288683524138,
0.00033515181152231707
],
"freqs": [
0.05333333333333334,
@@ -112,21 +112,21 @@
0.04
],
"phases": [
1.5465990065588082,
-1.6185095223679558,
1.5625278315612854,
0.8537101949347456,
-1.649974102551634
1.546599113619548,
-1.61850976860708,
1.5625281914270723,
0.8537100384203797,
-1.6499744631577824
]
},
{
"dc": 0.04362114487215876,
"dc": 0.0005592454961922943,
"amps": [
0.2437341265296108,
0.10332818930987306,
0.057340140519604656,
0.0564289078852383,
0.04846692686956963
0.0031247964556233307,
0.0013247203231757793,
0.0007351299724521773,
0.0007234475293688269,
0.0006213708431280353
],
"freqs": [
0.05333333333333334,
@@ -136,21 +136,21 @@
0.060000000000000005
],
"phases": [
2.6708663593754087,
-0.40513239623713626,
-0.06426761319675102,
2.9170325957817727,
2.5947271126041254
2.6708663676491753,
-0.4051324007724031,
-0.06426744894600595,
2.9170326355916663,
2.5947269806545727
]
},
{
"dc": 0.0028073104269181687,
"dc": 0.0028073479825009903,
"amps": [
1.3964627608664915e-05,
1.0641956583797102e-05,
4.3721419678955225e-06,
3.7235257770112522e-06,
2.6478054812257544e-06
1.4015937181383077e-05,
1.0671813575533977e-05,
4.409131521263914e-06,
3.7382921829427647e-06,
2.6881525102464034e-06
],
"freqs": [
0.1,
@@ -160,21 +160,21 @@
0.08666666666666667
],
"phases": [
-0.008346715563909252,
3.134154764223535,
-0.007106260242131926,
3.13161924702753,
-0.007459162304746806
-0.004601597990179667,
3.1357694057171637,
-0.004769742879130775,
3.1361106883689014,
-0.0015047488047546214
]
},
{
"dc": -4.900522320288777e-06,
"dc": -4.900578702896989e-06,
"amps": [
0.0006028109860900706,
0.00022089300997016063,
0.0001390934157273362,
8.588200912189985e-05,
8.16852145193509e-05
0.0006028104026365945,
0.00022089374238093185,
0.0001390907528064047,
8.588062207428474e-05,
8.168758713528594e-05
],
"freqs": [
0.05333333333333334,
@@ -184,11 +184,11 @@
0.06666666666666667
],
"phases": [
-1.182274934210081,
1.9691619558487863,
-1.1855982163802612,
1.9884734806658761,
-1.1842874340466223
-1.182276270844418,
1.9691535128302826,
-1.1856146746053084,
1.9884708784318685,
-1.1842880436059668
]
}
]
]
@@ -10,10 +10,9 @@
"SI": 1200,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0027,
"SENS_SCALE": 0.93,
"dtw_norm_scale": 0.433,
"SENS_SCALE": 0.011923076923076923,
"dtw_norm_scale": 0.005551282051282051,
"SIM_BP": [
0.0,
0.21,
@@ -30,8 +29,8 @@
0.85,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"K_CD": 12.0,
"K_CL": 25.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
@@ -47,5 +46,7 @@
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
Binary file not shown.
@@ -1,12 +1,12 @@
[
{
"dc": 0.5942543417215347,
"dc": 0.007618645325175717,
"amps": [
0.3019850417664959,
0.08160454953075313,
0.058301710483107845,
0.0544747476609962,
0.039862698816389305
0.003871603015245751,
0.0010462122889282762,
0.0007474577474848674,
0.0006983942182890194,
0.0005110604085490717
],
"freqs": [
0.03333333333333333,
@@ -16,21 +16,21 @@
0.060000000000000005
],
"phases": [
2.534821875775996,
-0.5429755507018492,
2.4925302413193626,
-2.8396914013393335,
2.3471326700331137
2.534821909359247,
-0.5429753720059705,
2.4925302033304453,
-2.8396912754309813,
2.3471329891866146
]
},
{
"dc": -0.11012142162770033,
"dc": -0.0014118131527151817,
"amps": [
0.48068411406011297,
0.1524480328909725,
0.11692915263110391,
0.0751168326269912,
0.0747130122507944
0.006162616862476316,
0.001954461998065287,
0.0014990917631886387,
0.0009630363501510042,
0.000957859081783658
],
"freqs": [
0.03333333333333333,
@@ -40,21 +40,21 @@
0.04
],
"phases": [
0.470296517741658,
-2.7377287843427305,
1.2986260770158415,
-2.824709979499191,
0.49814841794041553
0.47029650601052814,
-2.7377287391337943,
1.298626107017752,
-2.824709901348531,
0.4981483716433705
]
},
{
"dc": 0.5153132931391398,
"dc": 0.006606580681271022,
"amps": [
0.07925114648536222,
0.06416828398684,
0.02486756552471102,
0.02396105106046917,
0.02357284381774593
0.0010160403395559259,
0.0008226703075235898,
0.00031881494262450027,
0.0003071929623137073,
0.0003022159463813581
],
"freqs": [
0.06666666666666667,
@@ -72,13 +72,13 @@
]
},
{
"dc": -0.027726365104317665,
"dc": -0.0003554662192861239,
"amps": [
0.6643698174881782,
0.19945593793429098,
0.11468672779610138,
0.11289183277422135,
0.09541898908540014
0.008517561762668952,
0.002557127409413987,
0.0014703426640525817,
0.0014473311894130942,
0.0012233203728897453
],
"freqs": [
0.03333333333333333,
@@ -96,13 +96,13 @@
]
},
{
"dc": 0.5756912664572398,
"dc": 0.007380657148921592,
"amps": [
0.2971849584753252,
0.08546200288457928,
0.056185965966854816,
0.05199100131281855,
0.04433469349174801
0.003810063451946285,
0.0010956668000717703,
0.0007203326546256061,
0.0006665512859131556,
0.0005683935774500977
],
"freqs": [
0.03333333333333333,
@@ -112,21 +112,21 @@
0.06666666666666667
],
"phases": [
-0.6051267213005925,
2.5953707671296398,
0.17997803765211065,
-0.6326158950659138,
-0.8305731267474725
-0.6051267718289992,
2.595370797578676,
0.1799783276612615,
-0.6326160831777162,
-0.8305732564007975
]
},
{
"dc": 0.06891472408858439,
"dc": 0.0008835220666458973,
"amps": [
0.4939877939312975,
0.14182933287038246,
0.09542418578567632,
0.09238873840109178,
0.08339300360694776
0.00633317691350893,
0.0018183245845542044,
0.0012233871402188069,
0.0011844710656437464,
0.0010691410504508059
],
"freqs": [
0.03333333333333333,
@@ -136,21 +136,21 @@
0.06666666666666667
],
"phases": [
0.48480140913888486,
-2.7722480905064066,
1.1975329965204313,
0.618507835379864,
-1.6516116590440921
0.48480138985190574,
-2.7722480319964093,
1.1975329047854553,
0.6185078695760705,
-1.65161160244187
]
},
{
"dc": 0.006008172007277608,
"dc": 0.006008192943409085,
"amps": [
0.00014509432721004472,
0.0001134838631202557,
4.463804029767137e-05,
4.034021665887571e-05,
2.676946217843137e-05
0.00014509050761529017,
0.00011350527400744749,
4.463450917123621e-05,
4.032513685922175e-05,
2.675954746131117e-05
],
"freqs": [
0.06666666666666667,
@@ -160,21 +160,21 @@
0.08
],
"phases": [
-0.7433797814500979,
2.435150608779618,
-0.759862378816383,
2.4800522883576814,
-0.776499679618213
-0.7432301917689894,
2.4349577971505947,
-0.7600834524079255,
2.4798509912210784,
-0.7750185371428461
]
},
{
"dc": -0.0001301015308369339,
"dc": -0.00013009978716581826,
"amps": [
0.0027993124476053143,
0.0008745864809045856,
0.0004589225344713892,
0.0004310081571789182,
0.0003159326235892787
0.002799315770214687,
0.0008745901223606793,
0.00045892253463317015,
0.00043100868605214275,
0.00031593153487008906
],
"freqs": [
0.03333333333333333,
@@ -184,11 +184,11 @@
0.013333333333333334
],
"phases": [
-0.06721702416991604,
3.089276889965643,
-0.08070896525391963,
3.102755835824026,
3.1156324380089453
-0.06721775751807832,
3.0892715336597796,
-0.0807108437730493,
3.1027491698456315,
3.115628328525561
]
}
]
]
@@ -0,0 +1,51 @@
{
"case": "kar_d075",
"dist_radius": 0.75,
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 800,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"FORCE_SCALE": 0.0018,
"SENS_SCALE": 0.010769230769230769,
"dtw_norm_scale": 0.0018717948717948717,
"SIM_BP": [
0.0,
0.3,
0.65,
0.79,
0.89,
1.0
],
"SIM_VAL": [
0.0,
0.2,
0.5,
0.8,
0.9,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
"FLOOR_CD": 0.1,
"FLOOR_CL": 0.1,
"FLOOR_SIM": 0.1,
"FLOOR_PENALTY": 0.05,
"ACTION_SCALE": 12.0,
"ACTION_BIAS": [
0.0,
0.0,
0.0
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
@@ -0,0 +1,51 @@
{
"case": "kar_d15",
"dist_radius": 1.5,
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 800,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"FORCE_SCALE": 0.0036,
"SENS_SCALE": 0.009615384615384616,
"dtw_norm_scale": 0.003935897435897436,
"SIM_BP": [
0.0,
0.3,
0.65,
0.79,
0.89,
1.0
],
"SIM_VAL": [
0.0,
0.2,
0.5,
0.8,
0.9,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
"FLOOR_CD": 0.1,
"FLOOR_CL": 0.1,
"FLOOR_SIM": 0.1,
"FLOOR_PENALTY": 0.05,
"ACTION_SCALE": 12.0,
"ACTION_BIAS": [
0.0,
0.0,
0.0
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
@@ -0,0 +1,51 @@
{
"case": "kar_d2",
"dist_radius": 2.0,
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 800,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"FORCE_SCALE": 0.0046,
"SENS_SCALE": 0.009871794871794872,
"dtw_norm_scale": 0.0050384615384615385,
"SIM_BP": [
0.0,
0.3,
0.65,
0.79,
0.89,
1.0
],
"SIM_VAL": [
0.0,
0.2,
0.5,
0.8,
0.9,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
"FLOOR_CD": 0.1,
"FLOOR_CL": 0.1,
"FLOOR_SIM": 0.1,
"FLOOR_PENALTY": 0.05,
"ACTION_SCALE": 12.0,
"ACTION_BIAS": [
0.0,
0.0,
0.0
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
@@ -8,10 +8,9 @@
"SI": 800,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0024,
"SENS_SCALE": 0.75,
"dtw_norm_scale": 0.204,
"SENS_SCALE": 0.009615384615384616,
"dtw_norm_scale": 0.0026153846153846153,
"SIM_BP": [
0.0,
0.32,
@@ -45,5 +44,7 @@
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
@@ -4,20 +4,19 @@
"nx": 2000,
"ny": 600
},
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600_re200.json",
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600_re200.json",
"SI": 500,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0026,
"SENS_SCALE": 0.9,
"dtw_norm_scale": 0.269,
"SENS_SCALE": 0.011538461538461539,
"dtw_norm_scale": 0.003448717948717949,
"SIM_BP": [
0.0,
0.45,
0.77,
0.86,
0.93,
0.3,
0.65,
0.79,
0.89,
1.0
],
"SIM_VAL": [
@@ -28,8 +27,8 @@
0.9,
1.0
],
"K_CD": 12.0,
"K_CL": 25.0,
"K_CD": 50.0,
"K_CL": 100.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
@@ -45,5 +44,7 @@
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
@@ -4,20 +4,19 @@
"nx": 2000,
"ny": 600
},
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600_re400.json",
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600_re400.json",
"SI": 400,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0042,
"SENS_SCALE": 0.98,
"dtw_norm_scale": 0.31,
"SENS_SCALE": 0.012564102564102564,
"dtw_norm_scale": 0.0039743589743589745,
"SIM_BP": [
0.0,
0.56,
0.73,
0.84,
0.92,
0.3,
0.65,
0.79,
0.89,
1.0
],
"SIM_VAL": [
@@ -28,8 +27,8 @@
0.9,
1.0
],
"K_CD": 12.0,
"K_CL": 25.0,
"K_CD": 50.0,
"K_CL": 100.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
@@ -45,5 +44,7 @@
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
@@ -4,20 +4,19 @@
"nx": 2000,
"ny": 600
},
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600_re60.json",
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600_re60.json",
"SI": 800,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0021,
"SENS_SCALE": 0.72,
"dtw_norm_scale": 0.107,
"SENS_SCALE": 0.00923076923076923,
"dtw_norm_scale": 0.0013717948717948717,
"SIM_BP": [
0.0,
0.41,
0.61,
0.77,
0.88,
0.3,
0.65,
0.79,
0.89,
1.0
],
"SIM_VAL": [
@@ -28,8 +27,8 @@
0.9,
1.0
],
"K_CD": 12.0,
"K_CL": 25.0,
"K_CD": 50.0,
"K_CL": 100.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
@@ -45,5 +44,7 @@
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
"L0": 20.0,
"schema_version": "drl-pinball-calibration-v2",
"sensor_units": "celeris-area-time-average"
}
+89
View File
@@ -0,0 +1,89 @@
"""Canonical registry for active V5 scratch-training cases.
Canonical case IDs do not encode the training method. Historical transfer
(``_tr``) names are archive-only and are intentionally absent here.
"""
from __future__ import annotations
import argparse
from dataclasses import dataclass
from pathlib import Path
from typing import Mapping
REPO_ROOT = Path(__file__).resolve().parents[2]
TRAIN_DIR = REPO_ROOT / "src" / "drl_pinball" / "train"
CONFIG_DIR = REPO_ROOT / "configs"
@dataclass(frozen=True)
class CaseSpec:
case_id: str
config: str
calibration: str
trainer: str
si: int
seeds: tuple[int, ...]
scene_type: str
target_diam: float | None = None
@property
def config_path(self) -> Path:
return CONFIG_DIR / self.config
@property
def calibration_path(self) -> Path:
return TRAIN_DIR / "calibrations" / self.calibration / "calibration.json"
@property
def trainer_path(self) -> Path:
return TRAIN_DIR / self.trainer
def model_dir(self, seed: int) -> Path:
return TRAIN_DIR / "output" / f"{self.case_id}_seed{seed}" / "models"
_CASES = (
CaseSpec("kar_re100", "config_lbm_karman_2000x600.json", "kar_re100", "train_karman.py", 800, (41, 42, 43, 44, 45), "karman"),
CaseSpec("kar_d075", "config_lbm_karman_2000x600.json", "kar_d075", "train_karman.py", 800, (44,), "karman"),
CaseSpec("kar_d15", "config_lbm_karman_2000x600.json", "kar_d15", "train_karman.py", 800, (45,), "karman"),
CaseSpec("kar_d2", "config_lbm_karman_2000x600.json", "kar_d2", "train_karman.py", 800, (45,), "karman"),
CaseSpec("ill_075L", "config_lbm_karman_2000x600.json", "ill_075L", "train_illusion.py", 1100, (43,), "illusion", 0.75),
CaseSpec("ill_1L", "config_lbm_karman_2000x600.json", "ill_1L", "train_illusion.py", 1200, (43,), "illusion", 1.0),
CaseSpec("ill_15L", "config_lbm_karman_2000x600.json", "ill_15L", "train_illusion.py", 1200, (43,), "illusion", 1.5),
CaseSpec("ill_2L", "config_lbm_karman_2000x600.json", "ill_2L", "train_illusion.py", 1200, (43,), "illusion", 2.0),
CaseSpec("kar_re60", "config_lbm_karman_2000x600_re60.json", "kar_re60", "train_karman.py", 800, (43,), "karman"),
CaseSpec("kar_re200", "config_lbm_karman_2000x600_re200.json", "kar_re200", "train_karman.py", 500, (43,), "karman"),
CaseSpec("kar_re400", "config_lbm_karman_2000x600_re400.json", "kar_re400", "train_karman.py", 400, (43,), "karman"),
)
CASE_REGISTRY: Mapping[str, CaseSpec] = {case.case_id: case for case in _CASES}
CASE_IDS = tuple(CASE_REGISTRY)
def get_case(case_id: str) -> CaseSpec:
try:
return CASE_REGISTRY[case_id]
except KeyError as exc:
raise KeyError(f"Unknown canonical V5 case {case_id!r}; expected one of {CASE_IDS}") from exc
def main() -> int:
parser = argparse.ArgumentParser(description="Query the canonical V5 case registry")
parser.add_argument("case_id", choices=CASE_IDS)
parser.add_argument("field", choices=("config", "calibration", "trainer", "si", "seeds", "scene_type"))
args = parser.parse_args()
case = get_case(args.case_id)
values = {
"config": case.config_path,
"calibration": case.calibration_path,
"trainer": case.trainer_path,
"si": case.si,
"seeds": ",".join(map(str, case.seeds)),
"scene_type": case.scene_type,
}
print(values[args.field])
return 0
if __name__ == "__main__":
raise SystemExit(main())
+33 -24
View File
@@ -45,6 +45,7 @@ if str(_REPO) not in sys.path:
sys.path.insert(0, str(_REPO))
from CelerisLab import Simulation
from normalization import dtw_sensor_factor, policy_sensor_scale
# ---------------------------------------------------------------------------
L0 = 20.0; U0 = 0.01; RADIUS = L0 / 2.0
@@ -61,7 +62,6 @@ TARGET_CYL_RADIUS = 1.0 * L0 # 1L target diameter
FIFO_LEN = 150; CONV_LEN = 30; MAX_STEPS = 500
EMA_FAST = 0.2
S_DIM = 14; A_DIM = 3
SENSOR_CC = 78.0
ACTION_SCALE = 12.0
ACTION_BIAS = np.array([0.0, 0.0, 0.0], dtype=np.float32)
@@ -154,7 +154,7 @@ def gen_target_states_at(t, harmonics):
# ---------------------------------------------------------------------------
def record_illusion_target(config_path, device_id, si, target_diam=1.0):
def record_illusion_target(config_path, device_id, si, target_diam=1.0, sensor_factor=1.0):
warmup = int(4.0 * NX / U0)
sim = Simulation(lbm_config_path=config_path, device_id=device_id)
sim._assert_object_count_contract = lambda *a, **kw: None
@@ -170,12 +170,12 @@ def record_illusion_target(config_path, device_id, si, target_diam=1.0):
for i in range(FIFO_LEN):
sim.run(si, zero_obs=True)
target_states[i] = [
sim.read_sensor(s0, normalize=True)[0] * SENSOR_CC,
sim.read_sensor(s0, normalize=True)[1] * SENSOR_CC,
sim.read_sensor(s1, normalize=True)[0] * SENSOR_CC,
sim.read_sensor(s1, normalize=True)[1] * SENSOR_CC,
sim.read_sensor(s2, normalize=True)[0] * SENSOR_CC,
sim.read_sensor(s2, normalize=True)[1] * SENSOR_CC,
sim.read_sensor(s0, normalize=True)[0] * sensor_factor,
sim.read_sensor(s0, normalize=True)[1] * sensor_factor,
sim.read_sensor(s1, normalize=True)[0] * sensor_factor,
sim.read_sensor(s1, normalize=True)[1] * sensor_factor,
sim.read_sensor(s2, normalize=True)[0] * sensor_factor,
sim.read_sensor(s2, normalize=True)[1] * sensor_factor,
sim.read_force(0, normalize=True)[0],
sim.read_force(0, normalize=True)[1],
]
@@ -190,7 +190,7 @@ class IllusionCloakEnv(gym.Env):
def __init__(self, device_id=0, seed=42, calibration=None,
config_path=None, target_states=None, target_harmonics=None,
target_diam=1.0):
target_diam=1.0, policy_sensor_units=None):
super().__init__()
self.device_id = device_id
self.seed = seed
@@ -205,7 +205,8 @@ class IllusionCloakEnv(gym.Env):
self._target_diam = float(self._cal["target_diam"])
self._si = int(self._cal["SI"])
self._force_scale = np.float32(self._cal["FORCE_SCALE"])
self._sens_scale = np.float32(self._cal["SENS_SCALE"])
self._sens_scale = np.float32(policy_sensor_scale(self._cal, policy_sensor_units))
self._dtw_sensor_factor = np.float32(dtw_sensor_factor(self._cal))
self._dtw_norm_scale = float(self._cal["dtw_norm_scale"])
self._sim_bp = np.array(self._cal["SIM_BP"], dtype=np.float64)
self._sim_val = np.array(self._cal["SIM_VAL"], dtype=np.float64)
@@ -254,7 +255,8 @@ class IllusionCloakEnv(gym.Env):
if self._target_states is None or self._target_harmonics is None:
print(" [illusion] Phase 1: Recording target + harmonics...", flush=True)
self._target_states, self._target_harmonics = record_illusion_target(
self._config_path, self.device_id, self._si, self._target_diam)
self._config_path, self.device_id, self._si, self._target_diam,
sensor_factor=float(self._dtw_sensor_factor))
print(" [illusion] Target recorded.", flush=True)
self.target_sensor = self._target_states[:, 0:6].copy()
@@ -284,11 +286,23 @@ class IllusionCloakEnv(gym.Env):
self._set_omega(zero_omega)
self._gpu_block(lambda: self.sim.run(self._si, zero_obs=True))
obs = self._read_obs()
sl = obs[0:6] * SENSOR_CC
sl = obs[0:6] * self._dtw_sensor_factor
fifo_save.append(sl.copy())
self.save_states = np.array(fifo_save, dtype=np.float32)
self._gpu_block(lambda: self.sim.snapshot())
self._snapshot_fifo = self.save_states.copy()
self._snapshot_omega = zero_omega.copy()
self._snapshot_ema = (0.0, 0.0)
self._snapshot_step = 0
snapshot_raw = self._read_obs()
snapshot_target = gen_target_states_at(
self._snapshot_step, self._target_harmonics)
self._snapshot_obs = self._make_obs(
snapshot_raw,
float(snapshot_target[6]) / self._force_scale,
float(snapshot_target[7]) / self._force_scale,
).copy()
print(f" [illusion] Init done ({time.perf_counter()-t0:.0f}s)")
def _read_obs(self):
@@ -361,18 +375,13 @@ class IllusionCloakEnv(gym.Env):
def reset(self, seed=None, options=None):
super().reset(seed=seed)
self._gpu_block(lambda: self.sim.restore())
self.smoother.reset(self._action_to_omega(np.zeros(3, dtype=np.float32)))
self.smoother.reset(self._snapshot_omega)
self.fifo_states.clear()
for i in range(len(self.save_states)):
self.fifo_states.append(self.save_states[i, :])
self.current_step = 0
self._ema_r_cd = 0.0; self._ema_r_cl = 0.0
obs_raw = self._read_obs()
tgt = gen_target_states_at(self.current_step, self._target_harmonics)
tgt_cd = float(tgt[6]) / self._force_scale
tgt_cl = float(tgt[7]) / self._force_scale
obs = self._make_obs(obs_raw, tgt_cd, tgt_cl)
return obs, {}
for row in self._snapshot_fifo:
self.fifo_states.append(row.copy())
self.current_step = self._snapshot_step
self._ema_r_cd, self._ema_r_cl = self._snapshot_ema
return self._snapshot_obs.copy(), {}
def step(self, action):
assert self.action_space.contains(action), f"Invalid action: {action}"
@@ -384,7 +393,7 @@ class IllusionCloakEnv(gym.Env):
self.current_step += 1
obs_raw = self._read_obs()
self.fifo_states.append(obs_raw[0:6] * SENSOR_CC)
self.fifo_states.append(obs_raw[0:6] * self._dtw_sensor_factor)
reward, info = self._compute_reward(obs_raw)
tgt = gen_target_states_at(self.current_step, self._target_harmonics)
+8 -4
View File
@@ -40,6 +40,7 @@ if str(_REPO) not in sys.path:
sys.path.insert(0, str(_REPO))
from CelerisLab import Simulation
from normalization import dtw_sensor_factor, policy_sensor_scale
# ---------------------------------------------------------------------------
# Geometry constants (fixed across all Karman cloak cases)
@@ -167,7 +168,8 @@ class KarmanCloakEnv(gym.Env):
def __init__(self, device_id: int = 0, seed: int = 42,
calibration: Optional[dict] = None,
config_path: Optional[str] = None,
target_states: Optional[np.ndarray] = None):
target_states: Optional[np.ndarray] = None,
policy_sensor_units: Optional[str] = None):
super().__init__()
self.device_id = device_id
self.seed = seed
@@ -179,7 +181,9 @@ class KarmanCloakEnv(gym.Env):
self._cal = calibration.copy()
self._si = int(self._cal["SI"])
self._force_scale = np.float32(self._cal["FORCE_SCALE"])
self._sens_scale = np.float32(self._cal["SENS_SCALE"])
self._sens_scale = np.float32(
policy_sensor_scale(self._cal, policy_sensor_units))
self._dtw_sensor_factor = np.float32(dtw_sensor_factor(self._cal))
self._dtw_norm_scale = float(self._cal["dtw_norm_scale"])
self._sim_bp = np.array(self._cal["SIM_BP"], dtype=np.float64)
self._sim_val = np.array(self._cal["SIM_VAL"], dtype=np.float64)
@@ -269,7 +273,7 @@ class KarmanCloakEnv(gym.Env):
self._gpu_block(lambda: self.sim.run(self._si, zero_obs=True))
obs = self._read_obs()
sl = obs[2:14].copy()
sl[0:6] *= SENSOR_CC
sl[0:6] *= self._dtw_sensor_factor
fifo_save.append(sl)
self.save_states = np.array(fifo_save, dtype=np.float32)
print("done.")
@@ -361,7 +365,7 @@ class KarmanCloakEnv(gym.Env):
obs_raw = self._read_obs()
obs_slice = obs_raw[2:14]
obs = self._normalize_obs(obs_slice)
self.fifo_states.append(obs_slice[0:6] * SENSOR_CC)
self.fifo_states.append(obs_slice[0:6] * self._dtw_sensor_factor)
reward, info = self._compute_reward(obs_slice)
self.current_step += 1
+83
View File
@@ -0,0 +1,83 @@
"""Sensor-unit schema helpers for calibration, policy observations, and DTW."""
from __future__ import annotations
from typing import Any, Mapping
import numpy as np
CALIBRATION_SCHEMA_NATIVE_V2 = "drl-pinball-calibration-v2"
SENSOR_UNITS_NATIVE = "celeris-area-time-average"
POLICY_SENSOR_UNITS_LEGACY = "legacy-policy-v1"
LEGACY_SENSOR_FACTOR = 78.0
def is_native_sensor_schema(calibration: Mapping[str, Any]) -> bool:
"""Return whether sensor-valued fields use CelerisLab native units."""
return (
calibration.get("schema_version") == CALIBRATION_SCHEMA_NATIVE_V2
and calibration.get("sensor_units") == SENSOR_UNITS_NATIVE
)
def dtw_sensor_factor(calibration: Mapping[str, Any]) -> float:
"""Scale a native solver sensor sample into the calibration's DTW units."""
if is_native_sensor_schema(calibration):
return 1.0
return float(calibration.get("SENSOR_CC", LEGACY_SENSOR_FACTOR))
def policy_sensor_scale(
calibration: Mapping[str, Any], policy_sensor_units: str | None = None
) -> float:
"""Return the divisor that produces the policy's historical raw observation.
Native-v2 policies default to native sensor units. ``legacy-policy-v1`` is an
explicit compatibility mode for an existing model/VecNormalize pair.
Schema-less calibrations already store the legacy policy divisor and remain
unchanged.
"""
scale = float(calibration["SENS_SCALE"])
if is_native_sensor_schema(calibration) and policy_sensor_units == POLICY_SENSOR_UNITS_LEGACY:
return scale * LEGACY_SENSOR_FACTOR
return scale
def convert_legacy_calibration(calibration: Mapping[str, Any]) -> dict[str, Any]:
"""Convert sensor-valued legacy calibration fields to native-v2 units."""
if is_native_sensor_schema(calibration):
return dict(calibration)
factor = float(calibration.get("SENSOR_CC", LEGACY_SENSOR_FACTOR))
converted = dict(calibration)
converted["schema_version"] = CALIBRATION_SCHEMA_NATIVE_V2
converted["sensor_units"] = SENSOR_UNITS_NATIVE
converted["SENS_SCALE"] = float(calibration["SENS_SCALE"]) / factor
converted["dtw_norm_scale"] = float(calibration["dtw_norm_scale"]) / factor
converted.pop("SENSOR_CC", None)
return converted
def convert_legacy_target(target: np.ndarray, factor: float = LEGACY_SENSOR_FACTOR) -> np.ndarray:
"""Convert a sensor target array from legacy-equivalent to native units."""
converted = np.asarray(target).copy()
if converted.ndim < 2 or converted.shape[-1] <= 6:
return converted / float(factor)
converted[..., :6] /= float(factor)
return converted
def convert_legacy_harmonics(document: Any, factor: float = LEGACY_SENSOR_FACTOR) -> Any:
"""Convert the first six sensor channels in an illusion harmonics document."""
wrapped = isinstance(document, dict)
harmonics = document["harmonics"] if wrapped else document
converted = []
for index, source in enumerate(harmonics):
item = dict(source)
if index < 6:
item["dc"] = float(item["dc"]) / factor
item["amps"] = [float(value) / factor for value in item["amps"]]
converted.append(item)
if not wrapped:
return converted
result = dict(document)
result["harmonics"] = converted
return result
@@ -0,0 +1,51 @@
{
"case": "ill_075L",
"scene": "illusion",
"target_diam": 0.75,
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 1100,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0027,
"SENS_SCALE": 0.93,
"dtw_norm_scale": 0.186,
"SIM_BP": [
0.0,
0.23,
0.51,
0.71,
0.85,
1.0
],
"SIM_VAL": [
0.0,
0.1,
0.35,
0.7,
0.85,
1.0
],
"K_CD": 12.0,
"K_CL": 25.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
"FLOOR_CD": 0.1,
"FLOOR_CL": 0.1,
"FLOOR_SIM": 0.1,
"FLOOR_PENALTY": 0.05,
"ACTION_SCALE": 12.0,
"ACTION_BIAS": [
0.0,
0.0,
0.0
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
@@ -0,0 +1,13 @@
{
"case_name": "ill_075L",
"seed": 43,
"total_episodes": 500,
"best_reward": 0.8949789073732164,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "calibrations/ill_075L/calibration.json",
"best_episode": 468
}
@@ -0,0 +1 @@
../../calibrations/ill_075L/target.npy
@@ -0,0 +1,51 @@
{
"case": "ill_15L",
"scene": "illusion",
"target_diam": 1.5,
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 1200,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0027,
"SENS_SCALE": 0.93,
"dtw_norm_scale": 0.355,
"SIM_BP": [
0.0,
0.21,
0.81,
0.89,
0.94,
1.0
],
"SIM_VAL": [
0.0,
0.1,
0.35,
0.7,
0.85,
1.0
],
"K_CD": 12.0,
"K_CL": 25.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
"FLOOR_CD": 0.1,
"FLOOR_CL": 0.1,
"FLOOR_SIM": 0.1,
"FLOOR_PENALTY": 0.05,
"ACTION_SCALE": 12.0,
"ACTION_BIAS": [
0.0,
0.0,
0.0
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
@@ -0,0 +1,13 @@
{
"case_name": "ill_15L",
"seed": 43,
"total_episodes": 500,
"best_reward": 0.6258453220129013,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "calibrations/ill_15L/calibration.json",
"best_episode": 349
}
@@ -0,0 +1 @@
../../calibrations/ill_15L/target.npy
@@ -1,36 +1,37 @@
{
"case": "kar_d075_tr",
"dist_radius": 0.75,
"case": "ill_1L",
"scene": "illusion",
"target_diam": 1.0,
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 800,
"SI": 1200,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0018,
"SENS_SCALE": 0.84,
"dtw_norm_scale": 0.146,
"FORCE_SCALE": 0.0027,
"SENS_SCALE": 0.93,
"dtw_norm_scale": 0.252,
"SIM_BP": [
0.0,
0.25,
0.79,
0.88,
0.21,
0.81,
0.89,
0.94,
1.0
],
"SIM_VAL": [
0.0,
0.2,
0.5,
0.8,
0.9,
0.1,
0.35,
0.7,
0.85,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"K_CD": 12.0,
"K_CL": 25.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
@@ -0,0 +1,13 @@
{
"case_name": "ill_1L",
"seed": 43,
"total_episodes": 500,
"best_reward": 0.7991066402859158,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "calibrations/ill_1L/calibration.json",
"best_episode": 401
}
@@ -0,0 +1 @@
../../calibrations/ill_1L/target.npy
@@ -0,0 +1,51 @@
{
"case": "ill_2L",
"scene": "illusion",
"target_diam": 2.0,
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 1200,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0027,
"SENS_SCALE": 0.93,
"dtw_norm_scale": 0.433,
"SIM_BP": [
0.0,
0.21,
0.7,
0.82,
0.91,
1.0
],
"SIM_VAL": [
0.0,
0.1,
0.35,
0.7,
0.85,
1.0
],
"K_CD": 12.0,
"K_CL": 25.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
"FLOOR_CD": 0.1,
"FLOOR_CL": 0.1,
"FLOOR_SIM": 0.1,
"FLOOR_PENALTY": 0.05,
"ACTION_SCALE": 12.0,
"ACTION_BIAS": [
0.0,
0.0,
0.0
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
@@ -0,0 +1,13 @@
{
"case_name": "ill_2L",
"seed": 43,
"total_episodes": 500,
"best_reward": 0.6285770369900597,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "calibrations/ill_2L/calibration.json",
"best_episode": 234
}
@@ -0,0 +1 @@
../../calibrations/ill_2L/target.npy
@@ -1,5 +1,5 @@
{
"case": "kar_d075_sc",
"case": "karman_d075_v2",
"dist_radius": 0.75,
"grid": {
"nx": 2000,
@@ -0,0 +1,14 @@
{
"case_name": "kar_d075",
"seed": 44,
"total_episodes": 500,
"best_reward": 0.9170221057203081,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0001,
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "/root/private_data/DynamisLab/src/drl_pinball/train/calibrations/kar_d075/calibration.json",
"transfer_model": null,
"best_episode": 492
}
@@ -1,5 +1,5 @@
{
"case": "kar_d15_sc",
"case": "karman_d15_v2",
"dist_radius": 1.5,
"grid": {
"nx": 2000,
@@ -0,0 +1,14 @@
{
"case_name": "kar_d15",
"seed": 45,
"total_episodes": 500,
"best_reward": 0.9565292182895873,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "/root/private_data/DynamisLab/src/drl_pinball/train/calibrations/kar_d15/calibration.json",
"transfer_model": null,
"best_episode": 492
}
@@ -1,5 +1,5 @@
{
"case": "kar_d2_sc",
"case": "karman_d2_v2",
"dist_radius": 2.0,
"grid": {
"nx": 2000,
@@ -0,0 +1,14 @@
{
"case_name": "kar_d2",
"seed": 45,
"total_episodes": 500,
"best_reward": 0.8954825901322895,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0001,
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "/root/private_data/DynamisLab/src/drl_pinball/train/calibrations/kar_d2/calibration.json",
"transfer_model": null,
"best_episode": 499
}
@@ -1,24 +1,23 @@
{
"case": "kar_d15_tr",
"dist_radius": 1.5,
"case": "re100",
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600.json",
"config_path": "../../../configs/config_lbm_karman_2000x600.json",
"SI": 800,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0036,
"FORCE_SCALE": 0.0024,
"SENS_SCALE": 0.75,
"dtw_norm_scale": 0.307,
"dtw_norm_scale": 0.204,
"SIM_BP": [
0.0,
0.3,
0.65,
0.79,
0.32,
0.82,
0.89,
0.94,
1.0
],
"SIM_VAL": [
@@ -0,0 +1,14 @@
{
"case_name": "kar_re100",
"seed": 41,
"total_episodes": 500,
"best_reward": 0.9266385002268686,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "/root/private_data/DynamisLab/src/drl_pinball/train/calibrations/kar_re100/calibration.json",
"transfer_model": null,
"best_episode": 471
}
@@ -0,0 +1 @@
../kar_re100_seed42/target.npy
@@ -1,24 +1,23 @@
{
"case": "kar_d2_tr",
"dist_radius": 2.0,
"case": "re100",
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/home/frank14f/DynamisLab/configs/config_lbm_karman_2000x600.json",
"config_path": "/root/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 800,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0046,
"SENS_SCALE": 0.77,
"dtw_norm_scale": 0.393,
"FORCE_SCALE": 0.0024,
"SENS_SCALE": 0.75,
"dtw_norm_scale": 0.204,
"SIM_BP": [
0.0,
0.3,
0.65,
0.79,
0.32,
0.82,
0.89,
0.94,
1.0
],
"SIM_VAL": [
@@ -0,0 +1,14 @@
{
"case_name": "kar_re100",
"seed": 42,
"total_episodes": 500,
"best_reward": 0.9301960359017054,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "/root/private_data/DynamisLab/src/drl_pinball/train/calibrations/kar_re100/calibration.json",
"transfer_model": null,
"best_episode": 315
}
@@ -0,0 +1,49 @@
{
"case": "re100",
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/root/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 800,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0024,
"SENS_SCALE": 0.75,
"dtw_norm_scale": 0.204,
"SIM_BP": [
0.0,
0.32,
0.82,
0.89,
0.94,
1.0
],
"SIM_VAL": [
0.0,
0.2,
0.5,
0.8,
0.9,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
"FLOOR_CD": 0.1,
"FLOOR_CL": 0.1,
"FLOOR_SIM": 0.1,
"FLOOR_PENALTY": 0.05,
"ACTION_SCALE": 12.0,
"ACTION_BIAS": [
0.0,
0.0,
0.0
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
@@ -0,0 +1,14 @@
{
"case_name": "kar_re100",
"seed": 43,
"total_episodes": 500,
"best_reward": 0.916006507145034,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "/root/private_data/DynamisLab/src/drl_pinball/train/calibrations/kar_re100/calibration.json",
"transfer_model": null,
"best_episode": 360
}
@@ -0,0 +1,49 @@
{
"case": "re100",
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/root/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 800,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0024,
"SENS_SCALE": 0.75,
"dtw_norm_scale": 0.204,
"SIM_BP": [
0.0,
0.32,
0.82,
0.89,
0.94,
1.0
],
"SIM_VAL": [
0.0,
0.2,
0.5,
0.8,
0.9,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
"FLOOR_CD": 0.1,
"FLOOR_CL": 0.1,
"FLOOR_SIM": 0.1,
"FLOOR_PENALTY": 0.05,
"ACTION_SCALE": 12.0,
"ACTION_BIAS": [
0.0,
0.0,
0.0
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
@@ -0,0 +1,14 @@
{
"case_name": "kar_re100",
"seed": 44,
"total_episodes": 500,
"best_reward": 0.9221958074304792,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "/root/private_data/DynamisLab/src/drl_pinball/train/calibrations/kar_re100/calibration.json",
"transfer_model": null,
"best_episode": 465
}
@@ -0,0 +1,49 @@
{
"case": "re100",
"grid": {
"nx": 2000,
"ny": 600
},
"config_path": "/root/DynamisLab/configs/config_lbm_karman_2000x600.json",
"SI": 800,
"FIFO_LEN": 150,
"CONV_LEN": 30,
"SENSOR_CC": 78.0,
"FORCE_SCALE": 0.0024,
"SENS_SCALE": 0.75,
"dtw_norm_scale": 0.204,
"SIM_BP": [
0.0,
0.32,
0.82,
0.89,
0.94,
1.0
],
"SIM_VAL": [
0.0,
0.2,
0.5,
0.8,
0.9,
1.0
],
"K_CD": 50.0,
"K_CL": 100.0,
"W_CD": 0.3,
"W_CL": 0.3,
"W_SIM": 0.4,
"FLOOR_CD": 0.1,
"FLOOR_CL": 0.1,
"FLOOR_SIM": 0.1,
"FLOOR_PENALTY": 0.05,
"ACTION_SCALE": 12.0,
"ACTION_BIAS": [
0.0,
0.0,
0.0
],
"U0": 0.01,
"RADIUS": 10.0,
"L0": 20.0
}
@@ -0,0 +1,14 @@
{
"case_name": "kar_re100",
"seed": 45,
"total_episodes": 500,
"best_reward": 0.9411612408028709,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600.json",
"calibration_path": "/root/private_data/DynamisLab/src/drl_pinball/train/calibrations/kar_re100/calibration.json",
"transfer_model": null,
"best_episode": 447
}
@@ -1,5 +1,5 @@
{
"case": "kar_re200_sc",
"case": "re200_v2",
"grid": {
"nx": 2000,
"ny": 600
@@ -0,0 +1,14 @@
{
"case_name": "kar_re200",
"seed": 43,
"total_episodes": 500,
"best_reward": 0.5458830540378888,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600_re200.json",
"calibration_path": "/root/private_data/DynamisLab/src/drl_pinball/train/calibrations/kar_re200/calibration.json",
"transfer_model": null,
"best_episode": 114
}
@@ -1,5 +1,5 @@
{
"case": "kar_re400_sc",
"case": "re400_v2",
"grid": {
"nx": 2000,
"ny": 600
@@ -0,0 +1,14 @@
{
"case_name": "kar_re400",
"seed": 43,
"total_episodes": 500,
"best_reward": 0.5418410102526346,
"n_steps": 2048,
"batch_size": 64,
"n_epochs": 10,
"lr": 0.0003,
"config_path": "/root/private_data/DynamisLab/configs/config_lbm_karman_2000x600_re400.json",
"calibration_path": "/root/private_data/DynamisLab/src/drl_pinball/train/calibrations/kar_re400/calibration.json",
"transfer_model": null,
"best_episode": 87
}

Some files were not shown because too many files have changed in this diff Show More