CMSAnalyst.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. import ast
  2. import json
  3. import math
  4. import numpy as np
  5. import pandas as pd
  6. from scipy.signal import hilbert
  7. from app.config import dataBase
  8. from app.database import get_engine
  9. class CMSAnalyst:
  10. def __init__(self, fmin, fmax, table_name, ids):
  11. self.datas = self._get_by_id(table_name, ids)
  12. self.datas = [df[['mesure_data', 'time_stamp', 'sampling_frequency', 'wind_turbine_number', 'rotational_speed',
  13. 'mesure_point_name']] for df in self.datas]
  14. # 只输入一个id,返回一个[df],所以拿到self.data[0]
  15. self.data_filter = self.datas[0]
  16. # 取数据列
  17. self.data = np.array(ast.literal_eval(self.data_filter['mesure_data'][0]))
  18. self.envelope_spectrum_m = self.data.shape[0]
  19. self.envelope_spectrum_n = 1
  20. self.fs = int(self.data_filter['sampling_frequency'].iloc[0])
  21. self.envelope_spectrum_t = np.arange(self.envelope_spectrum_m) / self.fs
  22. self.fmin = fmin if fmin is not None else 0
  23. self.fmax = fmax if fmax is not None else float('inf')
  24. self.envelope_spectrum_y = self._bandpass_filter(self.data)
  25. self.f, self.HP = self._calculate_envelope_spectrum(self.envelope_spectrum_y)
  26. self.wind_code = self.data_filter['wind_turbine_number'].iloc[0]
  27. self.rpm_Gen = self.data_filter['rotational_speed'].iloc[0]
  28. self.mesure_point_name = self.data_filter['mesure_point_name'].iloc[0]
  29. self.fn_Gen = round(self.rpm_Gen / 60, 2)
  30. self.CF = self.Characteristic_Frequency()
  31. self.CF = pd.DataFrame(self.CF, index=[0])
  32. if self.CF['type'].iloc[0] == 'bearing':
  33. n_rolls_m = self.CF['n_rolls'].iloc[0]
  34. d_rolls_m = self.CF['d_rolls'].iloc[0]
  35. D_diameter_m = self.CF['D_diameter'].iloc[0]
  36. theta_deg_m = self.CF['theta_deg'].iloc[0]
  37. self.bearing_frequencies = self.calculate_bearing_frequencies(n_rolls_m, d_rolls_m, D_diameter_m,
  38. theta_deg_m, self.rpm_Gen)
  39. self.bearing_frequencies = pd.DataFrame(self.bearing_frequencies, index=[0])
  40. (
  41. self.frequency_domain_analysis_t,
  42. self.frequency_domain_analysis_f,
  43. self.frequency_domain_analysis_m,
  44. self.frequency_domain_analysis_mag,
  45. self.frequency_domain_analysis_Xrms,
  46. ) = self._calculate_spectrum(self.data)
  47. # time_domain_analysis
  48. self.time_domain_analysis_t = np.arange(self.data.shape[0]) / self.fs
  49. def _get_by_id(self, windcode, ids):
  50. df_res = []
  51. engine = get_engine(dataBase.DATA_DB)
  52. for id in ids:
  53. table_name = windcode + '_wave'
  54. lastday_df_sql = f"SELECT * FROM {table_name} where id = {id} "
  55. df = pd.read_sql(lastday_df_sql, engine)
  56. df_res.append(df)
  57. return df_res
  58. # envelope_spectrum_analysis
  59. def _bandpass_filter(self, data):
  60. """带通滤波"""
  61. m = data.shape[0]
  62. ni = round(self.fmin * self.envelope_spectrum_m / self.fs + 1)
  63. if self.fmax == float('inf'):
  64. na = m
  65. else:
  66. na = round(self.fmax * m / self.fs + 1)
  67. col = 1
  68. y = np.zeros((self.envelope_spectrum_m, col))
  69. z = np.fft.fft(data)
  70. a = np.zeros(self.envelope_spectrum_m, dtype=complex)
  71. a[ni:na] = z[ni:na]
  72. a[self.envelope_spectrum_m - na + 1: self.envelope_spectrum_m - ni + 1] = z[self.envelope_spectrum_m - na + 1: self.envelope_spectrum_m - ni + 1]
  73. z = np.fft.ifft(a)
  74. y[:, 0] = np.real(z)
  75. return y
  76. def _calculate_envelope_spectrum(self, y):
  77. """计算包络谱"""
  78. m, n = y.shape
  79. HP = np.zeros((m, n))
  80. col = 1
  81. for p in range(col):
  82. H = np.abs(hilbert(y[:, p] - np.mean(y[:, p])))
  83. HP[:, p] = np.abs(np.fft.fft(H - np.mean(H))) * 2 / m
  84. f = np.fft.fftfreq(m, d=1 / self.fs)
  85. return f, HP
  86. def envelope_spectrum(self):
  87. """绘制包络谱"""
  88. # 只取正频率部分
  89. positive_frequencies = self.f[: self.envelope_spectrum_m // 2]
  90. positive_HP = self.HP[: self.envelope_spectrum_m // 2, 0]
  91. x = positive_frequencies
  92. y = positive_HP
  93. title = "包络谱"
  94. xaxis = "频率(Hz)"
  95. yaxis = "加速度(m/s^2)"
  96. Xrms = np.sqrt(np.mean(y ** 2)) # 加速度均方根值(有效值)
  97. rpm_Gen = round(self.rpm_Gen, 2)
  98. BPFI_1X = round(self.bearing_frequencies['BPFI'].iloc[0], 2)
  99. BPFO_1X = round(self.bearing_frequencies['BPFO'].iloc[0], 2)
  100. BSF_1X = round(self.bearing_frequencies['BSF'].iloc[0], 2)
  101. FTF_1X = round(self.bearing_frequencies['FTF'].iloc[0], 2)
  102. fn_Gen = round(self.fn_Gen, 2)
  103. _3P_1X = round(self.fn_Gen, 2) * 3
  104. if self.CF['type'].iloc[0] == 'bearing':
  105. result = {
  106. "fs": self.fs,
  107. "Xrms": round(Xrms, 2),
  108. "x": list(x),
  109. "y": list(y),
  110. "title": title,
  111. "xaxis": xaxis,
  112. "yaxis": yaxis,
  113. "rpm_Gen": round(rpm_Gen, 2), # 转速r/min
  114. "BPFI": [{"Xaxis": BPFI_1X, "val": "1BPFI"}, {"Xaxis": BPFI_1X * 2, "val": "2BPFI"},
  115. {"Xaxis": BPFI_1X * 3, "val": "3BPFI"}, {"Xaxis": BPFI_1X * 4, "val": "4BPFI"},
  116. {"Xaxis": BPFI_1X * 5, "val": "5BPFI"}, {"Xaxis": BPFI_1X * 6, "val": "6BPFI"}],
  117. "BPFO": [{"Xaxis": BPFO_1X, "val": "1BPFO"}, {"Xaxis": BPFO_1X * 2, "val": "2BPFO"},
  118. {"Xaxis": BPFO_1X * 3, "val": "3BPFO"}, {"Xaxis": BPFO_1X * 4, "val": "4BPFO"},
  119. {"Xaxis": BPFO_1X * 5, "val": "5BPFO"}, {"Xaxis": BPFO_1X * 6, "val": "6BPFO"}],
  120. "BSF": [{"Xaxis": BSF_1X, "val": "1BSF"}, {"Xaxis": BSF_1X * 2, "val": "2BSF"},
  121. {"Xaxis": BSF_1X * 3, "val": "3BSF"}, {"Xaxis": BSF_1X * 4, "val": "4BSF"},
  122. {"Xaxis": BSF_1X * 5, "val": "5BSF"}, {"Xaxis": BSF_1X * 6, "val": "6BSF"}],
  123. "FTF": [{"Xaxis": FTF_1X, "val": "1FTF"}, {"Xaxis": FTF_1X * 2, "val": "2FTF"},
  124. {"Xaxis": FTF_1X * 3, "val": "3FTF"}, {"Xaxis": FTF_1X * 4, "val": "4FTF"},
  125. {"Xaxis": FTF_1X * 5, "val": "5FTF"}, {"Xaxis": FTF_1X * 6, "val": "6FTF"}],
  126. "fn_Gen": [{"Xaxis": fn_Gen, "val": "1X"}, {"Xaxis": fn_Gen * 2, "val": "2X"},
  127. {"Xaxis": fn_Gen * 3, "val": "3X"}, {"Xaxis": fn_Gen * 4, "val": "4X"},
  128. {"Xaxis": fn_Gen * 5, "val": "5X"}, {"Xaxis": fn_Gen * 6, "val": "6X"}],
  129. "B3P": _3P_1X,
  130. }
  131. return result
  132. def _calculate_spectrum(self, data):
  133. """计算频谱"""
  134. m = data.shape[0]
  135. n = 1
  136. t = np.arange(m) / self.fs
  137. mag = np.zeros((m, n))
  138. Xrms = np.sqrt(np.mean(data ** 2)) # 加速度均方根值(有效值)
  139. # col=1
  140. # for p in range(col):
  141. mag = np.abs(np.fft.fft(data - np.mean(data))) * 2 / m
  142. f = np.fft.fftfreq(m, d=1 / self.fs)
  143. return t, f, m, mag, Xrms
  144. def frequency_domain(self):
  145. """绘制频域波形参数"""
  146. # 只取正频率部分
  147. positive_frequencies = self.frequency_domain_analysis_f[
  148. : self.frequency_domain_analysis_m // 2
  149. ]
  150. positive_mag = self.frequency_domain_analysis_mag[
  151. : self.frequency_domain_analysis_m // 2
  152. ]
  153. x = positive_frequencies
  154. y = positive_mag
  155. title = "频域信号"
  156. xaxis = "频率(Hz)"
  157. yaxis = "加速度(m/s^2)"
  158. Xrms = self.frequency_domain_analysis_Xrms
  159. rpm_Gen = round(self.rpm_Gen, 2)
  160. BPFI_1X = round(self.bearing_frequencies['BPFI'].iloc[0], 2)
  161. BPFO_1X = round(self.bearing_frequencies['BPFO'].iloc[0], 2)
  162. BSF_1X = round(self.bearing_frequencies['BSF'].iloc[0], 2)
  163. FTF_1X = round(self.bearing_frequencies['FTF'].iloc[0], 2)
  164. fn_Gen = round(self.fn_Gen, 2)
  165. _3P_1X = round(self.fn_Gen, 2) * 3
  166. if self.CF['type'].iloc[0] == 'bearing':
  167. result = {
  168. "fs": self.fs,
  169. "Xrms": round(Xrms, 2),
  170. "x": list(x),
  171. "y": list(y),
  172. "title": title,
  173. "xaxis": xaxis,
  174. "yaxis": yaxis,
  175. "rpm_Gen": round(rpm_Gen, 2), # 转速r/min
  176. "BPFI": [{"Xaxis": BPFI_1X, "val": "1BPFI"}, {"Xaxis": BPFI_1X * 2, "val": "2BPFI"},
  177. {"Xaxis": BPFI_1X * 3, "val": "3BPFI"}, {"Xaxis": BPFI_1X * 4, "val": "4BPFI"},
  178. {"Xaxis": BPFI_1X * 5, "val": "5BPFI"}, {"Xaxis": BPFI_1X * 6, "val": "6BPFI"}],
  179. "BPFO": [{"Xaxis": BPFO_1X, "val": "1BPFO"}, {"Xaxis": BPFO_1X * 2, "val": "2BPFO"},
  180. {"Xaxis": BPFO_1X * 3, "val": "3BPFO"}, {"Xaxis": BPFO_1X * 4, "val": "4BPFO"},
  181. {"Xaxis": BPFO_1X * 5, "val": "5BPFO"}, {"Xaxis": BPFO_1X * 6, "val": "6BPFO"}],
  182. "BSF": [{"Xaxis": BSF_1X, "val": "1BSF"}, {"Xaxis": BSF_1X * 2, "val": "2BSF"},
  183. {"Xaxis": BSF_1X * 3, "val": "3BSF"}, {"Xaxis": BSF_1X * 4, "val": "4BSF"},
  184. {"Xaxis": BSF_1X * 5, "val": "5BSF"}, {"Xaxis": BSF_1X * 6, "val": "6BSF"}],
  185. "FTF": [{"Xaxis": FTF_1X, "val": "1FTF"}, {"Xaxis": FTF_1X * 2, "val": "2FTF"},
  186. {"Xaxis": FTF_1X * 3, "val": "3FTF"}, {"Xaxis": FTF_1X * 4, "val": "4FTF"},
  187. {"Xaxis": FTF_1X * 5, "val": "5FTF"}, {"Xaxis": FTF_1X * 6, "val": "6FTF"}],
  188. "fn_Gen": [{"Xaxis": fn_Gen, "val": "1X"}, {"Xaxis": fn_Gen * 2, "val": "2X"},
  189. {"Xaxis": fn_Gen * 3, "val": "3X"}, {"Xaxis": fn_Gen * 4, "val": "4X"},
  190. {"Xaxis": fn_Gen * 5, "val": "5X"}, {"Xaxis": fn_Gen * 6, "val": "6X"}],
  191. "B3P": _3P_1X,
  192. }
  193. result = json.dumps(result, ensure_ascii=False)
  194. return result
  195. # time_domain_analysis
  196. def time_domain(self):
  197. """绘制时域波形参数"""
  198. x = self.time_domain_analysis_t
  199. y = self.data
  200. rpm_Gen = self.rpm_Gen
  201. title = "时间域信号"
  202. xaxis = "时间(s)"
  203. yaxis = "加速度(m/s^2)"
  204. # 图片右侧统计量
  205. mean_value = np.mean(y) # 平均值
  206. max_value = np.max(y) # 最大值
  207. min_value = np.min(y) # 最小值
  208. Xrms = np.sqrt(np.mean(y ** 2)) # 加速度均方根值(有效值)
  209. Xp = (max_value - min_value) / 2 # 峰值(单峰最大值) # 峰值
  210. Xpp = max_value - min_value # 峰峰值
  211. Cf = Xp / Xrms # 峰值指标
  212. Sf = Xrms / mean_value # 波形指标
  213. If = Xp / np.mean(np.abs(y)) # 脉冲指标
  214. Xr = np.mean(np.sqrt(np.abs(y))) ** 2 # 方根幅值
  215. Ce = Xp / Xr # 裕度指标
  216. # 计算每个数据点的绝对值减去均值后的三次方,并求和
  217. sum_abs_diff_cubed_3 = np.mean((np.abs(y) - mean_value) ** 3)
  218. # 计算偏度指标
  219. Cw = sum_abs_diff_cubed_3 / (Xrms ** 3)
  220. # 计算每个数据点的绝对值减去均值后的四次方,并求和
  221. sum_abs_diff_cubed_4 = np.mean((np.abs(y) - mean_value) ** 4)
  222. # 计算峭度指标
  223. Cq = sum_abs_diff_cubed_4 / (Xrms ** 4)
  224. result = {
  225. "x": list(x),
  226. "y": list(y),
  227. "title": title,
  228. "xaxis": xaxis,
  229. "yaxis": yaxis,
  230. "fs": self.fs,
  231. "Xrms": round(Xrms, 2), # 有效值
  232. "mean_value": round(mean_value, 2), # 均值
  233. "max_value": round(max_value, 2), # 最大值
  234. "min_value": round(min_value, 2), # 最小值
  235. "Xp": round(Xp, 2), # 峰值
  236. "Xpp": round(Xpp, 2), # 峰峰值
  237. "Cf": round(Cf, 2), # 峰值指标
  238. "Sf": round(Sf, 2), # 波形因子
  239. "If": round(If, 2), # 脉冲指标
  240. "Ce": round(Ce, 2), # 裕度指标
  241. "Cw": round(Cw, 2), # 偏度指标
  242. "Cq": round(Cq, 2), # 峭度指标
  243. "rpm_Gen": round(rpm_Gen, 2), # 转速r/min
  244. }
  245. result = json.dumps(result, ensure_ascii=False)
  246. return result
  247. # trend_analysis
  248. def trend_analysis(self):
  249. all_stats = []
  250. # 定义积分函数
  251. def _integrate(data, dt):
  252. return np.cumsum(data) * dt
  253. # 定义计算统计指标的函数
  254. def _calculate_stats(data):
  255. mean_value = np.mean(data)
  256. max_value = np.max(data)
  257. min_value = np.min(data)
  258. Xrms = np.sqrt(np.mean(data ** 2)) # 加速度均方根值(有效值)
  259. # Xrms = filtered_acceleration_rms # 加速度均方根值(有效值)
  260. Xp = (max_value - min_value) / 2 # 峰值(单峰最大值) # 峰值
  261. Cf = Xp / Xrms # 峰值指标
  262. Sf = Xrms / mean_value # 波形指标
  263. If = Xp / np.mean(np.abs(data)) # 脉冲指标
  264. Xr = np.mean(np.sqrt(np.abs(data))) ** 2 # 方根幅值
  265. Ce = Xp / Xr # 裕度指标
  266. # 计算每个数据点的绝对值减去均值后的三次方,并求和
  267. sum_abs_diff_cubed_3 = np.mean((np.abs(data) - mean_value) ** 3)
  268. # 计算偏度指标
  269. Cw = sum_abs_diff_cubed_3 / (Xrms ** 3)
  270. # 计算每个数据点的绝对值减去均值后的四次方,并求和
  271. sum_abs_diff_cubed_4 = np.mean((np.abs(data) - mean_value) ** 4)
  272. # 计算峭度指标
  273. Cq = sum_abs_diff_cubed_4 / (Xrms ** 4)
  274. #
  275. return {
  276. "fs": self.fs, # 采样频率
  277. "Mean": round(mean_value, 2), # 平均值
  278. "Max": round(max_value, 2), # 最大值
  279. "Min": round(min_value, 2), # 最小值
  280. "Xrms": round(Xrms, 2), # 有效值
  281. "Xp": round(Xp, 2), # 峰值
  282. "If": round(If, 2), # 脉冲指标
  283. "Cf": round(Cf, 2), # 峰值指标
  284. "Sf": round(Sf, 2), # 波形指标
  285. "Ce": round(Ce, 2), # 裕度指标
  286. "Cw": round(Cw, 2), # 偏度指标
  287. "Cq": round(Cq, 2), # 峭度指标
  288. # velocity_rms :速度有效值
  289. # time_stamp:时间戳
  290. }
  291. for data in self.datas:
  292. fs = int(self.data_filter['sampling_frequency'].iloc[0])
  293. dt = 1 / fs
  294. time_stamp = data['time_stamp'][0]
  295. data = np.array(ast.literal_eval(data['mesure_data'][0]))
  296. velocity = _integrate(data, dt)
  297. velocity_rms = np.sqrt(np.mean(velocity ** 2))
  298. stats = _calculate_stats(data)
  299. stats["velocity_rms"] = round(velocity_rms, 2) # 速度有效值
  300. stats["time_stamp"] = str(time_stamp) # 时间戳
  301. all_stats.append(stats)
  302. # df = pd.DataFrame(all_stats)
  303. all_stats = json.dumps(all_stats, ensure_ascii=False)
  304. return all_stats
  305. def Characteristic_Frequency(self):
  306. """提取轴承、齿轮等参数"""
  307. # 1、从测点名称中提取部件名称(计算特征频率的部件)
  308. str1 = self.mesure_point_name
  309. str2 = ["main_bearing", "front_main_bearing", "rear_main_bearing", "generator_non_drive_end"]
  310. for str in str2:
  311. if str in str1:
  312. parts = str
  313. if parts == "front_main_bearing":
  314. parts = "front_bearing"
  315. elif parts == "rear_main_bearing":
  316. parts = "rear_bearing"
  317. # 2、连接233的数据库'energy_show',从表'wind_engine_group'查找风机编号'engine_code'对应的机型编号'mill_type_code'
  318. engine = get_engine(dataBase.PLATFORM_DB)
  319. engine_code = self.wind_code
  320. df_sql2 = f"SELECT * FROM wind_engine_group where engine_code = {engine_code} "
  321. df2 = pd.read_sql(df_sql2, engine)
  322. mill_type_code = df2['mill_type_code'].iloc[0]
  323. # 3、从表'unit_bearings'中通过机型编号'mill_type_code'查找部件'brand'、'model'的参数信息
  324. df_sql3 = f"SELECT * FROM unit_bearings where mill_type_code = {mill_type_code} "
  325. df3 = pd.read_sql(df_sql3, engine)
  326. brand = 'front_bearing' + '_brand' # parts代替'front_bearing'
  327. model = 'front_bearing' + '_model' # parts代替'front_bearing'
  328. _brand = df3[brand].iloc[0]
  329. _model = df3[model].iloc[0]
  330. # 4、从表'unit_dict_brand_model'中通过'_brand'、'_model'查找部件的参数信息
  331. df_sql4 = f"SELECT * FROM unit_dict_brand_model where manufacture = %s AND model_number = %s"
  332. params = [(_brand, _model)]
  333. df4 = pd.read_sql(df_sql4, engine, params=params)
  334. if 'bearing' in parts:
  335. n_rolls = df4['rolls_number'].iloc[0]
  336. d_rolls = df4['rolls_diameter'].iloc[0]
  337. D_diameter = df4['circle_diameter'].iloc[0]
  338. theta_deg = df4['theta_deg'].iloc[0]
  339. result = {
  340. "type": 'bearing',
  341. "n_rolls": round(n_rolls, 2),
  342. "d_rolls": round(d_rolls, 2),
  343. "D_diameter": round(D_diameter, 2),
  344. "theta_deg": round(theta_deg, 2),
  345. }
  346. return result
  347. def calculate_bearing_frequencies(self, n, d, D, theta_deg, rpm):
  348. """
  349. 计算轴承各部件特征频率
  350. 参数:
  351. n (int): 滚动体数量
  352. d (float): 滚动体直径(单位:mm)
  353. D (float): 轴承节圆直径(滚动体中心圆直径,单位:mm)
  354. theta_deg (float): 接触角(单位:度)
  355. rpm (float): 转速(转/分钟)
  356. 返回:
  357. dict: 包含各特征频率的字典(单位:Hz)
  358. """
  359. # 转换角度为弧度
  360. theta = math.radians(theta_deg)
  361. # 转换直径单位为米(保持单位一致性,实际计算中比值抵消单位影响)
  362. # 注意:由于公式中使用的是比值,单位可以保持mm不需要转换
  363. ratio = d / D
  364. # 基础频率计算(转/秒)
  365. f_r = rpm / 60.0
  366. # 计算各特征频率
  367. BPFI = n / 2 * (1 + ratio * math.cos(theta)) * f_r # 内圈故障频率
  368. BPFO = n / 2 * (1 - ratio * math.cos(theta)) * f_r # 外圈故障频率
  369. BSF = (D / (2 * d)) * (1 - (ratio ** 2) * (math.cos(theta) ** 2)) * f_r # 滚动体故障频率
  370. FTF = 0.5 * (1 - ratio * math.cos(theta)) * f_r # 保持架故障频率
  371. return {
  372. "BPFI": round(BPFI, 2),
  373. "BPFO": round(BPFO, 2),
  374. "BSF": round(BSF, 2),
  375. "FTF": round(FTF, 2),
  376. }