| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692 |
- import os
- import json
- import pandas as pd
- import numpy as np
- import seaborn as sns
- import matplotlib.pyplot as plt
- from matplotlib.ticker import MaxNLocator
- from typing import Tuple, List
- import warnings
- import time
- import sys
- import frequency_filter as ff
- from datetime import datetime
- from scipy.optimize import least_squares, differential_evolution
- from scipy.signal import savgol_filter
- warnings.filterwarnings("ignore", category=FutureWarning) # 忽略特定警告
- plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体
- plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
- # TODO: 将偏斜角度还原叶片数据的代码转移到最后的步骤
- def result_main():
- """
- 创建data目录,返回历史分析数据存放的文件路径
- """
- # 获取当前程序的绝对路径
- python_interpreter_path = sys.executable
- project_directory = os.path.dirname(python_interpreter_path)
- data_folder = os.path.join(project_directory, 'data')
- # 检查data文件夹是否存在,如果不存在则创建
- if not os.path.exists(data_folder):
- os.makedirs(data_folder)
- # CSV文件路径
- csv_file_path = os.path.join(data_folder, 'history_data.csv')
- # 检查CSV文件是否存在,如果不存在则创建一个空的CSV文件
- if not os.path.exists(csv_file_path):
- pd.DataFrame(columns=['时间', '场站', '风机编号', '采样频率',
- '叶片1角度偏差', '叶片2角度偏差', '叶片3角度偏差', '相对角度偏差',
- '叶片1净空值', '叶片2净空值', '叶片3净空值',
- '叶片1扭转', '叶片2扭转', '叶片3扭转', '平均扭转',
- '振动幅值', '振动主频']).to_csv(csv_file_path, index=False)
- return csv_file_path
- def delete_data(name):
- """
- 删除历史分析数据
- :param name: 删除条件
- :return: csv文件路径
- """
- # 获取当前程序的绝对路径
- python_interpreter_path = sys.executable
- project_directory = os.path.dirname(python_interpreter_path)
- data_folder = os.path.join(project_directory, 'data')
- # CSV文件路径
- csv_file_path = os.path.join(data_folder, 'history_data.csv')
- df = pd.read_csv(csv_file_path)
- condition = ((df['时间'].astype(str).str.contains(name[0])) &
- (df['场站'].astype(str).str.contains(name[1])) &
- (df['风机编号'].astype(str).str.contains(name[2])))
- # 删除满足条件的行
- df = df[~condition]
- # 如果需要,可以将修改后的 DataFrame 保存回 CSV 文件
- df.to_csv(csv_file_path, index=False)
- return csv_file_path
- def history_data(name):
- """
- 读取历史分析数据
- :param name: 接口返回列表
- :return:
- """
- time_code = name[0]
- wind_name = name[1]
- turbine_code = name[2]
- # 获取当前程序的绝对路径
- python_interpreter_path = sys.executable
- project_directory = os.path.dirname(python_interpreter_path)
- data_folder = os.path.join(project_directory, 'data')
- time_code_cleaned = time_code.replace("-", "").replace(":", "").replace(" ", "")
- json_filename = f"{wind_name}_{turbine_code}_{time_code_cleaned}.json"
- json_file_path = os.path.join(data_folder, json_filename)
- if not os.path.exists(json_file_path):
- raise ValueError("文件不存在")
- with open(json_file_path, 'r') as f:
- data = json.load(f)
- return data
- def data_analyse(path: List[str]):
- """
- 创建data目录,把分析数据保存到历史记录中,同时返回全量分析数据
- locate_file_0:非中心线数据,1通道下方,2通道上方
- locate_file:轮毂中心数据,1通道叶尖,2通道轮毂中心
- measure_file:测量数据,1通道法兰,2通道叶片最宽处
- """
- # 基础配置参数
- locate_file_0 = path[0]
- locate_file = path[1]
- measure_file = path[2]
- angle_cone = float(path[3]) # 锥角
- axial_inclination = float(path[4]) # 轴向倾角
- rotate_angle = float(path[5]) # 偏航角
- skew_angle_meas = 0 # 偏航角
- noise_reduction = 0.000001 # 如果一个距离值的所有样本量小于总样本量的noise_reduction,则被去掉
- min_difference = 1.5 # 如果相邻2个点的距离差大于min_difference,则被注意是否是周期节点
- group_length = [10000, 10000, 5000, 10000] # 计算叶片轮廓时每个小切片的长度,4个数分别为叶中、叶根、叶尖、轴线计算切片长度
- lift_up_limit = 0 # 如果法兰中的数据大于这个值,则提升5m
- return_list = []
- # 读取文件信息,包括风场名、风机编号、时间、采样频率、2个通道俯仰角
- wind_name_0, turbine_code_0, time_code_0, sampling_fq_0, angle_locate_1, angle_locate_2 = find_param(locate_file_0)
- wind_name, turbine_code, time_code, sampling_fq, angle_nan, angle_cen = find_param(locate_file)
- wind_name_1, turbine_code_1, time_code_1, sampling_fq_1, angle_flange, angle_root = find_param(measure_file)
- sampling_fq_0 = sampling_fq_0 * 1000
- sampling_fq_1 = sampling_fq_1 * 1000
- sampling_fq = sampling_fq * 1000
- print(wind_name_0, turbine_code_0, time_code_0, sampling_fq_0, angle_locate_1, angle_locate_2)
- print(wind_name, turbine_code, time_code, sampling_fq, angle_nan, angle_cen)
- print(wind_name_1, turbine_code_1, time_code, sampling_fq_1, angle_flange, angle_root)
- # 读取数据,并检查是否有时间序列异常,分离2通道数据
- data_locate1, data_locate2 = process_data(locate_file_0, 0) # 定位数据,1是下方,2是上方
- data_locate0, data_locate_nan = process_data(locate_file, 0)
- data_nan, data_cen = process_data(locate_file, skew_angle_meas)
- data_flange, data_root = process_data(measure_file, skew_angle_meas)
- # 偏斜计算部分
- locate1, data_loc1 = locate_filter(data_locate1)
- locate2, data_loc2 = locate_filter(data_locate2)
- locate0_filter = tower_filter(data_locate0, noise_reduction)
- locate0 = locate0_filter['distance'].mean()
- start_loc1, end_loc1, filtered_data_loc1 = cycle_calculate(data_loc1, noise_reduction, min_difference)
- start_loc2, end_loc2, filtered_data_loc2 = cycle_calculate(data_loc2, noise_reduction, min_difference)
- if np.abs(start_loc2.iloc[1, 1] - start_loc1.iloc[0, 1]) < np.abs(start_loc2.iloc[0, 1] - start_loc1.iloc[0, 1]):
- start_loc2 = start_loc2.iloc[1:]
- end_loc2 = end_loc2.iloc[1:]
- angle_list = []
- for i in range(2, min(len(start_loc2), len(start_loc1)) - 2):
- time_period = start_loc2.iloc[i, 0] - start_loc1.iloc[i, 0] # 2个测量点的时间差
- time_angle1 = start_loc1.iloc[i + 1, 0] - start_loc1.iloc[i, 0] # 以下方测量点计一个周期
- time_angle2 = start_loc2.iloc[i, 0] - start_loc2.iloc[i - 1, 0] # 以上方测量点计一个周期
- if np.abs(time_angle1 - time_angle2) < 50000:
- angle_diff = 360 * time_period / (time_angle1 * 3)
- angle_list.append(angle_diff)
- else:
- pass
- angle_avg = np.mean(angle_list) # 计算2个测量点的平均相位角度
- locate0_coordinate = [locate0 * np.cos(np.deg2rad(angle_cen)), 0, locate0 * np.sin(np.deg2rad(angle_cen))]
- locate1_coordinate = [locate1 * np.cos(np.deg2rad(angle_locate_1)) * np.cos(np.deg2rad(rotate_angle)),
- locate1 * np.cos(np.deg2rad(angle_locate_1)) * np.sin(np.deg2rad(rotate_angle)),
- locate1 * np.sin(np.deg2rad(angle_locate_1))]
- locate2_coordinate = [locate2 * np.cos(np.deg2rad(angle_locate_2)) * np.cos(np.deg2rad(rotate_angle)),
- locate2 * np.cos(np.deg2rad(angle_locate_2)) * np.sin(np.deg2rad(rotate_angle)),
- locate2 * np.sin(np.deg2rad(angle_locate_2))]
- equation_list = [locate0_coordinate, locate1_coordinate, locate2_coordinate,
- axial_inclination, angle_avg, angle_cone]
- print("\033[34m偏斜方程计算输入:\033[0m", equation_list)
- # skew_angle = ff.process_equations(equation_list)
- if lift_up_limit >= 0.1:
- discrete_values = np.arange(0, 0.101, 0.001)
- condition = data_flange['distance'] > lift_up_limit
- n = condition.sum()
- random_discrete = np.random.choice(discrete_values, size=n)
- data_flange.loc[condition, 'distance'] = lift_up_limit + 3 + random_discrete
- elif np.abs(lift_up_limit) < 0.1:
- pass
- else:
- raise ValueError("lift_up_limit error.")
- # 全部数据进行降噪、去除异常点处理,叶根叶尖数据计算叶片扫掠起始、结束点,轮毂中心数据计算距离均值
- start_flange, end_flange, filtered_data_flange = cycle_calculate(data_flange, noise_reduction, min_difference)
- start_root, end_root, filtered_data_root = cycle_calculate(data_root, noise_reduction, min_difference)
- start_nan, end_nan, filtered_data_nan = cycle_calculate(data_nan, noise_reduction, min_difference)
- # 轮毂中心数据降噪,并求均值得到塔筒中心距离,再把降噪后的数据根据俯仰角转换为水平方向的振动
- filtered_data_cen = tower_filter(data_cen, noise_reduction)
- dist_cen = np.mean(filtered_data_cen.iloc[:, 1].tolist())
- filtered_data_cen.iloc[:, 1] = filtered_data_cen.iloc[:, 1] * np.cos(np.deg2rad(angle_cen + axial_inclination))
- # 计算叶根、叶中、叶尖处的塔筒距离,对轮毂中心做FFT分析
- tower_dist_flange = ff.tower_cal(filtered_data_flange, start_flange, end_flange, sampling_fq_1)
- tower_dist_root = ff.tower_cal(filtered_data_root, start_root, end_root, sampling_fq_1)
- tower_dist_nan = ff.tower_cal(filtered_data_nan, start_nan, end_nan, sampling_fq)
- lowpass_data, fft_x, fft_y, tower_freq, tower_max= ff.process_fft(filtered_data_cen, sampling_fq)
- # 根据起始结束点,对叶根、对叶片数据进行归一化处理,计算每个叶片的散点表、线表、边界点表、标准循环周期长度、每个叶片平均最小值
- result_line_flange, result_scatter_flange, border_rows_flange, cycle_len_flange, min_flange \
- = data_normalize(filtered_data_flange, start_flange, end_flange, group_length[0])
- result_line_root, result_scatter_root, border_rows_root, cycle_len_root, min_root \
- = data_normalize(filtered_data_root, start_root, end_root, group_length[1])
- result_line_nan, result_scatter_nan, border_rows_nan, cycle_len_nan, min_nan \
- = data_normalize(filtered_data_nan, start_nan, end_nan, group_length[2])
- # 计算3个叶片的平均轮廓,3个叶片的形状差
- result_avg_flange, result_diff_flange = blade_shape(result_line_flange)
- result_avg_root, result_diff_root = blade_shape(result_line_root)
- # 对叶尖的边界点表和俯仰角做坐标归一化处理
- border_rows_flange_new, angle_flange_new = coordinate_normalize(border_rows_flange, angle_flange)
- border_rows_nan_new, angle_nan_new = coordinate_normalize(border_rows_nan, angle_nan)
- # 将所有 DataFrame 合并成一个并求平均
- flange_ava = pd.concat([df['distance'] for df in border_rows_flange_new]).mean(numeric_only=True).mean()
- root_ava = pd.concat([df['distance'] for df in border_rows_root]).mean(numeric_only=True).mean()
- d_radius = np.abs((flange_ava * np.cos(np.deg2rad(angle_flange_new))
- - root_ava * np.cos(np.deg2rad(angle_root))) * np.sin(np.deg2rad(axial_inclination))
- + (flange_ava * np.sin(np.deg2rad(angle_flange_new))
- - root_ava * np.sin(np.deg2rad(angle_root))) * np.cos(np.deg2rad(axial_inclination)))
- flange_root_dist = np.sqrt(flange_ava ** 2 + root_ava ** 2 - 2 * flange_ava * root_ava * np.cos(np.deg2rad(angle_flange_new - angle_root)))
- # 对叶片的边界点表做半径计算
- flange_r = radius_cal(border_rows_flange_new, angle_flange_new, dist_cen, angle_cen, axial_inclination, angle_cone)
- root_r = radius_cal(border_rows_root, angle_root, dist_cen, angle_cen, axial_inclination, angle_cone)
- nan_r = radius_cal(border_rows_nan_new, angle_nan_new, dist_cen, angle_cen, axial_inclination, angle_cone)
- blade_axis, blade_axis_tuple, result_line_flange = blade_axis_cal(filtered_data_flange, start_flange, end_flange,
- angle_flange + angle_cone + axial_inclination, group_length[3], flange_r)
- blade_axis_new, angle_flange_new = flange_coordinate_normalize(blade_axis, angle_flange)
- blade_axis_tuple_new, angle_flange_new = flange_coordinate_normalize(blade_axis_tuple, angle_flange)
- if np.abs((root_r - flange_r) - d_radius) > 0.5:
- print(str(root_r - flange_r) + "对比" + str(d_radius))
- root_r = flange_r + d_radius
- if np.abs(flange_root_dist - d_radius) > 0.5:
- print(str(flange_root_dist) + "对比" + str(d_radius))
- root_r = flange_r + flange_root_dist
- # root_r = d_radius + blade_axis_new["旋转半径"].iloc[0]
- # root_flange_dist = d_radius / np.cos(np.deg2rad(angle_cone))
- # blade_axis_new["中心y"] = np.sqrt(blade_axis_new["中心y"] ** 2 + root_flange_dist ** 2 - 2 *
- # root_flange_dist * blade_axis_new["中心y"]
- # * np.cos(np.deg2rad(90 - angle_cone - axial_inclination - angle_flange_new)))
- blade_axis_new["中心y"] = blade_axis_new["中心y"] - (flange_ava - root_ava)
- blade_axis_tuple_new["中心y"] = blade_axis_tuple_new["中心y"] - (flange_ava - root_ava)
- # 计算叶片测量位置处的绝对桨距角、相对桨距角、线速度、叶片内部中心点距离
- aero_dist_flange, v_speed_flange, cen_blade_flange = (
- blade_angle_aero_dist(border_rows_flange, flange_r, cycle_len_flange, tower_dist_flange, angle_flange_new))
- aero_dist_nan, v_speed_nan, cen_blade_nan = (
- blade_angle_aero_dist(border_rows_nan_new, nan_r, cycle_len_nan, tower_dist_nan, angle_nan_new))
- pitch_angle_root, v_speed_root, blade_axis_tuple_new = (
- blade_angle(border_rows_root, blade_axis_tuple_new, root_r, cycle_len_root, angle_root + angle_cone + axial_inclination))
- blade_axis_tuple_new["中心y"] = blade_axis_tuple_new["中心y"]*np.cos(np.deg2rad(angle_root + angle_cone + axial_inclination))
- # 将列表转换为 numpy 数组
- cen_blade_nan_array = np.array(cen_blade_nan)
- min_nan_array = np.array(min_nan)
- # 计算叶片内部中心点距离与叶片最小值之间的差值
- abs_diff_nan = np.abs(cen_blade_nan_array - min_nan_array)
- blade_dist_nan = abs_diff_nan * np.cos(np.deg2rad(angle_nan_new))
- blade_dist_nan.tolist()
- # 计算叶片转速-净空散点表
- dist_distribute_nan = blade_dist_distribute_cal(filtered_data_nan, start_nan, end_nan,
- tower_dist_nan, angle_nan_new + angle_cone + axial_inclination, blade_dist_nan)
- # dist_distribute = [df.round(5) for df in dist_distribute]
- dist_distribute = [df.round(5) for df in dist_distribute_nan]
- # 获取净空距离的最小值和最大值,以及它们对应的转速值,并分别保存在列表中
- min_values = []
- min_keys = []
- max_values = []
- max_keys = []
- mean_values = []
- for df in dist_distribute:
- second_col_min = df[df.columns[1]].min()
- second_col_max = df[df.columns[1]].max()
- min_row = df[df[df.columns[1]] == second_col_min]
- max_row = df[df[df.columns[1]] == second_col_max]
- min_values.append(round(second_col_min, 2))
- min_keys.append(round(min_row.iloc[0][df.columns[0]], 2))
- max_values.append(round(second_col_max, 2))
- max_keys.append(round(max_row.iloc[0][df.columns[0]], 2))
- for i in range(3):
- mean_values.append(round((max_values[i] + min_values[i]) / 2, 2))
- # 将叶片线表数据乘以线速度,和俯仰角,得到叶片横截面的真实轮廓
- # for i in range(3):
- # df = result_line_flange[i]
- # first_column = df.iloc[:, 0]
- # sec_column = df.iloc[:, 1]
- # df.iloc[:, 0] = first_column * v_speed_flange
- # blade_axis_tuple.iloc[i, 1] = blade_axis_tuple.iloc[i, 1] * v_speed_flange * 5000000
- # min_time = df.iloc[:, 0].min()
- # df.iloc[:, 0] -= min_time
- # blade_axis_tuple.iloc[i, 1] -= min_time
- for i in range(3):
- df = result_line_root[i]
- first_column = df.iloc[:, 0]
- sec_column = df.iloc[:, 1]
- df.iloc[:, 0] = first_column * v_speed_root
- min_time = df.iloc[:, 0].min()
- df.iloc[:, 0] -= min_time
- blade_axis_tuple_new.iloc[i, 1] -= min_time
- df.iloc[:, 1] = sec_column * np.cos(np.deg2rad(angle_root + angle_cone + axial_inclination))
- for df in result_scatter_flange:
- first_column = df.iloc[:, 0]
- sec_column = df.iloc[:, 1]
- df.iloc[:, 0] = first_column * v_speed_flange
- df.iloc[:, 1] = sec_column * np.cos(np.deg2rad(angle_flange_new + angle_cone + axial_inclination))
- for df in result_scatter_root:
- first_column = df.iloc[:, 0]
- sec_column = df.iloc[:, 1]
- df.iloc[:, 0] = first_column * v_speed_root
- df.iloc[:, 1] = sec_column * np.cos(np.deg2rad(angle_root + angle_cone + axial_inclination))
- # 将叶片平均轮廓数据乘以线速度,得到实际叶片长度
- avg_flange = result_avg_flange.iloc[:, 0]
- result_avg_flange.iloc[:, 0] = avg_flange * v_speed_flange
- avg_root = result_avg_root.iloc[:, 0]
- result_avg_root.iloc[:, 0] = avg_root * v_speed_root
- # 计算叶片扭转角度
- pitch_angle_flange = [0, 0, 0]
- twist_1 = round(np.abs(pitch_angle_root[0] - pitch_angle_flange[0]), 2)
- twist_2 = round(np.abs(pitch_angle_root[1] - pitch_angle_flange[1]), 2)
- twist_3 = round(np.abs(pitch_angle_root[2] - pitch_angle_flange[2]), 2)
- twist_avg = round((twist_1 + twist_2 + twist_3) / 3, 2)
- # 降低给数据采样频率,降低接口负担
- sampling_num = int(0.015 * sampling_fq_1)
- # 将原始数据的时间列由计时时钟转换为实际时间
- data_flange.iloc[:, 0] = data_flange.iloc[:, 0] / 5000000
- data_root.iloc[:, 0] = data_root.iloc[:, 0] / 5000000
- lowpass_data.iloc[:, 0] = lowpass_data.iloc[:, 0] / 5000000
- # 将叶片轮廓旋转重合
- rotated_root = [pd.DataFrame() for _ in range(3)]
- for i in range(3):
- angle_rad = np.deg2rad(-pitch_angle_root[i])
- rotation_matrix = np.array([
- [np.cos(angle_rad), -np.sin(angle_rad)],
- [np.sin(angle_rad), np.cos(angle_rad)]
- ])
- # 获取中心点坐标
- center_x = blade_axis_tuple_new.iloc[i, 1]
- center_y = blade_axis_tuple_new.iloc[i, 2]
- # 创建结果DataFrame
- rotated_points = result_line_root[i].copy()
- # 对每个点进行旋转
- for idx, row in result_line_root[i].iterrows():
- # 获取当前点坐标
- x = row.iloc[0] # 获取第一列作为x坐标
- y = row.iloc[1] # 获取第二列作为y坐标
- # 将点平移到原点
- translated_x = x - center_x
- translated_y = y - center_y
- # 应用旋转矩阵
- rotated = rotation_matrix @ np.array([translated_x, translated_y])
- # 将点平移回原位置
- final_x = rotated[0] + center_x
- final_y = rotated[1] + center_y
- # 更新结果DataFrame
- rotated_points.iloc[idx, 0] = final_x
- rotated_points.iloc[idx, 1] = final_y
- rotated_root[i % 3] = pd.concat([rotated_root[i % 3], rotated_points])
- # 将需要保存到CSV的数据添加到return_list中
- return_list.append(str(time_code))
- return_list.append(str(wind_name))
- return_list.append(str(turbine_code))
- return_list.append(sampling_fq_1)
- return_list.append(pitch_angle_root[0])
- return_list.append(pitch_angle_root[1])
- return_list.append(pitch_angle_root[2])
- return_list.append(pitch_angle_root[3])
- return_list.append(mean_values[0])
- return_list.append(mean_values[1])
- return_list.append(mean_values[2])
- return_list.append(twist_1)
- return_list.append(twist_2)
- return_list.append(twist_3)
- return_list.append(twist_avg)
- return_list.append(tower_max)
- return_list.append(tower_freq)
- # 将return_list转换为DataFrame并追加到CSV文件
- df_new_row = pd.DataFrame([return_list],
- columns=['时间', '场站', '风机编号', '采样频率',
- '叶片1角度偏差', '叶片2角度偏差', '叶片3角度偏差', '相对角度偏差',
- '叶片1净空值', '叶片2净空值', '叶片3净空值',
- '叶片1扭转', '叶片2扭转', '叶片3扭转', '平均扭转',
- '振动幅值', '振动主频'])
- json_output = {
- 'original_plot': {
- 'blade_tip': {
- 'xdata': data_flange.iloc[:, 0].tolist()[::sampling_num],
- 'ydata': data_flange.iloc[:, 1].tolist()[::sampling_num]
- },
- 'blade_root': {
- 'xdata': data_root.iloc[:, 0].tolist()[::sampling_num],
- 'ydata': data_root.iloc[:, 1].tolist()[::sampling_num]
- }
- },
- 'fft_plot': {
- 'lowpass': {
- 'xdata': lowpass_data['time'].tolist()[::sampling_num],
- 'ydata': lowpass_data['distance_filtered'].tolist()[::sampling_num],
- 'xmax': max(lowpass_data['time'].tolist()),
- 'xmin': min(lowpass_data['time'].tolist()),
- 'ymax': max(lowpass_data['distance_filtered'].tolist()) + 0.02,
- 'ymin': min(lowpass_data['distance_filtered'].tolist()) - 0.02
- },
- 'fft': {
- 'xdata': fft_x,
- 'ydata': fft_y,
- 'xmax': max(fft_x),
- 'xmin': min(fft_x),
- 'ymax': max(fft_y) + 0.02,
- 'ymin': 0
- }
- },
- 'blade_tip': {
- 'first_blade': {
- 'xdata': result_line_flange[0].iloc[:, 0].tolist(),
- 'ydata': result_line_flange[0].iloc[:, 1].tolist()
- },
- 'second_blade': {
- 'xdata': result_line_flange[1].iloc[:, 0].tolist(),
- 'ydata': result_line_flange[1].iloc[:, 1].tolist()
- },
- 'third_blade': {
- 'xdata': result_line_flange[2].iloc[:, 0].tolist(),
- 'ydata': result_line_flange[2].iloc[:, 1].tolist()
- },
- 'avg_blade': {
- 'xdata': result_avg_flange.iloc[:, 0].tolist(),
- 'ydata': result_avg_flange.iloc[:, 1].tolist()
- },
- 'blade_center': {
- 'xdata': blade_axis_tuple.iloc[:, 1].tolist(),
- 'ydata': blade_axis_tuple.iloc[:, 2].tolist()
- }
- },
- 'blade_root': {
- 'first_blade': {
- 'xdata': result_line_root[0].iloc[:, 0].tolist(),
- 'ydata': result_line_root[0].iloc[:, 1].tolist()
- },
- 'second_blade': {
- 'xdata': result_line_root[1].iloc[:, 0].tolist(),
- 'ydata': result_line_root[1].iloc[:, 1].tolist()
- },
- 'third_blade': {
- 'xdata': result_line_root[2].iloc[:, 0].tolist(),
- 'ydata': result_line_root[2].iloc[:, 1].tolist()
- },
- 'avg_blade': {
- 'xdata': result_avg_root.iloc[:, 0].tolist(),
- 'ydata': result_avg_root.iloc[:, 1].tolist()
- },
- 'first_rotate_blade': {
- 'xdata': rotated_root[0].iloc[:, 0].tolist(),
- 'ydata': rotated_root[0].iloc[:, 1].tolist()
- },
- 'second_rotate_blade': {
- 'xdata': rotated_root[1].iloc[:, 0].tolist(),
- 'ydata': rotated_root[1].iloc[:, 1].tolist()
- },
- 'third_rotate_blade': {
- 'xdata': rotated_root[2].iloc[:, 0].tolist(),
- 'ydata': rotated_root[2].iloc[:, 1].tolist()
- },
- 'blade_center': {
- 'xdata': blade_axis_tuple_new.iloc[:, 1].tolist(),
- 'ydata': blade_axis_tuple_new.iloc[:, 2].tolist()
- }
- },
- 'dist_distribution': {
- 'first_blade': {
- 'xdata': dist_distribute[0].iloc[:, 0].tolist(),
- 'ydata': dist_distribute[0].iloc[:, 1].tolist()
- },
- 'second_blade': {
- 'xdata': dist_distribute[1].iloc[:, 0].tolist(),
- 'ydata': dist_distribute[1].iloc[:, 1].tolist()
- },
- 'third_blade': {
- 'xdata': dist_distribute[2].iloc[:, 0].tolist(),
- 'ydata': dist_distribute[2].iloc[:, 1].tolist()
- }
- },
- 'analyse_table': {
- 'pitch_angle_diff': {
- 'blade_1': pitch_angle_root[0],
- 'blade_2': pitch_angle_root[1],
- 'blade_3': pitch_angle_root[2],
- 'blade_relate': pitch_angle_root[3]
- },
- 'aero_dist': {
- 'first_blade': {
- 'x_min': min_keys[0],
- 'y_min': min_values[0],
- 'x_max': max_keys[0],
- 'y_max': max_values[0],
- 'y_diff': np.abs(max_values[0] - min_values[0]),
- 'y_ava': mean_values[0]
- },
- 'second_blade': {
- 'x_min': min_keys[1],
- 'y_min': min_values[1],
- 'x_max': max_keys[1],
- 'y_max': max_values[1],
- 'y_diff': np.abs(max_values[1] - min_values[1]),
- 'y_ava': mean_values[1]
- },
- 'third_blade': {
- 'x_min': min_keys[2],
- 'y_min': min_values[2],
- 'x_max': max_keys[2],
- 'y_max': max_values[2],
- 'y_diff': np.abs(max_values[2] - min_values[2]),
- 'y_ava': mean_values[2]
- }
- },
- 'blade_twist': {
- 'blade_1': twist_1,
- 'blade_2': twist_2,
- 'blade_3': twist_3,
- 'blade_avg': twist_avg
- },
- 'tower_vibration': {
- 'max_vibration': tower_max,
- 'main_vibration_freq': tower_freq
- }
- }
- }
- # 获取当前程序的绝对路径
- python_interpreter_path = sys.executable
- project_directory = os.path.dirname(python_interpreter_path)
- data_folder = os.path.join(project_directory, 'data')
- # 检查data文件夹是否存在,如果不存在则创建
- if not os.path.exists(data_folder):
- os.makedirs(data_folder)
- # CSV文件路径
- csv_file_path = os.path.join(data_folder, 'history_data.csv')
- # 检查CSV文件是否存在,如果不存在则创建一个空的CSV文件
- if not os.path.exists(csv_file_path):
- pd.DataFrame(columns=['时间', '场站', '风机编号', '采样频率',
- '叶片1角度偏差', '叶片2角度偏差', '叶片3角度偏差', '相对角度偏差',
- '叶片1净空值', '叶片2净空值', '叶片3净空值',
- '叶片1扭转', '叶片2扭转', '叶片3扭转', '平均扭转',
- '振动幅值', '振动主频']).to_csv(csv_file_path, index=False)
- df_new_row.to_csv(csv_file_path, mode='a', header=False, index=False)
- time_code_cleaned = time_code.replace("-", "").replace(":", "").replace(" ", "")
- json_filename = f"{wind_name}_{turbine_code}_{time_code_cleaned}.json"
- json_file_path = os.path.join(data_folder, json_filename)
- with open(json_file_path, 'w') as json_file:
- json.dump(json_output, json_file, indent=4)
- print('-' * 50)
- print('csv文件路径' + str(csv_file_path))
- # print(result_line_flange[0].iloc[:, 0])
- print('振动主频' + str(tower_freq))
- print('振动幅值' + str(tower_max))
- print('净空最小值', min_values)
- print('最小值对应的键', min_keys)
- print('净空最大值', max_values)
- print('最大值对应的键', max_keys)
- print('叶尖速度' + str(v_speed_flange), '叶根速度' + str(v_speed_root))
- print('新俯仰角' + str(angle_flange_new))
- print('轮毂中心距离' + str(dist_cen))
- print('叶根原始数据采样时间长度' + str(data_root.iloc[-1, 0]))
- print('-' * 50)
- plot_data(result_line_flange, blade_axis_tuple, 'line', 'data1')
- # plot_data(result_diff_flange, 'line', 'data_diff_1')
- # plot_data(result_scatter_flange, 'scatter', 'data1')
- plot_data(result_line_root, blade_axis_tuple_new, 'line', 'data2')
- plot_data(rotated_root, blade_axis_tuple_new, 'line', 'data3')
- # plot_data(result_diff_root, 'line', 'data_diff_2')
- # plot_data(result_scatter_root, blade_axis_tuple_new, 'scatter', 'data2')
- # plot_data(dist_distribute, 'scatter', 'dist_distribute')
- return json_output
- def process_data(file_path, skew_angle):
- """
- 打开、解决时间重置、按时间清洗异常值、分列数据
- """
- skew_angle = 90 - skew_angle
- # 读取第2、4、9列的数据
- data = pd.read_csv(file_path, usecols=[1, 3, 4, 8, 9], header=None, engine='c')
- data = data.head(int(len(data) * 0.95))
- # n = len(data)
- # start = int(n * 0.35) # 开始位置在35%处,这样可以取中间30%
- # end = int(n * 0.65) # 结束位置在65%处,这样总共是30%的数据
- # data = data.iloc[start:end]
- print('原始数据长度' + str(len(data)))
- '''
- # 绘制原始数据图
- # 只取前1%的数据
- # data = data.head(int(len(data)* 0.01))
- data.columns = ['time', 'distance1', 'distance2']
- plt.figure(figsize=(300, 150))
- sns.scatterplot(data=data, x='time', y='distance1', s=50, color='green')
- sns.scatterplot(data=data, x='time', y='distance2', s=50, color='red')
- abxy = plt.gca() # 获取当前坐标轴对象
- plt.grid(linewidth=2) # 设置网格线宽度为2
- abxy.xaxis.set_major_locator(MaxNLocator(nbins=100)) # 设置x轴主刻度的最大数量为10
- plt.xlabel('时间', fontsize=16, fontweight='bold') # 添加x轴标签
- plt.ylabel('距离(m)', fontsize=16, fontweight='bold') # 添加y轴标签
- abxy.tick_params(axis='x', labelsize=14, labelcolor='black', width=2) # 设置x轴刻度标签
- abxy.tick_params(axis='y', labelsize=14, labelcolor='black', width=2) # 设置y轴刻度标签
- plt.savefig(f"{"original"}.png", dpi=100, pil_kwargs={"icc_profile": False})
- plt.close()
- '''
- # 找到第一列中最大值和最小值的位置
- max_value = data.iloc[:, 0].max()
- max_index = data.iloc[:, 0].idxmax()
- min_index = data.iloc[:, 0].idxmin()
- # 检查最小值的位置是否是最大值位置的下一个
- if min_index == max_index + 1:
- # 将最小值及其之后的所有值都加上最大值
- data.iloc[min_index:, 0] += max_value
- # 按时间列筛选清洗异常值
- last_time = data.iloc[-1, 0]
- first_time = data.iloc[0, 0]
- filtered_data = data[(data.iloc[:, 0] > last_time) & (data.iloc[:, 0] < first_time)]
- print(f'时间列异常数据: {filtered_data}')
- print(f'起止时间: {first_time}, {last_time}')
- data = data[data.iloc[:, 0] >= first_time]
- data = data[data.iloc[:, 0] <= last_time]
- data.reset_index(drop=True, inplace=True)
- # 计算最小值
- min_time = data.iloc[:, 0].min()
- data.iloc[:, 0] -= min_time
- # 分为两组数据
- data_1 = data.iloc[:, [0, 1, 2]]
- data_2 = data.iloc[:, [0, 3, 4]]
- # 分别命名列
- data_1.columns = ['time', 'distance', 'grey']
- data_2.columns = ['time', 'distance', 'grey']
- data_1['time'] = data_1['time'] + data_1['distance'] * np.cos(np.deg2rad(skew_angle))
- data_1['distance'] = data_1['distance'] * np.sin(np.deg2rad(skew_angle))
- data_2['time'] = data_2['time'] + data_2['distance'] * np.cos(np.deg2rad(skew_angle))
- data_2['distance'] = data_2['distance'] * np.sin(np.deg2rad(skew_angle))
- data_1['time'] = data_1['time'].round().astype(int)
- data_2['time'] = data_2['time'].round().astype(int)
- return data_1, data_2
- def locate_filter(data_group: pd.DataFrame):
- """
- 求定位数据的叶片距离均值,并将数据标准化,以便放入cycle_calculate,用作求2组定位点的旋转角度
- :param data_group: process_data计算完成后的数据。
- :return: distance_mean:测量距离值
- :return: filtered_data:w后的数据
- """
- # 创建新的DataFrame以避免修改原始数据
- filtered_data = data_group.copy()
- # 去掉distance为0的行
- data_group = data_group[data_group['distance'] != 0]
- # 计算grey列20%-80%分位数的均值
- grey_mean = data_group['grey'].quantile(0.2)
- grey_upper = data_group['grey'].quantile(0.8)
- grey_threshold = data_group[(data_group['grey'] >= grey_mean) & (data_group['grey'] <= grey_upper)]['grey'].mean()
- # 去掉grey小于均值*0.8的行
- data_group = data_group[data_group['grey'] >= grey_threshold * 0.8]
- # 计算剩余行distance列的均值
- distance_mean = data_group['distance'].mean()
- # 将distance为0的值替换为distance_mean+10
- filtered_data.loc[filtered_data['distance'] == 0, 'distance'] = distance_mean + 10
- return distance_mean, filtered_data
- def tower_filter(data_group: pd.DataFrame, noise_threshold: float):
- """
- 对轮毂中心数据进行降噪,和前项填充
- :param data_group: process_data计算完成后轮毂中心的数据。
- :param noise_threshold: 去掉占比小于noise_threshold的数据。
- :return: filtered_data:降噪后的数据
- """
- print('正在进行数据清洗......')
- time.sleep(1)
- # 计算distance的分布
- distance_counts = data_group['distance'].value_counts(normalize=True)
- noise_distance_threshold = distance_counts[distance_counts < noise_threshold].index
- noise_indices = data_group[data_group['distance'].isin(noise_distance_threshold)].index
- data_group.loc[noise_indices, 'distance'] = np.nan
- # 选择频率最大的5个值
- top_5_distances = distance_counts.head(5).index
- mean_values = data_group[data_group['distance'].isin(top_5_distances)]['distance'].mean()
- data_group.loc[(data_group['distance'] < mean_values*0.9) | (
- data_group['distance'] > mean_values*1.1), 'distance'] = np.nan
- nan_count = data_group['distance'].isna().sum()
- all_count = data_group.shape[0]
- print(f"中值是:{mean_values},替换为NaN的异常distance值的数量是: {nan_count}, 总数量是: {all_count},"
- f"占比: {nan_count / all_count * 100:.2f}%")
- # 前向填充
- data_group['distance'] = data_group['distance'].fillna(method='ffill')
- filtered_data = data_group
- return filtered_data
- def cycle_calculate(data_group: pd.DataFrame, noise_threshold: float, min_distance: float):
- """
- 对数据进行降噪,和前项填充;计算数据的周期节点,叶片前缘突变点、后缘突变点
- :param data_group: process_data计算完成后的数据。
- :param noise_threshold: 去掉占比小于noise_threshold的数据。
- :param min_distance: 区分叶片和塔筒的距离差值。
- :return: start_points:周期开始点, end_points:周期结束点, filtered_data:降噪后的数据
- """
- print('正在计算周期节点......')
- time.sleep(1)
- # 计算distance的分布
- distance_counts = data_group['distance'].value_counts(normalize=True)
- noise_distance_threshold = distance_counts[distance_counts < noise_threshold].index
- noise_indices = data_group[data_group['distance'].isin(noise_distance_threshold)].index
- data_group.loc[noise_indices, 'distance'] = np.nan
- print(distance_counts.get(0, 0))
- if distance_counts.get(0, 0) >= 0.1:
- top_5_distances = distance_counts[distance_counts.index != 0].head(5).index
- mean_values = data_group[data_group['distance'].isin(top_5_distances)]['distance'].mean()
- data_group.loc[((data_group['distance'] > 0) & (data_group['distance'] < mean_values - 30)) |
- (data_group['distance'] > mean_values * 1.1), 'distance'] = np.nan
- else:
- # 选择频率最大的5个值
- top_5_distances = distance_counts[distance_counts.index != 0].head(5).index
- mean_values = data_group[data_group['distance'].isin(top_5_distances)]['distance'].mean()
- data_group.loc[(data_group['distance'] < mean_values-30) | (
- data_group['distance'] > mean_values*1.1), 'distance'] = np.nan
- print(f"中值是:{mean_values}")
- nan_count = data_group['distance'].isna().sum()
- all_count = data_group.shape[0]
- print(f"替换为NaN的distance异常值的数量是: {nan_count}, 总数量是: {all_count},"
- f"占比: {nan_count / all_count * 100:.2f}%")
- # 前向填充
- data_group['distance'] = data_group['distance'].fillna(method='ffill')
- filtered_data = data_group
- # 计算相邻两行distance的差值
- filtered_data['distance_diff'] = filtered_data['distance'].diff()
- large_diff_indices = filtered_data[filtered_data['distance_diff'] > min_distance].index
- small_diff_indices = filtered_data[filtered_data['distance_diff'] < -min_distance].index
- filtered_data = filtered_data.drop(columns=['distance_diff'])
- start_points = pd.DataFrame()
- end_points = pd.DataFrame()
- # 遍历所有差值大于的行
- for idx in large_diff_indices:
- # 获取当前行的 distance 值
- current_distance = filtered_data.loc[idx, 'distance']
- next_rows_large = filtered_data.loc[idx - 500: idx - 1]
- # 检查是否任意 distance 的值小于 current_distance - 2
- if next_rows_large['distance'].le(current_distance - min_distance).all():
- # 如果都小于,则将当前行和下一行添加到 special_points 中
- end_points = pd.concat([end_points, filtered_data.loc[[idx - 1]]])
- for idx in small_diff_indices:
- # 获取当前行的 distance 值
- current_distance = filtered_data.loc[idx - 1, 'distance']
- next_rows_small = filtered_data.iloc[idx: idx + 500]
- # 检查是否任意 distance 的值小于 current_distance - 2
- if next_rows_small['distance'].le(current_distance - min_distance).all():
- # 如果都小于,则将当前行和下一行添加到 special_points 中
- start_points = pd.concat([start_points, filtered_data.loc[[idx]]])
- if 0 in distance_counts.nlargest(3).index:
- end_points, start_points = start_points, end_points # 互换
- if end_points.iloc[0, 0] < start_points.iloc[0, 0]:
- end_points = end_points.drop(end_points.index[0])
- if end_points.iloc[-1, 0] < start_points.iloc[-1, 0]:
- start_points = start_points.drop(start_points.index[-1])
- else:
- pass
- return start_points, end_points, filtered_data
- def data_normalize(data_group: pd.DataFrame, start_points: pd.DataFrame, end_points: pd.DataFrame, group_len: int) \
- -> Tuple[List[pd.DataFrame], List[pd.DataFrame], List[pd.DataFrame], int, list]:
- """
- 提取每个叶片的数据并归一化,输出散点图和拟合图
- :param data_group: cycle_calculate计算完成后的数据。
- :param start_points: 所有每个周期开始点,叶片前缘突变点。
- :param end_points: 叶片后缘突变点。
- :param group_len: 每个分组的长度。
- :return: turbines_processed: 每个叶片的拟合数据,
- turbines_scattered: 每个叶片的散点数据,
- border_rows: 每个叶片的2个边缘数据,
- normalize_cycle: 周期长度
- """
- print('正在进行各周期归一化......')
- time.sleep(1)
- combined_df_sorted = pd.concat([start_points, end_points]).sort_values(by='time')
- # 检查排序后的数据从start开始,end结束
- if combined_df_sorted.iloc[0].equals(end_points.iloc[0]):
- combined_df_sorted = combined_df_sorted.iloc[1:]
- if combined_df_sorted.iloc[-1].equals(start_points.iloc[-1]):
- combined_df_sorted = combined_df_sorted.iloc[:-1]
- combined_df_sorted.reset_index(drop=True, inplace=True)
- # 将 start_points 中的时间点转换为列表
- start_times = combined_df_sorted['time'].tolist()
- print('本次测量风机完整旋转圈数:'+ str(int(len(start_times) / 6)))
- time.sleep(1)
- normalize_cycle = int(start_times[1] - start_times[0])
- full_cycle = int((start_times[2] - start_times[0]) * 3)
- turbines = [pd.DataFrame() for _ in range(3)]
- # 遍历所有起始时间点
- for i in range(0, len(start_times), 2):
- # 获取当前起始和结束时间点
- start_time = start_times[i]
- end_time = start_times[i + 1]
- # 根据当前起始时间点和结束时间点对数据进行分段
- segment = data_group[(data_group['time'] > start_time) & (data_group['time'] <= end_time)]
- if not segment.empty:
- # 周期归一化
- ratio = (end_time - start_time) / normalize_cycle
- segment.loc[:, 'time'] = (segment['time'] - start_time) / ratio
- # segment.loc[:, 'distance'] = ff.butter_lowpass_filter(segment['distance'], cutoff_low, fs)
- # 将结果添加到相应的 turbine 数据框中
- turbines[int(i / 2) % 3] = pd.concat([turbines[int(i / 2) % 3], segment])
- # 数据分组清洗、求平均
- turbines_processed = []
- turbines_scattered = []
- min_list = []
- plot_points = []
- diff_line = []
- sd_time = [-1, -1]
- time_list = list(range(0, normalize_cycle, group_len))
- # time_list = [(i + 1) * normalize_cycle / fs * 100 for i in range(fs * 100)] # 生成时间序列
- for turbine in turbines:
- # 按时间排序
- turbine_sorted = turbine.sort_values(by='time').reset_index(drop=True)
- grey_start_index = int(len(turbine_sorted) * 0.1)
- grey_end_index = int(len(turbine_sorted) * 0.9)
- subset_grey = turbine_sorted[grey_start_index:grey_end_index]
- turbine_sorted = ff.median_filter_correction(turbine_sorted, 2, 10)
- mean_grey = subset_grey['grey'].mean() * 0.8 # 0.8
- # 计算分界点
- n = len(turbine_sorted)
- n_10 = int(0.1 * n)
- # 创建筛选掩码
- is_extreme = (turbine_sorted.index < n_10) | (turbine_sorted.index >= len(turbine_sorted) - n_10)
- meets_condition = turbine_sorted['grey'] > mean_grey
- # 筛选:如果是前10%或后10%,需要满足条件;如果是中间80%,直接保留
- turbine_sorted = turbine_sorted[~is_extreme | (is_extreme & meets_condition)]
- # 找到time列的第一个值
- first_time = turbine_sorted['time'].iloc[0]
- # 分组,时间列每1000为一组(每40个时间点一组)
- bins = list(range(int(first_time), int(turbine_sorted['time'].max()), group_len))
- grouped = turbine_sorted.groupby(pd.cut(turbine_sorted['time'], bins=bins, right=False))
- # 初始化一个空的 DataFrame 用于存储处理后的数据
- processed_df = pd.DataFrame()
- scattered_df = pd.DataFrame()
- mean_points = []
- diff_points = []
- # 对每个组进行处理
- for _, group in grouped:
- # 去除 distance 最大和最小的前5%
- quantile_5 = group['distance'].quantile(0.05)
- quantile_95 = group['distance'].quantile(0.95)
- filtered_group = group[(group['distance'] > quantile_5) & (group['distance'] < quantile_95)]
- # 计算均值
- mean_point = filtered_group['distance'].mean()
- mean_points.append(mean_point)
- # 遍历 mean_points 列表,计算每个元素与其下一个元素的差值
- for i in range(len(mean_points) - 1):
- diff = abs(mean_points[i + 1] - mean_points[i])
- diff_points.append(diff)
- start_index = int(len(diff_points) * 0.05)
- end_index = int(len(diff_points) * 0.95)
- subset1 = diff_points[start_index:end_index]
- sdr_diff = np.max(subset1) * 1.1 # 1.1
- min_list.append(min(mean_points))
- # 找到第一个和最后一个小于 sdr_diff 的序号
- first_index = np.where(diff_points < sdr_diff)[0][0]
- last_index = np.where(diff_points < sdr_diff)[0][-1]
- plot_points.append(diff_points)
- diff_line.append(sdr_diff)
- for index, (bin, group) in enumerate(grouped):
- # 去除 distance 最大和最小的前5%
- quantile_5 = group['distance'].quantile(0.05)
- quantile_95 = group['distance'].quantile(0.95)
- filtered_group = group[(group['distance'] > quantile_5) & (group['distance'] < quantile_95)]
- if first_index <= index < last_index: # 如果斜率小于,则认为该组数据不是突变点
- # 计算中点
- mid_point = filtered_group.mean()
- # 将中点转换为 DataFrame 并添加到处理后的 DataFrame 中
- mid_point_df = pd.DataFrame([mid_point])
- mid_point_df.iloc[0, 0] = time_list[index]
- processed_df = pd.concat([processed_df, mid_point_df], ignore_index=True)
- scattered_df = pd.concat([scattered_df, filtered_group], ignore_index=True)
- else: pass
- # 找到time列的最小值和最大值
- min_time = processed_df['time'].min()
- max_time = processed_df['time'].max()
- if sd_time == [-1, -1]:
- sd_time = [min_time, max_time]
- elif sd_time[0] < min_time:
- sd_time[0] = min_time
- elif sd_time[1] > max_time:
- sd_time[1] = max_time
- # 将处理后的 DataFrame 添加到列表中
- turbines_processed.append(processed_df)
- turbines_scattered.append(scattered_df)
- """# 创建一个总图中有3个分图的形式
- fig, axs = plt.subplots(1, 3, figsize=(15, 9))
- plt.subplots_adjust(wspace=0.3) # 调整子图之间的水平间距
- # 绘制第一张图
- axs[0].plot(plot_points[0], label='Diff Points', color='blue', marker='x', markersize=5)
- axs[0].axhline(y=diff_line[0], color='red', linestyle='--')
- axs[0].legend()
- axs[0].set_title('Diff Points (Index 1)')
- axs[0].set_xlabel('Index')
- axs[0].set_ylabel('Value')
- # 绘制第二张图
- axs[1].plot(plot_points[1], label='Diff Points', color='blue', marker='x', markersize=5)
- axs[1].axhline(y=diff_line[1], color='red', linestyle='--')
- axs[1].legend()
- axs[1].set_title('Diff Points (Index 2)')
- axs[1].set_xlabel('Index')
- axs[1].set_ylabel('Value')
- # 绘制第三张图
- axs[2].plot(plot_points[2], label='Diff Points', color='blue', marker='x', markersize=5)
- axs[2].axhline(y=diff_line[2], color='red', linestyle='--')
- axs[2].legend()
- axs[2].set_title('Diff Points (Index 3)')
- axs[2].set_xlabel('Index')
- axs[2].set_ylabel('Value')
- # 显示图形
- plt.tight_layout()
- plt.show()"""
- # 把三组叶片数据按sd_time进行筛选,并把每个的边界数据保存
- border_rows = []
- for i, turbine in enumerate(turbines_processed):
- # 找到离 sd_time[0] 最近的行的索引
- closest_index_0 = (turbine['time'] - sd_time[0]).abs().idxmin()
- turbine.at[closest_index_0, 'time'] = sd_time[0]
- sd_time_row_0 = turbine.loc[closest_index_0]
- # 找到离 sd_time[1] 最近的行的索引
- closest_index_1 = (turbine['time'] - sd_time[1]).abs().idxmin()
- turbine.at[closest_index_1, 'time'] = sd_time[1]
- sd_time_row_1 = turbine.loc[closest_index_1]
- # 切片 turbine,从 closest_index_0 到 closest_index_1
- turbines_processed[i] = turbine.iloc[closest_index_0:closest_index_1 + 1].reset_index(drop=True)
- sd_time_rows_turbine = pd.concat([pd.DataFrame([sd_time_row_0]), pd.DataFrame([sd_time_row_1])]
- , ignore_index=True)
- border_rows.append(sd_time_rows_turbine)
- return turbines_processed, turbines_scattered, border_rows, full_cycle, min_list
- def blade_shape(turbines_processed: List[pd.DataFrame]):
- """
- 计算叶片平均形状、叶片形状偏差。
- :param turbines_processed:叶片拟合曲线数据,来自data_normalize
- :return: 叶片平均形状、叶片形状偏差
- """
- print('正在进行叶片外形偏差计算......')
- row_counts = [df.shape[0] for df in turbines_processed]
- num_rows = min(row_counts)
- # 创建一个新的data.frame用于保存结果
- turbine_avg = pd.DataFrame(index=range(num_rows), columns=['time', 'distance'])
- turbine_diff = [pd.DataFrame(index=range(num_rows), columns=['time', 'distance']) for _ in turbines_processed]
- # 遍历每一行
- for i in range(num_rows):
- distances = [df.loc[i, 'distance'] for df in turbines_processed] # 获取每个data.frame的distance列的值
- avg_distance = sum(distances) / len(distances) # 计算distance列的平均值
- time_value = turbines_processed[0].loc[i, 'time'] # 获取time列的值
- turbine_avg.loc[i, 'time'] = time_value
- turbine_avg.loc[i, 'distance'] = avg_distance
- for j in range(len(distances)):
- distances[j] = distances[j] - avg_distance
- turbine_diff[j].loc[i, 'time'] = time_value
- turbine_diff[j].loc[i, 'distance'] = distances[j]
- return turbine_avg, turbine_diff
- def coordinate_normalize(tip_border_rows: List[pd.DataFrame], tip_angle):
- """
- 将叶尖、法兰测量数据和叶根、轮毂中心的测量原点归一化。
- :param tip_border_rows: 3个叶尖边缘数据
- :param tip_angle: 叶尖测量俯仰角
- :return: 归一化后叶尖数据,新俯仰角
- """
- tip_angle1 = np.deg2rad(tip_angle)
- tip_angle_list = []
- for turbine in tip_border_rows:
- tip_angle_cal0 = ((np.sin(tip_angle1) * turbine['distance'] - 0.07608) /
- (np.cos(tip_angle1) * turbine['distance']))
- tip_angle_cal = np.arctan(tip_angle_cal0)
- turbine['distance'] = (turbine['distance']**2 + 0.0057881664 -
- 0.15216*turbine['distance']*np.sin(tip_angle1)) ** 0.5
- tip_angle_list.append(tip_angle_cal)
- tip_angle_new = float(np.mean(tip_angle_list))
- tip_angle_new1 = np.rad2deg(tip_angle_new)
- print('叶尖俯仰角: ' + str(tip_angle) + '坐标转换后的新叶尖俯仰角: ' + str(tip_angle_new1))
-
- return tip_border_rows, tip_angle_new1
- def flange_coordinate_normalize(flange_cen_row: pd.DataFrame, flange_angle):
- """
- 将法兰中心计算数据测量原点归一化。
- :param flange_cen_row: 法兰数据
- :param flange_angle: 法兰测量俯仰角
- :return: 归一化后法兰数据,新俯仰角
- """
- flange_angle1 = np.deg2rad(flange_angle)
- center_y_mean = flange_cen_row['中心y'].mean()
- # 计算新的俯仰角
- flange_angle_cal0 = ((np.sin(flange_angle1) * center_y_mean - 0.07608) /
- (np.cos(flange_angle1) * center_y_mean))
- flange_angle_cal = np.arctan(flange_angle_cal0)
- # 更新中心y列
- flange_cen_row['中心y'] = (flange_cen_row['中心y'] ** 2 + 0.0057881664 -
- 0.15216 * flange_cen_row['中心y'] * np.sin(flange_angle1)) ** 0.5
- # 计算新的俯仰角(由于现在只有一个值,直接使用计算出的值)
- flange_angle_new = float(flange_angle_cal)
- flange_angle_new1 = np.rad2deg(flange_angle_new)
- print('坐标转换后的新法兰俯仰角: ' + str(flange_angle_new1))
- return flange_cen_row, flange_angle_new1
- def blade_axis_cal(data_group: pd.DataFrame, start_points: pd.DataFrame, end_points: pd.DataFrame, horizon_angle: float,
- group_len: int, radius_blade: float):
- """
- 每个叶片周期计算旋转中心及半径
- :param data_group: cycle_calculate计算完成后的叶根法兰数据。
- :param start_points: 所有每个周期开始点,叶片前缘突变点。
- :param end_points: 叶片后缘突变点。
- :param horizon_angle: 叶根法兰水平角度。
- :param group_len: 每数据长度。
- :param radius_blade: 半径。
- :return: 叶根法兰圆轮廓的圆心、测量点的旋转半径
- """
- print('正在进行叶片轴线计算......')
- def fit_circle(df, v_fixed, top_k=5, prefilter=True):
- """
- 修改版:速度v作为已知量,只优化圆心和半径
- Savitzky–Golay 平滑 + 差分进化 + least_squares 精修
- """
- # ========== Savitzky–Golay 平滑 ==========
- def smooth_savgol(y, window_length=101, polyorder=3):
- wl = min(window_length, len(y) if len(y) % 2 == 1 else len(y) - 1)
- if wl < 3:
- return y
- if wl % 2 == 0:
- wl -= 1
- return savgol_filter(y, wl, polyorder)
- t = np.asarray(df['time'])
- d_raw = np.asarray(df['distance'])
- # ---------- 平滑 ----------
- d_smooth = smooth_savgol(d_raw, window_length=101, polyorder=3) if prefilter else d_raw
- # 计算x坐标(已知速度v)
- x = v_fixed * t
- # 新的参数边界:只优化圆心坐标(xc, yc)和半径R
- bounds = [(min(x) - 5, max(x) + 5), # xc范围:x值范围附近
- (min(d_smooth), max(d_smooth) + 10), # yc范围:y值范围附近
- (0.5, 10)] # R范围
- def residuals_sq(params):
- # 参数现在只有三个:xc, yc, R
- xc, yc, R = params
- if R <= 0:
- return 1e6 * np.ones_like(t)
- return (x - xc) ** 2 + (d_smooth - yc) ** 2 - R ** 2
- def objective_mean_sq(params):
- res = residuals_sq(params)
- return np.mean(res ** 2)
- # ---------- 差分进化 ----------
- result = differential_evolution(
- objective_mean_sq,
- bounds,
- strategy='rand2bin',
- mutation=(0.8, 1.2),
- recombination=0.8,
- popsize=30,
- maxiter=1000,
- polish=False,
- seed=42,
- workers=1
- )
- # 多候选点精修
- pop = result.population
- energies = result.population_energies
- idx = np.argsort(energies)[:top_k]
- candidates = pop[idx]
- best_rmse = np.inf
- best_result = None
- for cand in candidates:
- res = least_squares(
- residuals_sq,
- x0=cand,
- bounds=([-np.inf, -np.inf, 1e-6], # 下界
- [np.inf, np.inf, np.inf]), # 上界
- method='trf',
- loss='linear',
- max_nfev=50000,
- xtol=1e-12,
- ftol=1e-12,
- gtol=1e-12
- )
- xc_opt, yc_opt, R_opt = res.x
- Ri_all = np.sqrt((x - xc_opt) ** 2 + (d_smooth - yc_opt) ** 2)
- geo_rmse = np.sqrt(np.mean((Ri_all - R_opt) ** 2))
- if geo_rmse < best_rmse:
- best_rmse = geo_rmse
- best_result = [v_fixed, xc_opt, yc_opt, R_opt, geo_rmse]
- result_df = pd.DataFrame([best_result],
- columns=['旋转半径', '中心x', '中心y', '圆半径', '几何RMSE'])
- return result_df
- def plot_circle_fit(df, fit_result):
- """
- df: 原始数据,包含 'time' 和 'distance'
- fit_result: fit_circle 输出的 DataFrame
- """
- t = np.asarray(df['time'])
- d_raw = np.asarray(df['distance'])
- # 最优参数
- v, xc, yc, R = fit_result[['旋转半径', '中心x', '中心y', '圆半径']].values[0]
- # x 轴乘速度后的原始散点
- x_all = v * t
- plt.figure(figsize=(8, 8))
- # 原始散点
- plt.scatter(x_all, d_raw, s=10, color='blue', alpha=0.5, label='原始散点 (v*t)')
- # 拟合圆
- theta = np.linspace(0, 2 * np.pi, 500)
- x_circle = xc + R * np.cos(theta)
- y_circle = yc + R * np.sin(theta)
- plt.plot(x_circle, y_circle, color='red', lw=2, label='拟合圆')
- # 圆心
- plt.scatter([xc], [yc], color='green', s=50, label='圆心')
- plt.xlabel('x = v * t')
- plt.ylabel('distance')
- plt.axis('equal')
- plt.legend()
- plt.title('速度乘时间后的散点与拟合圆')
- plt.show()
- combined_df_sorted = pd.concat([start_points, end_points]).sort_values(by='time')
- # 检查排序后的数据从start开始,end结束
- if combined_df_sorted.iloc[0].equals(end_points.iloc[0]):
- combined_df_sorted = combined_df_sorted.iloc[1:]
- if combined_df_sorted.iloc[-1].equals(start_points.iloc[-1]):
- combined_df_sorted = combined_df_sorted.iloc[:-1]
- combined_df_sorted.reset_index(drop=True, inplace=True)
- # 将 start_points 中的时间点转换为列表
- start_times = combined_df_sorted['time'].tolist()
- data_group['distance'] = data_group['distance'] * np.cos(np.deg2rad(horizon_angle))
- normalize_cycle = start_times[1] - start_times[0]
- full_cycle = int((start_times[2] - start_times[0]) * 3)
- v_blade = 10000000 * np.pi * radius_blade / full_cycle
- angle_speed = (np.pi / full_cycle) * 5000000
- turbines = [pd.DataFrame() for _ in range(3)]
- for i in range(0, len(start_times), 2):
- start_time = start_times[i]
- end_time = start_times[i + 1]
- segment = data_group[(data_group['time'] > start_time) & (data_group['time'] <= end_time)]
- if segment is None or segment.empty:
- raise ValueError("Segment is empty")
- segment = segment.copy()
- ratio = (end_time - start_time) / normalize_cycle
- segment.loc[:, 'time'] = (segment['time'] - start_time) / ratio
- turbines[int(i / 2) % 3] = pd.concat([turbines[int(i / 2) % 3], segment])
- turbines_processed = []
- turbines_scattered = []
- result_df = pd.DataFrame()
- time_list = list(range(0, normalize_cycle, group_len))
- for turbine in turbines:
- # 按时间排序
- turbine_sorted = turbine.sort_values(by='time').reset_index(drop=True)
- # 找到time列的第一个值
- first_time = turbine_sorted['time'].iloc[0]
- # 分组,时间列每1000为一组(每40个时间点一组)
- bins = list(range(int(first_time), int(turbine_sorted['time'].max()), group_len))
- grouped = turbine_sorted.groupby(pd.cut(turbine_sorted['time'], bins=bins, right=False))
- process_df = pd.DataFrame()
- for index, (bin, group) in enumerate(grouped):
- mid_point = group.mean()
- # 将中点转换为 DataFrame 并添加到处理后的 DataFrame 中
- mid_point_df = pd.DataFrame([mid_point])
- mid_point_df.iloc[0, 0] = time_list[index]
- process_df = pd.concat([process_df, mid_point_df], ignore_index=True)
- process_df['time'] = process_df['time'] / 5000000
- lower_bound = process_df['time'].quantile(0.2)
- upper_bound = process_df['time'].quantile(0.8)
- processed_df = process_df[(process_df['time'] >= lower_bound) & (process_df['time'] <= upper_bound)]
- blade_cen_est = fit_circle(processed_df, v_blade)
- plot_circle_fit(processed_df, blade_cen_est)
- processed_df['time'] = processed_df['time'] * v_blade
- turbines_processed.append(processed_df)
- turbines_scattered.append(turbine)
- result_df = pd.concat([result_df, blade_cen_est], ignore_index=True)
- if blade_cen_est['几何RMSE'].iloc[0] >= 0.1:
- raise ValueError("叶片几何误差过大")
- result_df_mean = result_df.mean(numeric_only=True).to_frame().T
- result_df_mean["中心y"] = result_df_mean["中心y"] / np.cos(np.deg2rad(horizon_angle))
- result_df["中心y"] = result_df["中心y"] / np.cos(np.deg2rad(horizon_angle))
- # turbines_mean["中心x"] = turbines_mean["中心x"] * 5000000
- # turbines_mean["圆半径"] = turbines_mean["圆半径"] / np.cos(np.deg2rad(horizon_angle))
- # turbines_mean['旋转半径'] = turbines_mean['旋转半径'] / angle_speed
- print(result_df)
- return result_df_mean, result_df, turbines_processed
- def radius_cal(border_rows, meas_angle, cen_dist, cen_angle, angle_main, angle_rotate):
- """
- 计算测量点处的旋转半径。
- :param border_rows: 三个叶片的边界
- :param meas_angle: 回波俯仰角
- :param cen_dist: 轮毂中心距离
- :param cen_angle: 轮毂中心俯仰角
- :param angle_main: 主轴倾角
- :param angle_rotate: 锥角
- :return: 旋转半径
- """
- aero_dist = (pd.concat([df['distance'] for df in border_rows]).mean())
- radius = np.abs(aero_dist * np.sin(np.deg2rad(meas_angle - angle_main))
- - cen_dist * np.sin(np.deg2rad(cen_angle - angle_main)))
- return radius
- def blade_angle_aero_dist(border_rows: List[pd.DataFrame], radius: float, full_cycle: int,
- tower_dist: float, v_angle: float):
- """
- 计算叶片相对桨距角和叶片净空距离。
- :param border_rows: 三个叶片的边界
- :param radius: 旋转半径
- :param full_cycle: 全周期
- :param tower_dist: 塔筒距离
- :param v_angle: 俯仰角度
- :return: 净空距离,叶片线速度,叶片中心距离
- """
- print('正在进行叶片净空距离计算......')
- v_speed = 2 * np.pi * radius / full_cycle # 叶片线速度m/(1计时器单位)
- aero_dist_list = []
- cen_blade = []
- for turbine in border_rows:
- mean_col2 = (turbine.iloc[1, 1] + turbine.iloc[0, 1]) / 2
- aero_dist = abs(mean_col2 - tower_dist) * np.cos(np.deg2rad(v_angle))
- aero_dist_list.append(aero_dist)
- cen_blade.append(mean_col2)
- aero_dist_list.append(np.mean(aero_dist_list))
- aero_dist_list = [round(num, 2) for num in aero_dist_list]
- return aero_dist_list, v_speed, cen_blade
- def blade_angle(border_rows: List[pd.DataFrame], cen_data: pd.DataFrame, radius: float, full_cycle: int,
- v_angle: float):
- """
- 计算叶片相对桨距角。
- :param border_rows: 三个叶片的边界
- :param cen_data: 旋转中心数据
- :param radius: 旋转半径
- :param full_cycle: 全周期
- :param v_angle: 俯仰角度
- :return: 绝对桨距角,叶片线速度
- """
- print('正在进行相对桨距角计算......')
- print('叶根半径:' + str(radius))
- v_speed = 2 * np.pi * radius / full_cycle # 叶片线速度m/(1计时器单位)
- values = []
- for df in border_rows:
- if df.shape[0] >= 2 and df.shape[1] >= 2:
- values.append(df.iloc[0, 1])
- values.append(df.iloc[1, 1])
- mean_value = sum(values) / len(values) if values else float('nan')
- for i in [0, 1, 2]:
- if np.abs(cen_data['中心y'].iloc[i] - mean_value) > 0.5:
- print('原本:' + str(cen_data['中心y'].iloc[i]) + '标准:' + str(mean_value))
- cen_data['中心y'].iloc[i] = mean_value
- print('y_change')
- if cen_data['中心x'].iloc[i] > 1.5:
- cen_data['中心x'].iloc[i] = 1.5
- print('x_change')
- if cen_data['中心x'].iloc[i] < 0.75:
- cen_data['中心x'].iloc[i] = 0.75
- print('x_change')
- print(cen_data['中心x'].iloc[i])
- pitch_angle_list = []
- for idx, turbine in enumerate(border_rows, start=1):
- # 使用圆拟合的情况(注释掉的是备选方案)
- diff_time = np.abs(cen_data['中心x'].iloc[idx - 1] - turbine.iloc[1, 0] * v_speed)
- # diff_time = np.abs((turbine.iloc[0, 0] - turbine.iloc[1, 0]) * 0.66 * v_speed)
- # 计算长度差,使用对应的cen_data行
- diff_len = np.abs((cen_data['中心y'].iloc[idx - 1] - turbine.iloc[1, 1]) * np.cos(np.deg2rad(v_angle)))
- # 计算桨距角
- pitch_angle = np.degrees(np.arctan(diff_len / diff_time))
- print(f'第{idx}个叶片绝对桨距角: {pitch_angle}')
- pitch_angle_list.append(pitch_angle)
- pitch_mean = np.mean(pitch_angle_list)
- pitch_angle_list = [angle - pitch_mean for angle in pitch_angle_list]
- pitch_angle_list.append(max(pitch_angle_list) - min(pitch_angle_list))
- pitch_angle_list = [round(num, 2) for num in pitch_angle_list]
- print("\033[34m叶片相对角度偏差:\033[0m", pitch_angle_list)
- return pitch_angle_list, v_speed, cen_data
- def plot_data(data, point, plot_type: str, data_name: str):
- """
- 绘制数据图表并保存为文件。
- :param data: 数据列表,每个元素是一个 DataFrame。
- :param point: 测量点。
- :param plot_type: 图表类型,'line' 或 'scatter'。
- :param data_name: 数据名称,用于生成文件名。
- """
- print('正在画图......')
- time.sleep(1)
- save_path = "C:/Users/laiwe/Desktop/"
- save_name = fr"{data_name}_{plot_type}.png" # 生成文件名
- plt.figure(figsize=(300, 150))
- if plot_type == 'line':
- for df, color in zip(data, ['blue', 'green', 'red']):
- sns.lineplot(data=df, x=df.iloc[:, 0], y=df.iloc[:, 1], color=color, linewidth=8)
- elif plot_type == 'scatter':
- for df, (size, color) in zip(data, [(50, 'blue'), (25, 'green'), (10, 'red')]):
- sns.scatterplot(data=df, x=df.iloc[:, 0], y=df.iloc[:, 1], s=size, color=color)
- else:
- raise ValueError("plot_type must be either 'line' or 'scatter'")
- plt.scatter(point['中心x'], point['中心y'], color='black', s=1000, marker='o', label='测量点')
- axy = plt.gca() # 获取当前坐标轴对象
- plt.grid(which='both', linewidth=2) # 设置网格线宽度为2
- axy.xaxis.set_major_locator(MaxNLocator(nbins=200)) # 设置x轴主刻度的最大数量为10
- axy.yaxis.set_major_locator(MaxNLocator(nbins=100)) # 设置y轴主刻度的最大数量为10
- plt.xlabel('时间', fontsize=100, fontweight='bold') # 添加x轴标签
- plt.ylabel('距离(m)', fontsize=100, fontweight='bold') # 添加y轴标签
- axy.tick_params(axis='x', labelsize=10, labelcolor='black', width=2) # 设置x轴刻度标签
- axy.tick_params(axis='y', labelsize=60, labelcolor='black', width=10) # 设置y轴刻度标签
- plt.savefig(save_path + save_name)
- plt.close()
- abs_path = os.path.abspath(save_name)
- print(f" {save_name} 已完成")
- return abs_path
- def find_param(path: str):
- """
- 根据文件路径获取参数
- """
- path = path.replace('\\', '/')
- last_slash_index = path.rfind('/')
- result = path[last_slash_index + 1:]
- underscore_indices = []
- start = 0
- while True:
- index = result.find('_', start)
- if index == -1:
- break
- underscore_indices.append(index)
- start = index + 1
- wind_name = result[: underscore_indices[0]]
- turbine_code = result[underscore_indices[0] + 1: underscore_indices[1]]
- time_code = result[underscore_indices[1] + 1: underscore_indices[2]]
- sampling_fq = int(result[underscore_indices[2] + 1: underscore_indices[3]])
- tunnel_1 = float(result[underscore_indices[3] + 1: underscore_indices[4]])
- tunnel_2 = float(result[underscore_indices[4] + 1: -4])
- dt = datetime.strptime(time_code, "%Y%m%d%H%M%S")
- standard_time_str = dt.strftime("%Y-%m-%d %H:%M:%S")
- return wind_name, turbine_code, standard_time_str, sampling_fq, tunnel_1, tunnel_2
- def blade_dist_distribute_cal(data_group: pd.DataFrame, start_points: pd.DataFrame, end_points: pd.DataFrame,
- tower_dist: float, v_angle: float, blade_cen_dist: list):
- """
- 计算每个叶片每个周期的转速和净空距离
- :param data_group: cycle_calculate计算完成后的数据。
- :param start_points: 所有每个周期开始点,叶片前缘突变点。
- :param end_points: 叶片后缘突变点。
- :param tower_dist: 塔筒距离。
- :param v_angle: 测量俯仰角度。
- :param blade_cen_dist: 叶片内部距离。
- """
- print('正在进行各周期净空距离分布计算......')
- time.sleep(1)
- combined_df_sorted = pd.concat([start_points, end_points]).sort_values(by='time')
- # 检查排序后的数据从start开始,end结束
- if combined_df_sorted.iloc[0].equals(end_points.iloc[0]):
- combined_df_sorted = combined_df_sorted.iloc[1:]
- if combined_df_sorted.iloc[-1].equals(start_points.iloc[-1]):
- combined_df_sorted = combined_df_sorted.iloc[:-1]
- combined_df_sorted.reset_index(drop=True, inplace=True)
- # 将 start_points 中的时间点转换为列表
- start_times = combined_df_sorted['time'].tolist()
- normalize_cycle = start_times[1] - start_times[0]
- tower_clearance = [pd.DataFrame() for _ in range(3)]
- # 遍历所有起始时间点
- for i in range(0, len(start_times) - 2, 2):
- # 获取当前起始和结束时间点
- start_time = start_times[i]
- end_time = start_times[i + 1]
- # 根据当前起始时间点和结束时间点对数据进行分段
- segment = data_group[(data_group['time'] > start_time) & (data_group['time'] <= end_time)]
- min_distance = segment['distance'].min()
- clearance = np.abs(tower_dist - min_distance - blade_cen_dist[i % 3]) * np.cos(np.deg2rad(v_angle))
- r_speed = round(60 / ((start_times[i + 2] - start_times[i]) * 3 / 5000000), 2)
- new_df = pd.DataFrame({
- 'r_speed': [r_speed],
- 'clearance': [clearance]
- })
- # 将结果添加到相应的 turbine 数据框中
- tower_clearance[i % 3] = pd.concat([tower_clearance[i % 3], new_df])
- tower_clearance = [df.sort_values(by='r_speed') for df in tower_clearance]
- return tower_clearance
- if __name__ == "__main__":
- # 左偏
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20250919怀来/1/hl_F14-L_20250819134200_50_11.25_13.42.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20250919怀来/1/hl_F14-L_20250819133535_50_7.55_12.10.csv"
- # measure_path= "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20250919怀来/1/hl_F14-L_20250819132731_50_11.71_10.34.csv"
- # 右偏
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20250919怀来/1/hl_F14-last_20250819144135_50_9.36_11.02.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20250919怀来/1/hl_F14-last_20250819143634_50_7.44_10.04.csv"
- # measure_path= "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20250919怀来/1/hl_F14-last_20250819143011_50_9.81_8.57.csv"
- # 左微偏
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20250919怀来/2/hl_F14-z_20250819124400_50_11.32_13.07.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20250919怀来/2/hl_F14-z_20250819124108_50_7.51_12.05.csv"
- # measure_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20250919怀来/2/hl_F14-z_20250819123507_50_11.23_10.51.csv"
- # 第一组正对
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-z_20251011100836_50_26.95_30.34.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-z_20251011095956_50_20.23_28.22.csv"
- # measure_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-z_20251011095339_50_27.64_26.44.csv"
- # 第二组正对
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-z_20251011100836_50_26.95_30.34.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-z_20251011102933_50_20.48_28.28.csv"
- # measure_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-z_20251011102339_50_27.72_26.33.csv"
- # 第三组左侧
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-l_20251011105053_50_25.67_27.83.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-l_20251011104654_50_18.84_26.34.csv"
- # measure_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-l_20251011104100_50_25.84_24.66.csv"
- # 第四组右侧
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-rr_20251011114639_50_28.64_31.72.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-rr_20251011114224_50_22.36_30.22.csv"
- # measure_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-r_20251011111012_50_29.57_28.26.csv"
- # 第五组左侧
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-rr_20251011114639_50_28.64_31.72.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-ll_20251011121305_50_25.22_34.48.csv"
- # measure_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-ll_20251011120724_50_33.79_32.64.csv"
- # 第六组正对
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-zz_20251011123416_50_31.48_33.93.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-zz_20251011123102_50_25.23_32.98.csv"
- # measure_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-zz_20251011122457_50_32.24_31.02.csv"
- # 第七组右侧
- locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-r-z_20251011124902_50_31.19_34.81.csv"
- locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-r-z_20251011124620_50_24.85_32.90.csv"
- measure_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-r-z_20251011124036_50_32.24_31.14.csv"
- # dq-8
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-r-z_20251011124902_50_31.19_34.81.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251023大庆/第三天/dh_8_20251109102949_50_17.22_27.45.csv"
- # measure_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251023大庆/第三天/dh_8_20251109102255_50_26.18_24.06.csv"
- # zb-9
- # locate_path0 = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251011沽源/验证/gy_20-r-z_20251011124902_50_31.19_34.81.csv"
- # locate_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251121淄博/数据/zb_9_20251122110132_50_14.19_19.83.csv"
- # measure_path = "C:/Users/laiwe/Desktop/风电/激光测量/测试数据/20251121淄博/数据/zb_9_20251122105534_50_19.37_17.73.csv"
- start_t = time.time() # 记录开始时间
- data_path = [locate_path0, locate_path, measure_path, 3.5, 5, 1.54] # 偏斜测量数据、轮毂数据、叶根数据、锥角、轴向倾角、偏航角
- list_1 = data_analyse(data_path)
- print(f"耗时: {time.time() - start_t:.2f} 秒")
|