Bladeren bron

激光测量v2,增加了分析数据本地保存,实时调取,分析历史管理

wei_lai 6 maanden geleden
bovenliggende
commit
fd93310e63
3 gewijzigde bestanden met toevoegingen van 321 en 89 verwijderingen
  1. 16 0
      api_test.py
  2. 163 43
      data_analyse_origin.py
  3. 142 46
      data_clean.py

+ 16 - 0
api_test.py

@@ -31,6 +31,22 @@ if __name__ == "__main__":
         return_list = dc.data_analyse(data)
         print(json.dumps(return_list, ensure_ascii=False))
 
+    elif api_name == "historydata":
+        if len(sys.argv) < 3:
+            print(json.dumps({"error": "No data"}))
+            sys.exit(1)
+        data = json.loads(base64.b64decode(sys.argv[2]).decode("utf-8"))
+        return_list = dc.history_data(data)
+        print(json.dumps(return_list, ensure_ascii=False))
+
+    elif api_name == "deletedata":
+        if len(sys.argv) < 3:
+            print(json.dumps({"error": "No data"}))
+            sys.exit(1)
+        data = json.loads(base64.b64decode(sys.argv[2]).decode("utf-8"))
+        return_path = dc.delete_data(data)
+        print(json.dumps(return_path, ensure_ascii=False))
+
     else:
         print(json.dumps({"error": "Invalid API"}))
 

+ 163 - 43
data_analyse_origin.py

@@ -1,4 +1,5 @@
 import os
+import json
 import pandas as pd
 import numpy as np
 import seaborn as sns
@@ -17,7 +18,6 @@ plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
 plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题
 
 
-# TODO 3个叶片净空距离的分布情况,每10圈算一次净空
 
 def result_main():
 
@@ -39,18 +39,76 @@ def result_main():
     if not os.path.exists(csv_file_path):
         pd.DataFrame(columns=['时间', '场站', '风机编号', '采样频率',
                               '叶片1角度偏差', '叶片2角度偏差', '叶片3角度偏差', '相对角度偏差',
-                              '叶片1净空值', '叶片2净空值', '叶片3净空值', '平均净空值',
+                              '叶片1净空值', '叶片2净空值', '叶片3净空值',
                               '叶片1扭转', '叶片2扭转', '叶片3扭转', '平均扭转',
                               '振动幅值', '振动主频']).to_csv(csv_file_path, index=False)
 
     return csv_file_path
 
 
+def delete_data(names):
+
+    """
+    删除历史分析数据
+    :param names: 删除条件
+    :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)
+
+    for name in names:
+        # 检查条件
+        condition = ((df['时间'].str.contains(name[0])) &
+                     (df['场站'].str.contains(name[1])) &
+                     (df['风机编号'].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:
+    """
+
+    wind_name, turbine_code, time_code = name[1], name[2], name[0]
+    # 获取当前程序的绝对路径
+    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 = path[0]
     measure_file = path[1]
     noise_reduction = 0.000001  # 如果一个距离值的所有样本量小于总样本量的noise_reduction,则被去掉
@@ -89,46 +147,66 @@ def data_analyse(path: List[str]):
     tower_dist_root = ff.tower_cal(filtered_data_root, start_root, end_root, sampling_fq_1)
     lowpass_data, fft_x, fft_y, tower_freq, tower_max= ff.process_fft(filtered_data_cen, sampling_fq)
 
-    result_line_tip, result_scatter_tip, border_rows_tip, cycle_len_tip \
+    result_line_tip, result_scatter_tip, border_rows_tip, cycle_len_tip, min_tip \
         = data_normalize(filtered_data_tip, start_tip, end_tip)
-    result_line_root, result_scatter_root, border_rows_root, cycle_len_root \
+    result_line_root, result_scatter_root, border_rows_root, cycle_len_root, min_root \
         = data_normalize(filtered_data_root, start_root, end_root)
 
     result_avg_tip, result_diff_tip = blade_shape(result_line_tip)
     result_avg_root, result_diff_root = blade_shape(result_line_root)
 
     border_rows_tip_new, angle_tip_new = coordinate_normalize(border_rows_tip, angle_tip)
-    print('新俯仰角' + str(angle_tip_new))
-    print('轮毂中心距离' + str(dist_cen))
 
     tip_r = radius_cal(border_rows_tip_new, angle_tip_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)
 
     pitch_angle_tip, aero_dist_tip, v_speed_tip, cen_blade_tip = (
         blade_angle_aero_dist(border_rows_tip, tip_r, cycle_len_tip, tower_dist_tip, angle_tip_new))
-    pitch_angle_root, aero_dist_root, v_speed_root = (
+    pitch_angle_root, aero_dist_root, v_speed_root, cen_blade_root = (
         blade_angle_aero_dist(border_rows_root, root_r, cycle_len_root, tower_dist_root, angle_root))
 
-    dist_distribute = blade_dist_distribute_cal(filtered_data_tip, start_tip, end_tip,
-                                                tower_dist_tip, angle_tip_new, cen_blade_tip)
-
-
-    plot_data(result_line_tip, 'line', 'data1')
-    # plot_data(result_diff_tip, 'line', 'data_diff_1')
-    plot_data(result_scatter_tip, 'scatter', 'data1')
-    plot_data(result_line_root, 'line', 'data2')
-    # plot_data(result_diff_root, 'line', 'data_diff_2')
-    plot_data(result_scatter_root, 'scatter', 'data2')
+    # 将列表转换为 numpy 数组
+    cen_blade_tip_array = np.array(cen_blade_tip)
+    min_tip_array = np.array(min_tip)
+    abs_diff = np.abs(cen_blade_tip_array - min_tip_array)   # 计算差值的绝对值
+    blade_dist_tip = abs_diff * np.cos(np.deg2rad(angle_tip_new))
+    blade_dist_tip.tolist()  # 如果需要将结果转换回列表
 
+    dist_distribute = blade_dist_distribute_cal(filtered_data_tip, start_tip, end_tip,
+                                                tower_dist_tip, angle_tip_new, blade_dist_tip)
+    dist_distribute = [df.round(5) for df in dist_distribute]
+
+    # 获取每个 DataFrame 第二列的最小值和最大值,以及它们对应的第一列的值,并分别保存在列表中
+    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(second_col_min)
+        min_keys.append(min_row.iloc[0][df.columns[0]])
+        max_values.append(second_col_max)
+        max_keys.append(max_row.iloc[0][df.columns[0]])
+
+    for i in range(3):
+        mean_values.append(round((max_values[i] + min_values[i]) / 2, 2))
 
     for df in result_line_tip:
         first_column = df.iloc[:, 0]
+        sec_column = df.iloc[:, 1]
         df.iloc[:, 0] = first_column * v_speed_tip
+        df.iloc[:, 1] = sec_column * np.cos(np.deg2rad(angle_tip_new))
 
     for df in result_line_root:
         first_column = df.iloc[:, 0]
+        sec_column = df.iloc[:, 1]
         df.iloc[:, 0] = first_column * v_speed_root
-    print(v_speed_tip, v_speed_root)
+        df.iloc[:, 1] = sec_column * np.cos(np.deg2rad(angle_root))
+
 
     avg_tip = result_avg_tip.iloc[:, 0]
     result_avg_tip.iloc[:, 0] = avg_tip * v_speed_tip
@@ -145,7 +223,6 @@ def data_analyse(path: List[str]):
     data_root.iloc[:, 0] = data_root.iloc[:, 0] / 5000000
     lowpass_data.iloc[:, 0] = lowpass_data.iloc[:, 0] / 5000000
 
-    print('time_length:' + str(data_root.iloc[-1, 0]))
 
     return_list.append(time_code)
     return_list.append(wind_name)
@@ -155,10 +232,9 @@ def data_analyse(path: List[str]):
     return_list.append(pitch_angle_root[1])
     return_list.append(pitch_angle_root[2])
     return_list.append(pitch_angle_root[3])
-    return_list.append(aero_dist_tip[0])
-    return_list.append(aero_dist_tip[1])
-    return_list.append(aero_dist_tip[2])
-    return_list.append(aero_dist_tip[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)
@@ -166,17 +242,11 @@ def data_analyse(path: List[str]):
     return_list.append(tower_max)
     return_list.append(tower_freq)
 
-    print(result_line_tip[0].iloc[:, 0])
-    print(result_line_root[0].iloc[:, 0])
-    print('振动主频' + str(tower_freq))
-    print('振动幅值' + str(tower_max))
-
-
     # 将return_list转换为DataFrame并追加到CSV文件
     df_new_row = pd.DataFrame([return_list],
                               columns=['时间', '场站', '风机编号', '采样频率',
                               '叶片1角度偏差', '叶片2角度偏差', '叶片3角度偏差', '相对角度偏差',
-                              '叶片1净空值', '叶片2净空值', '叶片3净空值', '平均净空值',
+                              '叶片1净空值', '叶片2净空值', '叶片3净空值',
                               '叶片1扭转', '叶片2扭转', '叶片3扭转', '平均扭转',
                               '振动幅值', '振动主频'])
 
@@ -268,10 +338,30 @@ def data_analyse(path: List[str]):
                 'blade_relate': pitch_angle_root[3]
             },
             'aero_dist': {
-                'blade_1': aero_dist_tip[0],
-                'blade_2': aero_dist_tip[1],
-                'blade_3': aero_dist_tip[2],
-                'blade_avg': aero_dist_tip[3]
+                '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,
@@ -301,12 +391,39 @@ def data_analyse(path: List[str]):
     if not os.path.exists(csv_file_path):
         pd.DataFrame(columns=['时间', '场站', '风机编号', '采样频率',
                               '叶片1角度偏差', '叶片2角度偏差', '叶片3角度偏差', '相对角度偏差',
-                              '叶片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(csv_file_path)
+    print(result_line_tip[0].iloc[:, 0])
+    print(result_line_root[0].iloc[:, 0])
+    print('振动主频' + str(tower_freq))
+    print('振动幅值' + str(tower_max))
+    print('最小值', min_values)
+    print('最小值对应的键', min_keys)
+    print('最大值', max_values)
+    print('最大值对应的键', max_keys)
+    print(v_speed_tip, v_speed_root)
+    print('新俯仰角' + str(angle_tip_new))
+    print('轮毂中心距离' + str(dist_cen))
+    print('time_length:' + str(data_root.iloc[-1, 0]))
+
+    plot_data(result_line_tip, 'line', 'data1')
+    # plot_data(result_diff_tip, 'line', 'data_diff_1')
+    plot_data(result_scatter_tip, 'scatter', 'data1')
+    plot_data(result_line_root, 'line', 'data2')
+    # plot_data(result_diff_root, 'line', 'data_diff_2')
+    plot_data(result_scatter_root, 'scatter', 'data2')
+    plot_data(dist_distribute, 'scatter', 'dist_distribute')
 
     return json_output
 
@@ -491,7 +608,7 @@ def cycle_calculate(data_group: pd.DataFrame, noise_threshold: float, min_distan
 
 
 def data_normalize(data_group: pd.DataFrame, start_points: pd.DataFrame, end_points: pd.DataFrame) \
-        -> Tuple[List[pd.DataFrame], List[pd.DataFrame], List[pd.DataFrame], int]:
+        -> Tuple[List[pd.DataFrame], List[pd.DataFrame], List[pd.DataFrame], int, list]:
 
     """
     提取每个叶片的数据并归一化,输出散点图和拟合图
@@ -550,6 +667,7 @@ def data_normalize(data_group: pd.DataFrame, start_points: pd.DataFrame, end_poi
     # 数据分组清洗、求平均
     turbines_processed = []
     turbines_scattered = []
+    min_list = []
     sd_time = [-1, -1]
     time_list = list(range(0, normalize_cycle, 1000))
     # time_list = [(i + 1) * normalize_cycle / fs * 100 for i in range(fs * 100)]  # 生成时间序列
@@ -593,6 +711,7 @@ def data_normalize(data_group: pd.DataFrame, start_points: pd.DataFrame, end_poi
         end_index = int(len(diff_points) * 0.95)
         subset1 = diff_points[start_index:end_index]
         sdr_diff = np.max(subset1) * 1.1
+        min_list.append(min(mean_points))
 
         # 找到第一个和最后一个小于 sdr_diff 的序号
         first_index = np.where(diff_points < sdr_diff)[0][0]
@@ -672,7 +791,7 @@ def data_normalize(data_group: pd.DataFrame, start_points: pd.DataFrame, end_poi
 
     time.sleep(1)
 
-    return turbines_processed, turbines_scattered, border_rows, full_cycle
+    return turbines_processed, turbines_scattered, border_rows, full_cycle, min_list
 
 
 
@@ -832,15 +951,16 @@ def plot_data(data, plot_type: str, data_name: str):
 
     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='time', y='distance', color=color)
+            sns.lineplot(data=df, x=df.iloc[:, 0], y=df.iloc[:, 1], color=color)
     elif plot_type == 'scatter':
         for df, (size, color) in zip(data, [(50, 'blue'), (25, 'green'), (10, 'red')]):
-            sns.scatterplot(data=df, x='time', y='distance', s=size, color=color)
+            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'")
 
@@ -852,7 +972,7 @@ def plot_data(data, plot_type: str, data_name: str):
     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_name)
+    plt.savefig(save_path + save_name)
     plt.close()
     abs_path = os.path.abspath(save_name)
     print(f" {save_name} 已完成")
@@ -924,7 +1044,7 @@ def blade_dist_distribute_cal(data_group: pd.DataFrame, start_points: pd.DataFra
     tower_clearance = [pd.DataFrame() for _ in range(3)]
 
     # 遍历所有起始时间点
-    for i in range(0, len(start_times), 2):
+    for i in range(0, len(start_times) - 2, 2):
 
         # 获取当前起始和结束时间点
         start_time = start_times[i]
@@ -941,8 +1061,8 @@ def blade_dist_distribute_cal(data_group: pd.DataFrame, start_points: pd.DataFra
         segment.loc[:, 'time'] = (segment['time'] - start_time) / ratio
 
         new_df = pd.DataFrame({
-            'clearance': [clearance],
-            'r_speed': [r_speed]
+            'r_speed': [r_speed],
+            'clearance': [clearance]
         })
 
         # 将结果添加到相应的 turbine 数据框中

+ 142 - 46
data_clean.py

@@ -1,4 +1,5 @@
 import os
+import json
 import pandas as pd
 import numpy as np
 import matplotlib.pyplot as plt
@@ -15,6 +16,7 @@ plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显
 
 
 def result_main():
+
     """
     创建data目录,返回历史分析数据存放的文件路径
     """
@@ -33,17 +35,75 @@ def result_main():
     if not os.path.exists(csv_file_path):
         pd.DataFrame(columns=['时间', '场站', '风机编号', '采样频率',
                               '叶片1角度偏差', '叶片2角度偏差', '叶片3角度偏差', '相对角度偏差',
-                              '叶片1净空值', '叶片2净空值', '叶片3净空值', '平均净空值',
+                              '叶片1净空值', '叶片2净空值', '叶片3净空值',
                               '叶片1扭转', '叶片2扭转', '叶片3扭转', '平均扭转',
                               '振动幅值', '振动主频']).to_csv(csv_file_path, index=False)
 
     return csv_file_path
 
 
+def delete_data(names):
+
+    """
+    删除历史分析数据
+    :param names: 删除条件
+    :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)
+
+    for name in names:
+        # 检查条件
+        condition = ((df['时间'].str.contains(name[0])) &
+                     (df['场站'].str.contains(name[1])) &
+                     (df['风机编号'].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:
+    """
+
+    wind_name, turbine_code, time_code = name[1], name[2], name[0]
+    # 获取当前程序的绝对路径
+    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 = path[0]
     measure_file = path[1]
     noise_reduction = 0.000001  # 如果一个距离值的所有样本量小于总样本量的noise_reduction,则被去掉
@@ -78,10 +138,10 @@ def data_analyse(path: List[str]):
     tower_dist_root = ff.tower_cal(filtered_data_root, start_root, end_root, sampling_fq_1)
     lowpass_data, fft_x, fft_y, tower_freq, tower_max = ff.process_fft(filtered_data_cen, sampling_fq)
 
-    result_line_tip, result_scatter_tip, border_rows_tip, cycle_len_tip \
-        = data_normalize(filtered_data_tip, start_tip, end_tip, sampling_fq_1)
-    result_line_root, result_scatter_root, border_rows_root, cycle_len_root \
-        = data_normalize(filtered_data_root, start_root, end_root, sampling_fq_1)
+    result_line_tip, result_scatter_tip, border_rows_tip, cycle_len_tip, min_tip \
+        = data_normalize(filtered_data_tip, start_tip, end_tip)
+    result_line_root, result_scatter_root, border_rows_root, cycle_len_root, min_root \
+        = data_normalize(filtered_data_root, start_root, end_root)
 
     result_avg_tip, result_diff_tip = blade_shape(result_line_tip)
     result_avg_root, result_diff_root = blade_shape(result_line_root)
@@ -93,20 +153,50 @@ def data_analyse(path: List[str]):
 
     pitch_angle_tip, aero_dist_tip, v_speed_tip, cen_blade_tip = (
         blade_angle_aero_dist(border_rows_tip, tip_r, cycle_len_tip, tower_dist_tip, angle_tip_new))
-    pitch_angle_root, aero_dist_root, v_speed_root = (
+    pitch_angle_root, aero_dist_root, v_speed_root, cen_blade_root = (
         blade_angle_aero_dist(border_rows_root, root_r, cycle_len_root, tower_dist_root, angle_root))
 
-    dist_distribute = blade_dist_distribute_cal(filtered_data_tip, start_tip, end_tip,
-                                                tower_dist_tip, angle_tip_new, cen_blade_tip)
+    # 将列表转换为 numpy 数组
+    cen_blade_tip_array = np.array(cen_blade_tip)
+    min_tip_array = np.array(min_tip)
+    abs_diff = np.abs(cen_blade_tip_array - min_tip_array)  # 计算差值的绝对值
+    blade_dist_tip = abs_diff * np.cos(np.deg2rad(angle_tip_new))
+    blade_dist_tip.tolist()  # 如果需要将结果转换回列表
 
+    dist_distribute = blade_dist_distribute_cal(filtered_data_tip, start_tip, end_tip,
+                                                tower_dist_tip, angle_tip_new, blade_dist_tip)
+    dist_distribute = [df.round(5) for df in dist_distribute]
+
+    # 获取每个 DataFrame 第二列的最小值和最大值,以及它们对应的第一列的值,并分别保存在列表中
+    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(second_col_min)
+        min_keys.append(min_row.iloc[0][df.columns[0]])
+        max_values.append(second_col_max)
+        max_keys.append(max_row.iloc[0][df.columns[0]])
+
+    for i in range(3):
+        mean_values.append(round((max_values[i] + min_values[i]) / 2, 2))
 
     for df in result_line_tip:
         first_column = df.iloc[:, 0]
+        sec_column = df.iloc[:, 1]
         df.iloc[:, 0] = first_column * v_speed_tip
+        df.iloc[:, 1] = sec_column * np.cos(np.deg2rad(angle_tip_new))
 
     for df in result_line_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))
 
     avg_tip = result_avg_tip.iloc[:, 0]
     result_avg_tip.iloc[:, 0] = avg_tip * v_speed_tip
@@ -132,10 +222,9 @@ def data_analyse(path: List[str]):
     return_list.append(pitch_angle_root[1])
     return_list.append(pitch_angle_root[2])
     return_list.append(pitch_angle_root[3])
-    return_list.append(aero_dist_tip[0])
-    return_list.append(aero_dist_tip[1])
-    return_list.append(aero_dist_tip[2])
-    return_list.append(aero_dist_tip[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)
@@ -148,7 +237,7 @@ def data_analyse(path: List[str]):
     df_new_row = pd.DataFrame([return_list],
                               columns=['时间', '场站', '风机编号', '采样频率',
                                        '叶片1角度偏差', '叶片2角度偏差', '叶片3角度偏差', '相对角度偏差',
-                                       '叶片1净空值', '叶片2净空值', '叶片3净空值', '平均净空值',
+                                       '叶片1净空值', '叶片2净空值', '叶片3净空值',
                                        '叶片1扭转', '叶片2扭转', '叶片3扭转', '平均扭转',
                                        '振动幅值', '振动主频'])
 
@@ -239,10 +328,30 @@ def data_analyse(path: List[str]):
                 'blade_relate': pitch_angle_root[3]
             },
             'aero_dist': {
-                'blade_1': aero_dist_tip[0],
-                'blade_2': aero_dist_tip[1],
-                'blade_3': aero_dist_tip[2],
-                'blade_avg': aero_dist_tip[3]
+                '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,
@@ -271,12 +380,18 @@ def data_analyse(path: List[str]):
     if not os.path.exists(csv_file_path):
         pd.DataFrame(columns=['时间', '场站', '风机编号', '采样频率',
                               '叶片1角度偏差', '叶片2角度偏差', '叶片3角度偏差', '相对角度偏差',
-                              '叶片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)
+
     return json_output
 
 
@@ -290,25 +405,6 @@ def process_data(file_path):
     data = pd.read_csv(file_path, usecols=[1, 3, 8], header=None, engine='c')
     data = data.head(int(len(data) * 0.95))
 
-    '''
-    # 绘制原始数据图
-    # 只取前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()
@@ -441,22 +537,20 @@ def cycle_calculate(data_group: pd.DataFrame, noise_threshold: float, min_distan
     return start_points, end_points, filtered_data
 
 
-def data_normalize(data_group: pd.DataFrame, start_points: pd.DataFrame, end_points: pd.DataFrame, fs) \
-        -> Tuple[List[pd.DataFrame], List[pd.DataFrame], List[pd.DataFrame], int]:
+def data_normalize(data_group: pd.DataFrame, start_points: pd.DataFrame, end_points: pd.DataFrame) \
+        -> Tuple[List[pd.DataFrame], List[pd.DataFrame], List[pd.DataFrame], int, list]:
 
     """
     提取每个叶片的数据并归一化,输出散点图和拟合图
     :param data_group: cycle_calculate计算完成后的数据。
     :param start_points: 所有每个周期开始点,叶片前缘突变点。
     :param end_points: 叶片后缘突变点。
-    :param fs: 采样频率。
     :return: turbines_processed: 每个叶片的拟合数据,
              turbines_scattered: 每个叶片的散点数据,
              border_rows: 每个叶片的2个边缘数据,
              normalize_cycle: 周期长度
     """
 
-    a = fs
     time.sleep(1)
 
     combined_df_sorted = pd.concat([start_points, end_points]).sort_values(by='time')
@@ -498,6 +592,7 @@ def data_normalize(data_group: pd.DataFrame, start_points: pd.DataFrame, end_poi
     # 数据分组清洗、求平均
     turbines_processed = []
     turbines_scattered = []
+    min_list = []
     sd_time = [-1, -1]
     time_list = list(range(0, normalize_cycle, 1000))
 
@@ -540,6 +635,7 @@ def data_normalize(data_group: pd.DataFrame, start_points: pd.DataFrame, end_poi
         end_index = int(len(diff_points) * 0.95)
         subset1 = diff_points[start_index:end_index]
         sdr_diff = np.max(subset1) * 1.1
+        min_list.append(min(mean_points))
 
         # 找到第一个和最后一个小于 sdr_diff 的序号
         first_index = np.where(diff_points < sdr_diff)[0][0]
@@ -600,7 +696,7 @@ def data_normalize(data_group: pd.DataFrame, start_points: pd.DataFrame, end_poi
 
     time.sleep(1)
 
-    return turbines_processed, turbines_scattered, border_rows, full_cycle
+    return turbines_processed, turbines_scattered, border_rows, full_cycle, min_list
 
 
 def blade_shape(turbines_processed: List[pd.DataFrame]):
@@ -791,7 +887,7 @@ def blade_dist_distribute_cal(data_group: pd.DataFrame, start_points: pd.DataFra
     tower_clearance = [pd.DataFrame() for _ in range(3)]
 
     # 遍历所有起始时间点
-    for i in range(0, len(start_times), 2):
+    for i in range(0, len(start_times) - 2, 2):
         # 获取当前起始和结束时间点
         start_time = start_times[i]
         end_time = start_times[i + 1]
@@ -807,11 +903,11 @@ def blade_dist_distribute_cal(data_group: pd.DataFrame, start_points: pd.DataFra
         segment.loc[:, 'time'] = (segment['time'] - start_time) / ratio
 
         new_df = pd.DataFrame({
-            'clearance': [clearance],
-            'r_speed': [r_speed]
+            'r_speed': [r_speed],
+            'clearance': [clearance]
         })
 
         # 将结果添加到相应的 turbine 数据框中
         tower_clearance[i % 3] = pd.concat([tower_clearance[i % 3], new_df])
 
-    return tower_clearance
+    return tower_clearance