service.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import os
  2. import sys
  3. import json
  4. import time
  5. import serial
  6. import numpy as np
  7. import pandas as pd
  8. import libsvm.svmutil as svm
  9. class GpioMonitor:
  10. class Gpio:
  11. OUTPUT, HIGH, LOW = -1, 0, 1
  12. GPIO = Gpio()
  13. @staticmethod
  14. def wiringPiSetup():
  15. pass
  16. @staticmethod
  17. def pinMode(port: "int", gpio: "Gpio"):
  18. pass
  19. @staticmethod
  20. def digitalWrite(port: "int", gpio: "Gpio"):
  21. pass
  22. try:
  23. import wiringpi # noqa
  24. except ModuleNotFoundError:
  25. wiringpi = GpioMonitor
  26. class Service:
  27. _Mode = {"predict": 1, "train": 2, "have": 3, "none": 4}
  28. def __init__(self, conf_path: "str"):
  29. self._conf: "dict" = {
  30. "data": {
  31. "count": 700,
  32. "size": 131,
  33. "have": "data/have",
  34. "none": "data/none"
  35. },
  36. "fft": {
  37. "win": 10,
  38. "hold": 500,
  39. "rate": 337
  40. },
  41. "mode": "predict",
  42. "model": {
  43. "file": "data/svm.tin",
  44. "gamma": 2.57e-6,
  45. "nu": 7.1e-3
  46. },
  47. "uart": {
  48. "file": "/dev/ttyS5",
  49. "baud": 115200
  50. },
  51. "gpio": {
  52. "wpi": 2,
  53. "time": 3
  54. },
  55. "log": True
  56. }
  57. self._uart: "serial.Serial"
  58. self._model: "svm.svm_model"
  59. self._read_conf(conf_path)
  60. self._log(self._conf)
  61. self._init_gpio()
  62. self._open_uart()
  63. def _read_conf(self, file: "str") -> "None":
  64. with open(file, encoding="utf-8") as fp:
  65. data = json.load(fp)
  66. for key in self._conf:
  67. if key in data:
  68. self._conf[key] = data[key]
  69. if self._conf["mode"] not in self._Mode:
  70. self._conf["mode"] = "predict"
  71. def _log(self, *args):
  72. if self._conf["log"]:
  73. print("Log =>", *args)
  74. def _init_gpio(self) -> "None":
  75. wiringpi.wiringPiSetup()
  76. wiringpi.pinMode(self._conf["gpio"]["wpi"], wiringpi.GPIO.OUTPUT)
  77. self._log(f"gpio {self._conf['gpio']['wpi']} has been set to `OUTPUT`")
  78. def _lon(self, sec: "float" = -1) -> "None":
  79. wiringpi.digitalWrite(self._conf["gpio"]["wpi"], wiringpi.GPIO.HIGH)
  80. self._log("light on")
  81. if sec > 0:
  82. time.sleep(sec)
  83. self._lof()
  84. def _lof(self, sec: "float" = -1) -> "None":
  85. wiringpi.digitalWrite(self._conf["gpio"]["wpi"], wiringpi.GPIO.LOW)
  86. self._log("light off")
  87. if sec > 0:
  88. time.sleep(sec)
  89. self._lon()
  90. def _flash(self, total: "float", count: "int") -> "None":
  91. gap: "float" = total / (5 * count - 2)
  92. on, off = 2 * gap, 3 * gap
  93. for i in range(count):
  94. if i != 0:
  95. time.sleep(on)
  96. self._lof(off)
  97. def _open_uart(self) -> "None":
  98. self._uart = serial.Serial(self._conf["uart"]["file"], self._conf["uart"]["baud"])
  99. if not self._uart.isOpen():
  100. self._log(f"uart: {self._conf['uart']['file']}@{self._conf['uart']['baud']} open failed.")
  101. sys.exit(-1)
  102. def _isMax(self, data: "np.ndarray", idx: "int") -> "bool":
  103. if idx < self._conf["fft"]["win"] or idx > data.size - self._conf["fft"]["win"]:
  104. return False
  105. for i in range(1, self._conf["fft"]["win"] + 1):
  106. if data[idx] < data[idx - i] or data[idx] < data[idx + i]:
  107. return False
  108. return True
  109. def _fft(self, data: "np.ndarray") -> "np.ndarray":
  110. scale = np.linspace(0, data.size / self._conf["fft"]["rate"], data.size)
  111. fft = np.fft.fft(data)
  112. frq = np.fft.fftfreq(data.size, scale[1] - scale[0])
  113. pFrq = frq[:frq.size // 2]
  114. pFft = np.abs(2.0 / data.size * fft[:fft.size // 2])
  115. result, idx = np.zeros((10, 2)), 0
  116. for i in range(self._conf["fft"]["win"], pFft.size - self._conf["fft"]["win"]):
  117. if self._isMax(pFft, i) and pFft[i] > self._conf["fft"]["hold"]:
  118. result[idx][0], result[idx][1] = pFrq[i], pFft[i]
  119. idx += 1
  120. if idx >= 10:
  121. break
  122. return result.flatten()
  123. def _l2x(self, buff: "list[str]") -> "list[dict]":
  124. arr = np.array([float(itr) for itr in buff])
  125. data = arr[:3].tolist() + self._fft(arr[3:]).tolist()
  126. return [{i + 1: data[i] for i in range(len(data))}]
  127. def _run_predict(self):
  128. self._model = svm.svm_load_model(self._conf["model"]["file"])
  129. self._log(f"model loaded from: `{self._conf['model']['file']}`")
  130. buff = ""
  131. try:
  132. while True:
  133. get = self._uart.read().decode("utf-8")
  134. if get in "\r\n":
  135. buff = buff.strip(",")
  136. if buff == "":
  137. continue
  138. arr = buff.split(",")
  139. if len(arr) != self._conf["data"]["size"]:
  140. self._log(f"expect {self._conf['data']['size']} nums, got {len(arr)} instead.")
  141. buff = ""
  142. continue
  143. x = self._l2x(arr)
  144. res = svm.svm_predict([], x, self._model, "-q")
  145. buff = ""
  146. self._log(f"feature size: {len(x[0])}, predict: {res[0][0]}")
  147. if res[0][0] == 1:
  148. self._flash(self._conf["gpio"]["time"], 4)
  149. else:
  150. buff += get
  151. except Exception as e:
  152. self._uart.close()
  153. self._log(f"error: {e}")
  154. def _load_data(self) -> "tuple[np.ndarray, np.ndarray, np.ndarray]":
  155. root = self._conf["data"]["have"]
  156. have: "pd.DataFrame" = pd.DataFrame()
  157. for name in os.listdir(root):
  158. data = pd.read_csv(f"{root}/{name}", header=None)
  159. have = pd.concat([have, data])
  160. haveNp = have.to_numpy()
  161. np.random.shuffle(haveNp)
  162. root = self._conf["data"]["none"]
  163. none: "pd.DataFrame" = pd.DataFrame()
  164. for name in os.listdir(root):
  165. data = pd.read_csv(f"{root}/{name}", header=None)
  166. none = pd.concat([none, data])
  167. count = int(haveNp.shape[0] * 0.85)
  168. self._log("data loaded from dataset dir.")
  169. return haveNp[:count], haveNp[count:], none.to_numpy()
  170. def _run_train(self):
  171. have, test, none = self._load_data()
  172. have = [np.append(line[:3], self._fft(line[3:])) for line in have]
  173. test = [np.append(line[:3], self._fft(line[3:])) for line in test]
  174. none = [np.append(line[:3], self._fft(line[3:])) for line in none]
  175. data = [{i + 1: line[i] for i in range(len(line))} for line in have]
  176. tags = np.ones(len(data)).tolist()
  177. pro = svm.svm_problem(tags, data)
  178. pra = svm.svm_parameter(f"-s 2 -g {self._conf['model']['gamma']} -n {self._conf['model']['nu']}")
  179. model = svm.svm_train(pro, pra)
  180. # predict test
  181. data = [{i + 1: line[i] for i in range(len(line))} for line in test]
  182. tags = np.ones(len(data)).tolist()
  183. svm.svm_predict(y=tags, x=data, m=model)
  184. # predict none
  185. data = [{i + 1: line[i] for i in range(len(line))} for line in none]
  186. tags = (np.ones(len(data)) * -1).tolist()
  187. svm.svm_predict(y=tags, x=data, m=model)
  188. svm.svm_save_model(self._conf["model"]["file"], model)
  189. self._log(f"model saved in {self._conf['model']['file']}.")
  190. def _run_collect(self):
  191. buff = ""
  192. try:
  193. res, count = [], 0
  194. while count < self._conf["data"]["count"]:
  195. get = self._uart.read().decode("utf-8")
  196. if get in "\r\n":
  197. buff = buff.strip(",")
  198. if buff == "":
  199. continue
  200. if tmp := buff.count(",") != self._conf["data"]["size"] - 1:
  201. self._log(f"expect {self._conf['data']['size']} nums, got {tmp} instead.")
  202. continue
  203. res.append(buff)
  204. count += 1
  205. else:
  206. buff += get
  207. path = self._conf["data"][self._conf["mode"]]
  208. name = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(time.time()))
  209. file = f"{path}/{name}.csv"
  210. with open(file, "wt") as fp:
  211. fp.writelines(res)
  212. self._log(f"saved {count} samples into `{file}`")
  213. self._flash(3, 1)
  214. except Exception as e:
  215. self._uart.close()
  216. self._log(f"error: {e}")
  217. def run(self) -> "None":
  218. try:
  219. self._lon()
  220. if self._conf["mode"] == "predict":
  221. self._run_predict()
  222. elif self._conf["mode"] == "train":
  223. self._run_train()
  224. else:
  225. self._run_collect()
  226. except Exception as e:
  227. self._log(e)
  228. self._lof()
  229. if __name__ == "__main__":
  230. config_file = "config.json"
  231. if len(sys.argv) == 2:
  232. config_file = sys.argv[1]
  233. server = Service(config_file)
  234. server.run()