Эх сурвалжийг харах

增加锡山水厂膜阻力48hCEB/48次CEB模型,相关模型及参数为models/uf-rl/xishan,调用入口为models/uf-rl/uf_train/rl_model/DQN/run_dqn_decide.py或models/uf-rl/uf_train/rl_model/DQN/run_dqn_deicde_totalstate.py

junc_WHU 1 сар өмнө
parent
commit
1778ade1e0
69 өөрчлөгдсөн 1151 нэмэгдсэн , 454 устгасан
  1. 221 0
      models/uf-rl/longting/data_to_rl_config.yaml
  2. 26 0
      models/uf-rl/longting/dqn_config.yaml
  3. 27 12
      models/uf-rl/longting/env_config.yaml
  4. 49 0
      models/uf-rl/longting/uf_analyze_config.yaml
  5. 11 11
      models/uf-rl/uf_data_process/calculate.py
  6. 349 0
      models/uf-rl/uf_data_process/data_export.py
  7. 9 5
      models/uf-rl/uf_data_process/filter.py
  8. 0 0
      models/uf-rl/uf_data_process/fit.py
  9. 8 8
      models/uf-rl/uf_data_process/label.py
  10. 0 0
      models/uf-rl/uf_data_process/load.py
  11. 28 19
      models/uf-rl/uf_data_process/pipeline.py
  12. 93 0
      models/uf-rl/uf_data_process/plot.py
  13. 1 1
      models/uf-rl/uf_data_process/run_ufdata_pipeline.py
  14. 0 0
      models/uf-rl/uf_train/README.md
  15. 0 0
      models/uf-rl/uf_train/UF_RL_架构问题与优化方案.md
  16. 0 0
      models/uf-rl/uf_train/UF_RL_训练与预测流程详解.md
  17. 0 0
      models/uf-rl/uf_train/UF_RL_详细技术文档.md
  18. 0 0
      models/uf-rl/uf_train/__init__.py
  19. 0 0
      models/uf-rl/uf_train/data_to_rl/__init__.py
  20. 0 0
      models/uf-rl/uf_train/data_to_rl/data_splitter.py
  21. 0 0
      models/uf-rl/uf_train/data_to_rl/get_reset_pool.py
  22. 0 0
      models/uf-rl/uf_train/data_to_rl/loader.py
  23. 3 4
      models/uf-rl/uf_train/data_to_rl/run_data_to_rl_pipeline.py
  24. 0 0
      models/uf-rl/uf_train/data_to_rl/state_construction.py
  25. 0 0
      models/uf-rl/uf_train/data_to_rl/state_space_bounds.py
  26. 0 0
      models/uf-rl/uf_train/env/__init__.py
  27. 17 8
      models/uf-rl/uf_train/env/env_params.py
  28. 0 0
      models/uf-rl/uf_train/env/env_reset.py
  29. 0 0
      models/uf-rl/uf_train/env/env_visual.py
  30. 58 0
      models/uf-rl/uf_train/env/reset_plot.py
  31. 0 0
      models/uf-rl/uf_train/env/resistance_model_bw.pth
  32. 0 0
      models/uf-rl/uf_train/env/resistance_model_fp.pth
  33. 2 2
      models/uf-rl/uf_train/env/uf_env.py
  34. 19 7
      models/uf-rl/uf_train/env/uf_physics.py
  35. 0 0
      models/uf-rl/uf_train/env/uf_resistance_models_define.py
  36. 0 0
      models/uf-rl/uf_train/env/uf_resistance_models_load.py
  37. 0 0
      models/uf-rl/uf_train/rl_model/DQN/__init__.py
  38. 0 0
      models/uf-rl/uf_train/rl_model/DQN/dqn_decider.py
  39. 0 0
      models/uf-rl/uf_train/rl_model/DQN/dqn_params.py
  40. 0 0
      models/uf-rl/uf_train/rl_model/DQN/dqn_statebuilder.py
  41. 0 0
      models/uf-rl/uf_train/rl_model/DQN/dqn_trainer.py
  42. 0 0
      models/uf-rl/uf_train/rl_model/DQN/online_datasets/UF1_init_cycle.csv
  43. 0 0
      models/uf-rl/uf_train/rl_model/DQN/online_datasets/UF1_prev_cycle.csv
  44. 26 6
      models/uf-rl/uf_train/rl_model/DQN/run_dqn_decide.py
  45. 4 2
      models/uf-rl/uf_train/rl_model/DQN/run_dqn_deicde_totalstate.py
  46. 33 10
      models/uf-rl/uf_train/rl_model/DQN/run_dqn_train.py
  47. 0 0
      models/uf-rl/uf_train/rl_model/__init__.py
  48. 0 186
      models/uf-rl/uf_train/rl_model/common/train_entry.py
  49. BIN
      models/uf-rl/xishan/48h_dqn_model.zip
  50. BIN
      models/uf-rl/xishan/48times_dqn_model.zip
  51. 3 3
      models/uf-rl/xishan/data_to_rl_config.yaml
  52. 26 0
      models/uf-rl/xishan/dqn_config.yaml
  53. 127 0
      models/uf-rl/xishan/env_config.yaml
  54. 11 7
      models/uf-rl/xishan/uf_analyze_config.yaml
  55. 0 25
      models/uf-rl/训练/config/dqn_config.yaml
  56. 0 138
      models/uf-rl/训练/uf_train/env/check_initial_state.py
  57. BIN
      models/uf-rl/训练/uf_train/rl_model/DQN/model/dqn_model.zip
  58. BIN
      models/uf-rl/训练/uf_train/rl_model/DQN/model/loss.png
  59. BIN
      models/uf-rl/训练/uf_train/rl_model/DQN/model/reward.png
  60. BIN
      models/uf-rl/训练/进水动作版超滤训练源码/model/dqn_model.zip
  61. BIN
      models/uf-rl/训练/进水动作版超滤训练源码/resistance_model_bw.pth
  62. BIN
      models/uf-rl/训练/进水动作版超滤训练源码/resistance_model_fp.pth
  63. 0 0
      models/uf-rl/进水动作版超滤训练源码/UF_resistance_models.py
  64. 0 0
      models/uf-rl/进水动作版超滤训练源码/fixed_DQN_decide.py
  65. 0 0
      models/uf-rl/进水动作版超滤训练源码/fixed_DQN_env.py
  66. 0 0
      models/uf-rl/进水动作版超滤训练源码/fixed_DQN_train.py
  67. 0 0
      models/uf-rl/进水动作版超滤训练源码/model/dqn_model.zip
  68. 0 0
      models/uf-rl/进水动作版超滤训练源码/resistance_model_bw.pth
  69. 0 0
      models/uf-rl/进水动作版超滤训练源码/resistance_model_fp.pth

+ 221 - 0
models/uf-rl/longting/data_to_rl_config.yaml

@@ -0,0 +1,221 @@
+# ============================================================
+# 项目级路径配置
+# ============================================================
+
+Paths:
+  project_root: "E:/Greentech"
+  __comment__: >
+    项目根目录,所有路径均相对于该目录展开。
+    不同工程师只需修改这一项即可迁移环境。
+
+  raw_data:
+    filtered_cycles_dir: "models/uf-rl/datasets/UF_longting_data/processed/filter_segments"
+    cycle_file_pattern: "UF{unit}_filtered_cycles.csv"
+
+    enabled_units: [1, 2]
+    disabled_units: None
+
+    __comment__: >
+      已完成清洗与周期切分的真实工厂数据。
+      每个 CSV 对应一个机组,文件内每一行是一个【物理周期】。
+      data_to_rl 阶段会:
+        - 读取 enabled_units 中所有机组
+        - 合并为统一数据集
+        - 机组编号仅用于筛选,不进入状态空间
+
+  data_to_rl:
+    output_dir: "models/uf-rl/datasets/UF_longting_data/rl_ready/output"
+    cache_dir: "models/uf-rl/datasets/UF_longting_data/rl_ready/cache"
+
+    __comment__: >
+      data_to_rl 模块输出内容:
+        - 全机组合并后的状态空间范围
+        - reset 初始状态分布
+        - reward 统计量
+
+
+# ============================================================
+# 数据层级定义(非常重要)
+# ============================================================
+
+DataHierarchy:
+
+  physical_cycle:
+    id_column: "seg_id"
+    time_columns:
+      start: "start_time"
+      end: "end_time"
+    __comment__: >
+      物理周期是最细粒度的数据层级:
+      - 用于物理模型验证
+      - 用于环境内部子步模拟
+      - 不直接暴露给强化学习算法
+
+  chemical_cycle:
+    id_column: "chem_cycle_id"
+    validity_column: "chem_cycle_valid"
+    step_definition: "one_rl_step_per_chemical_cycle"
+    __comment__: >
+      化学周期是强化学习的【唯一 step 单位】。
+      强化学习中:
+        - 一个 step = 一个化学周期
+        - 一个状态 = 一个化学周期的整体状态
+
+# ============================================================
+# 强化学习状态定义(工程师重点关注)
+# ============================================================
+
+RLState:
+  level: chemical_cycle
+  dimension: 8
+  __comment__: >
+    强化学习 observation 的定义。
+    所有变量在一个 step(化学周期)内保持不变。
+
+  variables:
+
+    q_UF:
+      source: "flow_mean"
+      aggregation: "mean_within_cycle"
+      unit: "m3_per_h"
+      description: "化学周期代表性进水流量"
+      __comment__: >
+        原始数据在物理周期层级变化,
+        这里使用化学周期内的平均值作为状态。
+
+    temp:
+      source: "temp_mean"
+      aggregation: "mean_within_cycle"
+      unit: "celsius"
+      description: "化学周期代表性水温"
+
+    TMP:
+      source: "tmp_start"
+      selection: "first_physical_cycle"
+      unit: "MPa"
+      description: "化学周期起始跨膜压差"
+      __comment__: >
+        虽然 TMP 在物理周期内会变化,
+        但强化学习只关心化学周期开始时的状态。
+
+    R:
+      source: "R_scaled_start"
+      selection: "first_physical_cycle"
+      unit: "scaled_resistance"
+      description: "化学周期起始膜阻力"
+
+    nuK:
+      source: "cycle_nuK"
+      unit: "scaled"
+      description: "短期污染增长系数"
+      __comment__: >
+        在同一化学周期内视为常数,
+        后续 step (下一化学周期)中根据动作进行经验性更新。
+
+    slope:
+      source: "cycle_long_a"
+      unit: "scaled"
+      description: "长期不可逆污染幂律系数 a"
+
+    power:
+      source: "cycle_long_b"
+      unit: "dimensionless"
+      description: "长期不可逆污染幂律指数 b"
+
+    ceb_removal:
+      source: "cycle_R_removed"
+      unit: "scaled"
+      description: "CEB 可去除的膜阻力"
+
+# ============================================================
+# 状态空间范围提取(reset 使用)
+# ============================================================
+
+StateSpaceExtraction:
+  enabled: true
+  level: chemical_cycle
+  include_only_valid_cycles: true
+
+  __comment__: >
+    用真实工厂数据统计【合理状态空间范围】,
+    用于:
+      - reset 初始状态采样
+      - 状态边界 sanity check
+
+  statistics:
+    method: "empirical"
+    percentiles: [1, 5, 50, 95, 99]
+    __comment__: >
+      使用经验分布统计,避免假设正态分布。
+
+  output:
+    save_csv: true
+    filename: "state_space_bounds.csv"
+
+# ============================================================
+# 物理周期层级的用途声明
+# ============================================================
+
+PhysicalCycleUsage:
+  enabled: true
+
+  purposes:
+    - internal_simulation
+    - parameter_sanity_check
+    - post_training_validation
+
+  variables_available:
+    - R_scaled_start
+    - R_scaled_end
+    - tmp_start
+    - tmp_end
+    - flow_mean
+    - flow_std
+    - temp_mean
+    - temp_std
+
+  __comment__: >
+    物理周期数据:
+      ✔ 可用于环境内部计算
+      ✔ 可用于训练后验证
+      ✘ 不可作为 RL observation
+
+# ============================================================
+# 真实数据在强化学习中的角色(工程边界声明)
+# ============================================================
+
+RealDataUsagePolicy:
+
+  reset_initialization:
+    enabled: true
+    source: "chemical_cycle_state_distribution"
+    __comment__: >
+      reset 时从真实化学周期状态分布中采样,
+      确保训练从“现实可达状态”开始。
+
+  training:
+    use_real_transitions: false
+    __comment__: >
+      真实数据不用于:
+        - 动力学拟合
+        - 监督策略
+      强化学习仍在模拟环境中进行。
+
+  validation:
+    enabled: true
+    compare_with_real_cycles: true
+    __comment__: >
+      训练完成后:
+        - 将策略运行结果
+        - 与真实化学周期统计特性进行对比
+      用于评估策略的现实可行性与安全性。
+
+# ============================================================
+# 方法论与工程风险声明
+# ============================================================
+
+MethodologyNotes:
+  - "强化学习的 step 定义严格等同于一个化学周期"
+  - "所有 RL 状态变量在一个 step 内视为常数"
+  - "物理周期层级仅用于环境内部模拟"
+  - "真实数据用于分布约束与验证,而非动力学建模"

+ 26 - 0
models/uf-rl/longting/dqn_config.yaml

@@ -0,0 +1,26 @@
+# ============================================================
+# DQN 超参数配置
+# ============================================================
+# 神经网络参数
+learning_rate: 0.0001           # 学习率 1e-4
+
+# 经验回放参数
+buffer_size: 100000              # 经验回放缓冲区大小
+learning_starts: 10000           # 开始训练前收集的步数
+batch_size: 32                   # 训练批次大小
+
+# 强化学习参数
+gamma: 0.95                      # 折扣因子
+train_freq: 4                    # 训练频率(步数)
+
+# 目标网络参数
+target_update_interval: 1        # 目标网络更新间隔
+tau: 0.005                       # 软更新系数
+
+# 探索策略参数
+exploration_initial_eps: 1.0     # 初始探索率
+exploration_fraction: 0.3        # 探索率衰减比例
+exploration_final_eps: 0.02      # 最终探索率
+
+# 日志参数
+remark: "uf_dqn_real_reset"      # 实验备注

+ 27 - 12
models/uf-rl/训练/config/env_config.yaml → models/uf-rl/longting/env_config.yaml

@@ -14,8 +14,8 @@ UFState:
 
 UFPhysicsParams:
   # ===== TMP 全局约束 =====
-  global_TMP_hard_limit: 0.08
-  global_TMP_soft_limit: 0.06
+  global_TMP_hard_limit: 0.08 # 跨膜压差硬上限
+  global_TMP_soft_limit: 0.06 # 跨膜压差软上限
 
   # ===== 物理反洗参数 =====
   tau_bw_s: 20.0
@@ -32,6 +32,21 @@ UFPhysicsParams:
 
   # ===== 吨水电耗查找表 =====
   energy_lookup:
+    2700: 0.1088
+    2760: 0.1083
+    2820: 0.1078
+    2880: 0.1074
+    2940: 0.1070
+    3000: 0.1066
+    3060: 0.1062
+    3120: 0.1059
+    3180: 0.1055
+    3240: 0.1052
+    3300: 0.1049
+    3360: 0.1045
+    3420: 0.1042
+    3480: 0.1039
+    3540: 0.1036
     3600: 0.1034
     3660: 0.1031
     3720: 0.1029
@@ -57,10 +72,10 @@ UFPhysicsParams:
 
 UFActionSpec:
   # ===== 动作空间范围 =====
-  L_min_s: 3800.0
-  L_max_s: 4800.0
-  t_bw_min_s: 40.0
-  t_bw_max_s: 60.0
+  L_min_s: 3000.0
+  L_max_s: 3800.0
+  t_bw_min_s: 50.0
+  t_bw_max_s: 70.0
 
   # ===== 动作离散化步长 =====
   L_step_s: 60.0
@@ -97,11 +112,11 @@ UFRewardParams:
 
 UFStateBounds:
   # ===== 流量初始化约束 =====
-  q_UF_min: 210.0
-  q_UF_max: 380.0
+  q_UF_min: 130.0
+  q_UF_max: 170.0
 
   # ===== 温度初始化约束 =====
-  temp_min: 16.0
+  temp_min: 14.0
   temp_max: 32.0
 
   # ===== TMP 初始化约束 =====
@@ -114,9 +129,9 @@ UFStateBounds:
   nuK_max: 260.0
 
   # ===== 长期污染参数 =====
-  slope_min: 0.03
-  slope_max: 27.0
-  power_min: 0.4
+  slope_min: 1.28
+  slope_max: 150
+  power_min: 0.18
   power_max: 2.2
 
   # ===== CEB 去除能力 =====

+ 49 - 0
models/uf-rl/longting/uf_analyze_config.yaml

@@ -0,0 +1,49 @@
+UF:
+  units: [ "1", "2" ]
+  area_m2: 128 * 40
+
+  inlet_codes: [215.0, 260.0]
+  stable_codes: [220.0, 260.0]
+
+  physical_bw_code: [300.0, 340.0]
+  chemical_bw_code: [400.0, 660.0]
+
+  # 列名
+  column_formats:
+    flow_col: "ns=3;s={unit}#UF_JSFLOW_O"
+    tmp_col: "ns=3;s=UF{unit}_SSD_KMYC"
+    press_col: "ns=3;s={unit}#UF_JSPRESS_O"
+    ctrl_col: "ns=3;s=UF{unit}_STEP"
+    temp_col: "ns=3;s=ZJS_TEMP_O"
+    orp_col: "ns=3;s=RO_JSORP_O"
+    ph_col: "ns=3;s=RO_JSPH_O"
+
+
+Params:
+  # 稳定段提取
+  min_stable_points: 30
+  initial_points: 10
+
+  # 阻力趋势计算
+  segment_head_n: 5
+  segment_tail_n: 5
+
+  scale_factor: 1e10
+
+
+Plot:
+  figsize: [12, 6]
+  dpi: 120
+  color_inlet: "#1f77b4"
+  color_bw_phys: "#ff7f0e"
+  color_bw_chem: "#d62728"
+
+Paths:
+  project_root: "E:/Greentech" # 请根据项目根目录修改相应路径
+
+  raw_data_path: "models/uf-rl/datasets/UF_longting_data/raw"
+  output_path: "models/uf-rl/datasets/UF_longting_data/processed/segments"
+  filter_output_path: "models/uf-rl/datasets/UF_longting_data/processed/filter_segments"
+
+  output_format: "csv"
+

+ 11 - 11
models/uf-rl/训练/uf_data_process/calculate.py → models/uf-rl/uf_data_process/calculate.py

@@ -38,19 +38,19 @@ class UFResistanceCalculator:
         mu = 0.00089 / factor
         return mu
 
-    def calculate_for_segment(self, seg_df: pd.DataFrame, temp_col="C.M.RO_TT_ZJS@out", flow_col=None):
+    def calculate_for_segment(self, seg_df: pd.DataFrame, temp_col="C.M.RO_TT_ZJS@out", flow_col=None, tmp_col=None):
         """计算单个稳定进水段的 TMP、通量、粘度和膜阻力"""
         seg_df = seg_df.copy()
 
         for unit in self.units:
             unit_flow_col = flow_col or f"C.M.{unit}_FT_JS@out"
-            press_col = f"C.M.{unit}_DB@press_PV"
+            tmp_col = tmp_col or f"C.M.{unit}_DB@press_PV"
 
-            if not all(col in seg_df.columns for col in [unit_flow_col, press_col, temp_col]):
+            if not all(col in seg_df.columns for col in [unit_flow_col, tmp_col, temp_col]):
                 continue
 
             # 计算前检查
-            cols_to_check = [press_col, unit_flow_col, temp_col]
+            cols_to_check = [tmp_col, unit_flow_col, temp_col]
             for col in cols_to_check:
                 # 检查非数值
                 if not pd.api.types.is_numeric_dtype(seg_df[col]):
@@ -62,7 +62,7 @@ class UFResistanceCalculator:
                     print(f"⚠️ 列 {col} 包含 NaN 或非有限值")
                     print(seg_df[col][~np.isfinite(seg_df[col])])
 
-            seg_df[f"{unit}_TMP_Pa"] = seg_df[press_col] * 1e6
+            seg_df[f"{unit}_TMP_Pa"] = seg_df[tmp_col] * 1e6
             seg_df[f"{unit}_J"] = seg_df[unit_flow_col] / self.A / 3600
             seg_df[f"{unit}_mu"] = self.xishan_viscosity(seg_df[temp_col])
             seg_df[f"{unit}_R"] = seg_df[f"{unit}_TMP_Pa"] / (seg_df[f"{unit}_J"] * seg_df[f"{unit}_mu"])
@@ -70,10 +70,10 @@ class UFResistanceCalculator:
 
         return seg_df
 
-    def calculate_for_segments(self, segments: list, temp_col="C.M.RO_TT_ZJS@out", flow_col=None):
+    def calculate_for_segments(self, segments: list, temp_col="C.M.RO_TT_ZJS@out", flow_col=None, tmp_col=None):
         result_segments = []
         for seg in segments:
-            seg_res = self.calculate_for_segment(seg, temp_col=temp_col, flow_col=flow_col)
+            seg_res = self.calculate_for_segment(seg, temp_col=temp_col, flow_col=flow_col, tmp_col=tmp_col)
             result_segments.append(seg_res)
         return result_segments
 
@@ -122,13 +122,13 @@ class UFPressureAnalyzer:
 
     def __init__(
         self,
-        press_col,
+        tmp_col,
         head_n=20,
         tail_n=20,
         feature_start="tmp_start",
         feature_end="tmp_end",
     ):
-        self.press_col = press_col
+        self.tmp_col = tmp_col
         self.head_n = head_n
         self.tail_n = tail_n
         self.feature_start = feature_start
@@ -143,10 +143,10 @@ class UFPressureAnalyzer:
             seg[self.feature_end] = None
         else:
             seg[self.feature_start] = (
-                seg[self.press_col].iloc[: self.head_n].mean()
+                seg[self.tmp_col].iloc[: self.head_n].mean()
             )
             seg[self.feature_end] = (
-                seg[self.press_col].iloc[-self.tail_n :].mean()
+                seg[self.tmp_col].iloc[-self.tail_n :].mean()
             )
 
         return seg

+ 349 - 0
models/uf-rl/uf_data_process/data_export.py

@@ -0,0 +1,349 @@
+import os, pandas as pd
+from datetime import datetime
+from sqlalchemy import create_engine, text
+from functools import reduce
+
+# ---------- 配置 ----------
+DB_USER = "whu"
+DB_PASS = "09093f4e6b33ddd"
+DB_HOST = "222.130.26.206"
+DB_PORT = 4000
+
+# 时间配置
+START_TIME = datetime(2025, 11, 28, 0, 0, 0)
+END_TIME = datetime(2026, 2, 20, 0, 0, 0)
+BOUNDARY = datetime(2025, 3, 25, 0, 0, 0)
+
+DELETE_PERIODS = [
+    ("2024-04-24 13:42:00", "2024-04-24 18:26:00"),
+    ("2024-11-09 12:34:00", "2024-11-11 10:46:00"),
+    ("2024-12-12 08:52:00", "2024-12-12 17:22:00"),
+    ("2024-12-15 16:00:00", "2024-12-16 09:34:00"),
+    ("2025-01-28 10:58:00", "2025-02-05 11:24:00"),
+]  # 来自张昊师兄的,需要被去除的黑名单区间
+DELETE_PERIODS = [(pd.to_datetime(s), pd.to_datetime(e)) for s, e in DELETE_PERIODS]
+
+# 新水岛
+# ---------- 传感器配置(新水厂系统) ----------
+# 机组编号
+UNITS = [1, 2]
+
+# 新系统变量模板
+BASE_VARIABLES = [
+    "ns=3;s={}#UF_JSFLOW_O",  # 进水流量
+    "ns=3;s={}#UF_JSPRESS_O",  # 进水压力(如有)
+    "ns=3;s=UF{}_SSD_KMYC",  # 跨膜压差(如有)
+    "ns=3;s=UF{}_STEP" # 步序
+]
+
+# 系统总变量(如果存在)
+SYSTEM_VARIABLES = [
+    "ns=3;s=ZJS_TEMP_O", # 进水温度
+    "ns=3;s=RO_JSORP_O",  # 总产水ORP(示例)
+    "ns=3;s=RO_JSPH_O",  # 总产水PH(示例)
+]
+
+# 生成所有变量名称
+SENSOR_NAMES = []
+
+for unit in UNITS:
+    for var_template in BASE_VARIABLES:
+        SENSOR_NAMES.append(var_template.format(unit))
+
+SENSOR_NAMES.extend(SYSTEM_VARIABLES)
+
+print(f"总共查询 {len(SENSOR_NAMES)} 个变量")
+for i, var in enumerate(SENSOR_NAMES, 1):
+    print(f"{i:2d}. {var}")
+
+# 输出目录 - 只需要processed文件夹
+BASE_OUTPUT_DIR = "../datasets/UF_longting_data"
+PROCESSED_OUTPUT_DIR = os.path.join(BASE_OUTPUT_DIR, "raw")
+
+# 创建目录
+os.makedirs(PROCESSED_OUTPUT_DIR, exist_ok=True)
+
+
+# ---------- 创建数据库引擎 ----------
+def create_db_engines():
+    """
+    创建数据库引擎,每个数据库只创建一次
+    """
+    try:
+        # 为两个数据库分别创建引擎
+        engine_test = create_engine(
+            f"mysql+pymysql://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/ws_data_test?charset=utf8mb4"
+        )
+        engine_prod = create_engine(
+            f"mysql+pymysql://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/ws_data?charset=utf8mb4"
+        )
+
+        # 测试连接
+        with engine_test.connect() as conn:
+            print("✅ 测试数据库连接成功!")
+        with engine_prod.connect() as conn:
+            print("✅ 生产数据库连接成功!")
+
+        return engine_test, engine_prod
+    except Exception as e:
+        print(f"❌ 数据库连接失败: {e}")
+        return None, None
+
+
+# ---------- 一分钟聚合查询函数(子查询方式)----------
+def fetch_valve_aggregated(name, start, end, engine, interval_minutes=1):
+    """
+    从数据库获取传感器数据并直接进行时间聚合
+    使用子查询方式避免 ONLY_FULL_GROUP_BY 问题
+    """
+    interval_seconds = interval_minutes * 60
+
+    sql = text(f"""
+        SELECT 
+            MIN(h_time) AS time,
+            AVG(val) AS val
+        FROM (
+            SELECT 
+                h_time,
+                val,
+                FLOOR(UNIX_TIMESTAMP(h_time) / {interval_seconds}) AS time_group
+            FROM dc_item_history_data_1450
+            WHERE item_name = :name
+              AND h_time BETWEEN :st AND :et
+              AND val IS NOT NULL
+        ) t
+        GROUP BY t.time_group
+        ORDER BY time
+    """)
+
+    try:
+        df = pd.read_sql(sql, engine, params={"name": name, "st": start, "et": end})
+        if not df.empty:
+            df['time'] = pd.to_datetime(df['time'])
+            print(f"  ✓ {name}: {len(df)} 条记录")
+        return df
+    except Exception as e:
+        print(f"  ✗ {name} 查询失败: {str(e)}")
+        return pd.DataFrame()
+
+
+# ---------- 传感器数据查询函数(只获取聚合数据)----------
+def fetch_sensor_data(sensor_names, start_time, end_time, boundary, engine_test, engine_prod):
+    """
+    获取多个传感器的分钟级聚合数据并合并为宽表
+
+    参数:
+        sensor_names: 传感器名称列表
+        start_time: 开始时间
+        end_time: 结束时间
+        boundary: 数据库切换边界
+        engine_test: 测试数据库引擎
+        engine_prod: 生产数据库引擎
+    """
+    all_data = []  # 只存储聚合数据
+
+    print("\n开始获取各传感器数据:")
+
+    for sensor in sensor_names:
+        try:
+            # 根据边界时间选择合适的数据库引擎
+            if end_time <= boundary:
+                # 全部在测试数据库
+                df = fetch_valve_aggregated(sensor, start_time, end_time, engine_test)
+            elif start_time >= boundary:
+                # 全部在生产数据库
+                df = fetch_valve_aggregated(sensor, start_time, end_time, engine_prod)
+            else:
+                # 跨越两个数据库
+                df1 = fetch_valve_aggregated(sensor, start_time, boundary, engine_test)
+                df2 = fetch_valve_aggregated(sensor, boundary, end_time, engine_prod)
+                df = pd.concat([df1, df2], ignore_index=True)
+
+            if not df.empty:
+                # 重命名列以区分不同传感器
+                df_renamed = df.rename(columns={'val': sensor})
+                all_data.append(df_renamed[['time', sensor]])
+            else:
+                print(f"  ⚠ {sensor}: 无数据")
+
+        except Exception as e:
+            print(f"  ⚠ {sensor}: 处理失败 - {str(e)}")
+            continue
+
+    if not all_data:
+        print("\n❌ 未获取到任何传感器数据")
+        return pd.DataFrame()
+
+    print(f"\n开始合并 {len(all_data)} 个传感器的数据...")
+
+    # 使用reduce逐步合并DataFrame
+    merged_df = reduce(
+        lambda left, right: pd.merge(left, right, on='time', how='outer'),
+        all_data
+    )
+
+    # 按时间排序
+    merged_df = merged_df.sort_values('time').reset_index(drop=True)
+
+    print(f"合并完成,共 {len(merged_df)} 条时间记录")
+
+    # 删除黑名单时段
+    print("删除黑名单时段...")
+    original_len = len(merged_df)
+    for s, e in DELETE_PERIODS:
+        merged_df = merged_df[(merged_df['time'] < s) | (merged_df['time'] > e)]
+
+    deleted_count = original_len - len(merged_df)
+    if deleted_count > 0:
+        print(f"已删除 {deleted_count} 条黑名单时段数据")
+
+    return merged_df
+
+
+# ---------- 数据后处理函数(填充空值)----------
+def post_process_data(df, method='both'):
+    """
+    对聚合后的数据进行后处理:填充空值
+
+    参数:
+        df: 输入的宽表DataFrame
+        method: 填充方法
+
+    返回:
+        pd.DataFrame: 处理后的DataFrame
+    """
+    if df.empty:
+        print("警告:输入数据为空")
+        return df
+
+    df_processed = df.copy()
+    df_processed['time'] = pd.to_datetime(df_processed['time'])
+    df_processed = df_processed.set_index('time')
+
+    print(f"\n开始填充空值...")
+    print(f"数据时间范围: {df_processed.index.min()} 到 {df_processed.index.max()}")
+    print(f"数据行数: {len(df_processed)}")
+
+    missing_before = df_processed.isnull().sum().sum()
+    print(f"填充前空值数量: {missing_before}")
+
+    if method == 'bfill':
+        df_processed = df_processed.bfill()
+    elif method == 'ffill':
+        df_processed = df_processed.ffill()
+    else:  # both
+        df_processed = df_processed.ffill().bfill()
+
+    missing_after = df_processed.isnull().sum().sum()
+    print(f"填充后空值数量: {missing_after}")
+    print(f"填充了 {missing_before - missing_after} 个空值")
+
+    # 重置索引
+    df_processed = df_processed.reset_index().rename(columns={'index': 'time'})
+
+    return df_processed
+
+
+# ---------- 数据分块保存函数 ----------
+def save_data_chunks(df, output_dir, prefix="sensor_data", chunk_size=200000):
+    """
+    将数据集分块保存,命名格式为 {prefix}_partX_of_Y.csv
+    """
+    total_rows = len(df)
+    num_chunks = (total_rows + chunk_size - 1) // chunk_size  # 向上取整
+
+    print(f"\n开始分块保存数据(共 {num_chunks} 个文件)...")
+
+    for i in range(num_chunks):
+        start_idx = i * chunk_size
+        end_idx = min((i + 1) * chunk_size, total_rows)
+        chunk_df = df.iloc[start_idx:end_idx]
+
+        if not chunk_df.empty:
+            file_path = os.path.join(
+                output_dir,
+                f"{prefix}_part{i + 1}_of_{num_chunks}.csv"
+            )
+            chunk_df.to_csv(file_path, index=False, encoding='utf_8_sig')
+            print(f"  ✓ 已保存第 {i + 1}/{num_chunks} 个文件: {os.path.basename(file_path)} ({len(chunk_df)} 行)")
+
+    return num_chunks
+
+
+# ---------- 主程序 ----------
+if __name__ == "__main__":
+    print("=" * 70)
+    print("传感器数据采集程序 - 分钟级聚合版本")
+    print("=" * 70)
+
+    # 1. 创建数据库引擎
+    print("\n[1/4] 创建数据库连接...")
+    engine_test, engine_prod = create_db_engines()
+    if engine_test is None or engine_prod is None:
+        print("❌ 数据库连接失败,程序退出")
+        exit(1)
+
+    # 2. 获取一分钟聚合数据
+    print(f"\n[2/4] 获取一分钟聚合数据...")
+    print(f"时间范围: {START_TIME} 到 {END_TIME}")
+
+    agg_df = fetch_sensor_data(
+        SENSOR_NAMES,
+        START_TIME,
+        END_TIME,
+        BOUNDARY,
+        engine_test,
+        engine_prod
+    )
+
+    if agg_df.empty:
+        print("\n❌ 未获取到任何聚合数据,程序退出")
+        exit(1)
+
+    print(f"\n✅ 聚合数据获取完成!")
+    print(f"总数据量: {len(agg_df)} 条记录")
+    print(f"时间范围: {agg_df['time'].min()} 到 {agg_df['time'].max()}")
+    print(f"时间间隔: 1分钟")
+    print(f"传感器数量: {len(agg_df.columns) - 1} 个")  # 减1是因为time列
+
+    # 3. 后处理聚合数据(填充空值)
+    print(f"\n[3/4] 后处理聚合数据(填充空值)...")
+    processed_df = post_process_data(agg_df, method='both')
+
+    print(f"\n✅ 后处理完成!")
+    print(f"处理后数据行数: {len(processed_df)}")
+
+    # 4. 保存数据(分块保存)
+    print(f"\n[4/4] 保存数据...")
+
+    # 直接使用分块保存,不再保存单个大文件
+    chunk_size = 200000  # 每块20万行
+    num_chunks = save_data_chunks(
+        processed_df,
+        PROCESSED_OUTPUT_DIR,
+        "uf_all_units_processed_1min",
+        chunk_size
+    )
+
+    print("\n" + "=" * 70)
+    print("✅ 所有任务完成!")
+    print("=" * 70)
+
+    # 显示文件信息
+    print(f"\n文件信息:")
+    print(f"输出目录: {PROCESSED_OUTPUT_DIR}")
+    print(f"文件数量: {num_chunks} 个")
+
+    # 计算总大小
+    total_size = 0
+    for i in range(num_chunks):
+        file_path = os.path.join(
+            PROCESSED_OUTPUT_DIR,
+            f"uf_all_units_processed_1min_part{i + 1}_of_{num_chunks}.csv"
+        )
+        if os.path.exists(file_path):
+            file_size = os.path.getsize(file_path) / (1024 * 1024)
+            total_size += file_size
+            print(f"  {os.path.basename(file_path)}: {file_size:.2f} MB")
+
+    print(f"总文件大小: {total_size:.2f} MB")

+ 9 - 5
models/uf-rl/训练/uf_data_process/filter.py → models/uf-rl/uf_data_process/filter.py

@@ -80,9 +80,9 @@ class EventQualityFilter:
 #       稳定进水数据提取
 # =============================
 class InletSegmentFilter:
-    def __init__(self, control_col, stable_value=26.0, min_points=40):
+    def __init__(self, control_col, stable_codes, min_points=40):
         self.control_col = control_col
-        self.stable_value = stable_value
+        self.stable_min, self.stable_max = stable_codes
         self.min_points = min_points
 
     def extract(self, seg_df):
@@ -100,11 +100,15 @@ class InletSegmentFilter:
 
         stable_segments = []
         for df in seg_list:
-            for sid, g in df.groupby("segment_id"):
-                stable = g[g[self.control_col] == self.stable_value]
+            for sid, group in df.groupby("segment_id"):
+                # 筛选出在稳定范围内的数据点
+                stable = group[
+                    (group[self.control_col] >= self.stable_min) &
+                    (group[self.control_col] <= self.stable_max)
+                    ]
+                # 如果满足最小点数要求,则保留
                 if len(stable) >= self.min_points:
                     stable_segments.append(stable)
-
         return stable_segments
 
 

+ 0 - 0
models/uf-rl/训练/uf_data_process/fit.py → models/uf-rl/uf_data_process/fit.py


+ 8 - 8
models/uf-rl/训练/uf_data_process/label.py → models/uf-rl/uf_data_process/label.py

@@ -5,20 +5,20 @@ import pandas as pd
 #     事件识别和划分
 # =============================
 class UFEventClassifier:
-    def __init__(self, unit_name, inlet_codes, physical_code, chemical_code):
+    def __init__(self, unit_name, inlet_codes, physical_codes, chemical_codes, ctrl_col):
         self.unit = unit_name
-        self.inlet_codes = inlet_codes
-        self.physical_code = physical_code
-        self.chemical_code = chemical_code
-        self.ctrl_col = f"C.M.{unit_name}_DB@word_control"
+        self.inlet_min, self.inlet_max = inlet_codes
+        self.physical_min, self.physical_max = physical_codes
+        self.chemical_min, self.chemical_max = chemical_codes
+        self.ctrl_col = ctrl_col
 
     def classify(self, df):
         df = df.copy()
         df["event_type"] = "other"
 
-        df.loc[df[self.ctrl_col].isin(self.inlet_codes), "event_type"] = "inlet"
-        df.loc[(df[self.ctrl_col] >= self.physical_code - 5) &(df[self.ctrl_col] <= self.physical_code + 5),"event_type"] = "bw_phys"
-        df.loc[(df[self.ctrl_col] >= self.chemical_code - 5) &(df[self.ctrl_col] <= self.chemical_code + 5),"event_type"] = "bw_chem"
+        df.loc[(df[self.ctrl_col] >= self.inlet_min) & (df[self.ctrl_col] <= self.inlet_max), "event_type"] = "inlet"
+        df.loc[(df[self.ctrl_col] >= self.physical_min) & (df[self.ctrl_col] <= self.physical_max), "event_type"] = "bw_phys"
+        df.loc[(df[self.ctrl_col] >= self.chemical_min) & (df[self.ctrl_col] <= self.chemical_max), "event_type"] = "bw_chem"
 
         return df
 

+ 0 - 0
models/uf-rl/训练/uf_data_process/load.py → models/uf-rl/uf_data_process/load.py


+ 28 - 19
models/uf-rl/训练/uf_data_process/pipeline.py → models/uf-rl/uf_data_process/pipeline.py

@@ -28,7 +28,7 @@ class UFAnalysisPipeline:
 
         # 机组列表(优先从配置读取)
         self.units = uf_cfg.get("units", ["UF1", "UF2", "UF3", "UF4"])
-        self.stable_inlet_code = uf_cfg.get("stable_inlet_code", "26.0")
+        self.stable_codes = uf_cfg.get("stable_codes", "[24.0, 26.0]")
 
         project_root = Path(paths["project_root"])
         raw_data_path = project_root / paths["raw_data_path"]
@@ -39,6 +39,14 @@ class UFAnalysisPipeline:
         self.filter_output_dir = filter_output_path
         output_path.mkdir(parents=True, exist_ok=True)
 
+        # 列名配置
+        column_formats = uf_cfg.get("column_formats", {})
+        self.ctrl_format = column_formats.get("ctrl_col", "C.M.{unit}_DB@word_control")
+        self.flow_format = column_formats.get("flow_col", "C.M.{unit}_FT_JS@out")
+        self.tmp_format = column_formats.get("tmp_col", "C.M.{unit}_DB@press_PV")
+        self.temp_col = column_formats.get("temp_col", "C.M.RO_TT_ZJS@out")
+        self.orp_col = column_formats.get("orp_col", "C.M.UF_ORP_ZCS@out")
+
         # 过滤器
         self.min_points = params.get("min_points", 40)
         self.initial_points = params.get("initial_points", 10)
@@ -51,10 +59,7 @@ class UFAnalysisPipeline:
         self.segment_head_n = params.get("segment_head_n", 10)
         self.segment_tail_n = params.get("segment_tail_n", 10)
 
-        # 需要检查的常量列名(除流量外)
-        self.temp_col = uf_cfg.get("temp_col", "C.M.RO_TT_ZJS@out")
-        self.orp_col = uf_cfg.get("orp_col", "C.M.UF_ORP_ZCS@out")
-        self.cond_col = uf_cfg.get("cond_col", "C.M.RO_Cond_ZJS@out")
+
 
     # ----------------------------
     # 加载所有 CSV 并合并
@@ -77,7 +82,6 @@ class UFAnalysisPipeline:
             "flow": flow_col,
             "temp": self.temp_col,
             "orp": self.orp_col,
-            "cond": self.cond_col
         }
         results = {}
         for name, col in cols.items():
@@ -104,7 +108,10 @@ class UFAnalysisPipeline:
     # ----------------------------
     def summarize_R_for_unit(self, stable_segments: List[pd.DataFrame], unit: str) -> pd.DataFrame:
         rows = []
-        res_col = f"{unit}_R"
+        if unit in ["1", "2", "3", "4"] or unit in [1, 2, 3, 4]:  # 处理字符串和数字
+            res_col = f"UF{unit}_R"
+        else:
+            res_col = f"{unit}_R"
         for seg in stable_segments:
             seg = seg.sort_values("time").reset_index(drop=True)
             if len(seg) < (self.segment_head_n + self.segment_tail_n):
@@ -144,8 +151,10 @@ class UFAnalysisPipeline:
         # 逐机组处理
         for unit in self.units:
             print(f"Processing {unit} ...")
-            ctrl_col = f"C.M.{unit}_DB@word_control"
-            flow_col = f"C.M.{unit}_FT_JS@out"
+            ctrl_col = self.ctrl_format.format(unit=unit)
+            flow_col = self.flow_format.format(unit=unit)
+            tmp_col = self.tmp_format.format(unit=unit)
+
 
             # 去除无关列
             other_units = [u for u in self.units if u != unit]
@@ -154,19 +163,19 @@ class UFAnalysisPipeline:
 
             # 逐机组事件识别:使用 UFEventClassifier(实例化单机组)
             event_clf = UFEventClassifier(unit, self.cfg.uf["inlet_codes"],
-                                          self.cfg.uf["physical_bw_code"], self.cfg.uf["chemical_bw_code"])
+                                          self.cfg.uf["physical_bw_code"], self.cfg.uf["chemical_bw_code"],
+                                          ctrl_col)
             df_unit = event_clf.classify(df_unit)  # 产生 event_type 列
-            df_unit_mark = self.initial_label.mark(df_unit)
-            seg_df = event_clf.segment(df_unit_mark)
+            df_unit_mark = self.initial_label.mark(df_unit) # 标记反冲洗事件后的前 N 个进水点
+            seg_df = event_clf.segment(df_unit_mark) # 根据 event_type 列编号事件段落
 
             # 对 seg_df 进行按 segment 分组后逐段过滤:
             const_flow_filter = ConstantFlowFilter(flow_col=flow_col, repeat_len=20)
             segments = const_flow_filter.filter(seg_df) # 去除出现网络错误的进水段
             segments = self.quality_filter.filter(segments) # 去除时间过短的进水段
-            stable_extractor = InletSegmentFilter(ctrl_col,
-                                                  stable_value=self.stable_inlet_code,
-                                                  min_points=self.min_points)
 
+            # 提取稳定进水段
+            stable_extractor = InletSegmentFilter(ctrl_col,stable_codes=self.stable_codes,min_points=self.min_points)
             stable_segments = stable_extractor.extract(segments)  # 提取稳定进水数据
 
             # 若无稳定进水段,则记录不稳定段并跳过该机组
@@ -180,7 +189,8 @@ class UFAnalysisPipeline:
             stable_segments = self.res_calc.calculate_for_segments(
                 stable_segments,
                 temp_col=self.temp_col,
-                flow_col=flow_col
+                flow_col=flow_col,
+                tmp_col=tmp_col,
             )
 
             # -----------------------------
@@ -193,9 +203,8 @@ class UFAnalysisPipeline:
             stable_segments = vsa.analyze_segments(stable_segments, col=self.temp_col, prefix="temp")
 
             # 跨膜压差统计
-            press_col = f"C.M.{unit}_DB@press_PV"
             upa = UFPressureAnalyzer(
-                press_col=press_col,
+                tmp_col=tmp_col,
                 head_n=self.segment_head_n,
                 tail_n=self.segment_tail_n,
             )
@@ -346,7 +355,7 @@ class UFAnalysisPipeline:
                     long_r2 = g["cycle_long_r2"].iloc[0] if "cycle_long_r2" in g.columns else None
 
                     if nuk_r2 is not None and long_r2 is not None:
-                        if nuk_r2 > 0.5 and long_r2 > 0.5:
+                        if nuk_r2 > 0.4 and long_r2 > 0.4:
                             valid_cycle_ids.append(cid)
 
                 # 3. 过滤后的化学周期数据

+ 93 - 0
models/uf-rl/uf_data_process/plot.py

@@ -0,0 +1,93 @@
+import os
+import glob
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+from matplotlib.font_manager import FontProperties
+
+# ===================== 配置 =====================
+data_dir = r"/datasets/UF_longting_data/processed\segments"
+target_col = "cycle_long_r2"
+
+# ===================== 中文字体设置 =====================
+# 注意:这里使用 SimHei 字体,可显示中文
+font = FontProperties(fname=r"C:\Windows\Fonts\simhei.ttf", size=12)
+
+# ===================== 读取所有 CSV =====================
+all_files = glob.glob(os.path.join(data_dir, "*.csv"))
+
+values = []
+
+for file in all_files:
+    try:
+        df = pd.read_csv(file)
+        if target_col in df.columns:
+            vals = df[target_col].dropna().values
+            values.append(vals)
+    except Exception as e:
+        print(f"读取失败: {file}, 错误: {e}")
+
+# 合并所有数据
+if len(values) == 0:
+    raise ValueError("未在任何 CSV 中找到有效的 cycle_long_R2 数据")
+
+data = np.concatenate(values)
+total_count = len(data)
+
+# ===================== 定义区间 =====================
+bins = [
+    -np.inf,
+    0.0,
+    0.5,
+    0.6,
+    0.7,
+    0.8,
+    0.9,
+    1.0
+]
+
+labels = [
+    "<0",
+    "0 – 0.5",
+    "0.5 – 0.6",
+    "0.6 – 0.7",
+    "0.7 – 0.8",
+    "0.8 – 0.9",
+    "0.9 – 1.0"
+]
+
+# ===================== 统计分布 =====================
+counts = pd.cut(
+    data,
+    bins=bins,
+    labels=labels,
+    right=True,
+    include_lowest=True
+).value_counts().sort_index()
+
+ratios = counts / total_count * 100
+
+# ===================== 输出结果 =====================
+result = pd.DataFrame({
+    "样本数": counts,
+    "占比 (%)": ratios.round(2)
+})
+
+print(f"\n总样本数: {total_count}\n")
+print(result)
+
+# ===================== 绘制柱状图 =====================
+plt.figure(figsize=(10, 6))
+plt.bar(labels, ratios, color='skyblue', edgecolor='black')
+plt.title("cycle_long_R2 数据分布柱状图", fontproperties=font)
+plt.xlabel("区间", fontproperties=font)
+plt.ylabel("占比 (%)", fontproperties=font)
+plt.ylim(0, 100)
+plt.grid(axis='y', linestyle='--', alpha=0.7)
+
+# 在柱子上显示百分比
+for i, v in enumerate(ratios):
+    plt.text(i, v + 1, f"{v:.1f}%", ha='center', va='bottom', fontsize=10, fontproperties=font)
+
+plt.tight_layout()
+plt.show()

+ 1 - 1
models/uf-rl/训练/uf_data_process/run_ufdata_pipeline.py → models/uf-rl/uf_data_process/run_ufdata_pipeline.py

@@ -15,7 +15,6 @@ def main():
     print("=====================================")
 
     # 1. 加载配置文件
-    CONFIG_PATH = UF_RL_ROOT / "config" / "uf_analyze_config.yaml"
     cfg = UFConfigLoader(CONFIG_PATH)
 
     # 2. 创建 pipeline
@@ -31,4 +30,5 @@ def main():
 
 
 if __name__ == "__main__":
+    CONFIG_PATH = UF_RL_ROOT / "longting" / "uf_analyze_config.yaml"
     main()

+ 0 - 0
models/uf-rl/训练/uf_train/README.md → models/uf-rl/uf_train/README.md


+ 0 - 0
models/uf-rl/训练/uf_train/UF_RL_架构问题与优化方案.md → models/uf-rl/uf_train/UF_RL_架构问题与优化方案.md


+ 0 - 0
models/uf-rl/训练/uf_train/UF_RL_训练与预测流程详解.md → models/uf-rl/uf_train/UF_RL_训练与预测流程详解.md


+ 0 - 0
models/uf-rl/训练/uf_train/UF_RL_详细技术文档.md → models/uf-rl/uf_train/UF_RL_详细技术文档.md


+ 0 - 0
models/uf-rl/jianding/__init__.py → models/uf-rl/uf_train/__init__.py


+ 0 - 0
models/uf-rl/longting/__init__.py → models/uf-rl/uf_train/data_to_rl/__init__.py


+ 0 - 0
models/uf-rl/训练/uf_train/data_to_rl/data_splitter.py → models/uf-rl/uf_train/data_to_rl/data_splitter.py


+ 0 - 0
models/uf-rl/训练/uf_train/data_to_rl/get_reset_pool.py → models/uf-rl/uf_train/data_to_rl/get_reset_pool.py


+ 0 - 0
models/uf-rl/训练/uf_train/data_to_rl/loader.py → models/uf-rl/uf_train/data_to_rl/loader.py


+ 3 - 4
models/uf-rl/训练/uf_train/data_to_rl/run_data_to_rl_pipeline.py → models/uf-rl/uf_train/data_to_rl/run_data_to_rl_pipeline.py

@@ -23,16 +23,15 @@ import time
 from state_space_bounds import extract_state_space_bounds
 from get_reset_pool import generate_reset_state_pool
 
+THIS_FILE = Path(__file__).resolve()
+UF_RL_ROOT = THIS_FILE.parents[2]
 
 def main():
     # --------------------------------------------------------
     # 配置文件路径(统一入口)
     # --------------------------------------------------------
-    config_path = Path(__file__).parent / "data_to_rl_config.yaml"
+    config_path = UF_RL_ROOT / "longting" / "data_to_rl_config.yaml"
 
-    print("======================================================")
-    print(" data_to_rl 数据准备流水线启动 ")
-    print("------------------------------------------------------")
     print(f" 配置文件: {config_path}")
     print("======================================================\n")
 

+ 0 - 0
models/uf-rl/训练/uf_train/data_to_rl/state_construction.py → models/uf-rl/uf_train/data_to_rl/state_construction.py


+ 0 - 0
models/uf-rl/训练/uf_train/data_to_rl/state_space_bounds.py → models/uf-rl/uf_train/data_to_rl/state_space_bounds.py


+ 0 - 0
models/uf-rl/xishan/__init__.py → models/uf-rl/uf_train/env/__init__.py


+ 17 - 8
models/uf-rl/训练/uf_train/env/env_params.py → models/uf-rl/uf_train/env/env_params.py

@@ -7,6 +7,8 @@ env_params.py
 这些类仅用于描述系统配置、状态语义和约束边界,不包含任何数值计算、
 物理模型、奖励逻辑或 Gym 接口实现。
 
+本文件中的类只定义参数结构,不负责加载配置。具体数值应从YAML文件加载后在主程序中实例化。
+
 ========================
 参数类总览
 ========================
@@ -240,6 +242,10 @@ class UFPhysicsParams:
     # CEB 间隔时间(小时)
     # 说明:每运行约 60 小时执行一次化学增强反洗 TODO: PARAM 当前采用保守测试参数48h
 
+    T_ceb_interval_times: int = 48
+    # CEB 间隔次数
+    # 说明: 每执行48次物理反冲洗进行一次CEB,与T_ceb_interval_h为互斥参数
+
     v_ceb_m3: float = 20.0
     # CEB 用水体积(m³)
 
@@ -248,16 +254,19 @@ class UFPhysicsParams:
 
     # ========== 新增:膜面积 ==========
     # 膜有效面积(锡山水厂配置:128组膜,每组40m²)
-    A = 128 * 40.0  # [m²]
+    A: float = 5120.0  # [m²]
 
     # 吨水电耗查找表
     energy_lookup: Dict[int, float] = field(default_factory=lambda: {
-        3600: 0.1034, 3660: 0.1031, 3720: 0.1029, 3780: 0.1026,
-        3840: 0.1023, 3900: 0.1021, 3960: 0.1019, 4020: 0.1017,
-        4080: 0.1015, 4140: 0.1012, 4200: 0.1011, 4260: 0.1008,
-        4320: 0.1007, 4380: 0.1005, 4440: 0.1003, 4500: 0.1001,
-        4560: 0.0999, 4620: 0.0998, 4680: 0.0996, 4740: 0.0995,
-        4800: 0.0993,
+        2700: 0.1088, 2760: 0.1083, 2820: 0.1078, 2880: 0.1074,
+        2940: 0.1070, 3000: 0.1066, 3060: 0.1062, 3120: 0.1059,
+        3180: 0.1055, 3240: 0.1052, 3300: 0.1049, 3360: 0.1045,
+        3420: 0.1042, 3480: 0.1039, 3540: 0.1036, 3600: 0.1034,
+        3660: 0.1031, 3720: 0.1029, 3780: 0.1026, 3840: 0.1023,
+        3900: 0.1021, 3960: 0.1019, 4020: 0.1017, 4080: 0.1015,
+        4140: 0.1012, 4200: 0.1011, 4260: 0.1008, 4320: 0.1007,
+        4380: 0.1005, 4440: 0.1003, 4500: 0.1001, 4560: 0.0999,
+        4620: 0.0998, 4680: 0.0996, 4740: 0.0995, 4800: 0.0993,
     })
 
 @dataclass(frozen=True)
@@ -300,7 +309,7 @@ class UFRewardParams:
     w_rec: float = 1.0             # 回收率权重
 
     # 残余污染
-    k_res: float = 10.0
+    k_res: float = 5.0
     residual_ref_ratio: float = None   # 动态=1/max_episode_steps
     w_res: float = 2.0                 # 残余污染权重
 

+ 0 - 0
models/uf-rl/训练/uf_train/env/env_reset.py → models/uf-rl/uf_train/env/env_reset.py


+ 0 - 0
models/uf-rl/训练/uf_train/env/env_visual.py → models/uf-rl/uf_train/env/env_visual.py


+ 58 - 0
models/uf-rl/uf_train/env/reset_plot.py

@@ -0,0 +1,58 @@
+import numpy as np
+import matplotlib.pyplot as plt
+
+# 训练进度
+progress = np.linspace(0, 1, 200)
+
+# -----------------------------
+# 参数设置(可调)
+# -----------------------------
+alpha = 0.5    # 虚拟工况最终最大占比
+k = 10.0       # Sigmoid 陡峭程度
+p0 = 0.7       # 虚拟工况启动拐点
+beta = 0.5     # 扰动工况线性增长系数
+
+# -----------------------------
+# 权重定义
+# -----------------------------
+# 虚拟工况(非线性,后期快速增长)
+w_virtual = alpha / (1.0 + np.exp(-k * (progress - p0)))
+
+# 扰动工况(线性增长)
+w_perturb = beta * progress
+
+# 真实工况(剩余比例)
+w_real = 1.0 - w_virtual - w_perturb
+w_real = np.clip(w_real, 0.0, 1.0)  # 数值安全
+
+# -----------------------------
+# 扰动幅度
+# -----------------------------
+perturb_scale = 0.02 + 0.04 * progress
+
+# -----------------------------
+# 绘图
+# -----------------------------
+fig, ax1 = plt.subplots(figsize=(8, 5))
+
+ax1.plot(progress, w_real, label="w_real", linewidth=2)
+ax1.plot(progress, w_perturb, label="w_perturb", linewidth=2)
+ax1.plot(progress, w_virtual, label="w_virtual", linewidth=2)
+
+ax1.set_xlabel("Training Progress")
+ax1.set_ylabel("Sampling Weights")
+ax1.set_ylim(0, 1.05)
+ax1.grid(True, linestyle="--", alpha=0.5)
+ax1.legend(loc="upper left")
+
+# 第二纵轴:扰动幅度
+ax2 = ax1.twinx()
+ax2.plot(progress, perturb_scale, label="perturb_scale",
+         linestyle="--", linewidth=2)
+ax2.set_ylabel("Perturb Scale")
+ax2.set_ylim(0, 0.07)
+ax2.legend(loc="upper right")
+
+plt.title("Progressive Reset Sampling Strategy")
+plt.tight_layout()
+plt.show()

+ 0 - 0
models/uf-rl/训练/uf_train/env/resistance_model_bw.pth → models/uf-rl/uf_train/env/resistance_model_bw.pth


+ 0 - 0
models/uf-rl/训练/uf_train/env/resistance_model_fp.pth → models/uf-rl/uf_train/env/resistance_model_fp.pth


+ 2 - 2
models/uf-rl/训练/uf_train/env/uf_env.py → models/uf-rl/uf_train/env/uf_env.py

@@ -436,8 +436,8 @@ class UFSuperCycleEnv(gym.Env):
 
         # ========== 残余污染惩罚项 ==========
         # 新参考点:每步允许上升比例 = 1 / max_episode_steps
-        # 平衡点 = 0.5 / max_episode_steps
-        ref_residual = 0.5 / self.max_episode_steps
+        # 平衡点 = 0.8 / max_episode_steps
+        ref_residual = 0.8 / self.max_episode_steps
 
         # 使用 tanh 构建惩罚曲线
         # - residual_ratio < 平衡点时,res_penalty > 0(奖励低污染)

+ 19 - 7
models/uf-rl/训练/uf_train/env/uf_physics.py → models/uf-rl/uf_train/env/uf_physics.py

@@ -1,5 +1,5 @@
 """
-uf_physics.py
+uf_physics_48h.py
 
 超滤(UF)系统物理模型与确定性计算规则模块。
 
@@ -40,16 +40,19 @@ class UFPhysicsModel:
             phys_params: UFPhysicsParams,
             resistance_model_fp=None,
             resistance_model_bw=None,
+            IS_TIMES: bool = False,
     ):
         """
         参数:
             phys_params: 物理/工艺固定参数
             resistance_model_fp: 过滤阶段阻力增长模型
             resistance_model_bw: 反洗阶段阻力下降模型
+            IS_TIMES: CEB是否为固定次数,T为固定次数
         """
         self.p = phys_params
         self.model_fp = resistance_model_fp
         self.model_bw = resistance_model_bw
+        self.IS_TIMES = IS_TIMES
 
     # ==========================================================
     # 水温-粘度关系
@@ -252,10 +255,12 @@ class UFPhysicsModel:
         """
         return float(self.p.q_bw_m3ph * t_bw_s / 3600.0)
 
-    def simulate_one_supercycle(self, state: UFState, L_s: float, t_bw_s: float):
+    def simulate_one_supercycle(self, state: UFState, L_s: float, t_bw_s: float) -> UFState:
         """
         模拟一个超级周期(Super Cycle)
         返回 info 字典 + 更新后的 UFState
+
+        IS_TIMES: False CEB执行是否采用次数控制,默认为 False, 采用时长控制
         """
         # ========== 初始化周期参数 ==========
         L_h = float(L_s) / 3600.0  # 过滤时长转换:秒 → 小时
@@ -276,10 +281,13 @@ class UFPhysicsModel:
         t_small_cycle_h = (L_s + t_bw_s) / 3600.0  # [小时]
 
         # 计算一个超级周期内包含多少个小周期
-        # k = floor(CEB间隔时间 / 小周期时长)
-        k_bw_per_ceb = int(np.floor(self.p.T_ceb_interval_h / t_small_cycle_h))
-        if k_bw_per_ceb < 1:
-            k_bw_per_ceb = 1  # 至少包含1个小周期
+        if self.IS_TIMES: # 采用次数控制
+            k_bw_per_ceb = self.p.T_ceb_interval_times
+        else: # 采用时长控制
+            # k = floor(CEB间隔时间 / 小周期时长)
+            k_bw_per_ceb = int(np.floor(self.p.T_ceb_interval_h / t_small_cycle_h))
+            if k_bw_per_ceb < 1:
+                k_bw_per_ceb = 1  # 至少包含1个小周期
 
         # ========== 循环模拟每个小周期(过滤 + 物理反洗) ==========
         for idx in range(k_bw_per_ceb):
@@ -355,7 +363,10 @@ class UFPhysicsModel:
             self.resistance_from_tmp(max_tmp_during_filtration, state.q_UF, state.temp),
             1e-6
         )
-        residual_ratio = (R_after_ceb - initial_R) / delta_R_allow
+        if delta_R_allow > 50:
+            residual_ratio = (R_after_ceb - initial_R) / delta_R_allow
+        else:
+            residual_ratio = 1.0
 
         # ========== 构建性能指标字典 ==========
         info = {
@@ -503,6 +514,7 @@ class UFPhysicsModel:
                     curr_state,
                     L_s=L_s,
                     t_bw_s=t_bw_s
+
                 )
             except Exception:
                 # 任何异常都视为物理不可行

+ 0 - 0
models/uf-rl/训练/uf_train/env/uf_resistance_models_define.py → models/uf-rl/uf_train/env/uf_resistance_models_define.py


+ 0 - 0
models/uf-rl/训练/uf_train/env/uf_resistance_models_load.py → models/uf-rl/uf_train/env/uf_resistance_models_load.py


+ 0 - 0
models/uf-rl/训练/uf_train/__init__.py → models/uf-rl/uf_train/rl_model/DQN/__init__.py


+ 0 - 0
models/uf-rl/训练/uf_train/rl_model/DQN/dqn_decider.py → models/uf-rl/uf_train/rl_model/DQN/dqn_decider.py


+ 0 - 0
models/uf-rl/训练/uf_train/rl_model/DQN/dqn_params.py → models/uf-rl/uf_train/rl_model/DQN/dqn_params.py


+ 0 - 0
models/uf-rl/训练/uf_train/rl_model/DQN/dqn_statebuilder.py → models/uf-rl/uf_train/rl_model/DQN/dqn_statebuilder.py


+ 0 - 0
models/uf-rl/训练/uf_train/rl_model/DQN/dqn_trainer.py → models/uf-rl/uf_train/rl_model/DQN/dqn_trainer.py


+ 0 - 0
models/uf-rl/训练/uf_train/rl_model/DQN/online_datasets/UF1_init_cycle.csv → models/uf-rl/uf_train/rl_model/DQN/online_datasets/UF1_init_cycle.csv


+ 0 - 0
models/uf-rl/训练/uf_train/rl_model/DQN/online_datasets/UF1_prev_cycle.csv → models/uf-rl/uf_train/rl_model/DQN/online_datasets/UF1_prev_cycle.csv


+ 26 - 6
models/uf-rl/训练/uf_train/rl_model/DQN/run_dqn_decide.py → models/uf-rl/uf_train/rl_model/DQN/run_dqn_decide.py

@@ -15,27 +15,34 @@ UF 超滤 DQN 决策主入口(Inference / Online Assist)
 from pathlib import Path
 import numpy as np
 
+# ============================================================
+# 1. 导入模块
+# ============================================================
+CURRENT_DIR = Path(__file__).resolve().parent
+
+PROJECT_ROOT = CURRENT_DIR.parents[2]     # uf_train  # uf-rl
+
 # ========== 参数 / 物理 ==========
 from uf_train.env.uf_resistance_models_load import load_resistance_models
 from uf_train.env.uf_physics import UFPhysicsModel
 from uf_train.env.env_params import UFState, UFPhysicsParams, UFStateBounds, UFRewardParams, UFActionSpec
-
+from uf_train.env.env_config_loader import EnvConfigLoader, create_env_params_from_yaml
 
 # ========== 决策器 ==========
 from uf_train.rl_model.DQN.dqn_decider import UFDQNDecider
 
 
-def build_physics():
+def build_physics(IS_TIMES, phys_params):
     """
     构造与训练一致的物理模型(只做一次)
     """
-    phys_params = UFPhysicsParams()
     res_fp, res_bw = load_resistance_models(phys_params)
 
     physics = UFPhysicsModel(
         phys_params=phys_params,
         resistance_model_fp=res_fp,
         resistance_model_bw=res_bw,
+        IS_TIMES = IS_TIMES
     )
     return physics
 
@@ -213,13 +220,26 @@ def run_dqn_decide(
 # ==============================
 if __name__ == "__main__":
 
-    MODEL_PATH = "model/dqn_model.zip"
+    MODEL_PATH = PROJECT_ROOT / "xishan" / "48h_dqn_model.zip"
+    ENV_CONFIG_PATH = PROJECT_ROOT / "xishan" / "env_config.yaml"
     TMP0 = 0.019  # 原始 TMP0
     q_UF = 300 # 进水流量
     temp = 20.0 #进水温度
-    current_state = UFState(TMP=TMP0, q_UF=q_UF, temp=temp)
+    IS_TIMES = False # 新增指定变量,表示CEB间隔为时间控制/次数控制,T表示48次bw一次CEB,F表示48h一次CEB
 
-    physics = build_physics()
+    current_state = UFState(TMP=TMP0, q_UF=q_UF, temp=temp)
+    config_loader = EnvConfigLoader(ENV_CONFIG_PATH)
+    config_loader.validate_config()
+    config_loader.print_config_summary()
+    (
+        uf_state_default,  # UFState默认值(可用于reset)
+        phys_params,  # UFPhysicsParams
+        action_spec,  # UFActionSpec
+        reward_params,  # UFRewardParams
+        state_bounds  # UFStateBounds
+    ) = create_env_params_from_yaml(ENV_CONFIG_PATH)
+
+    physics = build_physics(IS_TIMES, phys_params)
 
     action_id, model_L_s, model_t_bw_s = run_dqn_decide(
         model_path=MODEL_PATH,

+ 4 - 2
models/uf-rl/训练/uf_train/rl_model/DQN/run_dqn_deicde_totalstate.py → models/uf-rl/uf_train/rl_model/DQN/run_dqn_deicde_totalstate.py

@@ -32,7 +32,7 @@ from uf_train.rl_model.DQN.dqn_statebuilder import DQNStateBuilder
 
 
 
-def build_physics():
+def build_physics(IS_TIMES):
     """
     构造与训练一致的物理模型(只做一次)
     """
@@ -43,6 +43,7 @@ def build_physics():
         phys_params=phys_params,
         resistance_model_fp=res_fp,
         resistance_model_bw=res_bw,
+        IS_TIMES = IS_TIMES
     )
     return physics
 
@@ -227,6 +228,7 @@ if __name__ == "__main__":
     MODEL_PATH = "model/dqn_model.zip"
     prev_cycle_csv = "online_datasets/UF1_prev_cycle.csv"
     init_cycle_csv = "online_datasets/UF1_init_cycle.csv"
+    IS_TIMES = False # 新增指定变量,表示CEB间隔为时间控制/次数控制,T表示48次bw一次CEB,F表示48h一次CEB
 
     # 构建强化学习状态
     state_builder = DQNStateBuilder(config_path=CONFIG_PATH)
@@ -235,7 +237,7 @@ if __name__ == "__main__":
         init_cycle_csv=init_cycle_csv,
     )
 
-    physics = build_physics()
+    physics = build_physics(IS_TIMES)
 
     action_id, model_L_s, model_t_bw_s = run_dqn_decide(
         model_path=MODEL_PATH,

+ 33 - 10
models/uf-rl/训练/uf_train/rl_model/DQN/run_dqn_train.py → models/uf-rl/uf_train/rl_model/DQN/run_dqn_train.py

@@ -23,12 +23,16 @@ from uf_train.data_to_rl.data_splitter import ResetStatePoolLoader
 # ---------- 阻力模型 ----------
 from uf_train.env.uf_resistance_models_load import load_resistance_models
 from uf_train.env.uf_physics import UFPhysicsModel
-from uf_train.env.env_params import  UFPhysicsParams, UFStateBounds, UFRewardParams, UFActionSpec
+
+# ---------- 强化学习环境 ----------
+from uf_train.env.env_params import (UFState,UFPhysicsParams,UFActionSpec,UFRewardParams,UFStateBounds)
+from uf_train.env.env_config_loader import EnvConfigLoader, create_env_params_from_yaml
 from uf_train.env.uf_env import UFSuperCycleEnv
 
 from uf_train.env.env_visual import UFEpisodeRecorder, UFTrainingCallback
 
 from uf_train.rl_model.DQN.dqn_params import DQNParams
+from uf_train.rl_model.DQN.dqn_config_loader import DQNConfigLoader, load_dqn_config_with_validation
 from uf_train.rl_model.DQN.dqn_trainer import DQNTrainer
 
 
@@ -106,6 +110,22 @@ def make_env(
 # 6. 主流程
 # ============================================================
 def main():
+    # 创建配置加载器
+    config_loader = EnvConfigLoader(ENV_CONFIG_PATH)
+
+    # 验证配置
+    config_loader.validate_config()
+    config_loader.print_config_summary()
+
+    # 加载所有参数类
+    (
+        uf_state_default,  # UFState默认值(可用于reset)
+        phys_params,  # UFPhysicsParams
+        action_spec,  # UFActionSpec
+        reward_params,  # UFRewardParams
+        state_bounds  # UFStateBounds
+    ) = create_env_params_from_yaml(ENV_CONFIG_PATH)
+
     # ---------- Seed ----------
     set_global_seed(RANDOM_SEED)
 
@@ -113,7 +133,6 @@ def main():
     train_pool, val_pool = load_reset_state_pools()
 
     # ---------- Resistance models ----------
-    phys_params = UFPhysicsParams()
     res_fp, res_bw = load_resistance_models(phys_params)
 
     # ---------- Physics ----------
@@ -121,13 +140,9 @@ def main():
         phys_params=phys_params,
         resistance_model_fp=res_fp,
         resistance_model_bw=res_bw,
+        IS_TIMES=IS_TIMES
     )
 
-    # ---------- RL specs ----------
-    reward_params = UFRewardParams()
-    action_spec = UFActionSpec()
-    state_bounds = UFStateBounds()
-
     # ---------- Environments ----------
     train_env = DummyVecEnv([
         make_env(
@@ -156,7 +171,11 @@ def main():
     callback = UFTrainingCallback(recorder, verbose=1)
 
     # ---------- Trainer ----------
-    dqn_params = DQNParams(remark="uf_dqn_real_reset")
+    # ========== 2. 加载DQN配置 ==========
+    dqn_loader = DQNConfigLoader(MODEL_CONFIG_PATH)
+    dqn_loader.validate_config()
+    dqn_params = dqn_loader.load_params()
+    dqn_loader.print_config_summary()
     trainer = DQNTrainer(
         env=train_env,
         params=dqn_params,
@@ -270,11 +289,15 @@ if __name__ == "__main__":
     # 2. 全局配置
     # ============================================================
     RANDOM_SEED = 2025
-    TOTAL_TIMESTEPS = 1500000
+    TOTAL_TIMESTEPS = 300000
+    IS_TIMES = True
 
     RESET_STATE_CSV = (
             PROJECT_ROOT
-            / "datasets/rl_ready/output/reset_state_pool.csv"
+            / "datasets/UF_xishan_data/rl_ready/output/reset_state_pool.csv"
     )
 
+    ENV_CONFIG_PATH = PROJECT_ROOT / "xishan" / "env_config.yaml"
+    MODEL_CONFIG_PATH = PROJECT_ROOT / "xishan" / "dqn_config.yaml"
+
     main()

+ 0 - 0
models/uf-rl/训练/uf_train/env/__init__.py → models/uf-rl/uf_train/rl_model/__init__.py


+ 0 - 186
models/uf-rl/uf_train/rl_model/common/train_entry.py

@@ -1,186 +0,0 @@
-"""
-通用强化学习训练入口(当前绑定 DQN,实现已验证)
-仅负责:
-- 构造环境
-- 构造 Trainer
-- 启动训练并保存模型
-"""
-
-import random
-from pathlib import Path
-import numpy as np
-import torch
-
-# ============================================================
-# 1. 路径解析
-# ============================================================
-CURRENT_DIR = Path(__file__).resolve().parent
-PROJECT_ROOT = CURRENT_DIR.parents[2]   # uf_train / uf-rl
-
-
-# ============================================================
-# 2. 导入:数据 / 环境
-# ============================================================
-from uf_train.data_to_rl.data_splitter import ResetStatePoolLoader
-
-from uf_train.env.uf_resistance_models_load import load_resistance_models
-from uf_train.env.uf_physics import UFPhysicsModel
-from uf_train.env.env_params import (
-    UFPhysicsParams,
-    UFStateBounds,
-    UFRewardParams,
-    UFActionSpec,
-)
-from uf_train.env.uf_env import UFSuperCycleEnv
-
-from uf_train.env.env_visual import UFEpisodeRecorder, UFTrainingCallback
-
-
-# ============================================================
-# 3. 导入:算法(当前为 DQN)
-# ============================================================
-from uf_train.rl_model.DQN.dqn_params import DQNParams
-from uf_train.rl_model.DQN.dqn_trainer import DQNTrainer
-
-
-# ============================================================
-# 4. SB3 VecEnv
-# ============================================================
-from stable_baselines3.common.monitor import Monitor
-from stable_baselines3.common.vec_env import DummyVecEnv
-
-
-# ============================================================
-# 5. 随机种子
-# ============================================================
-def set_global_seed(seed: int):
-    random.seed(seed)
-    np.random.seed(seed)
-    torch.manual_seed(seed)
-    torch.cuda.manual_seed_all(seed)
-
-    torch.backends.cudnn.deterministic = True
-    torch.backends.cudnn.benchmark = False
-
-    print(f"[Seed] Global random seed = {seed}")
-
-
-# ============================================================
-# 6. Reset State Pool 加载
-# ============================================================
-def load_reset_state_pool():
-    loader = ResetStatePoolLoader(
-        csv_path=RESET_STATE_CSV,
-        train_ratio=0.8,
-        shuffle=True,
-        random_state=RANDOM_SEED,
-    )
-
-    train_pool, _ = loader.split()
-
-    print("[Data] Reset state pool loaded")
-    print(f"       Train pool size: {len(train_pool)}")
-
-    return train_pool
-
-
-# ============================================================
-# 7. 环境构造函数
-# ============================================================
-def make_env(
-    physics: UFPhysicsModel,
-    reward_params: UFRewardParams,
-    action_spec: UFActionSpec,
-    statebounds: UFStateBounds,
-    reset_state_pool,
-    seed: int,
-):
-    def _init():
-        env = UFSuperCycleEnv(
-            physics=physics,
-            reward_params=reward_params,
-            action_spec=action_spec,
-            statebounds=statebounds,
-            real_state_pool=reset_state_pool,
-            RANDOM_SEED=seed,
-        )
-        env.action_space.seed(seed)
-        env.observation_space.seed(seed)
-        return Monitor(env)
-
-    return _init
-
-
-# ============================================================
-# 8. 主训练流程
-# ============================================================
-def main():
-    # ---------- Seed ----------
-    set_global_seed(RANDOM_SEED)
-
-    # ---------- Reset states ----------
-    train_pool = load_reset_state_pool()
-
-    # ---------- Resistance models ----------
-    phys_params = UFPhysicsParams()
-    res_fp, res_bw = load_resistance_models(phys_params)
-
-    # ---------- Physics ----------
-    physics_model = UFPhysicsModel(
-        phys_params=phys_params,
-        resistance_model_fp=res_fp,
-        resistance_model_bw=res_bw,
-    )
-
-    # ---------- RL specs ----------
-    reward_params = UFRewardParams()
-    action_spec = UFActionSpec()
-    state_bounds = UFStateBounds()
-
-    # ---------- Training Env ----------
-    train_env = DummyVecEnv([
-        make_env(
-            physics_model,
-            reward_params,
-            action_spec,
-            state_bounds,
-            train_pool,
-            RANDOM_SEED,
-        )
-    ])
-
-    # ---------- Callback ----------
-    recorder = UFEpisodeRecorder()
-    callback = UFTrainingCallback(recorder, verbose=1)
-
-    # ---------- Trainer ----------
-    algo_params = DQNParams(remark="uf_dqn_train_only")
-
-    trainer = DQNTrainer(
-        env=train_env,
-        params=algo_params,
-        callback=callback,
-        PROJECT_ROOT=PROJECT_ROOT,
-    )
-
-    # ---------- Training ----------
-    print("\n[Train] Start training")
-    trainer.train(total_timesteps=TOTAL_TIMESTEPS)
-    trainer.save()
-
-    print("[Train] Finished")
-
-
-# ============================================================
-# 9. 入口
-# ============================================================
-if __name__ == "__main__":
-    RANDOM_SEED = 2025
-    TOTAL_TIMESTEPS = 1_500_000
-
-    RESET_STATE_CSV = (
-        PROJECT_ROOT
-        / "datasets/rl_ready/output/reset_state_pool.csv"
-    )
-
-    main()

BIN
models/uf-rl/xishan/48h_dqn_model.zip


BIN
models/uf-rl/xishan/48times_dqn_model.zip


+ 3 - 3
models/uf-rl/训练/uf_train/data_to_rl/data_to_rl_config.yaml → models/uf-rl/xishan/data_to_rl_config.yaml

@@ -9,7 +9,7 @@ Paths:
     不同工程师只需修改这一项即可迁移环境。
 
   raw_data:
-    filtered_cycles_dir: "models/uf-rl/datasets/processed/filter_segments"
+    filtered_cycles_dir: "models/uf-rl/datasets/UF_xishan_data/processed/filter_segments"
     cycle_file_pattern: "UF{unit}_filtered_cycles.csv"
 
     enabled_units: [1, 2, 4]
@@ -24,8 +24,8 @@ Paths:
         - 机组编号仅用于筛选,不进入状态空间
 
   data_to_rl:
-    output_dir: "models/uf-rl/datasets/rl_ready/output"
-    cache_dir: "models/uf-rl/datasets/rl_ready/cache"
+    output_dir: "models/uf-rl/datasets/UF_xishan_data/rl_ready/output"
+    cache_dir: "models/uf-rl/datasets/UF_xishan_data/rl_ready/cache"
 
     __comment__: >
       data_to_rl 模块输出内容:

+ 26 - 0
models/uf-rl/xishan/dqn_config.yaml

@@ -0,0 +1,26 @@
+# ============================================================
+# DQN 超参数配置
+# ============================================================
+# 神经网络参数
+learning_rate: 0.0001           # 学习率 1e-4
+
+# 经验回放参数
+buffer_size: 100000              # 经验回放缓冲区大小
+learning_starts: 10000           # 开始训练前收集的步数
+batch_size: 32                   # 训练批次大小
+
+# 强化学习参数
+gamma: 0.95                      # 折扣因子
+train_freq: 4                    # 训练频率(步数)
+
+# 目标网络参数
+target_update_interval: 1        # 目标网络更新间隔
+tau: 0.005                       # 软更新系数
+
+# 探索策略参数
+exploration_initial_eps: 1.0     # 初始探索率
+exploration_fraction: 0.3        # 探索率衰减比例
+exploration_final_eps: 0.02      # 最终探索率
+
+# 日志参数
+remark: "uf_dqn_real_reset"      # 实验备注

+ 127 - 0
models/uf-rl/xishan/env_config.yaml

@@ -0,0 +1,127 @@
+UFState:
+  # ===== 膜动态运行参数 =====
+  q_UF: 360.0
+  TMP: 0.03
+  temp: 25.0
+  R: 200.0
+
+  # ===== 膜阻力模型参数 =====
+  nuK: 170.0
+  slope: 2.0
+  power: 1.032
+  ceb_removal: 100.0
+
+
+UFPhysicsParams:
+  # ===== TMP 全局约束 =====
+  global_TMP_hard_limit: 0.08
+  global_TMP_soft_limit: 0.06
+
+  # ===== 物理反洗参数 =====
+  tau_bw_s: 20.0
+  gamma_t: 1.0
+  q_bw_m3ph: 1000.0
+
+  # ===== CEB 化学反洗参数 =====
+  T_ceb_interval_h: 48.0
+  T_ceb_interval_times: 48
+  v_ceb_m3: 20.0
+  t_ceb_s: 2400.0
+
+  # ===== 膜组件参数 =====
+  A: 5120.0
+
+  # ===== 吨水电耗查找表 =====
+  energy_lookup:
+    2700: 0.1088
+    2760: 0.1083
+    2820: 0.1078
+    2880: 0.1074
+    2940: 0.1070
+    3000: 0.1066
+    3060: 0.1062
+    3120: 0.1059
+    3180: 0.1055
+    3240: 0.1052
+    3300: 0.1049
+    3360: 0.1045
+    3420: 0.1042
+    3480: 0.1039
+    3540: 0.1036
+    3600: 0.1034
+    3660: 0.1031
+    3720: 0.1029
+    3780: 0.1026
+    3840: 0.1023
+    3900: 0.1021
+    3960: 0.1019
+    4020: 0.1017
+    4080: 0.1015
+    4140: 0.1012
+    4200: 0.1011
+    4260: 0.1008
+    4320: 0.1007
+    4380: 0.1005
+    4440: 0.1003
+    4500: 0.1001
+    4560: 0.0999
+    4620: 0.0998
+    4680: 0.0996
+    4740: 0.0995
+    4800: 0.0993
+
+
+UFActionSpec:
+  L_min_s: 3800.0
+  L_max_s: 4800.0
+  t_bw_min_s: 40.0
+  t_bw_max_s: 60.0
+  L_step_s: 60.0
+  t_bw_step_s: 5.0
+
+
+UFRewardParams:
+  global_TMP_hard_limit: 0.08
+  global_TMP_soft_limit: 0.06
+  w_tmp_hard: 5.0
+  w_tmp: 1.5
+  p: 3.0
+  w_trend: 1.0
+
+  k_rec: 5.0
+  rec_low: 0.92
+  rec_high: 0.99
+  w_rec: 1.0
+
+  k_res: 10.0
+  residual_ref_ratio: null    # ✅ Python None 的正确表示
+  w_res: 2.0
+
+  k_energy: 5.0
+  energy_low: 0.0993
+  energy_high: 0.1034
+  energy_ref: 0.1011
+  w_energy: 1.0
+
+
+UFStateBounds:
+  q_UF_min: 210.0
+  q_UF_max: 380.0
+
+  temp_min: 16.0
+  temp_max: 32.0
+
+  TMP0_min: 0.01
+  TMP0_max: 0.045
+  global_TMP_hard_limit: 0.08
+
+  nuK_min: 40.0
+  nuK_max: 260.0
+
+  slope_min: 0.03
+  slope_max: 27.0
+  power_min: 0.4
+  power_max: 2.2
+
+  ceb_removal_min: 40.0
+  ceb_removal_max: 250.0

+ 11 - 7
models/uf-rl/训练/config/uf_analyze_config.yaml → models/uf-rl/xishan/uf_analyze_config.yaml

@@ -2,18 +2,22 @@ UF:
   units: ["UF1", "UF2", "UF3", "UF4"]
   area_m2: 128 * 40
 
-  inlet_codes: [21.0, 22.0, 23.0, 24.0, 25.0, 26.0]
-  stable_inlet_code: 26.0
+  inlet_codes: [21.0, 26.0]
+  stable_inlet_code: [24.0, 26.0]
 
-  physical_bw_code: 45.0
-  chemical_bw_code: 95.0
+  physical_bw_code: [40.0, 50.0]
+  chemical_bw_code: [90.0, 100.0]
 
   # 列名
   flow_col_template: "C.M.{unit}_FT_JS@out"
+  tmp_col: "C.M.{unit}_DB@press_PV"
+  press_col: "C.M.{unit}_PT_JS@out"
+  ctrl_col: "C.M.{unit}_DB@word_control"
   temp_col: "C.M.RO_TT_ZJS@out"
   orp_col: "C.M.UF_ORP_ZCS@out"
   cond_col: "C.M.RO_Cond_ZJS@out"
 
+
 Params:
   # 稳定段提取
   min_stable_points: 30
@@ -36,9 +40,9 @@ Plot:
 Paths:
   project_root: "E:/Greentech" # 请根据项目根目录修改相应路径
 
-  raw_data_path: "models/uf-rl/datasets/raw"
-  output_path: "models/uf-rl/datasets/processed/segments"
-  filter_output_path: "models/uf-rl/datasets/processed/filter_segments"
+  raw_data_path: "models/uf-rl/datasets/UF_xishan_data/raw"
+  output_path: "models/uf-rl/datasets/UF_xishan_data/processed/segments"
+  filter_output_path: "models/uf-rl/datasets/UF_xishan_data/processed/filter_segments"
 
   output_format: "csv"
 

+ 0 - 25
models/uf-rl/训练/config/dqn_config.yaml

@@ -1,25 +0,0 @@
-# ==================== DQN 超参数配置 ====================
-
-# ===== 神经网络参数 =====
-learning_rate: 1.0e-4
-
-# ===== 经验回放参数 =====
-buffer_size: 100000
-learning_starts: 10000
-batch_size: 32
-
-# ===== 强化学习核心参数 =====
-gamma: 0.95
-train_freq: 4
-
-# ===== 目标网络更新参数 =====
-target_update_interval: 1
-tau: 0.005
-
-# ===== 探索策略(ε-greedy) =====
-exploration_initial_eps: 1.0
-exploration_fraction: 0.3
-exploration_final_eps: 0.02
-
-# ===== 实验标识 =====
-remark: "default"

+ 0 - 138
models/uf-rl/训练/uf_train/env/check_initial_state.py

@@ -1,138 +0,0 @@
-# check_initial_state.py
-"""
-检查初始状态是否为“必死状态”(conservatively dead):
-1) 实例化 base_params(优先使用 rl_dqn_env 中提供的 base_params 或 UFParams)
-2) 实例化环境类 UFSuperCycleEnv(base_params)
-3) 调用 env.generate_initial_state() 生成 env.current_params(不调用 reset())
-4) 用最保守策略 (L_s=3600s, t_bw_s=60s) 连续模拟 max_steps 次,
-   若任意一次 is_dead_cycle(info) 返回 False 则判定为必死(返回 True),否则返回 False。
-"""
-
-from typing import Any
-import copy
-import traceback
-
-# 从 rl_dqn_env 导入必需项
-try:
-    from uf_env import (
-        simulate_one_supercycle,
-        is_dead_cycle,
-        UFSuperCycleEnv,
-        UFParams,       # 如果模块里有 UFParams 类就导入
-        base_params     # 如果模块直接提供 base_params 实例也尝试导入
-    )
-except Exception:
-    # 有可能某些名字不存在 —— 我们会稍后用回退方案处理
-    # 先导入模块并再尝试访问属性,确保错误信息更友好
-    import importlib
-    rl = importlib.import_module("rl_dqn_env")
-    simulate_one_supercycle = getattr(rl, "simulate_one_supercycle", None)
-    is_dead_cycle = getattr(rl, "is_dead_cycle", None)
-    UFSuperCycleEnv = getattr(rl, "UFSuperCycleEnv", None)
-    UFParams = getattr(rl, "UFParams", None)
-    base_params = getattr(rl, "base_params", None)
-
-# 检查导入完整性
-_missing = []
-if simulate_one_supercycle is None:
-    _missing.append("simulate_one_supercycle")
-if is_dead_cycle is None:
-    _missing.append("is_dead_cycle")
-if UFSuperCycleEnv is None:
-    _missing.append("UFSuperCycleEnv")
-if _missing:
-    raise ImportError(f"无法从 rl_dqn_env 导入以下必要项: {', '.join(_missing)}")
-
-def is_dead_initial_state_env(env: UFSuperCycleEnv, max_steps: int = 15,
-                              L_s: int = 4200, t_bw_s: int = 50,
-                              verbose: bool = True) -> bool:
-    """
-    使用 env.current_params 作为初始状态判断是否为必死状态(保守策略)。
-
-    参数:
-        env: 已实例化的 UFSuperCycleEnv(必须包含 generate_initial_state() 与 current_params)
-        max_steps: 模拟步数(默认 15)
-        L_s: 过滤时长(s),保守值 3600
-        t_bw_s: 物理反洗时长(s),保守值 60
-        verbose: 是否打印每步结果
-
-    返回:
-        True 表示必死(conservatively dead)
-        False 表示可行
-    """
-    # 1) 确保 env 有 current_params,并且 generate_initial_state 可用
-    if not hasattr(env, "generate_initial_state"):
-        raise AttributeError("env 缺少 generate_initial_state() 方法。")
-    # 生成初始状态(不会调用 reset)
-    env.generate_initial_state()
-
-    if not hasattr(env, "current_params"):
-        raise AttributeError("env.generate_initial_state() 未设置 env.current_params。")
-
-    curr_p = copy.deepcopy(env.current_params)
-
-    for step in range(1, max_steps + 1):
-        try:
-            info, next_params = simulate_one_supercycle(curr_p, L_s, t_bw_s)
-        except Exception as e:
-            # 如果 simulate 出错,把异常视为“失败”(保守处理)
-            if verbose:
-                print(f"[Step {step}] simulate_one_supercycle 抛出异常,视为失败。异常信息:{e}")
-                traceback.print_exc()
-            return True
-
-        success = is_dead_cycle(info)  # True 表示成功循环
-
-        if verbose:
-            print(f"[Step {step}] 循环结果:{'成功' if success else '失败'}")
-            # 如果 info 中有关键诊断字段,打印简要信息
-            try:
-                print(f"     TMP0: {info.get('TMP0')},max_TMP: {info.get('max_TMP_during_filtration')}, recovery: {info.get('recovery')}, "
-                      f"R0: {info.get('R0')}, R_after_ceb: {info.get('R_after_ceb')}")
-            except Exception:
-                pass
-
-        if not success:
-            if verbose:
-                print(f"在第 {step} 步检测到失败,判定为必死初始状态(conservatively dead)。")
-            return True
-
-        # 否则继续,用 next_params 作为下一步起始参数
-        curr_p = next_params
-
-    if verbose:
-        print(f"{max_steps} 步均成功,初始状态判定为可行(non-dead)。")
-    return False
-
-
-if __name__ == "__main__":
-    print("=== check_initial_state.py: 使用 env.generate_initial_state() 检查初始状态是否为必死 ===")
-
-    try:
-        # 1) 构造 base_params
-        if base_params is not None:
-            bp = base_params
-            print("使用 rl_dqn_env 中提供的 base_params。")
-        elif UFParams is not None:
-            bp = UFParams()  # 使用默认构造
-            print("使用 UFParams() 构造 base_params 的实例。")
-        else:
-            raise ImportError("无法构造 base_params:rl_dqn_env 中既无 base_params 也无 UFParams。")
-
-        # 2) 实例化环境类(将 base_params 传入构造器)
-        env = UFSuperCycleEnv(bp)
-        print("已实例化 UFSuperCycleEnv 环境。")
-
-        # 3) 调用 env.generate_initial_state() 并检查 env.current_params 是否为必死
-        dead = is_dead_initial_state_env(env, max_steps=getattr(env, "max_episode_steps", 15),
-                                        L_s=6000, t_bw_s=40, verbose=True)
-
-        print("\n=== 判定结果 ===")
-        if dead:
-            print("当前生成的初始状态为【必死状态】(conservatively dead)。")
-        else:
-            print("当前生成的初始状态为【可行状态】(non-dead)。")
-
-    except Exception as e:
-        print("脚本执行出现错误:", e)
-        traceback.print_exc()

BIN
models/uf-rl/训练/uf_train/rl_model/DQN/model/dqn_model.zip


BIN
models/uf-rl/训练/uf_train/rl_model/DQN/model/loss.png


BIN
models/uf-rl/训练/uf_train/rl_model/DQN/model/reward.png


BIN
models/uf-rl/训练/进水动作版超滤训练源码/model/dqn_model.zip


BIN
models/uf-rl/训练/进水动作版超滤训练源码/resistance_model_bw.pth


BIN
models/uf-rl/训练/进水动作版超滤训练源码/resistance_model_fp.pth


+ 0 - 0
models/uf-rl/训练/进水动作版超滤训练源码/UF_resistance_models.py → models/uf-rl/进水动作版超滤训练源码/UF_resistance_models.py


+ 0 - 0
models/uf-rl/训练/进水动作版超滤训练源码/fixed_DQN_decide.py → models/uf-rl/进水动作版超滤训练源码/fixed_DQN_decide.py


+ 0 - 0
models/uf-rl/训练/进水动作版超滤训练源码/fixed_DQN_env.py → models/uf-rl/进水动作版超滤训练源码/fixed_DQN_env.py


+ 0 - 0
models/uf-rl/训练/进水动作版超滤训练源码/fixed_DQN_train.py → models/uf-rl/进水动作版超滤训练源码/fixed_DQN_train.py


+ 0 - 0
models/uf-rl/训练/uf_train/rl_model/DQN/__init__.py → models/uf-rl/进水动作版超滤训练源码/model/dqn_model.zip


+ 0 - 0
models/uf-rl/训练/uf_train/rl_model/__init__.py → models/uf-rl/进水动作版超滤训练源码/resistance_model_bw.pth


+ 0 - 0
models/uf-rl/进水动作版超滤训练源码/resistance_model_fp.pth