CMSAnalyst.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  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 typing import List, Dict, Any
  8. from app.config import dataBase
  9. from app.database import get_engine
  10. from app.logger import logger
  11. class CMSAnalyst:
  12. def __init__(self, fmin, fmax, table_name, ids):
  13. # 从数据库获取原始数据
  14. self.table_name =table_name
  15. self.ids = ids
  16. self.datas = self._get_by_id(table_name, ids)
  17. self.datas = [
  18. df[['id', 'mesure_data', 'time_stamp', 'sampling_frequency',
  19. 'wind_turbine_number', 'rotational_speed', 'mesure_point_name']]
  20. for df in self.datas
  21. ]
  22. # 只输入一个id,返回一个[df],所以拿到self.data[0]
  23. self.data_filter = self.datas[0]
  24. # 取数据列
  25. self.data = np.array(ast.literal_eval(self.data_filter['mesure_data'].iloc[0]))
  26. self.envelope_spectrum_m = self.data.shape[0]
  27. self.envelope_spectrum_n = 1
  28. # 设置分析参数
  29. self.fs = int(self.data_filter['sampling_frequency'].iloc[0])
  30. self.envelope_spectrum_t = np.arange(self.envelope_spectrum_m) / self.fs
  31. self.fmin = fmin if fmin is not None else 0
  32. self.fmax = fmax if fmax is not None else float('inf')
  33. self.envelope_spectrum_y = self._bandpass_filter(self.data)
  34. self.f, self.HP = self._calculate_envelope_spectrum(self.envelope_spectrum_y)
  35. # 设备信息
  36. self.wind_code = self.data_filter['wind_turbine_number'].iloc[0]
  37. self.rpm_Gen = self.data_filter['rotational_speed'].iloc[0]
  38. self.mesure_point_name = self.data_filter['mesure_point_name'].iloc[0]
  39. self.fn_Gen = round(self.rpm_Gen / 60, 2)
  40. self.CF = self.Characteristic_Frequency()
  41. self.CF = pd.DataFrame(self.CF, index=[0])
  42. n_rolls_m = self.CF['n_rolls'].iloc[0]
  43. d_rolls_m = self.CF['d_rolls'].iloc[0]
  44. D_diameter_m = self.CF['D_diameter'].iloc[0]
  45. theta_deg_m = self.CF['theta_deg'].iloc[0]
  46. self.bearing_frequencies = self.calculate_bearing_frequencies(n_rolls_m, d_rolls_m, D_diameter_m, theta_deg_m,
  47. self.rpm_Gen)
  48. self.bearing_frequencies = pd.DataFrame(self.bearing_frequencies, index=[0])
  49. # frequency_domain_analysis
  50. (
  51. self.frequency_domain_analysis_t,
  52. self.frequency_domain_analysis_f,
  53. self.frequency_domain_analysis_m,
  54. self.frequency_domain_analysis_mag,
  55. self.frequency_domain_analysis_Xrms,
  56. ) = self._calculate_spectrum(self.data)
  57. # time_domain_analysis
  58. self.time_domain_analysis_t = np.arange(self.data.shape[0]) / self.fs
  59. def _get_by_id(self, windcode, ids):
  60. engine = get_engine(dataBase.DATA_DB)
  61. table_name = windcode + '_wave'
  62. ids_str = ','.join(map(str, ids))
  63. sql = f"SELECT * FROM {table_name} WHERE id IN ({ids_str}) ORDER BY time_stamp"
  64. df = pd.read_sql(sql, engine)
  65. grouped = [group.reset_index(drop=True) for _, group in df.groupby('id')]
  66. return grouped
  67. # envelope_spectrum_analysis 包络谱分析
  68. def _bandpass_filter(self, data):
  69. """带通滤波"""
  70. m = data.shape[0]
  71. ni = round(self.fmin * self.envelope_spectrum_m / self.fs + 1)
  72. if self.fmax == float('inf'):
  73. na = m
  74. else:
  75. na = round(self.fmax * m / self.fs + 1)
  76. col = 1
  77. y = np.zeros((self.envelope_spectrum_m, col))
  78. z = np.fft.fft(data)
  79. a = np.zeros(self.envelope_spectrum_m, dtype=complex)
  80. a[ni:na] = z[ni:na]
  81. a[self.envelope_spectrum_m - na + 1: self.envelope_spectrum_m - ni + 1] = z[
  82. self.envelope_spectrum_m - na + 1: self.envelope_spectrum_m - ni + 1
  83. ]
  84. z = np.fft.ifft(a)
  85. y[:, 0] = np.real(z)
  86. return y
  87. def _calculate_envelope_spectrum(self, y):
  88. """计算包络谱"""
  89. m, n = y.shape
  90. HP = np.zeros((m, n))
  91. col = 1
  92. for p in range(col):
  93. H = np.abs(hilbert(y[:, p] - np.mean(y[:, p])))
  94. HP[:, p] = np.abs(np.fft.fft(H - np.mean(H))) * 2 / m
  95. f = np.fft.fftfreq(m, d=1 / self.fs)
  96. return f, HP
  97. def envelope_spectrum(self):
  98. """绘制包络谱"""
  99. # 只取正频率部分
  100. positive_frequencies = self.f[: self.envelope_spectrum_m // 2]
  101. positive_HP = self.HP[: self.envelope_spectrum_m // 2, 0]
  102. x = positive_frequencies
  103. y = positive_HP
  104. title = "包络谱"
  105. xaxis = "频率(Hz)"
  106. yaxis = "加速度(m/s^2)"
  107. # 加速度均方根值(有效值)
  108. Xrms = np.sqrt(np.mean(y ** 2))
  109. rpm_Gen = round(self.rpm_Gen, 2)
  110. BPFI_1X = round(self.bearing_frequencies['BPFI'].iloc[0], 2)
  111. BPFO_1X = round(self.bearing_frequencies['BPFO'].iloc[0], 2)
  112. BSF_1X = round(self.bearing_frequencies['BSF'].iloc[0], 2)
  113. FTF_1X = round(self.bearing_frequencies['FTF'].iloc[0], 2)
  114. fn_Gen = round(self.fn_Gen, 2)
  115. _3P_1X = round(self.fn_Gen, 2) * 3
  116. result = {
  117. "fs": self.fs,
  118. "Xrms": round(Xrms, 2),
  119. "x": list(x),
  120. "y": list(y),
  121. "title": title,
  122. "xaxis": xaxis,
  123. "yaxis": yaxis,
  124. "rpm_Gen": round(rpm_Gen, 2), # 转速r/min
  125. "BPFI": [{"Xaxis": BPFI_1X, "val": "1BPFI"}, {"Xaxis": BPFI_1X * 2, "val": "2BPFI"},
  126. {"Xaxis": BPFI_1X * 3, "val": "3BPFI"}, {"Xaxis": BPFI_1X * 4, "val": "4BPFI"},
  127. {"Xaxis": BPFI_1X * 5, "val": "5BPFI"}, {"Xaxis": BPFI_1X * 6, "val": "6BPFI"}],
  128. "BPFO": [{"Xaxis": BPFO_1X, "val": "1BPFO"}, {"Xaxis": BPFO_1X * 2, "val": "2BPFO"},
  129. {"Xaxis": BPFO_1X * 3, "val": "3BPFO"}, {"Xaxis": BPFO_1X * 4, "val": "4BPFO"},
  130. {"Xaxis": BPFO_1X * 5, "val": "5BPFO"}, {"Xaxis": BPFO_1X * 6, "val": "6BPFO"}],
  131. "BSF": [{"Xaxis": BSF_1X, "val": "1BSF"}, {"Xaxis": BSF_1X * 2, "val": "2BSF"},
  132. {"Xaxis": BSF_1X * 3, "val": "3BSF"}, {"Xaxis": BSF_1X * 4, "val": "4BSF"},
  133. {"Xaxis": BSF_1X * 5, "val": "5BSF"}, {"Xaxis": BSF_1X * 6, "val": "6BSF"}],
  134. "FTF": [{"Xaxis": FTF_1X, "val": "1FTF"}, {"Xaxis": FTF_1X * 2, "val": "2FTF"},
  135. {"Xaxis": FTF_1X * 3, "val": "3FTF"}, {"Xaxis": FTF_1X * 4, "val": "4FTF"},
  136. {"Xaxis": FTF_1X * 5, "val": "5FTF"}, {"Xaxis": FTF_1X * 6, "val": "6FTF"}],
  137. "fn_Gen": [{"Xaxis": fn_Gen, "val": "1X"}, {"Xaxis": fn_Gen * 2, "val": "2X"},
  138. {"Xaxis": fn_Gen * 3, "val": "3X"}, {"Xaxis": fn_Gen * 4, "val": "4X"},
  139. {"Xaxis": fn_Gen * 5, "val": "5X"}, {"Xaxis": fn_Gen * 6, "val": "6X"}],
  140. "B3P": _3P_1X,
  141. }
  142. # result = json.dumps(result, ensure_ascii=False)
  143. result = self.replace_nan(result)
  144. return result
  145. # frequency_domain_analysis 频谱分析
  146. def _calculate_spectrum(self, data):
  147. """计算频谱"""
  148. m = data.shape[0]
  149. n = 1
  150. t = np.arange(m) / self.fs
  151. mag = np.zeros((m, n))
  152. Xrms = np.sqrt(np.mean(data ** 2)) # 加速度均方根值(有效值)
  153. # col=1
  154. # for p in range(col):
  155. mag = np.abs(np.fft.fft(data - np.mean(data))) * 2 / m
  156. f = np.fft.fftfreq(m, d=1 / self.fs)
  157. return t, f, m, mag, Xrms
  158. def frequency_domain(self):
  159. """绘制频域波形参数"""
  160. # 只取正频率部分
  161. positive_frequencies = self.frequency_domain_analysis_f[
  162. : self.frequency_domain_analysis_m // 2
  163. ]
  164. positive_mag = self.frequency_domain_analysis_mag[
  165. : self.frequency_domain_analysis_m // 2
  166. ]
  167. x = positive_frequencies
  168. y = positive_mag
  169. title = "频域信号"
  170. xaxis = "频率(Hz)"
  171. yaxis = "加速度(m/s^2)"
  172. Xrms = self.frequency_domain_analysis_Xrms
  173. rpm_Gen = round(self.rpm_Gen, 2)
  174. BPFI_1X = round(self.bearing_frequencies['BPFI'].iloc[0], 2)
  175. BPFO_1X = round(self.bearing_frequencies['BPFO'].iloc[0], 2)
  176. BSF_1X = round(self.bearing_frequencies['BSF'].iloc[0], 2)
  177. FTF_1X = round(self.bearing_frequencies['FTF'].iloc[0], 2)
  178. fn_Gen = round(self.fn_Gen, 2)
  179. _3P_1X = round(self.fn_Gen, 2) * 3
  180. result = {
  181. "fs": self.fs,
  182. "Xrms": round(Xrms, 2),
  183. "x": list(x),
  184. "y": list(y),
  185. "title": title,
  186. "xaxis": xaxis,
  187. "yaxis": yaxis,
  188. "rpm_Gen": round(rpm_Gen, 2), # 转速r/min
  189. "BPFI": [{"Xaxis": BPFI_1X, "val": "1BPFI"}, {"Xaxis": BPFI_1X * 2, "val": "2BPFI"},
  190. {"Xaxis": BPFI_1X * 3, "val": "3BPFI"}, {"Xaxis": BPFI_1X * 4, "val": "4BPFI"},
  191. {"Xaxis": BPFI_1X * 5, "val": "5BPFI"}, {"Xaxis": BPFI_1X * 6, "val": "6BPFI"}],
  192. "BPFO": [{"Xaxis": BPFO_1X, "val": "1BPFO"}, {"Xaxis": BPFO_1X * 2, "val": "2BPFO"},
  193. {"Xaxis": BPFO_1X * 3, "val": "3BPFO"}, {"Xaxis": BPFO_1X * 4, "val": "4BPFO"},
  194. {"Xaxis": BPFO_1X * 5, "val": "5BPFO"}, {"Xaxis": BPFO_1X * 6, "val": "6BPFO"}],
  195. "BSF": [{"Xaxis": BSF_1X, "val": "1BSF"}, {"Xaxis": BSF_1X * 2, "val": "2BSF"},
  196. {"Xaxis": BSF_1X * 3, "val": "3BSF"}, {"Xaxis": BSF_1X * 4, "val": "4BSF"},
  197. {"Xaxis": BSF_1X * 5, "val": "5BSF"}, {"Xaxis": BSF_1X * 6, "val": "6BSF"}],
  198. "FTF": [{"Xaxis": FTF_1X, "val": "1FTF"}, {"Xaxis": FTF_1X * 2, "val": "2FTF"},
  199. {"Xaxis": FTF_1X * 3, "val": "3FTF"}, {"Xaxis": FTF_1X * 4, "val": "4FTF"},
  200. {"Xaxis": FTF_1X * 5, "val": "5FTF"}, {"Xaxis": FTF_1X * 6, "val": "6FTF"}],
  201. "fn_Gen": [{"Xaxis": fn_Gen, "val": "1X"}, {"Xaxis": fn_Gen * 2, "val": "2X"},
  202. {"Xaxis": fn_Gen * 3, "val": "3X"}, {"Xaxis": fn_Gen * 4, "val": "4X"},
  203. {"Xaxis": fn_Gen * 5, "val": "5X"}, {"Xaxis": fn_Gen * 6, "val": "6X"}],
  204. "B3P": _3P_1X,
  205. }
  206. result = self.replace_nan(result)
  207. result = json.dumps(result, ensure_ascii=False)
  208. return result
  209. # time_domain_analysis 时域分析
  210. def time_domain(self):
  211. """绘制时域波形参数"""
  212. x = self.time_domain_analysis_t
  213. y = self.data
  214. rpm_Gen = self.rpm_Gen
  215. title = "时间域信号"
  216. xaxis = "时间(s)"
  217. yaxis = "加速度(m/s^2)"
  218. # 图片右侧统计量
  219. # 平均值
  220. mean_value = np.mean(y)
  221. # 最大值
  222. max_value = np.max(y)
  223. # 最小值
  224. min_value = np.min(y)
  225. # 加速度均方根值(有效值)
  226. Xrms = np.sqrt(np.mean(y ** 2))
  227. # 峰值(单峰最大值) # 峰值
  228. Xp = (max_value - min_value) / 2
  229. # 峰峰值
  230. Xpp = max_value - min_value
  231. # 峰值指标
  232. Cf = Xp / Xrms
  233. # 波形指标
  234. Sf = Xrms / mean_value
  235. # 脉冲指标
  236. If = Xp / np.mean(np.abs(y))
  237. # 方根幅值
  238. Xr = np.mean(np.sqrt(np.abs(y))) ** 2
  239. # 裕度指标
  240. Ce = Xp / Xr
  241. # 计算每个数据点的绝对值减去均值后的三次方,并求和
  242. sum_abs_diff_cubed_3 = np.mean((np.abs(y) - mean_value) ** 3)
  243. # 计算偏度指标
  244. Cw = sum_abs_diff_cubed_3 / (Xrms ** 3)
  245. # 计算每个数据点的绝对值减去均值后的四次方,并求和
  246. sum_abs_diff_cubed_4 = np.mean((np.abs(y) - mean_value) ** 4)
  247. # 计算峭度指标
  248. Cq = sum_abs_diff_cubed_4 / (Xrms ** 4)
  249. result = {
  250. "x": list(x),
  251. "y": list(y),
  252. "title": title,
  253. "xaxis": xaxis,
  254. "yaxis": yaxis,
  255. "fs": self.fs,
  256. "Xrms": round(Xrms, 2), # 有效值
  257. "mean_value": round(mean_value, 2), # 均值
  258. "max_value": round(max_value, 2), # 最大值
  259. "min_value": round(min_value, 2), # 最小值
  260. "Xp": round(Xp, 2), # 峰值
  261. "Xpp": round(Xpp, 2), # 峰峰值
  262. "Cf": round(Cf, 2), # 峰值指标
  263. "Sf": round(Sf, 2), # 波形因子
  264. "If": round(If, 2), # 脉冲指标
  265. "Ce": round(Ce, 2), # 裕度指标
  266. "Cw": round(Cw, 2), # 偏度指标
  267. "Cq": round(Cq, 2), # 峭度指标
  268. "rpm_Gen": round(rpm_Gen, 2), # 转速r/min
  269. }
  270. result = self.replace_nan(result)
  271. result = json.dumps(result, ensure_ascii=False)
  272. return result
  273. def trend_analysis(self) -> str:
  274. """
  275. 优化后的趋势分析方法(向量化计算统计指标)
  276. 返回 JSON 字符串,包含所有时间点的统计结果。
  277. """
  278. for df in self.datas:
  279. df['parsed_data'] = df['mesure_data'].apply(json.loads)
  280. # 1. 合并所有数据并解析 mesure_data
  281. combined_df = pd.concat(self.datas)
  282. combined_df['parsed_data'] = combined_df['mesure_data'].apply(json.loads) # 批量解析 JSON
  283. # 2. 向量化计算统计指标(避免逐行循环)
  284. def calculate_stats(group: pd.DataFrame) -> Dict[str, Any]:
  285. data = np.array(group['parsed_data'].iloc[0]) # 提取振动数据数组
  286. fs = int(group['sampling_frequency'].iloc[0]) # 采样频率
  287. dt = 1 / fs # 时间间隔
  288. # 计算时域指标(向量化操作)
  289. mean = np.mean(data)
  290. max_val = np.max(data)
  291. min_val = np.min(data)
  292. Xrms = np.sqrt(np.mean(data ** 2))
  293. Xp = (max_val - min_val) / 2
  294. Cf = Xp / Xrms
  295. Sf = Xrms / mean if mean != 0 else 0
  296. If = Xp / np.mean(np.abs(data))
  297. # 计算速度和峭度指标
  298. velocity = np.cumsum(data) * dt # 积分计算速度
  299. velocity_rms = np.sqrt(np.mean(velocity ** 2))
  300. Cq = np.mean((data - mean) ** 4) / (Xrms ** 4) if Xrms != 0 else 0
  301. return {
  302. "time_stamp": str(group['time_stamp'].iloc[0]),
  303. "fs": fs,
  304. "Mean": round(mean, 2),
  305. "Max": round(max_val, 2),
  306. "Min": round(min_val, 2),
  307. "Xrms": round(Xrms, 2),
  308. "Xp": round(Xp, 2),
  309. "Cf": round(Cf, 2),
  310. "Sf": round(Sf, 2),
  311. "If": round(If, 2),
  312. "velocity_rms": round(velocity_rms, 2),
  313. "Cq": round(Cq, 2)
  314. }
  315. # 3. 按 ID 分组并应用统计计算
  316. stats = combined_df.groupby('id').apply(calculate_stats).tolist()
  317. # 4. 返回 JSON 格式结果
  318. return json.dumps(stats, ensure_ascii=False)
  319. def Characteristic_Frequency(self):
  320. """提取轴承、齿轮等参数"""
  321. # 1、从测点名称中提取部件名称(计算特征频率的部件)
  322. str1 = self.mesure_point_name
  323. # 2、连接233的数据库'energy_show',从表'wind_engine_group'查找风机编号'engine_code'对应的机型编号'mill_type_code'
  324. engine_code = self.wind_code
  325. engine = get_engine(dataBase.PLATFORM_DB)
  326. df_sql2 = f"SELECT * FROM wind_engine_group WHERE engine_code = '{engine_code}'"
  327. df2 = pd.read_sql(df_sql2, engine)
  328. mill_type_code = df2['mill_type_code'].iloc[0]
  329. # 3、从相关的表中通过机型编号'mill_type_code'或者齿轮箱编号gearbox_code查找部件'brand'、'model'的参数信息
  330. # unit_bearings主轴承参数表 关键词"main_bearing"
  331. if 'main_bearing' in str1:
  332. logger.info("main_bearing")
  333. # df_sql3 = f"SELECT * FROM {'unit_bearings'} where mill_type_code = {'mill_type_code'} "
  334. df_sql3 = f"SELECT * FROM unit_bearings WHERE mill_type_code = '{mill_type_code}' "
  335. df3 = pd.read_sql(df_sql3, engine)
  336. if df3.empty:
  337. logger.info("警告: 没有找到有效的机型信息")
  338. if 'front' in str1:
  339. brand = 'front_bearing' + '_brand'
  340. model = 'front_bearing' + '_model'
  341. front_has_value = not pd.isna(df3[brand].iloc[0]) and not pd.isna(df3[model].iloc[0])
  342. if not front_has_value:
  343. logger.info("警告: 没有找到有效的品牌信息")
  344. elif 'rear' in str1:
  345. brand = 'rear_bearing' + '_brand'
  346. model = 'rear_bearing' + '_model'
  347. end_has_value = not pd.isna(df3[brand].iloc[0]) and not pd.isna(df3[model].iloc[0])
  348. if not end_has_value:
  349. logger.info("警告: 没有找到有效的品牌信息")
  350. else:
  351. # 当没有指定 front 或 end 时,自动选择有值的轴承信息
  352. front_brand_col = 'front_bearing_brand'
  353. front_model_col = 'front_bearing_model'
  354. rear_brand_col = 'rear_bearing_brand'
  355. rear_model_col = 'rear_bearing_model'
  356. # 检查 front_bearing 是否有值
  357. front_has_value = not pd.isna(df3[front_brand_col].iloc[0]) and not pd.isna(
  358. df3[front_model_col].iloc[0])
  359. # 检查 end_bearing 是否有值
  360. end_has_value = not pd.isna(df3[rear_brand_col].iloc[0]) and not pd.isna(df3[rear_model_col].iloc[0])
  361. # 根据检查结果选择合适的列
  362. if front_has_value and end_has_value:
  363. # 如果两者都有值,默认选择 front
  364. brand = front_brand_col
  365. model = front_model_col
  366. elif front_has_value:
  367. brand = front_brand_col
  368. model = front_model_col
  369. elif end_has_value:
  370. brand = rear_brand_col
  371. model = rear_model_col
  372. else:
  373. # 如果两者都没有有效值,设置默认值或抛出异常
  374. logger.info("警告: 没有找到有效的轴承信息")
  375. brand = front_brand_col # 默认使用 front
  376. model = front_model_col # 默认使用 front
  377. _brand = df3[brand].iloc[0]
  378. _model = df3[model].iloc[0]
  379. logger.info(f"brand = {_brand}, model = {_model}")
  380. # unit_dynamo 发电机参数表 关键词generator stator
  381. elif 'generator' in str1 or 'stator' in str1:
  382. logger.info("generator or 'stator'")
  383. df_sql3 = f"SELECT * FROM unit_dynamo WHERE mill_type_code = '{mill_type_code}' "
  384. df3 = pd.read_sql(df_sql3, engine)
  385. if 'non' in str1:
  386. brand = 'non_drive_end_bearing' + '_brand'
  387. model = 'non_drive_end_bearing' + '_model'
  388. else:
  389. brand = 'drive_end_bearing' + '_brand'
  390. model = 'drive_end_bearing' + '_model'
  391. _brand = df3[brand].iloc[0]
  392. _model = df3[model].iloc[0]
  393. logger.info(f"brand = {_brand}, model = {_model}")
  394. # 齿轮箱区分行星轮/平行轮 和 轴承两个表
  395. elif 'gearbox' in str1:
  396. logger.info("gearbox")
  397. # 根据mill_type_code从unit_gearbox表中获得gearbox_code
  398. df_sql3 = f"SELECT * FROM unit_gearbox WHERE mill_type_code = '{mill_type_code}' "
  399. df3 = pd.read_sql(df_sql3, engine)
  400. gearbox_code = df3['code'].iloc[0]
  401. logger.info(gearbox_code)
  402. # 如果是行星轮/平行轮 则从unit_gearbox_structure 表中取数据
  403. if 'planet' in str1 or 'sun' in str1:
  404. logger.info("'planet' or 'sun' ")
  405. gearbox_structure = 1 if 'planet' in str1 else 2
  406. planetary_gear_grade = 1
  407. if 'first' in str1:
  408. planetary_gear_grade = 1
  409. elif 'second' in str1:
  410. planetary_gear_grade = 2
  411. elif 'third' in str1:
  412. planetary_gear_grade = 3
  413. df_sql33 = f"""
  414. SELECT bearing_brand, bearing_model
  415. FROM unit_gearbox_structure
  416. WHERE gearbox_code = '{gearbox_code}'
  417. AND gearbox_structure = '{gearbox_structure}'
  418. AND planetary_gear_grade = '{planetary_gear_grade}'
  419. """
  420. df33 = pd.read_sql(df_sql33, engine)
  421. if df33.empty:
  422. logger.info("unit_gearbox_structure没有该测点的参数")
  423. else:
  424. brand = 'bearing' + '_brand'
  425. model = 'bearing' + '_model'
  426. logger.info(brand)
  427. _brand = df33[brand].iloc[0]
  428. _model = df33[model].iloc[0]
  429. has_value = not pd.isna(df33[brand].iloc[0]) and not pd.isna(df33[model].iloc[0])
  430. if has_value:
  431. logger.info(_brand)
  432. logger.info(_model)
  433. else:
  434. logger.info("警告: 没有找到有效的轴承信息")
  435. # 如果是齿轮箱轴承 则从unit_gearbox_bearings 表中取数据
  436. elif 'shaft' in str1 or 'input' in str1:
  437. logger.info("'shaft'or'input'")
  438. # df_sql33 = f"SELECT * FROM unit_gearbox_bearings WHERE gearbox_code = '{gearbox_code}' "
  439. # df33 = pd.read_sql(df_sql33, Engine33)
  440. # 高速轴 低速中间轴 取bearing_rs/gs均可
  441. parallel_wheel_grade = 1
  442. if 'low_speed' in str1:
  443. parallel_wheel_grade = 1
  444. elif 'low_speed_intermediate' in str1:
  445. parallel_wheel_grade = 2
  446. elif 'high_speed' in str1:
  447. parallel_wheel_grade = 3
  448. df_sql33 = f"""
  449. SELECT bearing_rs_brand, bearing_rs_model, bearing_gs_brand, bearing_gs_model
  450. FROM unit_gearbox_bearings
  451. WHERE gearbox_code = '{gearbox_code}'
  452. AND parallel_wheel_grade = '{parallel_wheel_grade}'
  453. """
  454. df33 = pd.read_sql(df_sql33, engine)
  455. if not df33.empty:
  456. if 'high_speed' in str1 or 'low_speed_intermediate' in str1:
  457. rs_brand = 'bearing_rs' + '_brand'
  458. rs_model = 'bearing_rs' + '_model'
  459. gs_brand = 'bearing_gs' + '_brand'
  460. gs_model = 'bearing_gs' + '_model'
  461. rs_has_value = not pd.isna(df33[rs_brand].iloc[0]) and not pd.isna(df33[rs_model].iloc[0])
  462. gs_has_value = not pd.isna(df33[gs_brand].iloc[0]) and not pd.isna(df33[gs_model].iloc[0])
  463. if rs_has_value and gs_has_value:
  464. brand = rs_brand
  465. model = rs_model
  466. elif rs_has_value:
  467. brand = rs_brand
  468. model = rs_model
  469. elif gs_has_value:
  470. brand = gs_brand
  471. model = gs_model
  472. else:
  473. logger.info("警告: 没有找到有效的品牌信息")
  474. brand = rs_brand
  475. model = rs_model
  476. # 低速轴 取bearing_model
  477. elif 'low_speed' in str1:
  478. brand = 'bearing' + '_brand'
  479. model = 'bearing' + '_model'
  480. else:
  481. logger.info("警告: 没有找到有效的轴承信息")
  482. logger.info(f"brand = {brand}")
  483. _brand = df33[brand].iloc[0]
  484. _model = df33[model].iloc[0]
  485. logger.info(f"brand = {_brand}, model = {_model}")
  486. # 4、从表'unit_dict_brand_model'中通过'_brand'、'_model'查找部件的参数信息
  487. df_sql4 = f"SELECT * FROM unit_dict_brand_model where manufacture = %s AND model_number = %s"
  488. params = [(_brand, _model)]
  489. df4 = pd.read_sql(df_sql4, engine, params=params)
  490. n_rolls = df4['rolls_number'].iloc[0]
  491. d_rolls = df4['rolls_diameter'].iloc[0]
  492. D_diameter = df4['circle_diameter'].iloc[0]
  493. theta_deg = df4['theta_deg'].iloc[0]
  494. result = {
  495. "type": 'bearing',
  496. "n_rolls": round(n_rolls, 2),
  497. "d_rolls": round(d_rolls, 2),
  498. "D_diameter": round(D_diameter, 2),
  499. "theta_deg": round(theta_deg, 2),
  500. }
  501. return result
  502. def calculate_bearing_frequencies(self, n, d, D, theta_deg, rpm):
  503. """
  504. 计算轴承各部件特征频率
  505. 参数:
  506. n (int): 滚动体数量
  507. d (float): 滚动体直径(单位:mm)
  508. D (float): 轴承节圆直径(滚动体中心圆直径,单位:mm)
  509. theta_deg (float): 接触角(单位:度)
  510. rpm (float): 转速(转/分钟)
  511. 返回:
  512. dict: 包含各特征频率的字典(单位:Hz)
  513. """
  514. # 转换角度为弧度
  515. theta = math.radians(theta_deg)
  516. # 转换直径单位为米(保持单位一致性,实际计算中比值抵消单位影响)
  517. # 注意:由于公式中使用的是比值,单位可以保持mm不需要转换
  518. ratio = d / D
  519. # 基础频率计算(转/秒)
  520. f_r = rpm / 60.0
  521. # 计算各特征频率
  522. BPFI = n / 2 * (1 + ratio * math.cos(theta)) * f_r # 内圈故障频率
  523. BPFO = n / 2 * (1 - ratio * math.cos(theta)) * f_r # 外圈故障频率
  524. BSF = (D / (2 * d)) * (1 - (ratio ** 2) * (math.cos(theta) ** 2)) * f_r # 滚动体故障频率
  525. FTF = 0.5 * (1 - ratio * math.cos(theta)) * f_r # 保持架故障频率
  526. return {
  527. "BPFI": round(BPFI, 2),
  528. "BPFO": round(BPFO, 2),
  529. "BSF": round(BSF, 2),
  530. "FTF": round(FTF, 2),
  531. }
  532. #检查返回结果是否有nan 若有,则替换成none
  533. def replace_nan(self, obj):
  534. if isinstance(obj, dict):
  535. return {k: self.replace_nan(v) for k, v in obj.items()}
  536. elif isinstance(obj, list):
  537. return [self.replace_nan(item) for item in obj]
  538. elif isinstance(obj, float) and math.isnan(obj):
  539. return None
  540. return obj