Browse Source

add q learning model for predicting fcu command

chenhaiyang 4 years ago
parent
commit
5db64d2d41
3 changed files with 142 additions and 18 deletions
  1. 25 15
      app/api/routers/equipment.py
  2. 1 3
      app/api/routers/space.py
  3. 116 0
      app/controllers/equipment/fcu/q_learning.py

+ 25 - 15
app/api/routers/equipment.py

@@ -6,6 +6,7 @@ from fastapi import APIRouter, Query
 from loguru import logger
 
 from app.controllers.equipment.fcu.basic import get_fcu_control_result
+from app.controllers.equipment.fcu.q_learning import get_fcu_q_learning_control_result
 from app.controllers.equipment.vav import get_vav_control_result
 from app.models.domain.equipment import EquipmentControlResponse, EquipmentControlRequest
 from app.utils.date import get_time_str
@@ -27,13 +28,16 @@ async def get_equipment_command(
         equip_type: EquipmentName = Query(..., alias='equipType'),
 ):
     if equip_type.value == EquipmentName.FCU:
-        fcu = await get_fcu_control_result(project_id, equip_id)
-        output = {
-            'EquipSwitchSet': 1 if fcu.running_status else 0,
-            'WorkModeSet': 3,
-            'FanGearSet': fcu.air_valve_speed.value,
-            'IndoorAirTempSet': fcu.setting_temperature
-        }
+        if project_id == 'Pj1101050030':
+            fcu = await get_fcu_control_result(project_id, equip_id)
+            output = {
+                'EquipSwitchSet': 1 if fcu.running_status else 0,
+                'WorkModeSet': 3,
+                'FanGearSet': fcu.air_valve_speed.value,
+                'IndoorAirTempSet': fcu.setting_temperature
+            }
+        else:
+            output = await get_fcu_q_learning_control_result(project_id, equip_id)
     elif equip_type.value == EquipmentName.VAV:
         vav = await get_vav_control_result(project_id, equip_id)
         output = {
@@ -54,14 +58,20 @@ async def get_equipment_command(
 @router.post('/control', response_model=EquipmentControlResponse)
 async def get_equipment_command_v2(equipment_control_info: EquipmentControlRequest):
     if equipment_control_info.equipType == EquipmentName.FCU:
-        fcu = await get_fcu_control_result(equipment_control_info.projectId, equipment_control_info.equipId)
-        output = {
-            'EquipSwitchSet': 1 if fcu.running_status else 0,
-            'WorkModeSet': 3,
-            'IndoorAirTempSet': int(round_half_up(fcu.setting_temperature, 1) * 10)
-        }
-        if fcu.air_valve_speed.value != 0.0:
-            output.update({'FanGearSet': fcu.air_valve_speed.value})
+        if equipment_control_info.projectId == 'Pj1101050030':
+            fcu = await get_fcu_control_result(equipment_control_info.projectId, equipment_control_info.equipId)
+            output = {
+                'EquipSwitchSet': 1 if fcu.running_status else 0,
+                'WorkModeSet': 3,
+                'IndoorAirTempSet': int(round_half_up(fcu.setting_temperature, 1) * 10)
+            }
+            if fcu.air_valve_speed.value != 0.0:
+                output.update({'FanGearSet': fcu.air_valve_speed.value})
+        else:
+            output = await get_fcu_q_learning_control_result(
+                equipment_control_info.projectId,
+                equipment_control_info.equipId
+            )
     elif equipment_control_info.equipType == EquipmentName.VAV:
         vav = await get_vav_control_result(equipment_control_info.projectId, equipment_control_info.equipId)
         output = {

+ 1 - 3
app/api/routers/space.py

@@ -28,9 +28,7 @@ async def get_space_command(
 
 @router.get('/test')
 async def get_test_result(current: float, pre: float, target: float):
-    path = ('/Users/highing/code/sagacloud/python_server/sagacloud-python/pythonserver'
-            '/pythonserver/room_control_modularization/net_mat/net_1_summer.mat')
-    builder = QLearningCommandBuilder(path, Season('Cooling'))
+    builder = QLearningCommandBuilder(Season('Cooling'))
     command = await builder.get_command(current, pre, target)
 
     return command

+ 116 - 0
app/controllers/equipment/fcu/q_learning.py

@@ -0,0 +1,116 @@
+# -*- coding: utf-8 -*-
+
+from typing import Dict, Optional
+
+import numpy as np
+from httpx import AsyncClient
+from loguru import logger
+
+from app.controllers.events import q_learning_models
+from app.services.platform import DataPlatformService
+from app.services.transfer import EquipmentInfoService, SpaceInfoService
+from app.services.transfer import Season
+
+
+class QLearningCommandBuilder:
+    """
+    Build FCU command by Q learning net.
+    """
+
+    def __init__(self, season: Season):
+        self.season = season
+        if season == Season.cooling:
+            self.model = q_learning_models.get('summer')
+        elif season == Season.heating:
+            self.model = q_learning_models.get('winter')
+        else:
+            self.model = None
+
+    def get_type(self, layer: int) -> str:
+        return self.model[0, layer][0, 0][0][0]
+
+    def get_weight(self, layer: int, idx: int) -> np.ndarray:
+        return self.model[0, layer][0, 0][1][0, idx]
+
+    @staticmethod
+    def linear(input_v: np.ndarray, weight: np.ndarray, bias: Optional[np.ndarray] = None) -> np.ndarray:
+        y = np.dot(weight, input_v)
+        if bias.size > 0:
+            y += bias
+
+        return y
+
+    @staticmethod
+    def relu(x: np.ndarray) -> np.ndarray:
+        return np.maximum(x, 0)
+
+    def predict_speed(self, input_v: np.ndarray) -> int:
+        result = [input_v]
+        for layer in range(self.model.shape[1]):
+            if self.get_type(layer) == 'mlp' or self.get_type(layer) == 'linear':
+                y = self.linear(result[layer], self.get_weight(layer, 0), self.get_weight(layer, 1))
+                result.append(y)
+            elif self.get_type(layer) == 'relu':
+                result.append(self.relu(result[layer]))
+
+        speed = np.argmax(result[-1])
+
+        return int(speed)
+
+    @logger.catch()
+    async def get_command(self, current_temperature: float, pre_temperature: float, actual_target: float) -> Dict:
+        # actual_target = np.mean(np.array(target))
+        input_value = np.array([
+            [(current_temperature - actual_target) / 5],
+            [(current_temperature - pre_temperature) / 5]
+        ])
+        speed = self.predict_speed(input_value)
+        if speed == 0:
+            on_off = 0
+            water_on_off = 0
+        else:
+            on_off = 1
+            water_on_off = 1
+
+        if self.season == Season.cooling:
+            season = 1
+        elif self.season == Season.heating:
+            season = 2
+        else:
+            season = 0
+
+        command = {
+            'on_off': on_off,
+            'season': season,
+            'speed': int(speed),
+            'temperature_set': actual_target,
+            'water_on_off': water_on_off
+        }
+        return command
+
+
+@logger.catch()
+async def get_fcu_q_learning_control_result(project_id: str, equipment_id: str) -> Dict:
+    async with AsyncClient() as client:
+        duo_duo = EquipmentInfoService(client, project_id)
+        platform = DataPlatformService(client, project_id)
+
+        spaces = await duo_duo.get_space_by_equipment(equipment_id)
+        if len(spaces) > 1:
+            logger.error(f'FCU {equipment_id} control more than one spaces!')
+        transfer = SpaceInfoService(client, project_id, spaces[0].get('id'))
+        season = await transfer.get_season()
+        current_target = await transfer.get_current_temperature_target()
+        realtime_temperature = await platform.get_realtime_temperature(spaces[0].get('id'))
+        past_temperature = await platform.get_past_temperature(spaces[0].get('id'), 15 * 60)
+
+    logger.debug(
+        f'{spaces[0]["id"]} - {equipment_id} - '
+        f'realtime Tdb: {realtime_temperature} - '
+        f'pre Tdb: {past_temperature} - '
+        f'target: {current_target}'
+    )
+    builder = QLearningCommandBuilder(season)
+    command = await builder.get_command(realtime_temperature, past_temperature, current_target)
+
+    return command