DQN_decide.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import numpy as np
  2. from stable_baselines3 import DQN
  3. from DQN_env import UFSuperCycleEnv
  4. from DQN_env import UFParams
  5. # 模型路径
  6. MODEL_PATH = "dqn_model.zip"
  7. # 创建环境实例以获取观察空间和动作空间
  8. def _get_model_spaces():
  9. """获取模型的观察空间和动作空间"""
  10. env = UFSuperCycleEnv(UFParams())
  11. obs_space = env.observation_space
  12. action_space = env.action_space
  13. env.close()
  14. return obs_space, action_space
  15. # 加载模型(只加载一次,提高效率)
  16. try:
  17. # 尝试直接加载
  18. model = DQN.load(MODEL_PATH)
  19. except KeyError:
  20. # 如果失败,则提供观察空间和动作空间
  21. obs_space, action_space = _get_model_spaces()
  22. model = DQN.load(MODEL_PATH, custom_objects={
  23. 'observation_space': obs_space,
  24. 'action_space': action_space
  25. })
  26. def run_uf_DQN_decide(uf_params, TMP0_value: float):
  27. """
  28. 单步决策函数:输入原始 TMP0,预测并执行动作
  29. 参数:
  30. TMP0_value (float): 当前 TMP0 值(单位与环境一致)
  31. 返回:
  32. dict: 包含模型选择的动作、动作参数、新状态、奖励等
  33. """
  34. # 1. 实例化环境
  35. base_params = uf_params
  36. env = UFSuperCycleEnv(base_params)
  37. # 2. 将输入的 TMP0 写入环境
  38. env.current_params.TMP0 = TMP0_value
  39. # 3. 获取归一化状态
  40. obs = env._get_obs().reshape(1, -1)
  41. # 4. 模型预测动作
  42. action, _ = model.predict(obs, deterministic=True)
  43. # 5. 解析动作对应的 L_s 和 t_bw_s
  44. L_s, t_bw_s = env._get_action_values(action[0])
  45. # 6. 在环境中执行该动作
  46. next_obs, reward, terminated, truncated, info = env.step(action[0])
  47. # 7. 整理结果
  48. result = {
  49. "action": int(action[0]),
  50. "L_s": float(L_s),
  51. "t_bw_s": float(t_bw_s),
  52. "next_obs": next_obs,
  53. "reward": reward,
  54. "terminated": terminated,
  55. "truncated": truncated,
  56. "info": info
  57. }
  58. # 8. 关闭环境
  59. env.close()
  60. return result
  61. def generate_plc_instructions(current_L_s, current_t_bw_s, model_prev_L_s, model_prev_t_bw_s, model_L_s, model_t_bw_s):
  62. """
  63. 根据工厂当前值、模型上一轮决策值和模型当前轮决策值,生成PLC指令。
  64. 新增功能:
  65. 1. 处理None值情况:如果模型上一轮值为None,则使用工厂当前值;
  66. 如果工厂当前值也为None,则返回None并提示错误。
  67. """
  68. # 参数配置保持不变
  69. params = UFParams(
  70. L_min_s=3600.0, L_max_s=6000.0, L_step_s=60.0,
  71. t_bw_min_s=40.0, t_bw_max_s=60.0, t_bw_step_s=2.0,
  72. )
  73. # 参数解包
  74. L_step_s = params.L_step_s
  75. t_bw_step_s = params.t_bw_step_s
  76. L_min_s = params.L_min_s
  77. L_max_s = params.L_max_s
  78. t_bw_min_s = params.t_bw_min_s
  79. t_bw_max_s = params.t_bw_max_s
  80. adjustment_threshold = 1.0
  81. # 处理None值情况
  82. if model_prev_L_s is None:
  83. if current_L_s is None:
  84. print("错误: 过滤时长的工厂当前值和模型上一轮值均为None")
  85. return None, None
  86. else:
  87. # 使用工厂当前值作为基准
  88. effective_current_L = current_L_s
  89. source_L = "工厂当前值(模型上一轮值为None)"
  90. else:
  91. # 模型上一轮值不为None,继续检查工厂当前值
  92. if current_L_s is None:
  93. effective_current_L = model_prev_L_s
  94. source_L = "模型上一轮值(工厂当前值为None)"
  95. else:
  96. effective_current_L = model_prev_L_s
  97. source_L = "模型上一轮值"
  98. # 对反洗时长进行同样的处理
  99. if model_prev_t_bw_s is None:
  100. if current_t_bw_s is None:
  101. print("错误: 反洗时长的工厂当前值和模型上一轮值均为None")
  102. return None, None
  103. else:
  104. effective_current_t_bw = current_t_bw_s
  105. source_t_bw = "工厂当前值(模型上一轮值为None)"
  106. else:
  107. if current_t_bw_s is None:
  108. effective_current_t_bw = model_prev_t_bw_s
  109. source_t_bw = "模型上一轮值(工厂当前值为None)"
  110. else:
  111. effective_current_t_bw = model_prev_t_bw_s
  112. source_t_bw = "模型上一轮值"
  113. # 检测所有输入值是否在规定范围内(只对非None值进行检查)
  114. # 工厂当前值检查(警告)
  115. if current_L_s is not None and not (L_min_s <= current_L_s <= L_max_s):
  116. print(f"警告: 当前过滤时长 {current_L_s} 秒不在允许范围内 [{L_min_s}, {L_max_s}]")
  117. if current_t_bw_s is not None and not (t_bw_min_s <= current_t_bw_s <= t_bw_max_s):
  118. print(f"警告: 当前反洗时长 {current_t_bw_s} 秒不在允许范围内 [{t_bw_min_s}, {t_bw_max_s}]")
  119. # 模型上一轮决策值检查(警告)
  120. if model_prev_L_s is not None and not (L_min_s <= model_prev_L_s <= L_max_s):
  121. print(f"警告: 模型上一轮过滤时长 {model_prev_L_s} 秒不在允许范围内 [{L_min_s}, {L_max_s}]")
  122. if model_prev_t_bw_s is not None and not (t_bw_min_s <= model_prev_t_bw_s <= t_bw_max_s):
  123. print(f"警告: 模型上一轮反洗时长 {model_prev_t_bw_s} 秒不在允许范围内 [{t_bw_min_s}, {t_bw_max_s}]")
  124. # 模型当前轮决策值检查(错误)
  125. if model_L_s is None:
  126. raise ValueError("错误: 决策模型建议的过滤时长不能为None")
  127. elif not (L_min_s <= model_L_s <= L_max_s):
  128. raise ValueError(f"错误: 决策模型建议的过滤时长 {model_L_s} 秒不在允许范围内 [{L_min_s}, {L_max_s}]")
  129. if model_t_bw_s is None:
  130. raise ValueError("错误: 决策模型建议的反洗时长不能为None")
  131. elif not (t_bw_min_s <= model_t_bw_s <= t_bw_max_s):
  132. raise ValueError(f"错误: 决策模型建议的反洗时长 {model_t_bw_s} 秒不在允许范围内 [{t_bw_min_s}, {t_bw_max_s}]")
  133. print(f"过滤时长基准: {source_L}, 值: {effective_current_L}")
  134. print(f"反洗时长基准: {source_t_bw}, 值: {effective_current_t_bw}")
  135. # 使用选定的基准值进行计算调整
  136. L_diff = model_L_s - effective_current_L
  137. L_adjustment = 0
  138. if abs(L_diff) >= adjustment_threshold * L_step_s:
  139. if L_diff >= 0:
  140. L_adjustment = L_step_s
  141. else:
  142. L_adjustment = -L_step_s
  143. next_L_s = effective_current_L + L_adjustment
  144. t_bw_diff = model_t_bw_s - effective_current_t_bw
  145. t_bw_adjustment = 0
  146. if abs(t_bw_diff) >= adjustment_threshold * t_bw_step_s:
  147. if t_bw_diff >= 0:
  148. t_bw_adjustment = t_bw_step_s
  149. else:
  150. t_bw_adjustment = -t_bw_step_s
  151. next_t_bw_s = effective_current_t_bw + t_bw_adjustment
  152. return next_L_s, next_t_bw_s
  153. from DQN_env import simulate_one_supercycle
  154. def calc_uf_cycle_metrics(p, TMP0, max_tmp_during_filtration, min_tmp_during_filtration, L_s: float, t_bw_s: float):
  155. """
  156. 计算 UF 超滤系统的核心性能指标
  157. 参数:
  158. p (UFParams): UF 系统参数
  159. L_s (float): 单次过滤时间(秒)
  160. t_bw_s (float): 单次反洗时间(秒)
  161. 返回:
  162. dict: {
  163. "k_bw_per_ceb": 小周期次数,
  164. "ton_water_energy_kWh_per_m3": 吨水电耗,
  165. "recovery": 回收率,
  166. "net_delivery_rate_m3ph": 净供水率 (m³/h),
  167. "daily_prod_time_h": 日均产水时间 (小时/天)
  168. "max_permeability": 全周期最高渗透率(lmh/bar)
  169. }
  170. """
  171. # 将跨膜压差写入参数
  172. p.TMP0 = TMP0
  173. # 模拟该参数下的超级周期
  174. feasible, info = simulate_one_supercycle(p, L_s, t_bw_s)
  175. # 获得模型模拟周期信息
  176. k_bw_per_ceb = info["k_bw_per_ceb"]
  177. ton_water_energy_kWh_per_m3 = info["ton_water_energy_kWh_per_m3"]
  178. recovery = info["recovery"]
  179. net_delivery_rate_m3ph = info["net_delivery_rate_m3ph"]
  180. daily_prod_time_h = info["daily_prod_time_h"]
  181. # 获得模型模拟周期内最高跨膜压差/最低跨膜压差
  182. if max_tmp_during_filtration is None:
  183. max_tmp_during_filtration = info["max_TMP_during_filtration"]
  184. if min_tmp_during_filtration is None:
  185. min_tmp_during_filtration = info["min_TMP_during_filtration"]
  186. # 计算最高渗透率
  187. max_permeability = 100 * p.q_UF / (128*40) / min_tmp_during_filtration
  188. return {
  189. "k_bw_per_ceb": k_bw_per_ceb,
  190. "ton_water_energy_kWh_per_m3": ton_water_energy_kWh_per_m3,
  191. "recovery": recovery,
  192. "net_delivery_rate_m3ph": net_delivery_rate_m3ph,
  193. "daily_prod_time_h": daily_prod_time_h,
  194. "max_permeability": max_permeability
  195. }
  196. # ==============================
  197. # 示例调用
  198. # ==============================
  199. if __name__ == "__main__":
  200. uf_params = UFParams()
  201. TMP0 = 0.03 # 原始 TMP0
  202. model_decide_result = run_uf_DQN_decide(uf_params, TMP0) # 调用模型获得动作
  203. model_L_s = model_decide_result['L_s'] # 获得模型决策产水时长
  204. model_t_bw_s = model_decide_result['t_bw_s'] # 获得模型决策反洗时长
  205. current_L_s = 3800
  206. current_t_bw_s = 40
  207. model_prev_L_s = 4040
  208. model_prev_t_bw_s = 60
  209. L_s, t_bw_s = generate_plc_instructions(current_L_s, current_t_bw_s, model_prev_L_s, model_prev_t_bw_s, model_L_s, model_t_bw_s) # 获取模型下发指令
  210. L_s = 4100
  211. t_bw_s = 96
  212. max_tmp_during_filtration = 0.050176 # 新增工厂数据接口:周期最高/最低跨膜压差,无工厂数据接入时传入None,calc_uf_cycle_metrics()自动获取模拟周期中的跨膜压差最值
  213. min_tmp_during_filtration = 0.012496
  214. execution_result = calc_uf_cycle_metrics(uf_params, TMP0, max_tmp_during_filtration, min_tmp_during_filtration, L_s, t_bw_s)
  215. print("\n===== 单步决策结果 =====")
  216. print(f"模型选择的动作: {model_decide_result['action']}")
  217. print(f"模型选择的L_s: {model_L_s} 秒, 模型选择的t_bw_s: {model_t_bw_s} 秒")
  218. print(f"指令下发的L_s: {L_s} 秒, 指令下发的t_bw_s: {t_bw_s} 秒")
  219. print(f"指令对应的反洗次数: {execution_result['k_bw_per_ceb']}")
  220. print(f"指令对应的吨水电耗: {execution_result['ton_water_energy_kWh_per_m3']}")
  221. print(f"指令对应的回收率: {execution_result['recovery']}")
  222. print(f"指令对应的日均产水时间: {execution_result['daily_prod_time_h']}")
  223. print(f"指令对应的最高渗透率: {execution_result['max_permeability']}")