Browse Source

version -v2.0

XGJ_zhaoyuan 1 week ago
parent
commit
0bd907a925

+ 57 - 202
modules/lib/chart_generator.py

@@ -71,10 +71,12 @@ scenario_sign_dict 场景签名字典(简化实现)
 import os
 import numpy as np
 import pandas as pd
-import matplotlib.pyplot as plt
 
 from typing import Optional, Dict, List, Any, Union
 from pathlib import Path
+import matplotlib
+
+matplotlib.use('Agg')
 
 from modules.lib.log_manager import LogManager
 
@@ -954,7 +956,7 @@ def generate_limit_speed_chart(function_calculator, output_dir: str) -> Optional
         df_csv = pd.DataFrame({
             'simTime': ego_df['simTime'],
             'speed': ego_df['v'],
-            'max_threshold': ego_df.get('speed_limit', pd.Series([max_threshold] * len(ego_df)))
+            'speed_limit': ego_df.get('speed_limit', pd.Series([max_threshold] * len(ego_df)))
         })
         df_csv.to_csv(csv_filename, index=False)
         logger.info(f"{metric_name} data saved to: {csv_filename}")
@@ -967,7 +969,7 @@ def generate_limit_speed_chart(function_calculator, output_dir: str) -> Optional
 
         # # Plot speed
         # plt.plot(df['simTime'], df['speed'], 'b-', label='Vehicle Speed')
-        # plt.plot(df['simTime'], df['max_threshold'], 'r--', label='Max Threshold')
+        # plt.plot(df['simTime'], df['speed_limit'], 'r--', label='Speed Limit')
 
         # # Set y-axis range
         # plt.ylim(bottom=0, top=max(max_threshold * 1.1, df['speed'].max() * 1.1))
@@ -1029,7 +1031,7 @@ def generate_limit_speed_past_sign_chart(function_calculator, output_dir: str) -
         df_csv = pd.DataFrame({
             'simTime': ego_df['simTime'],
             'speed': ego_df['v'],
-            'max_threshold': ego_df.get('speed_limit', pd.Series([max_threshold] * len(ego_df))),
+            'speed_limit': ego_df.get('speed_limit', pd.Series([max_threshold] * len(ego_df))),
             'sign_pass_time': sign_time
         })
         df_csv.to_csv(csv_filename, index=False)
@@ -1044,7 +1046,7 @@ def generate_limit_speed_past_sign_chart(function_calculator, output_dir: str) -
 
         # # Plot speed
         # plt.plot(df['simTime'], df['speed'], 'b-', label='Vehicle Speed')
-        # plt.plot(df['simTime'], df['max_threshold'], 'r--', label='Max Speed')
+        # plt.plot(df['simTime'], df['speed_limit'], 'r--', label='Speed Limit')
 
         # # Mark sign passing time
         # plt.axvline(x=sign_time, color='g', linestyle='--', label='Speed Limit Sign')
@@ -1318,9 +1320,10 @@ def generate_shake_chart(comfort_calculator, output_dir: str) -> Optional[str]:
 
         # 保存 CSV 数据(第一步)
         csv_filename = os.path.join(output_dir, f"shake_data.csv")
+        # logger.info(f"self.data列名对象:: {df.columns}")
         df_csv = pd.DataFrame({
             'simTime': df['simTime'],
-            'lat_acc': df['lat_acc'],
+            'lat_acc': df['lat_acc_vehicle'],
             'lat_acc_rate': df['lat_acc_rate'],
             'speedH_std': df['speedH_std'],
             'lat_acc_threshold': df.get('lat_acc_threshold', pd.Series([None] * len(df))),
@@ -1432,8 +1435,8 @@ def generate_zigzag_chart(comfort_calculator, output_dir: str) -> Optional[str]:
             'simTime': df['simTime'],
             'speedH': df['speedH'],
             'posH': df['posH'],
-            'min_speedH_threshold': -2.3,  # 可替换为动态阈值
-            'max_speedH_threshold': 2.3
+            'min_threshold': -10,  # 可替换为动态阈值0.02618
+            'max_threshold': 10
         })
         df_csv.to_csv(csv_filename, index=False)
         logger.info(f"Zigzag data saved to: {csv_filename}")
@@ -1524,10 +1527,10 @@ def generate_cadence_chart(comfort_calculator, output_dir: str) -> Optional[str]
         csv_filename = os.path.join(output_dir, f"cadence_data.csv")
         df_csv = pd.DataFrame({
             'simTime': df['simTime'],
-            'lon_acc': df['lon_acc'],
+            'lon_acc': df['lon_acc_vehicle'],
             'v': df['v'],
-            'max_threshold': df.get('ip_acc', pd.Series([None] * len(df))),
-            'min_threshold': df.get('ip_dec', pd.Series([None] * len(df)))
+            'ip_acc': df.get('ip_acc', pd.Series([None] * len(df))),
+            'ip_dec': df.get('ip_dec', pd.Series([None] * len(df)))
         })
         df_csv.to_csv(csv_filename, index=False)
         logger.info(f"Cadence data saved to: {csv_filename}")
@@ -1542,9 +1545,9 @@ def generate_cadence_chart(comfort_calculator, output_dir: str) -> Optional[str]
         # # 图 1:纵向加速度
         # ax1 = plt.subplot(2, 1, 1)
         # ax1.plot(df['simTime'], df['lon_acc'], 'b-', label='Longitudinal Acceleration')
-        # if 'max_threshold' in df.columns and 'min_threshold' in df.columns:
-        #     ax1.plot(df['simTime'], df['max_threshold'], 'r--', label='Max Threshold')
-        #     ax1.plot(df['simTime'], df['min_threshold'], 'g--', label='Min Threshold')
+        # if 'ip_acc' in df.columns and 'ip_dec' in df.columns:
+        #     ax1.plot(df['simTime'], df['ip_acc'], 'r--', label='Acceleration Threshold')
+        #     ax1.plot(df['simTime'], df['ip_dec'], 'g--', label='Deceleration Threshold')
 
         # # 添加橙色背景标识顿挫事件
         # for idx, event in cadence_events.iterrows():
@@ -1617,59 +1620,42 @@ def generate_slam_brake_chart(comfort_calculator, output_dir: str) -> Optional[s
         csv_filename = os.path.join(output_dir, f"slam_brake_data.csv")
         df_csv = pd.DataFrame({
             'simTime': df['simTime'],
-            'lon_acc': df['lon_acc'],
-            'lon_acc_diff': df['lon_acc_diff'],
+            'lon_acc': df['lon_acc_vehicle'],
             'v': df['v'],
             'min_threshold': df.get('ip_dec', pd.Series([None] * len(df))),
             'max_threshold': 0.0
         })
         df_csv.to_csv(csv_filename, index=False)
         logger.info(f"Slam brake data saved to: {csv_filename}")
-        # return csv_filename
+        return csv_filename
 
         # # 第二步:从 CSV 读取(可验证保存数据无误)
-        df = pd.read_csv(csv_filename)
+        # df = pd.read_csv(csv_filename)
 
         # # 创建图表(第三步)
-        plt.figure(figsize=(12, 8), constrained_layout=True)
+        # plt.figure(figsize=(12, 8), constrained_layout=True)
 
         # # 图 1:纵向加速度
-        ax1 = plt.subplot(3, 1, 1)
-        ax1.plot(df['simTime'], df['lon_acc'], 'b-', label='Longitudinal Acceleration')
-        if 'min_threshold' in df.columns:
-            ax1.plot(df['simTime'], df['min_threshold'], 'r--', label='Min Threshold')
+        # ax1 = plt.subplot(2, 1, 1)
+        # ax1.plot(df['simTime'], df['lon_acc'], 'b-', label='Longitudinal Acceleration')
+        # if 'min_threshold' in df.columns:
+        #     ax1.plot(df['simTime'], df['min_threshold'], 'r--', label='Deceleration Threshold')
 
         # # 添加橙色背景标识急刹车事件
-        for idx, event in slam_brake_events.iterrows():
-            label = 'Slam Brake Event' if idx == 0 else None
-            ax1.axvspan(event['start_time'], event['end_time'],
-                        alpha=0.3, color='orange', label=label)
+        # for idx, event in slam_brake_events.iterrows():
+        #     label = 'Slam Brake Event' if idx == 0 else None
+        #     ax1.axvspan(event['start_time'], event['end_time'],
+        #                 alpha=0.3, color='orange', label=label)
 
-        ax1.set_xlabel('Time (s)')
-        ax1.set_ylabel('Longitudinal Acceleration (m/s²)')
-        ax1.set_title('Slam Brake Event Detection - Longitudinal Acceleration')
-        ax1.grid(True)
-        ax1.legend()
+        # ax1.set_xlabel('Time (s)')
+        # ax1.set_ylabel('Longitudinal Acceleration (m/s²)')
+        # ax1.set_title('Slam Brake Event Detection - Longitudinal Acceleration')
+        # ax1.grid(True)
+        # ax1.legend()
 
         # # 图 2:速度
-        ax2 = plt.subplot(3, 1, 2)
-        ax2.plot(df['simTime'], df['v'], 'g-', label='Velocity')
-
-        # # 添加橙色背景标识急刹车事件
-        for idx, event in slam_brake_events.iterrows():
-            label = 'Slam Brake Event' if idx == 0 else None
-            ax2.axvspan(event['start_time'], event['end_time'],
-                        alpha=0.3, color='orange', label=label)
-
-        ax2.set_xlabel('Time (s)')
-        ax2.set_ylabel('Velocity (m/s)')
-        ax2.set_title('Slam Brake Event Detection - Vehicle Speed')
-        ax2.grid(True)
-        ax2.legend()
-
-        # # 图 3:加速度变化率
-        ax3 = plt.subplot(3, 1, 3)
-        ax3.plot(df['simTime'], df['lon_acc_diff'], 'r-', label='lon_acc_diff')
+        # ax2 = plt.subplot(2, 1, 2)
+        # ax2.plot(df['simTime'], df['v'], 'g-', label='Velocity')
 
         # # 添加橙色背景标识急刹车事件
         # for idx, event in slam_brake_events.iterrows():
@@ -1677,19 +1663,19 @@ def generate_slam_brake_chart(comfort_calculator, output_dir: str) -> Optional[s
         #     ax2.axvspan(event['start_time'], event['end_time'],
         #                 alpha=0.3, color='orange', label=label)
 
-        ax3.set_xlabel('Time (s)')
-        ax3.set_ylabel('lon_acc_diff (m/s^3)')
-        ax3.set_title('Slam Brake Event Detection - Longitudinal Acceleration Difference')
-        ax3.grid(True)
-        ax3.legend()
+        # ax2.set_xlabel('Time (s)')
+        # ax2.set_ylabel('Velocity (m/s)')
+        # ax2.set_title('Slam Brake Event Detection - Vehicle Speed')
+        # ax2.grid(True)
+        # ax2.legend()
 
         # # 保存图像
-        chart_filename = os.path.join(output_dir, f"slam_brake_chart.png")
-        plt.savefig(chart_filename, dpi=300)
-        plt.close()
+        # chart_filename = os.path.join(output_dir, f"slam_brake_chart.png")
+        # plt.savefig(chart_filename, dpi=300)
+        # plt.close()
 
-        logger.info(f"Slam brake chart saved to: {chart_filename}")
-        return chart_filename
+        # logger.info(f"Slam brake chart saved to: {chart_filename}")
+        # return chart_filename
 
     except Exception as e:
         logger.error(f"Failed to generate slam brake chart: {str(e)}", exc_info=True)
@@ -1731,7 +1717,7 @@ def generate_slam_accelerate_chart(comfort_calculator, output_dir: str) -> Optio
 
         df_csv = pd.DataFrame({
             'simTime': df['simTime'],
-            'lon_acc': df['lon_acc'],
+            'lon_acc': df['lon_acc_vehicle'],
             'v': df['v'],
             'min_threshold': 0.0,  # 加速度最小阈值设为0
             'max_threshold': accel_threshold  # 急加速阈值
@@ -1752,7 +1738,7 @@ def generate_slam_accelerate_chart(comfort_calculator, output_dir: str) -> Optio
 
         # # 添加加速度阈值线
         # if 'max_threshold' in df.columns and not df['max_threshold'].isnull().all():
-        #     ax1.plot(df['simTime'], df['max_threshold'], 'r--', label='Max Threshold')
+        #     ax1.plot(df['simTime'], df['max_threshold'], 'r--', label='Acceleration Threshold')
 
         # # 添加橙色背景标识急加速事件
         # for idx, event in slam_accel_events.iterrows():
@@ -1998,42 +1984,15 @@ def generate_ttc_chart(safety_calculator, output_dir: str) -> Optional[str]:
                             'min_ttc': group['TTC'].min()
                         })
 
-        # # 创建图表(第三步)
-        # plt.figure(figsize=(12, 8))
-        # plt.plot(df['simTime'], df['TTC'], 'b-', label='TTC')
-
-        # # 添加阈值线
-        # if min_threshold is not None:
-        #     plt.axhline(y=min_threshold, color='r', linestyle='--', label=f'Min Threshold ({min_threshold}s)')
-        # if max_threshold is not None:
-        #     plt.axhline(y=max_threshold, color='g', linestyle='--', label=f'Max Threshold ({max_threshold})')
-
-        # # 添加橙色背景标识不安全事件
-        # for idx, event in enumerate(unsafe_events):
-        #     label = 'Unsafe TTC Event' if idx == 0 else None
-        #     plt.axvspan(event['start_time'], event['end_time'],
-        #                 alpha=0.3, color='orange', label=label)
-
-        # plt.xlabel('Time (s)')
-        # plt.ylabel('TTC (s)')
-        # plt.title('Time To Collision (TTC) Trend')
-        # plt.grid(True)
-        # plt.legend()
-
-        # # 保存图像
-        # chart_filename = os.path.join(output_dir, f"ttc_chart.png")
-        # plt.savefig(chart_filename, dpi=300)
-        # plt.close()
-
-        # # 记录不安全事件信息
-        # if unsafe_events:
-        #     logger.info(f"检测到 {len(unsafe_events)} 个TTC不安全事件")
-        #     for i, event in enumerate(unsafe_events):
-        #         logger.info(
-        #             f"TTC不安全事件 #{i + 1}: 开始时间={event['start_time']:.2f}s, 结束时间={event['end_time']:.2f}s, 持续时间={event['duration']:.2f}s, 最小TTC={event['min_ttc']:.2f}s")
+        # 记录不安全事件信息
+        if unsafe_events:
+            logger.info(f"检测到 {len(unsafe_events)} 个TTC不安全事件")
+            for i, event in enumerate(unsafe_events):
+                logger.info(
+                    f"TTC不安全事件 #{i + 1}: 开始时间={event['start_time']:.2f}s, 结束时间={event['end_time']:.2f}s, 持续时间={event['duration']:.2f}s, 最小TTC={event['min_ttc']:.2f}s")
 
-        # logger.info(f"TTC chart saved to: {chart_filename}")
-        # return chart_filename
+        logger.info(f"TTC chart saved to: {chart_filename}")
+        return chart_filename
 
     except Exception as e:
         logger.error(f"Failed to generate TTC chart: {str(e)}", exc_info=True)
@@ -2092,35 +2051,6 @@ def generate_mttc_chart(safety_calculator, output_dir: str) -> Optional[str]:
                             'min_mttc': group['MTTC'].min()
                         })
 
-        # # 创建图表
-        # plt.figure(figsize=(12, 6))
-        # plt.plot(df['simTime'], df['MTTC'], 'g-', label='MTTC')
-
-        # # 添加阈值线
-        # if min_threshold is not None:
-        #     plt.axhline(y=min_threshold, color='r', linestyle='--', label=f'Min Threshold ({min_threshold}s)')
-        # if max_threshold is not None:
-        #     plt.axhline(y=max_threshold, color='g', linestyle='--', label=f'Max Threshold ({max_threshold})')
-
-        # # 添加橙色背景标识不安全事件
-        # for idx, event in enumerate(unsafe_events):
-        #     label = 'Unsafe MTTC Event' if idx == 0 else None
-        #     plt.axvspan(event['start_time'], event['end_time'],
-        #                 alpha=0.3, color='orange', label=label)
-
-        # plt.xlabel('Time (s)')
-        # plt.ylabel('MTTC (s)')
-        # plt.title('Modified Time To Collision (MTTC) Trend')
-        # plt.grid(True)
-        # plt.legend()
-
-        # # 保存图表
-
-        # chart_filename = os.path.join(output_dir, f"mttc_chart.png")
-        # plt.savefig(chart_filename, dpi=300)
-        # plt.close()
-        # logger.info(f"MTTC chart saved to: {chart_filename}")
-
         # 保存CSV数据,包含阈值信息
         csv_filename = os.path.join(output_dir, f"mttc_data.csv")
         df_csv = df.copy()
@@ -2170,7 +2100,7 @@ def generate_thw_chart(safety_calculator, output_dir: str) -> Optional[str]:
         # 获取阈值
         thresholds = get_metric_thresholds(safety_calculator, 'THW')
         min_threshold = thresholds.get('min')
-        max_threshold = thresholds.get('max')
+        max_threshold = 60
 
         # 检测超阈值事件
         unsafe_events = []
@@ -2195,35 +2125,6 @@ def generate_thw_chart(safety_calculator, output_dir: str) -> Optional[str]:
                             'min_thw': group['THW'].min()
                         })
 
-        # # 创建图表
-        # plt.figure(figsize=(12, 10))
-        # plt.plot(df['simTime'], df['THW'], 'c-', label='THW')
-
-        # # 添加阈值线
-        # if min_threshold is not None:
-        #     plt.axhline(y=min_threshold, color='r', linestyle='--', label=f'Min Threshold ({min_threshold}s)')
-        # if max_threshold is not None:
-        #     plt.axhline(y=max_threshold, color='g', linestyle='--', label=f'Max Threshold ({max_threshold})')
-
-        # # 添加橙色背景标识不安全事件
-        # for idx, event in enumerate(unsafe_events):
-        #     label = 'Unsafe THW Event' if idx == 0 else None
-        #     plt.axvspan(event['start_time'], event['end_time'],
-        #                 alpha=0.3, color='orange', label=label)
-
-        # plt.xlabel('Time (s)')
-        # plt.ylabel('THW (s)')
-        # plt.title('Time Headway (THW) Trend')
-        # plt.grid(True)
-        # plt.legend()
-
-        # # 保存图表
-
-        # chart_filename = os.path.join(output_dir, f"thw_chart.png")
-        # plt.savefig(chart_filename, dpi=300)
-        # plt.close()
-        # logger.info(f"THW chart saved to: {chart_filename}")
-
         # 保存CSV数据,包含阈值信息
         csv_filename = os.path.join(output_dir, f"thw_data.csv")
         df_csv = df.copy()
@@ -2548,29 +2449,6 @@ def generate_collision_risk_chart(safety_calculator, output_dir: str) -> Optiona
         min_threshold = thresholds.get('min')
         max_threshold = thresholds.get('max')
 
-        # # 创建图表
-        # plt.figure(figsize=(12, 6))
-        # plt.plot(df['simTime'], df['collisionRisk'], 'r-', label='Collision Risk')
-
-        # # 添加阈值线
-        # if min_threshold is not None:
-        #     plt.axhline(y=min_threshold, color='r', linestyle='--', label=f'Min Threshold ({min_threshold}%)')
-        # if max_threshold is not None:
-        #     plt.axhline(y=max_threshold, color='g', linestyle='--', label=f'Max Threshold ({max_threshold}%)')
-
-        # plt.xlabel('Time (s)')
-        # plt.ylabel('Risk Value (%)')
-        # plt.title('Collision Risk (collisionRisk) Trend')
-        # plt.grid(True)
-        # plt.legend()
-
-        # # 保存图表
-
-        # chart_filename = os.path.join(output_dir, f"collision_risk_chart.png")
-        # plt.savefig(chart_filename, dpi=300)
-        # plt.close()
-        # logger.info(f"Collision Risk chart saved to: {chart_filename}")
-
         # 保存CSV数据,包含阈值信息
         csv_filename = os.path.join(output_dir, f"collisionrisk_data.csv")
         df_csv = df.copy()
@@ -2615,29 +2493,6 @@ def generate_collision_severity_chart(safety_calculator, output_dir: str) -> Opt
         min_threshold = thresholds.get('min')
         max_threshold = thresholds.get('max')
 
-        # # 创建图表
-        # plt.figure(figsize=(12, 6))
-        # plt.plot(df['simTime'], df['collisionSeverity'], 'r-', label='Collision Severity')
-
-        # # 添加阈值线
-        # if min_threshold is not None:
-        #     plt.axhline(y=min_threshold, color='r', linestyle='--', label=f'Min Threshold ({min_threshold}%)')
-        # if max_threshold is not None:
-        #     plt.axhline(y=max_threshold, color='g', linestyle='--', label=f'Max Threshold ({max_threshold}%)')
-
-        # plt.xlabel('Time (s)')
-        # plt.ylabel('Severity (%)')
-        # plt.title('Collision Severity (collisionSeverity) Trend')
-        # plt.grid(True)
-        # plt.legend()
-
-        # # 保存图表
-
-        # chart_filename = os.path.join(output_dir, f"collision_severity_chart.png")
-        # plt.savefig(chart_filename, dpi=300)
-        # plt.close()
-        # logger.info(f"Collision Severity chart saved to: {chart_filename}")
-
         # 保存CSV数据,包含阈值信息
         csv_filename = os.path.join(output_dir, f"collisionseverity_data.csv")
         df_csv = df.copy()

+ 277 - 248
modules/lib/data_process.py

@@ -1,54 +1,17 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-##################################################################
-#
-# Copyright (c) 2024 CICV, Inc. All Rights Reserved
-#
-##################################################################
-"""
-@Authors:           zhanghaiwen(zhanghaiwen@china-icv.cn)
-@Data:              2024/10/17
-@Last Modified:     2024/10/17
-@Summary:           Evaluation functions
-"""
-
 import os
-
 import numpy as np
 import pandas as pd
-
 import yaml
-
 from modules.lib.log_manager import LogManager
 
 
-# from lib import log  # 确保这个路径是正确的,或者调整它
-# logger = None  # 初始化为 None
-
-
 class DataPreprocessing:
     def __init__(self, data_path, config_path):
-        # Initialize paths and data containers
-        # self.logger = log.get_logger()
-
         self.data_path = data_path
         self.case_name = os.path.basename(os.path.normpath(data_path))
-
         self.config_path = config_path
 
-        # Initialize DataFrames
-        self.object_df = pd.DataFrame()
-        self.driver_ctrl_df = pd.DataFrame()
-        self.vehicle_sys_df = pd.DataFrame()
-        self.ego_data_df = pd.DataFrame()
-
-        # Environment data
-        self.lane_info_df = pd.DataFrame()
-        self.road_mark_df = pd.DataFrame()
-        self.road_pos_df = pd.DataFrame()
-        self.traffic_light_df = pd.DataFrame()
-        self.traffic_signal_df = pd.DataFrame()
-
+        # 初始化配置容器
         self.vehicle_config = {}
         self.safety_config = {}
         self.comfort_config = {}
@@ -56,273 +19,339 @@ class DataPreprocessing:
         self.function_config = {}
         self.traffic_config = {}
 
-        # Initialize data for later processing
+        # 数据容器
+        self.object_df = pd.DataFrame()
+        self.driver_ctrl_df = pd.DataFrame()
+        self.vehicle_sys_df = pd.DataFrame()
+        self.ego_data_df = pd.DataFrame()
         self.obj_data = {}
         self.ego_data = {}
         self.obj_id_list = []
-
-        # Data quality level
         self.data_quality_level = 15
 
-        # Process mode and prepare report information
-        self._process_mode()
+        # 加载配置并处理数据
         self._get_yaml_config()
-        self.report_info = self._get_report_info(self.obj_data.get(1, pd.DataFrame()))
+        self._process_mode()
+        self.report_info = self._get_report_info(self.ego_data)
 
     def _process_mode(self):
-        """Handle different processing modes."""
+        """当前支持默认处理方式"""
         self._real_process_object_df()
 
     def _get_yaml_config(self):
         with open(self.config_path, 'r') as f:
             full_config = yaml.safe_load(f)
 
-        modules = ["vehicle", "T_threshold", "safety", "comfort", "efficient", "function", "traffic"]
-
-        # 1. 初始化 vehicle_config(不涉及 T_threshold 合并)
-        self.vehicle_config = full_config.get(modules[0], {})
-
-        # 2. 定义 T_threshold_config(封装为字典)
-        T_threshold_config = {"T_threshold": full_config.get(modules[1], {})}
-
-        # 3. 统一处理需要合并 T_threshold 的模块
-        # 3.1 safety_config
-        self.safety_config = {"safety": full_config.get(modules[2], {})}
+        T_threshold_config = {"T_threshold": full_config.get("T_threshold", {})}
+        self.vehicle_config = full_config.get("vehicle", {})
+        self.safety_config = {"safety": full_config.get("safety", {})}
         self.safety_config.update(T_threshold_config)
-
-        # 3.2 comfort_config
-        self.comfort_config = {"comfort": full_config.get(modules[3], {})}
+        self.comfort_config = {"comfort": full_config.get("comfort", {})}
         self.comfort_config.update(T_threshold_config)
-
-        # 3.3 efficient_config
-        self.efficient_config = {"efficient": full_config.get(modules[4], {})}
+        self.efficient_config = {"efficient": full_config.get("efficient", {})}
         self.efficient_config.update(T_threshold_config)
-
-        # 3.4 function_config
-        self.function_config = {"function": full_config.get(modules[5], {})}
+        self.function_config = {"function": full_config.get("function", {})}
         self.function_config.update(T_threshold_config)
-
-        # 3.5 traffic_config
-        self.traffic_config = {"traffic": full_config.get(modules[6], {})}
+        self.traffic_config = {"traffic": full_config.get("traffic", {})}
         self.traffic_config.update(T_threshold_config)
 
-    @staticmethod
-    def cal_velocity(lat_v, lon_v):
-        """Calculate resultant velocity from lateral and longitudinal components."""
-        return np.sqrt(lat_v ** 2 + lon_v ** 2)
+    def _plot_coordinate_comparison(self, ego_df):
+        """绘制全局坐标系与自车坐标系的运动学量对比图"""
+        try:
+            import matplotlib.pyplot as plt
+            from matplotlib.gridspec import GridSpec
+            import os
+
+            plt.figure(figsize=(18, 10))
+            gs = GridSpec(3, 3, figure=plt.gcf())  # 改为 3 行 3 列布局
+
+            # 1. 速度对比 - 纵向分量
+            ax1 = plt.subplot(gs[0, 0])
+            # ax1.plot(ego_df['simTime'], ego_df['speedX'], 'r-', label='Global Vx')
+            # ax1.plot(ego_df['simTime'], ego_df['speedY'], 'g-', label='Global Vy')
+            ax1.plot(ego_df['simTime'], ego_df['lon_v_vehicle'], 'b-', label='Ego Frame Lon V (2D)')
+            # ax1.plot(ego_df['simTime'], ego_df['lon_v_vehicle_3D'], 'y--', label='Ego Frame Lon V (3D)')
+            ax1.set_title('Longitudinal Velocity Comparison')
+            ax1.set_ylabel('Velocity (m/s)')
+            ax1.legend()
+            ax1.grid(True)
+
+            # 2. 速度对比 - 横向分量
+            ax2 = plt.subplot(gs[0, 1])
+            # ax2.plot(ego_df['simTime'], ego_df['speedX'], 'r-', label='Global Vx')
+            # ax2.plot(ego_df['simTime'], ego_df['speedY'], 'g-', label='Global Vy')
+            ax2.plot(ego_df['simTime'], ego_df['lat_v_vehicle'], 'b-', label='Ego Frame Lat V (2D)')
+            # ax2.plot(ego_df['simTime'], ego_df['lat_v_vehicle_3D'], 'y--', label='Ego Frame Lat V (3D)')
+            ax2.set_title('Lateral Velocity Comparison')
+            ax2.legend()
+            ax2.grid(True)
+
+            # 3. 航向角速度(新增子图)
+            ax3 = plt.subplot(gs[0, 2])
+            ax3.plot(ego_df['simTime'], ego_df['speedH'], 'm-', label='Heading Rate')
+            ax3.set_title('Ego Heading Rate')
+            ax3.set_ylabel('Yaw Rate (deg/s)')
+            ax3.grid(True)
+
+            # 4. 加速度对比 - 纵向分量
+            ax4 = plt.subplot(gs[1, 0])
+            # ax4.plot(ego_df['simTime'], ego_df['accelX'], 'r-', label='Global Ax')
+            # ax4.plot(ego_df['simTime'], ego_df['accelY'], 'g-', label='Global Ay')
+            ax4.plot(ego_df['simTime'], ego_df['lon_acc_vehicle'], 'b-', label='Ego Frame Lon A (2D)')
+            # ax4.plot(ego_df['simTime'], ego_df['lon_acc_vehicle_3D'], 'y--', label='Ego Frame Lon A (3D)')
+            ax4.set_title('Longitudinal Acceleration Comparison')
+            ax4.set_ylabel('Acceleration (m/s²)')
+            ax4.legend()
+            ax4.grid(True)
+
+            # 5. 加速度对比 - 横向分量
+            ax5 = plt.subplot(gs[1, 1])
+            # ax5.plot(ego_df['simTime'], ego_df['accelX'], 'r-', label='Global Ax')
+            # ax5.plot(ego_df['simTime'], ego_df['accelY'], 'g-', label='Global Ay')
+            ax5.plot(ego_df['simTime'], ego_df['lon_acc_rate'], 'b-', label='Ego Frame Lat A (2D)')
+            # ax5.plot(ego_df['simTime'], ego_df['lat_acc_vehicle_3D'], 'y--', label='Ego Frame Lat A (3D)')
+            ax5.set_title('Lateral Acceleration Comparison')
+            ax5.legend()
+            ax5.grid(True)
+
+            # 6. 航向角变化(原图,平移至右侧)
+            ax6 = plt.subplot(gs[1, 2])
+            ax6.plot(ego_df['simTime'], ego_df['ego_posH'], 'b-', label='Ego Heading')
+            ax6.set_title('Ego Vehicle Heading')
+            ax6.set_ylabel('Heading (deg)')
+            ax6.grid(True)
+
+            plt.tight_layout()
+
+            # 保存图像到当前目录
+            plot_path = os.path.join(os.getcwd(), 'coordinate_transform_comparison.png')
+            plt.savefig(plot_path)
+            plt.close()
+
+            logger = LogManager().get_logger()
+            logger.info(f"坐标系转换对比图已保存至: {plot_path}")
+
+        except ImportError:
+            logger = LogManager().get_logger()
+            logger.warning("Matplotlib未安装,无法生成坐标系转换对比图")
+        except Exception as e:
+            logger = LogManager().get_logger()
+            logger.error(f"生成坐标系转换对比图时出错: {e}", exc_info=True)
 
     def _real_process_object_df(self):
-        """Process the object DataFrame."""
+        """读取并处理对象数据(含自车)"""
         try:
-            # 读取 CSV 文件
             merged_csv_path = os.path.join(self.data_path, "merged_ObjState.csv")
-
-            # 检查文件是否存在
             if not os.path.exists(merged_csv_path):
                 logger = LogManager().get_logger()
                 logger.error(f"文件不存在: {merged_csv_path}")
                 raise FileNotFoundError(f"文件不存在: {merged_csv_path}")
 
-            self.object_df = pd.read_csv(
+            df = pd.read_csv(
                 merged_csv_path,
                 dtype={"simTime": float},
-                engine="python",
-                on_bad_lines="skip",  # 自动跳过异常行
-                na_values=["", "NA", "null", "NaN"]  # 明确处理缺失值
+                on_bad_lines="skip",
+                na_values=["", "NA", "null", "NaN"]
             ).drop_duplicates(subset=["simTime", "simFrame", "playerId"])
-            self.object_df.columns = [col.replace("+AF8-", "_") for col in self.object_df.columns]
-
-            data = self.object_df.copy()
-
-            # 使用向量化操作计算速度和加速度,提高性能
-            data["lat_v"] = data["speedY"] * 1
-            data["lon_v"] = data["speedX"] * 1
-            # 使用向量化操作代替 apply
-            data["v"] = np.sqrt(data["lat_v"] ** 2 + data["lon_v"] ** 2)
 
-            # 计算加速度分量
-            data["lat_acc"] = data["accelY"] * 1
-            data["lon_acc"] = data["accelX"] * 1
-            # 使用向量化操作代替 apply
-            data["accel"] = np.sqrt(data["lat_acc"] ** 2 + data["lon_acc"] ** 2)
+            # 列名标准化
+            df.columns = [col.replace("+AF8-", "_") for col in df.columns]
+            df = df.dropna(subset=["type"]).reset_index(drop=True)
 
-            # Drop rows with missing 'type' and reset index
-            data = data.dropna(subset=["type"])
-            data.reset_index(drop=True, inplace=True)
-            self.object_df = data.copy()
+            # ===== 修正航向角处理 =====
+            # 确保航向角在-180到180度范围内(0度正北,90度正东)
+            if 'posH' in df.columns:
+                df['posH'] = df['posH'] % 360  # 规范化到0-360度
+                df['posH'] = np.where(df['posH'] > 180, df['posH'] - 360, df['posH'])
 
-            # Calculate respective parameters for each object
-            for obj_id, obj_data in data.groupby("playerId"):
-                self.obj_data[obj_id] = self._calculate_object_parameters(obj_data)
-
-            # Get object id list
+            # ===== 提取自车数据 =====
+            # 自车ID固定为1
             EGO_PLAYER_ID = 1
+            ego_mask = df['playerId'] == EGO_PLAYER_ID
+
+            # 确保所有车辆都有pitch和roll列
+            for col in ['pitch', 'roll']:
+                if col not in df.columns:
+                    df[col] = 0.0
+
+            # 提取自车姿态数据
+            ego_cols = ['simTime', 'posX', 'posY', 'posH', 'pitch', 'roll']
+            ego_df = df[ego_mask][ego_cols].copy()
+            ego_df.columns = ['simTime'] + [f'ego_{col}' for col in ego_cols[1:]]
+
+            # 合并自车姿态信息到主表
+            df = pd.merge(df, ego_df, on='simTime', how='left')
+
+            # ===== 计算相对位置(在自车坐标系下)=====
+            # 计算全局坐标差
+            dx_global = df['posX'] - df['ego_posX']
+            dy_global = df['posY'] - df['ego_posY']
+
+            # 转换为弧度并计算三角函数
+            ego_heading_rad = np.deg2rad(df['ego_posH'])
+            cos_h = np.cos(ego_heading_rad)
+            sin_h = np.sin(ego_heading_rad)
+
+            # 向量化计算相对坐标(在自车坐标系下)
+            df['x_relative_start_dist'] = dx_global * sin_h + dy_global * cos_h
+            df['y_relative_start_dist'] = -dx_global * cos_h + dy_global * sin_h
+
+            # 自车相对位置设为0
+            df.loc[ego_mask, 'x_relative_start_dist'] = 0.0
+            df.loc[ego_mask, 'y_relative_start_dist'] = 0.0
+
+            # 保存原始数据
+            self.object_df = df.copy()
+
+            # 处理运动学数据(所有车辆在自车坐标系下)
+            all_processed_dfs = []
+            self.obj_data = {}
+
+            for obj_id, obj_df in df.groupby("playerId"):
+                # 使用自车姿态计算运动学量
+                processed_df = self._calculate_kinematics_in_ego_frame(obj_df)
+                self.obj_data[obj_id] = processed_df
+                all_processed_dfs.append(processed_df)
+
+            # 合并处理后的数据
+            if all_processed_dfs:
+                combined_processed = pd.concat(all_processed_dfs)
+
+                # 生成唯一键
+                self.object_df['merge_key'] = self.object_df['playerId'].astype(str) + '_' + self.object_df[
+                    'simTime'].astype(str)
+                combined_processed['merge_key'] = combined_processed['playerId'].astype(str) + '_' + combined_processed[
+                    'simTime'].astype(str)
+
+                # 合并新列
+                new_columns = [col for col in combined_processed.columns
+                               if col not in self.object_df.columns or col == 'merge_key']
+                self.object_df = pd.merge(
+                    self.object_df,
+                    combined_processed[new_columns],
+                    on='merge_key',
+                    how='left'
+                ).drop(columns=['merge_key'])
+
+            # 设置自车数据
             self.obj_id_list = list(self.obj_data.keys())
-            self.ego_data = self.obj_data[EGO_PLAYER_ID]
-
-            # 添加这一行:处理自车数据,进行坐标系转换
-            self.ego_data = self.process_ego_data(self.ego_data)
-
+            self.ego_data = self.obj_data.get(EGO_PLAYER_ID, None)
+            if self.ego_data is not None:
+                # self._plot_coordinate_comparison(self.ego_data)
+                pass
         except Exception as e:
             logger = LogManager().get_logger()
             logger.error(f"处理对象数据帧时出错: {e}", exc_info=True)
             raise
 
-    def _calculate_object_parameters(self, obj_data):
-        """Calculate additional parameters for a single object."""
-        obj_data = obj_data.copy()
+    def _calculate_kinematics_in_ego_frame(self, df):
+        """计算车辆在自车坐标系下的运动学量,优化版本"""
+        # 创建副本避免修改原始数据,只复制必要的列以减少内存使用
+        needed_cols = ['simTime', 'ego_posH', 'ego_pitch', 'ego_roll', 'speedX', 'speedY', 'accelX', 'accelY', 'speedH']
+        df = df[needed_cols + [col for col in df.columns if col not in needed_cols]].copy()
 
-        # 确保数据按时间排序
-        obj_data = obj_data.sort_values(by="simTime").reset_index(drop=True)
-
-        obj_data["time_diff"] = obj_data["simTime"].diff()
-
-        # 处理可能的零时间差
-        zero_time_diff = obj_data["time_diff"] == 0
-        if zero_time_diff.any():
-            logger = LogManager().get_logger()
-            logger.warning(f"检测到零时间差: {sum(zero_time_diff)} 行")
-            # 将零时间差替换为最小非零时间差或一个小的默认值
-            min_non_zero = obj_data.loc[~zero_time_diff, "time_diff"].min() if (~zero_time_diff).any() else 0.01
-            obj_data.loc[zero_time_diff, "time_diff"] = min_non_zero
-
-        obj_data["lat_acc_diff"] = obj_data["lat_acc"].diff()
-        obj_data["lon_acc_diff"] = obj_data["lon_acc"].diff()
-        obj_data["yawrate_diff"] = obj_data["speedH"].diff()
-
-        obj_data["lat_acc_roc"] = (
-                obj_data["lat_acc_diff"] / obj_data["time_diff"]
-        ).replace([np.inf, -np.inf], [9999, -9999])
-        obj_data["lon_acc_roc"] = (
-                obj_data["lon_acc_diff"] / obj_data["time_diff"]
-        ).replace([np.inf, -np.inf], [9999, -9999])
-        obj_data["accelH"] = (
-                obj_data["yawrate_diff"] / obj_data["time_diff"]
-        ).replace([np.inf, -np.inf], [9999, -9999])
-
-        return obj_data
-
-    def _get_driver_ctrl_data(self, df):
-        """
-        Process and get driver control information.
-
-        Args:
-            df: A DataFrame containing driver control data.
-
-        Returns:
-            A dictionary of driver control info.
-        """
-        driver_ctrl_data = {
-            "time_list": df["simTime"].round(2).tolist(),
-            "frame_list": df["simFrame"].tolist(),
-            "brakePedal_list": (
-                (df["brakePedal"] * 100).tolist()
-                if df["brakePedal"].max() < 1
-                else df["brakePedal"].tolist()
-            ),
-            "throttlePedal_list": (
-                (df["throttlePedal"] * 100).tolist()
-                if df["throttlePedal"].max() < 1
-                else df["throttlePedal"].tolist()
-            ),
-            "steeringWheel_list": df["steeringWheel"].tolist(),
-        }
-        return driver_ctrl_data
+        # 确保有必要的列,使用向量化操作
+        for col in ['speedZ', 'accelZ']:
+            if col not in df.columns:
+                df[col] = 0.0
+
+        # 处理时间序列 - 使用向量化操作
+        df.sort_values(by="simTime", inplace=True)
+        df.reset_index(drop=True, inplace=True)
+        df["time_diff"] = df["simTime"].diff().replace(0, np.nan).fillna(method="bfill").fillna(0.01)
+
+        # 预计算所有三角函数值,避免重复计算
+        # ===== 1. 航向角处理 =====
+        ego_yaw_rad = np.deg2rad(90 - df['ego_posH'].values)  # 转换为数学坐标系
+        ego_pitch_rad = np.deg2rad(df['ego_pitch'].values)
+        ego_roll_rad = np.deg2rad(df['ego_roll'].values)
+        yaw_rad = np.deg2rad(df['ego_posH'].values)
+
+        # 预计算所有三角函数值
+        cy_math, sy_math = np.cos(ego_yaw_rad), np.sin(ego_yaw_rad)
+        cp, sp = np.cos(ego_pitch_rad), np.sin(ego_pitch_rad)
+        cr, sr = np.cos(ego_roll_rad), np.sin(ego_roll_rad)
+        cy, sy = np.cos(yaw_rad), np.sin(yaw_rad)
+
+        # 获取全局速度/加速度向量
+        vx = df['speedX'].values  # 东
+        vy = df['speedY'].values  # 北
+        vz = df['speedZ'].values  # 天
+        ax = df['accelX'].values
+        ay = df['accelY'].values
+        az = df['accelZ'].values
+
+        # ===== 2. 三维转换 =====
+        # 构建旋转矩阵 - 使用批量计算
+        r00 = cy_math * cp
+        r01 = cy_math * sp * sr - sy_math * cr
+        r02 = cy_math * sp * cr + sy_math * sr
+        r10 = sy_math * cp
+        r11 = sy_math * sp * sr + cy_math * cr
+        r12 = sy_math * sp * cr - cy_math * sr
+        r20 = -sp
+        r21 = cp * sr
+        r22 = cp * cr
+
+        # 转换速度向量 - 使用向量化计算代替einsum
+        df['lon_v_vehicle_3D'] = r00 * vx + r01 * vy + r02 * vz
+        df['lat_v_vehicle_3D'] = r10 * vx + r11 * vy + r12 * vz
+        df['vel_z_vehicle_3D'] = r20 * vx + r21 * vy + r22 * vz
+
+        # 转换加速度向量 - 使用向量化计算
+        df['lon_acc_vehicle_3D'] = r00 * ax + r01 * ay + r02 * az
+        df['lat_acc_vehicle_3D'] = r10 * ax + r11 * ay + r12 * az
+        df['acc_z_vehicle_3D'] = r20 * ax + r21 * ay + r22 * az
+
+        # ===== 3. 二维转换(与3D水平面一致)=====
+        # 使用原始航向角定义(0°=北,90°=东,顺时针为正)
+        # 使用向量化计算
+        df['lon_v_vehicle'] = vx * sy - vy * cy
+        df['lat_v_vehicle'] = vx * cy + vy * sy
+        df['lon_acc_vehicle'] = ax * sy - ay * cy
+        df['lat_acc_vehicle'] = ax * cy + ay * sy
+
+        # ===== 5. 计算其他运动学量 =====
+        # 速度大小 - 使用向量化计算
+        df["v"] = np.sqrt(vx ** 2 + vy ** 2)
+
+        # 加速度变化率 - 使用向量化计算
+        lat_acc_diff = np.diff(df["lat_acc_vehicle"].values, prepend=df["lat_acc_vehicle"].values[0])
+        lon_acc_diff = np.diff(df["lon_acc_vehicle"].values, prepend=df["lon_acc_vehicle"].values[0])
+        yawrate_diff = np.diff(df["speedH"].values, prepend=df["speedH"].values[0])
+        time_diff = df["time_diff"].values
+
+        # 使用numpy操作代替pandas操作
+        df["lat_acc_rate"] = np.where(time_diff == 0, 0, lat_acc_diff / time_diff)
+        df["lon_acc_rate"] = np.where(time_diff == 0, 0, lon_acc_diff / time_diff)
+        df["accelH"] = np.where(time_diff == 0, 0, yawrate_diff / time_diff)
+
+        # 替换无穷值
+        df.replace([np.inf, -np.inf], np.nan, inplace=True)
+        df.fillna(0, inplace=True)
+
+        return df
 
     def _get_report_info(self, df):
-        """Extract report information from the DataFrame."""
-        mileage = self._mileage_cal(df)
-        duration = self._duration_cal(df)
-        return {"mileage": mileage, "duration": duration}
+        # 提取里程与时长
+        return {
+            "mileage": self._mileage_cal(df),
+            "duration": self._duration_cal(df)
+        }
 
     def _mileage_cal(self, df):
-        """Calculate mileage based on the driving data."""
         if len(df) < 2:
-            return 0.0  # 数据不足,无法计算里程
-
+            return 0.0
         if df["travelDist"].nunique() == 1:
-            # 创建临时DataFrame进行计算,避免修改原始数据
             temp_df = df.copy()
             temp_df["time_diff"] = temp_df["simTime"].diff().fillna(0)
             temp_df["avg_speed"] = (temp_df["v"] + temp_df["v"].shift()).fillna(0) / 2
             temp_df["distance_increment"] = temp_df["avg_speed"] * temp_df["time_diff"] / 3.6
             temp_df["travelDist"] = temp_df["distance_increment"].cumsum().fillna(0)
-
-            mileage = round(temp_df["travelDist"].iloc[-1] - temp_df["travelDist"].iloc[0], 2)
-            return mileage
+            return round(temp_df["travelDist"].iloc[-1] - temp_df["travelDist"].iloc[0], 2)
         else:
-            # 如果travelDist已经有多个值,直接计算最大值和最小值的差
             return round(df["travelDist"].max() - df["travelDist"].min(), 2)
-        return 0.0  # Return 0 if travelDist is not valid
 
     def _duration_cal(self, df):
-        """Calculate duration of the driving data."""
         return df["simTime"].iloc[-1] - df["simTime"].iloc[0]
 
-    def process_ego_data(self, ego_data):
-        """处理自车数据:将东北天(ENU)坐标系下的速度/加速度转换为车辆坐标系(考虑yaw, pitch, roll)"""
-        '''
-        原字段	新字段名	描述
-        a_x_body	lon_acc_vehicle	车辆坐标系下的纵向加速度
-        a_y_body	lat_acc_vehicle	车辆坐标系下的横向加速度
-        a_z_body	acc_z_vehicle	车辆坐标系下的垂向加速度
-        v_x_body	lon_v_vehicle	车辆坐标系下的纵向速度
-        v_y_body	lat_v_vehicle	车辆坐标系下的横向速度
-        v_z_body	vel_z_vehicle	车辆坐标系下的垂向速度
-        posH	heading_rad	航向角(弧度)
-        pitch_rad	pitch_rad	俯仰角(弧度)
-        roll_rad	roll_rad	横滚角(弧度)
-        '''
-        logger = LogManager().get_logger()
-
-        if ego_data is None or len(ego_data) == 0:
-            logger.warning("自车数据为空,无法进行坐标系转换")
-            return ego_data
-
-        ego_data = ego_data.copy()
-        for col in ['speedZ', 'accelZ']:
-            if col not in ego_data.columns:
-                ego_data[col] = 0.0
-                logger.warning(f"自车数据中缺少列 '{col}',已将其填充为 0.0")
-
-        # 角度转为弧度(修正 posH 表示正北为 0° => 车辆朝正东为 0°)
-        ego_data['yaw_rad'] = np.deg2rad(90 - ego_data['posH'])
-        ego_data['pitch_rad'] = np.deg2rad(ego_data.get('pitch', 0))
-        ego_data['roll_rad'] = np.deg2rad(ego_data.get('roll', 0))
-
-        # 预计算三角函数(向量化)
-        cy = np.cos(ego_data['yaw_rad'])
-        sy = np.sin(ego_data['yaw_rad'])
-        cp = np.cos(ego_data['pitch_rad'])
-        sp = np.sin(ego_data['pitch_rad'])
-        cr = np.cos(ego_data['roll_rad'])
-        sr = np.sin(ego_data['roll_rad'])
-
-        # === 加速度(ENU → 车辆坐标系) ===
-        ego_data['lon_acc_vehicle'] = (ego_data['accelX'] * (cy * cp) +
-                                       ego_data['accelY'] * (cy * sp * sr - sy * cr) +
-                                       ego_data['accelZ'] * (cy * sp * cr + sy * sr))
-
-        ego_data['lat_acc_vehicle'] = (ego_data['accelX'] * (sy * cp) +
-                                       ego_data['accelY'] * (sy * sp * sr + cy * cr) +
-                                       ego_data['accelZ'] * (sy * sp * cr - cy * sr))
-
-        ego_data['acc_z_vehicle'] = (ego_data['accelX'] * (-sp) +
-                                     ego_data['accelY'] * (cp * sr) +
-                                     ego_data['accelZ'] * (cp * cr))
-
-        # === 速度(ENU → 车辆坐标系) ===
-        ego_data['lon_v_vehicle'] = (ego_data['speedX'] * (cy * cp) +
-                                     ego_data['speedY'] * (cy * sp * sr - sy * cr) +
-                                     ego_data['speedZ'] * (cy * sp * cr + sy * sr))
-
-        ego_data['lat_v_vehicle'] = (ego_data['speedX'] * (sy * cp) +
-                                     ego_data['speedY'] * (sy * sp * sr + cy * cr) +
-                                     ego_data['speedZ'] * (sy * sp * cr - cy * sr))
-
-        ego_data['vel_z_vehicle'] = (ego_data['speedX'] * (-sp) +
-                                     ego_data['speedY'] * (cp * sr) +
-                                     ego_data['speedZ'] * (cp * cr))
-
-        logger.info("完成车辆坐标系转换(考虑yaw/pitch/roll)")
-        return ego_data

+ 28 - 20
modules/lib/log_manager.py

@@ -1,19 +1,32 @@
 import logging
 import os
+import sys
 import threading
 from logging.handlers import QueueHandler, QueueListener
 from queue import Queue
+from pathlib import Path
+
+def resource_path():
+    """ 获取资源绝对路径,兼容开发环境和单文件模式 """
+    if hasattr(sys, '_MEIPASS'):
+        base_path = sys._MEIPASS
+    else:
+        base_path = os.path.abspath(".")
+    return base_path
 
 class LogManager:
     _instance = None
     _lock = threading.Lock()
     _configured = False  # 确保单例配置唯一性
-    
-    def __new__(cls, log_path="/home/kevin/kevin/zhaoyuan/zhaoyuan/log/app.log"):
+
+    def __new__(cls, log_path=None):
+        base_path = resource_path()
         with cls._lock:
             if not cls._instance:
                 cls._instance = super().__new__(cls)
                 # 路径处理逻辑
+                if log_path is None:
+                    log_path = Path(base_path) / "app.log"
                 cls._instance._full_path = log_path
                 cls._instance._init_logger()
             return cls._instance
@@ -23,7 +36,7 @@ class LogManager:
         """路径验证与创建"""
         default_path = os.path.join(os.getcwd(), "logs")
         target_path = path or default_path
-        
+
         try:
             os.makedirs(target_path, exist_ok=True)
             # 测试写入权限
@@ -48,7 +61,7 @@ class LogManager:
         return cleaned[:50]  # 限制文件名长度
 
     def _init_logger(self):
-        """初始化日志系统"""
+        """初始化日志系统 - 移除了控制台输出"""
         self.log_queue = Queue(-1)
         self.logger = logging.getLogger("GlobalLogger")
         self.logger.setLevel(logging.DEBUG)
@@ -58,25 +71,19 @@ class LogManager:
             formatter = logging.Formatter(
                 "[%(asctime)s][%(levelname)s][%(threadName)s][%(filename)s:%(lineno)d] %(message)s"
             )
-            
+
             # 文件处理器(自动UTF-8编码)
             file_handler = logging.FileHandler(
-                self._full_path, 
+                self._full_path,
                 encoding='utf-8',
                 delay=True  # 延迟打开文件直到实际写入
             )
             file_handler.setFormatter(formatter)
-            
-            # 控制台处理器(仅ERROR级别)
-            console_handler = logging.StreamHandler()
-            console_handler.setLevel(logging.ERROR)
-            console_handler.setFormatter(formatter)
 
-            # 异步监听器
+            # 异步监听器 - 仅包含文件处理器
             self.listener = QueueListener(
                 self.log_queue,
-                file_handler,
-                console_handler,
+                file_handler,  # 只保留文件处理器
                 respect_handler_level=True
             )
             self.listener.start()
@@ -98,18 +105,19 @@ class LogManager:
             cls._instance.listener.stop()
             cls._instance = None
 
+
 # 使用示例
 if __name__ == "__main__":
     # 自定义路径和文件名
     custom_logger = LogManager(
         log_path="/home/kevin/kevin/zhaoyuan/zhaoyuan/log/runtime.log"
     ).get_logger()
-    
-    custom_logger.info("Custom logger configured successfully")
-    
+
+    custom_logger.info("这条信息只会写入文件,不会显示在终端")  # 终端不再显示
+
     # 默认配置
     default_logger = LogManager().get_logger()
-    default_logger.warning("Using default configuration")
-    
+    default_logger.warning("警告信息也只会写入文件")
+
     # 安全关闭
-    LogManager.shutdown()
+    LogManager.shutdown()

+ 155 - 157
modules/lib/score.py

@@ -1,21 +1,20 @@
-  
-import json 
+import json
 
 from modules.lib.log_manager import LogManager
 
 
-class Score:  
-    def __init__(self, yaml_config, module_name: str ):
-        self.logger = LogManager().get_logger()  # 获取全局日志实例   
+class Score:
+    def __init__(self, yaml_config, module_name: str):
+        self.logger = LogManager().get_logger()  # 获取全局日志实例
         self.calculated_metrics = None
         self.config = yaml_config
         self.module_config = None
         self.module_name = module_name
         self.t_threshold = None
         self.process_config(self.config)
-        self.level_3_merics = self._extract_level_3_metrics(self.module_config) 
-        self.result = {}  
-    
+        self.level_3_merics = self._extract_level_3_metrics(self.module_config)
+        self.result = {}
+
     def process_config(self, config_dict):
         t_threshold = config_dict.get("T_threshold")
         if t_threshold is None:
@@ -24,7 +23,7 @@ class Score:
         module_keys = [key for key in config_dict if key != "T_threshold"]
         # if len(module_keys) != 1:
         #     raise ValueError("配置字典应包含且仅包含一个模块配置键")
-        
+
         # module_name = module_keys[0]
         module_config = config_dict[self.module_name]
         # print(f'模块名称:{module_name}')
@@ -38,6 +37,7 @@ class Score:
         self.logger.info(f'评分模型中模块名称:{self.module_name}')
         self.logger.info(f'评分模型中模块配置:{self.module_config}')
         self.logger.info(f'评分模型中T_threshold: {t_threshold}')
+
     def _extract_level_3_metrics(self, d):
         name = []
         for key, value in d.items():
@@ -46,198 +46,196 @@ class Score:
             elif key == 'name':  # 找到name键时,将值添加到列表
                 name.append(value)
         return name
-                         
-    def is_within_range(self, value, min_val, max_val):  
-        return min_val <= value <= max_val  
-  
-    def evaluate_level_3(self, metrics):  
-        result3 = {}  
-        name = metrics.get('name')  
-        priority = metrics.get('priority')  
-        max_val = metrics.get('max')  
+
+    def is_within_range(self, value, min_val, max_val):
+        return min_val <= value <= max_val
+
+    def evaluate_level_3(self, metrics):
+        result3 = {}
+        name = metrics.get('name')
+        priority = metrics.get('priority')
+        max_val = metrics.get('max')
         min_val = metrics.get('min')
-        
+
         self.level_3_merics.append(name)
-        print(f'name: {name}')  
-        print(f'self.calculated_metrics: {self.calculated_metrics}')
-        metric_value = self.calculated_metrics.get(name) 
-        print(f'metric_value: {metric_value}') 
-        result3[name] = {  
-            'result': True,  
-            'priority': priority 
-        } 
-        if metric_value is None:  
-            return result3  
-  
-        if not self.is_within_range(metric_value, min_val, max_val) and priority == 0:  
-            result3[name]['result'] = False  
-        elif not self.is_within_range(metric_value, min_val, max_val) and priority == 1:  
-            result3[name]['priority_1_count'] += 1  
-  
-        # Count priority 1 failures and override result if more than 3  
-       
-        priority_1_metrics = [v for v in result3.values() if v['priority'] == 1 and not v['result']]  
-        if len([m for m in priority_1_metrics if not m['result']]) > 3:  
+        # print(f'name: {name}')
+        # print(f'self.calculated_metrics: {self.calculated_metrics}')
+        metric_value = self.calculated_metrics.get(name)
+        # print(f'metric_value: {metric_value}')
+        result3[name] = {
+            'result': True,
+            'priority': priority
+        }
+        if metric_value is None:
+            return result3
+
+        if not self.is_within_range(metric_value, min_val, max_val) and priority == 0:
             result3[name]['result'] = False
-  
-        return result3  
-  
-    def evaluate_level_2(self, metrics):  
-        result2 = {}  
-        name = metrics.get('name')  
-        priority = metrics.get('priority') 
-        result2[name] = {}  
-  
-        for metric, sub_metrics in metrics.items():  
-            if metric not in ['name', 'priority']:  
-                result2[name].update(self.evaluate_level_3(sub_metrics))  
-  
-        # Aggregate results for level 2  config.T0 config.T1 config.T2
-        priority_0_count = sum(1 for v in result2[name].values() if v['priority'] == 0 and not v['result']) 
-        priority_1_count = sum(1 for v in result2[name].values() if v['priority'] == 1 and not v['result']) 
-        priority_2_count = sum(1 for v in result2[name].values() if v['priority'] == 2 and not v['result']) 
 
-        if priority_0_count > self.t_threshold['T0_threshold']:  
+        priority_1_metrics = [v for v in result3.values() if v['priority'] == 1 and not v['result']]
+        if len([m for m in priority_1_metrics if not m['result']]) > 3:
+            result3[name]['result'] = False
+
+        return result3
+
+    def evaluate_level_2(self, metrics):
+        result2 = {}
+        name = metrics.get('name')
+        priority = metrics.get('priority')
+        result2[name] = {}
+
+        for metric, sub_metrics in metrics.items():
+            if metric not in ['name', 'priority']:
+                result2[name].update(self.evaluate_level_3(sub_metrics))
+
+                # Aggregate results for level 2  config.T0 config.T1 config.T2
+        priority_0_count = sum(1 for v in result2[name].values() if v['priority'] == 0 and not v['result'])
+        priority_1_count = sum(1 for v in result2[name].values() if v['priority'] == 1 and not v['result'])
+        priority_2_count = sum(1 for v in result2[name].values() if v['priority'] == 2 and not v['result'])
+
+        if priority_0_count > self.t_threshold['T0_threshold']:
             result2[name]['result'] = False
-            
-        elif priority_1_count > self.t_threshold['T1_threshold']:  
-            for metric in result2[name].values():  
-                metric['result'] = False 
-        elif priority_2_count > self.t_threshold['T2_threshold']:  
-            for metric in result2[name].values():  
+
+        elif priority_1_count > self.t_threshold['T1_threshold']:
+            for metric in result2[name].values():
+                metric['result'] = False
+        elif priority_2_count > self.t_threshold['T2_threshold']:
+            for metric in result2[name].values():
                 metric['result'] = False
-        else:  
-            result2[name]['result'] = True  # Default to True unless overridden  
-        result2[name]['priority'] = priority 
-        result2[name]['priority_0_count'] = priority_0_count  
+        else:
+            result2[name]['result'] = True  # Default to True unless overridden
+        result2[name]['priority'] = priority
+        result2[name]['priority_0_count'] = priority_0_count
         result2[name]['priority_1_count'] = priority_1_count
-        result2[name]['priority_2_count'] = priority_2_count  
-  
-        return result2  
-  
-    def evaluate_level_1(self): 
+        result2[name]['priority_2_count'] = priority_2_count
+
+        return result2
+
+    def evaluate_level_1(self):
 
         name = self.module_config.get('name')
-        priority = self.module_config.get('priority') 
-        result1 = {} 
-        result1[name] = {}  
+        if name in ["efficient"]:
+            pass
+        priority = self.module_config.get('priority')
+        result1 = {}
+        result1[name] = {}
         for metric, metrics in self.module_config.items():
-            if metric not in ['name', 'priority']:  
+            if metric not in ['name', 'priority']:
                 result1[name].update(self.evaluate_level_2(metrics))
-                
+
         # Aggregate results for level 2  config.T0 config.T1 config.T2
-        priority_0_count = sum(1 for v in result1[name].values() if v['priority'] == 0 and not v['result']) 
-        priority_1_count = sum(1 for v in result1[name].values() if v['priority'] == 1 and not v['result']) 
-        priority_2_count = sum(1 for v in result1[name].values() if v['priority'] == 2 and not v['result']) 
+        priority_0_count = sum(1 for v in result1[name].values() if v['priority'] == 0 and not v['result'])
+        priority_1_count = sum(1 for v in result1[name].values() if v['priority'] == 1 and not v['result'])
+        priority_2_count = sum(1 for v in result1[name].values() if v['priority'] == 2 and not v['result'])
 
-        if priority_0_count > self.t_threshold['T0_threshold']:  
+        if priority_0_count > self.t_threshold['T0_threshold']:
             result1[name]['result'] = False
-            
-        elif priority_1_count > self.t_threshold['T1_threshold']:  
-            for metric in result1[name].values():  
-                metric['result'] = False 
-        elif priority_2_count > self.t_threshold['T2_threshold']:  
-            for metric in result1[name].values():  
+
+        elif priority_1_count > self.t_threshold['T1_threshold']:
+            for metric in result1[name].values():
+                metric['result'] = False
+        elif priority_2_count > self.t_threshold['T2_threshold']:
+            for metric in result1[name].values():
                 metric['result'] = False
-        else:  
-            result1[name]['result'] = True  # Default to True unless overridden  
-        result1[name]['priority'] = priority 
-        result1[name]['priority_0_count'] = priority_0_count  
+        else:
+            result1[name]['result'] = True  # Default to True unless overridden
+        result1[name]['priority'] = priority
+        result1[name]['priority_0_count'] = priority_0_count
         result1[name]['priority_1_count'] = priority_1_count
-        result1[name]['priority_2_count'] = priority_2_count  
+        result1[name]['priority_2_count'] = priority_2_count
+
+        return result1
 
-        return result1  
-  
     def evaluate(self, calculated_metrics):
-        self.calculated_metrics = calculated_metrics  
-        self.result = self.evaluate_level_1()  
-        return self.result 
+        self.calculated_metrics = calculated_metrics
+        self.result = self.evaluate_level_1()
+        return self.result
 
     def evaluate_single_case(self, case_name, priority, json_dict):
 
         name = case_name
-        result = {} 
-        result[name] = {}  
+        result = {}
+        result[name] = {}
         # print(json_dict)
         # Aggregate results for level 2  config.T0 config.T1 config.T2
-        priority_0_count = sum(1 for v in json_dict.values() if v['priority'] == 0 and not v['result']) 
-        priority_1_count = sum(1 for v in json_dict.values() if v['priority'] == 1 and not v['result']) 
-        priority_2_count = sum(1 for v in json_dict.values() if v['priority'] == 2 and not v['result']) 
+        priority_0_count = sum(1 for v in json_dict.values() if v['priority'] == 0 and not v['result'])
+        priority_1_count = sum(1 for v in json_dict.values() if v['priority'] == 1 and not v['result'])
+        priority_2_count = sum(1 for v in json_dict.values() if v['priority'] == 2 and not v['result'])
 
-        if priority_0_count > config.T0:  
+        if priority_0_count > config.T0:
             result[name]['result'] = False
-            
-        elif priority_1_count > config.T1:  
-            for metric in result[name].values():  
-                metric['result'] = False 
-        elif priority_2_count > config.T2:  
-            for metric in result[name].values():  
+
+        elif priority_1_count > config.T1:
+            for metric in result[name].values():
                 metric['result'] = False
-        else:  
-            result[name]['result'] = True  # Default to True unless overridden  
-        result[name]['priority'] = priority 
-        result[name]['priority_0_count'] = priority_0_count  
+        elif priority_2_count > config.T2:
+            for metric in result[name].values():
+                metric['result'] = False
+        else:
+            result[name]['result'] = True  # Default to True unless overridden
+        result[name]['priority'] = priority
+        result[name]['priority_0_count'] = priority_0_count
         result[name]['priority_1_count'] = priority_1_count
-        result[name]['priority_2_count'] = priority_2_count  
+        result[name]['priority_2_count'] = priority_2_count
         result[case_name].update(json_dict)
-        
-        return result  
+
+        return result
+
 
 def evaluate_single_case_back(case_name, priority, json_dict):
     """对单个案例进行评估"""
     result = {case_name: {}}
-    priority_counts = {priority: sum(1 for v in json_dict.values() if v['priority'] == priority and not v['result']) 
+    priority_counts = {priority: sum(1 for v in json_dict.values() if v['priority'] == priority and not v['result'])
                        for priority in [0, 1, 2]}
 
-    if priority_counts[0] > config.T0:  
+    if priority_counts[0] > config.T0:
         result[case_name]['result'] = False
-    elif priority_counts[1] > config.T1:  
-        for metric in result[case_name].values():  
-            metric['result'] = False 
-    elif priority_counts[2] > config.T2:  
-        for metric in result[case_name].values():  
+    elif priority_counts[1] > config.T1:
+        for metric in result[case_name].values():
+            metric['result'] = False
+    elif priority_counts[2] > config.T2:
+        for metric in result[case_name].values():
             metric['result'] = False
-    else:  
+    else:
         result[case_name]['result'] = True
 
     result[case_name].update(priority_counts)
     result[case_name].update(json_dict)  # 合并原始数据
-    return result 
-  
-  
-def main():  
-    # config_path = r'/home/kevin/kevin/zhaoyuan/evaluate_zhaoyuan/models/safety/config.yaml' 
-    # config_path1 = r'/home/kevin/kevin/zhaoyuan/evaluate_zhaoyuan/models/safety/config.json'  
-    # calculated_metrics = {  
-    #     'TTC': 1.0,  
-    #     'MTTC': 1.0,  
-    #     'THW': 1.0,  
-    #     'LonSD': 50.0,  
-    #     'LatSD': 3.0,  
-    #     'DRAC': 3.0,  
-    #     'BTN': -1000.0,  
-    #     'STN': 0.5,  
-    #     'collisionRisk': 5.0,  
-    #     'collisionSeverity': 2.0,  
-    # }  
-  
-    # # evaluator = Score(config_path, calculated_metrics) 
-    # evaluator = Score(config_path)  
-    # result = evaluator.evaluate(calculated_metrics) 
+    return result
+
+
+def main():
+    # config_path = r'/home/kevin/kevin/zhaoyuan/evaluate_zhaoyuan/models/safety/config.yaml'
+    # config_path1 = r'/home/kevin/kevin/zhaoyuan/evaluate_zhaoyuan/models/safety/config.json'
+    # calculated_metrics = {
+    #     'TTC': 1.0,
+    #     'MTTC': 1.0,
+    #     'THW': 1.0,
+    #     'LonSD': 50.0,
+    #     'LatSD': 3.0,
+    #     'DRAC': 3.0,
+    #     'BTN': -1000.0,
+    #     'STN': 0.5,
+    #     'collisionRisk': 5.0,
+    #     'collisionSeverity': 2.0,
+    # }
+
+    # # evaluator = Score(config_path, calculated_metrics)
+    # evaluator = Score(config_path)
+    # result = evaluator.evaluate(calculated_metrics)
     # with open(config_path1, 'w') as json_file:
     #     json.dump(result, json_file, indent=4)  # `indent` 参数用于美化输出
-    config_path = r'/home/kevin/kevin/zhaoyuan/zhaoyuan/models/caseMetric/single_config.yaml' 
-    config_path1 = r'/home/kevin/kevin/zhaoyuan/zhaoyuan/result/data_zhaoyuan/data_zhaoyuan_single_report.json'  
-    
-    # evaluator = Score(config_path, calculated_metrics) 
-     
+    config_path = r'/home/kevin/kevin/zhaoyuan/zhaoyuan/models/caseMetric/single_config.yaml'
+    config_path1 = r'/home/kevin/kevin/zhaoyuan/zhaoyuan/result/data_zhaoyuan/data_zhaoyuan_single_report.json'
+
+    # evaluator = Score(config_path, calculated_metrics)
+
     with open(config_path1, 'r') as file:
         data = json.load(file)
-         
+
         result = evaluate_single_case("case1", 0, data)
-        print(result)
-    
-   
-  
-if __name__ == '__main__':  
+        # print(result)
+
+
+if __name__ == '__main__':
     main()

File diff suppressed because it is too large
+ 591 - 1057
modules/metric/comfort.py


+ 6 - 10
modules/metric/efficient.py

@@ -64,12 +64,8 @@ class Efficient:
         return max_speed
 
     def _deviation_speed(self):
-        """计算速度方差
-
-        Returns:
-            float: 速度方差
-        """
-        deviation = self.ego_df['v'].var() * 3.6  # 转换为 km/h
+        """计算速度方差(单位 km²/h²) 改成标准差比较好"""
+        deviation = (self.ego_df['v'] * 3.6).std()  # m/s → km/h,再计算标准差
         self.calculated_value['deviationSpeed'] = deviation
         return deviation
 
@@ -229,21 +225,21 @@ class Efficient:
         # 首先检查road_speed_max列,其次检查speedLimit列,最后使用默认值
         if 'road_speed_max' in self.ego_df.columns:
             speed_limits = self.ego_df['road_speed_max'].values
-            self.logger.info("使用road_speed_max列作为道路限速信息")
+            # self.logger.info(f"使用road_speed_max列作为道路限速信息: {speed_limits} km/h")
         elif 'speedLimit' in self.ego_df.columns:
             speed_limits = self.ego_df['speedLimit'].values
-            self.logger.info("使用speedLimit列作为道路限速信息")
+            # self.logger.info("使用speedLimit列作为道路限速信息")
         else:
             # 默认限速转换为 m/s
             default_limit_ms = default_speed_limit / 3.6
             speed_limits = np.full_like(speeds, default_limit_ms)
-            self.logger.info(f"未找到道路限速信息,使用默认限速: {default_speed_limit} km/h")
+            # self.logger.info(f"未找到道路限速信息,使用默认限速: {default_speed_limit} km/h")
 
         # 确保限速值为m/s单位,如果数据是km/h需要转换
         # 假设如果限速值大于30,则认为是km/h单位,需要转换为m/s
         if np.mean(speed_limits) > 30:
             speed_limits = speed_limits / 3.6
-            self.logger.info("将限速单位从km/h转换为m/s")
+            # self.logger.info(f"将限速单位从km/h转换为m/s:  {speed_limits} m/s")
 
         # 计算每一帧的速度利用率
         ratios = np.divide(speeds, speed_limits,

File diff suppressed because it is too large
+ 431 - 207
modules/metric/function.py


+ 632 - 954
modules/metric/safety.py

@@ -1,7 +1,13 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 """
-安全指标计算模块
+安全指标计算模块(支持多目标物) - 场景感知版本
+优化要点:
+1. 重构代码结构,提高可读性
+2. 去除冗余代码,优化性能
+3. 保持原始计算逻辑不变
+4. 增加全面注释
+5. 优化数据处理流程
 """
 
 import os
@@ -10,256 +16,137 @@ import pandas as pd
 import math
 import scipy.integrate as spi
 from collections import defaultdict
-from typing import Dict, Any, List, Optional
+from typing import Dict, Any, List, Optional, Tuple
 from pathlib import Path
-import ast
 
 from modules.lib.score import Score
 from modules.lib.log_manager import LogManager
 from modules.lib.chart_generator import generate_safety_chart_data
 
-# 安全指标相关常量
-SAFETY_INFO = [
-    "simTime",
-    "simFrame",
-    "playerId",
-    "posX",
-    "posY",
-    "posH",
-    "speedX",
-    "speedY",
-    "accelX",
-    "accelY",
-    "v",
-    "type"
-]
-
-
-# ----------------------
-# 独立指标计算函数
-# ----------------------
+
+# ==================== 安全指标计算接口函数 ====================
+# 每个函数对应一个安全指标的计算入口
+# ------------------------------------------------------------
+
 def calculate_ttc(data_processed, plot_path) -> dict:
-    """计算TTC (Time To Collision)"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"TTC": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        ttc_value = safety.get_ttc_value()
-        # 只生成图表,数据导出由chart_generator处理
-        if safety.ttc_data:
-            safety.generate_metric_chart('TTC', plot_path)
-        LogManager().get_logger().info(f"安全指标[TTC]计算结果: {ttc_value}")
-        return {"TTC": ttc_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"TTC计算异常: {str(e)}", exc_info=True)
-        return {"TTC": None}
+    """计算碰撞时间(TTC)"""
+    return _calculate_metric('TTC', data_processed, plot_path)
 
 
 def calculate_mttc(data_processed, plot_path) -> dict:
-    """计算MTTC (Modified Time To Collision)"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"MTTC": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        mttc_value = safety.get_mttc_value()
-        if safety.mttc_data:
-            safety.generate_metric_chart('MTTC', plot_path)
-        LogManager().get_logger().info(f"安全指标[MTTC]计算结果: {mttc_value}")
-        return {"MTTC": mttc_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"MTTC计算异常: {str(e)}", exc_info=True)
-        return {"MTTC": None}
+    """计算修正碰撞时间(MTTC)"""
+    return _calculate_metric('MTTC', data_processed, plot_path)
 
 
 def calculate_thw(data_processed, plot_path) -> dict:
-    """计算THW (Time Headway)"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"THW": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        thw_value = safety.get_thw_value()
-        if safety.thw_data:
-            safety.generate_metric_chart('THW', plot_path)
-        LogManager().get_logger().info(f"安全指标[THW]计算结果: {thw_value}")
-        return {"THW": thw_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"THW计算异常: {str(e)}", exc_info=True)
-        return {"THW": None}
-
-
-def calculate_tlc(data_processed, plot_path) -> dict:
-    """计算TLC (Time to Line Crossing)"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"TLC": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        tlc_value = safety.get_tlc_value()
-        if safety.tlc_data:
-            safety.generate_metric_chart('TLC', plot_path)
-        LogManager().get_logger().info(f"安全指标[TLC]计算结果: {tlc_value}")
-        return {"TLC": tlc_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"TLC计算异常: {str(e)}", exc_info=True)
-        return {"TLC": None}
+    """计算车头时距(THW)"""
+    return _calculate_metric('THW', data_processed, plot_path)
 
 
 def calculate_ttb(data_processed, plot_path) -> dict:
-    """计算TTB (Time to Brake)"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"TTB": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        ttb_value = safety.get_ttb_value()
-        if safety.ttb_data:
-            safety.generate_metric_chart('TTB', plot_path)
-        LogManager().get_logger().info(f"安全指标[TTB]计算结果: {ttb_value}")
-        return {"TTB": ttb_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"TTB计算异常: {str(e)}", exc_info=True)
-        return {"TTB": None}
+    """计算制动时间(TTB)"""
+    return _calculate_metric('TTB', data_processed, plot_path)
 
 
 def calculate_tm(data_processed, plot_path) -> dict:
-    """计算TM (Time Margin)"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"TM": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        tm_value = safety.get_tm_value()
-        if safety.tm_data:
-            safety.generate_metric_chart('TM', plot_path)
-        LogManager().get_logger().info(f"安全指标[TM]计算结果: {tm_value}")
-        return {"TM": tm_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"TM计算异常: {str(e)}", exc_info=True)
-        return {"TM": None}
+    """计算时间裕度(TM)"""
+    return _calculate_metric('TM', data_processed, plot_path)
 
 
 def calculate_dtc(data_processed, plot_path) -> dict:
-    """计算DTC (Distance to Collision)"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"DTC": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        dtc_value = safety.get_dtc_value()
-        LogManager().get_logger().info(f"安全指标[DTC]计算结果: {dtc_value}")
-        return {"DTC": dtc_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"DTC计算异常: {str(e)}", exc_info=True)
-        return {"DTC": None}
+    """计算碰撞距离(DTC)"""
+    return _calculate_metric('DTC', data_processed, plot_path)
 
 
 def calculate_psd(data_processed, plot_path) -> dict:
-    """计算PSD (Potential Safety Distance)"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"PSD": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        psd_value = safety.get_psd_value()
-        LogManager().get_logger().info(f"安全指标[PSD]计算结果: {psd_value}")
-        return {"PSD": psd_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"PSD计算异常: {str(e)}", exc_info=True)
-        return {"PSD": None}
+    """计算预测安全距离比(PSD)"""
+    return _calculate_metric('PSD', data_processed, plot_path)
 
 
 def calculate_collisionrisk(data_processed, plot_path) -> dict:
-    """计算碰撞风险"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"collisionRisk": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        collision_risk_value = safety.get_collision_risk_value()
-        if safety.collision_risk_data:
-            safety.generate_metric_chart('collisionRisk', plot_path)
-        LogManager().get_logger().info(f"安全指标[collisionRisk]计算结果: {collision_risk_value}")
-        return {"collisionRisk": collision_risk_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"collisionRisk计算异常: {str(e)}", exc_info=True)
-        return {"collisionRisk": None}
+    """计算碰撞风险(collisionRisk)"""
+    return _calculate_metric('collisionRisk', data_processed, plot_path)
 
 
 def calculate_lonsd(data_processed, plot_path) -> dict:
-    """计算纵向安全距离"""
-    safety = SafetyCalculator(data_processed)
-    lonsd_value = safety.get_lonsd_value()
-    if safety.lonsd_data:
-        safety.generate_metric_chart('LonSD', plot_path)
-    LogManager().get_logger().info(f"安全指标[LonSD]计算结果: {lonsd_value}")
-    return {"LonSD": lonsd_value}
+    """计算纵向安全距离(LonSD)"""
+    return _calculate_metric('LonSD', data_processed, plot_path)
 
 
 def calculate_latsd(data_processed, plot_path) -> dict:
-    """计算横向安全距离"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"LatSD": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        latsd_value = safety.get_latsd_value()
-        if safety.latsd_data:
-            # 只生成图表,数据导出由chart_generator处理
-            safety.generate_metric_chart('LatSD', plot_path)
-        LogManager().get_logger().info(f"安全指标[LatSD]计算结果: {latsd_value}")
-        return {"LatSD": latsd_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"LatSD计算异常: {str(e)}", exc_info=True)
-        return {"LatSD": None}
+    """计算横向安全距离(LatSD)"""
+    return _calculate_metric('LatSD', data_processed, plot_path)
 
 
 def calculate_btn(data_processed, plot_path) -> dict:
-    """计算制动威胁数"""
-    if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"BTN": None}
-    try:
-        safety = SafetyCalculator(data_processed)
-        btn_value = safety.get_btn_value()
-        if safety.btn_data:
-            # 只生成图表,数据导出由chart_generator处理
-            safety.generate_metric_chart('BTN', plot_path)
-        LogManager().get_logger().info(f"安全指标[BTN]计算结果: {btn_value}")
-        return {"BTN": btn_value}
-    except Exception as e:
-        LogManager().get_logger().error(f"BTN计算异常: {str(e)}", exc_info=True)
-        return {"BTN": None}
+    """计算制动威胁数(BTN)"""
+    return _calculate_metric('BTN', data_processed, plot_path)
 
 
 def calculate_collisionseverity(data_processed, plot_path) -> dict:
-    """计算碰撞严重性"""
+    """计算碰撞严重性(collisionSeverity)"""
+    return _calculate_metric('collisionSeverity', data_processed, plot_path)
+
+
+def _calculate_metric(metric_name, data_processed, plot_path) -> dict:
+    """安全指标计算的通用处理函数"""
+    key_name = metric_name
+    result_key = {metric_name: None}
+
+    # 检查输入数据有效性
     if data_processed is None or not hasattr(data_processed, 'object_df'):
-        return {"collisionSeverity": None}
+        return result_key
+
     try:
         safety = SafetyCalculator(data_processed)
-        collision_severity_value = safety.get_collision_severity_value()
-        if safety.collision_severity_data:
-            # 只生成图表,数据导出由chart_generator处理
-            safety.generate_metric_chart('collisionSeverity', plot_path)
-        LogManager().get_logger().info(f"安全指标[collisionSeverity]计算结果: {collision_severity_value}")
-        return {"collisionSeverity": collision_severity_value}
+
+        # 特殊处理 collisionRisk 和 collisionSeverity 指标
+        if metric_name.lower() == 'collisionrisk':
+            metric_value = safety.get_collision_risk_value()
+        elif metric_name.lower() == 'collisionseverity':
+            metric_value = safety.get_collision_severity_value()
+        else:
+            # 其他指标使用原有逻辑
+            metric_value = getattr(safety, f'get_{metric_name.lower()}_value')()
+
+        # 生成图表数据
+        metric_data = getattr(safety, f'{metric_name.lower()}_data', None)
+        if metric_data:
+            safety.generate_metric_chart(metric_name, plot_path)
+
+        LogManager().get_logger().info(f"安全指标[{metric_name}]计算结果: {metric_value}")
+        return {metric_name: metric_value}
     except Exception as e:
-        LogManager().get_logger().error(f"collisionSeverity计算异常: {str(e)}", exc_info=True)
-        return {"collisionSeverity": None}
+        LogManager().get_logger().error(f"{metric_name}计算异常: {str(e)}", exc_info=True)
+        return result_key
+
 
+# ==================== 安全指标注册与批处理类 ====================
+# 用于根据配置批量执行安全指标计算
+# ------------------------------------------------------------
 
 class SafetyRegistry:
-    """安全指标注册器"""
+    """安全指标注册器,根据配置动态注册和执行指标计算"""
 
     def __init__(self, data_processed, plot_path):
         self.logger = LogManager().get_logger()
         self.data = data_processed
         self.plot_path = plot_path
-        # 检查safety_config是否为空
+
+        # 检查安全配置
         if not hasattr(data_processed, 'safety_config') or not data_processed.safety_config:
             self.logger.warning("安全配置为空,跳过安全指标计算")
             self.safety_config = {}
             self.metrics = []
             self._registry = {}
             return
+
         self.safety_config = data_processed.safety_config.get("safety", {})
         self.metrics = self._extract_metrics(self.safety_config)
         self._registry = self._build_registry()
 
     def _extract_metrics(self, config_node: dict) -> list:
-        """从配置中提取指标名称"""
+        """从配置中递归提取所有指标名称"""
         metrics = []
 
         def _recurse(node):
@@ -270,11 +157,11 @@ class SafetyRegistry:
                     _recurse(v)
 
         _recurse(config_node)
-        self.logger.info(f'评比的安全指标列表:{metrics}')
+        self.logger.info(f'评比的安全指标列表: {metrics}')
         return metrics
 
     def _build_registry(self) -> dict:
-        """构建指标函数注册表"""
+        """构建指标名称到计算函数的映射"""
         registry = {}
         for metric_name in self.metrics:
             func_name = f"calculate_{metric_name.lower()}"
@@ -285,9 +172,8 @@ class SafetyRegistry:
         return registry
 
     def batch_execute(self) -> dict:
-        """批量执行指标计算"""
+        """批量执行所有注册的安全指标计算"""
         results = {}
-        # 如果配置为空或没有注册的指标,直接返回空结果
         if not hasattr(self, 'safety_config') or not self.safety_config or not self._registry:
             self.logger.info("安全配置为空或无注册指标,返回空结果")
             return results
@@ -299,18 +185,19 @@ class SafetyRegistry:
             except Exception as e:
                 self.logger.error(f"{name} 执行失败: {str(e)}", exc_info=True)
                 results[name] = None
-        self.logger.info(f'安全指标计算结果:{results}')
+        self.logger.info(f'安全指标计算结果: {results}')
         return results
 
 
 class SafeManager:
-    """安全指标管理类"""
+    """安全管理器,封装安全指标计算流程"""
 
     def __init__(self, data_processed, plot_path):
         self.data = data_processed
         self.logger = LogManager().get_logger()
         self.plot_path = plot_path
-        # 检查safety_config是否为空
+
+        # 初始化安全指标注册器
         if not hasattr(data_processed, 'safety_config') or not data_processed.safety_config:
             self.logger.warning("安全配置为空,跳过安全指标计算初始化")
             self.registry = None
@@ -318,845 +205,636 @@ class SafeManager:
             self.registry = SafetyRegistry(self.data, self.plot_path)
 
     def report_statistic(self):
-        """计算并报告安全指标结果"""
-        # 如果registry为None,直接返回空字典
+        """生成安全指标统计报告"""
         if self.registry is None:
             self.logger.info("安全指标管理器未初始化,返回空结果")
             return {}
+        return self.registry.batch_execute()
 
-        safety_result = self.registry.batch_execute()
-        return safety_result
 
+# ==================== 安全指标计算核心类 ====================
+# 包含所有安全指标的具体计算实现
+# ------------------------------------------------------------
 
 class SafetyCalculator:
-    """安全指标计算类 - 兼容Safe类风格"""
+    """安全指标计算器,实现各种安全指标的具体计算"""
+
+    DEFAULT_VALUES = {
+        'TTC': 10.0, 'MTTC': 3.3, 'THW': 2.5,
+        'TTB': 10.0, 'TM': 10.0, 'DTC': 10.0, 'PSD': 10.0,
+        'LonSD': 100.0, 'LatSD': 2.0, 'BTN': 0.0,
+        'collisionRisk': 0.0, 'collisionSeverity': 0.0
+    }
 
     def __init__(self, data_processed):
         self.logger = LogManager().get_logger()
         self.data_processed = data_processed
-
         self.df = data_processed.object_df.copy()
-        self.ego_df = data_processed.ego_data.copy()  # 使用copy()避免修改原始数据
+        self.ego_df = data_processed.ego_data.copy()
         self.obj_id_list = data_processed.obj_id_list
-        self.obj_df = self.df[self.df['playerId'] == 2].copy().reset_index(drop=True) if len(
-            self.obj_id_list) > 1 else pd.DataFrame(columns=self.ego_df.columns)  # 使用copy()避免修改原始数据
-        self.metric_list = [
-            'TTC', 'MTTC', 'THW', 'TLC', 'TTB', 'TM', 'DTC', 'PSD', 'LonSD', 'LatSD', 'BTN', 'collisionRisk',
-            'collisionSeverity'
-        ]
 
-        # 初始化默认值
-        self.calculated_value = {
-            "TTC": 10.0,
-            "MTTC": 10.0,
-            "THW": 10.0,
-            "TLC": 10.0,
-            "TTB": 10.0,
-            "TM": 10.0,
-            # "MPrTTC": 10.0,
-            "DTC": 10.0,
-            "PSD": 10.0,
-            "LatSD": 3.0,
-            "BTN": 1.0,
-            "collisionRisk": 0.0,
-            "collisionSeverity": 0.0,
-        }
+        # 初始化配置参数
+        self._init_config_params()
 
+        # 初始化数据结构和指标存储
+        self._init_data_structures()
+
+        # 执行安全参数计算
+        if self.obj_id_list:
+            self.logger.info("开始执行安全参数计算,目标物数量: %d", len(self.obj_id_list) - 1)
+            self._calculate_safety_parameters()
+            self.logger.info("安全参数计算完成")
+        else:
+            self.logger.info("没有目标物,跳过安全参数计算")
+            self.empty_flag = True
+
+    def _init_config_params(self):
+        """从配置中初始化车辆参数"""
+        config = self.data_processed.vehicle_config
+        self.rho = config.get("RHO", 1.5)  # 默认反应时间1.5秒
+        self.ego_accel_max = config.get("EGO_ACCEL_MAX", 3.0)  # 默认最大加速度3.0 m/s²
+        self.obj_decel_max = config.get("OBJ_DECEL_MAX", 3.0)  # 默认目标最大减速度3.0 m/s²
+        self.ego_decel_min = config.get("EGO_DECEL_MIN", 4.0)  # 默认自车最小减速度4.0 m/s²
+        self.ego_decel_lon_max = config.get("EGO_DECEL_LON_MAX", 4.0)  # 默认纵向最大减速度4.0 m/s²
+        self.ego_decel_lat_max = config.get("EGO_DECEL_LAT_MAX", 3.0)  # 默认横向最大减速度3.0 m/s²
+        self.ego_width = config.get("CAR_WIDTH", 2.0)  # 默认车辆宽度2.0米
+        self.ego_length = config.get("CAR_LENGTH", 4.5)  # 默认车辆长度4.5米
+        self.vehicle_length = config.get("VEHICLE_LENGTH", 4.5)  # 默认车辆长度4.5米
+        self.vehicle_width = config.get("VEHICLE_WIDTH", 2.0)  # 默认车辆宽度2.0米
+        self.pedestrian_length = config.get("PEDESTRIAN_LENGTH", 0.5)  # 默认行人长度0.5米
+        self.pedestrian_width = config.get("PEDESTRIAN_WIDTH", 0.5)  # 默认行人宽度0.5米
+
+        # 计算派生参数
+        self.max_deceleration = self.ego_decel_lon_max
+        self.ego_decel_max = np.sqrt(self.ego_decel_lon_max ** 2 + self.ego_decel_lat_max ** 2)
+        self.ped_width = self.pedestrian_width
+        self.ped_length = self.pedestrian_length
+
+    def _init_data_structures(self):
+        """初始化数据存储结构"""
         self.time_list = self.ego_df['simTime'].values.tolist()
         self.frame_list = self.ego_df['simFrame'].values.tolist()
-        self.collisionRisk = 0
-        self.empty_flag = True
-
-        # 初始化数据存储列表
-        self.ttc_data = []
-        self.mttc_data = []
-        self.thw_data = []
-        self.tlc_data = []
-        self.ttb_data = []
-        self.tm_data = []
-        self.lonsd_data = []
-        self.latsd_data = []
-        self.btn_data = []
-        self.collision_risk_data = []
-        self.collision_severity_data = []
-
-        # 初始化安全事件记录表
-        self.unsafe_events_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
-
-        # 设置输出目录
+        self.empty_flag = True  # 标记是否有有效目标
+
+        # 初始化指标数据存储
+        self._init_metric_storage()
+
+        # 创建输出目录
         self.output_dir = os.path.join(os.getcwd(), 'data')
         os.makedirs(self.output_dir, exist_ok=True)
 
-        self.logger.info("SafetyCalculator初始化完成,场景中包含自车的目标物一共为: %d", len(self.obj_id_list))
-
-        if len(self.obj_id_list) > 1:
-            self.unsafe_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
-            self.unsafe_time_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
-            self.unsafe_dist_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
-            self.unsafe_acce_drac_df = pd.DataFrame(
-                columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
-            self.unsafe_acce_xtn_df = pd.DataFrame(
-                columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
-            self.unsafe_prob_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
-            self.most_dangerous = {}
-            self.pass_percent = {}
-            self.logger.info("开始执行安全参数计算 _safe_param_cal_new")
-            self._safe_param_cal_new()
-            self.logger.info("安全参数计算完成")
+    def _init_metric_storage(self):
+        """初始化所有指标的存储列表"""
+        metrics = [
+            'ttc', 'mttc', 'thw', 'ttb', 'tm', 'lonsd', 'latsd', 'btn',
+            'collision_risk', 'collision_severity'
+        ]
+        for metric in metrics:
+            setattr(self, f'{metric}_data', [])
 
-    def _safe_param_cal_new(self):
-        self.logger.debug("进入 _safe_param_cal_new 方法")
-        # 直接复用Safe类的实现
-        Tc = 0.3  # 安全距离
-
-        rho = self.data_processed.vehicle_config["RHO"]
-        ego_accel_max = self.data_processed.vehicle_config["EGO_ACCEL_MAX"]
-        obj_decel_max = self.data_processed.vehicle_config["OBJ_DECEL_MAX"]
-        ego_decel_min = self.data_processed.vehicle_config["EGO_DECEL_MIN"]
-        ego_decel_lon_max = self.data_processed.vehicle_config["EGO_DECEL_LON_MAX"]
-        ego_decel_lat_max = self.data_processed.vehicle_config["EGO_DECEL_LAT_MAX"]
-        ego_decel_max = np.sqrt(ego_decel_lon_max ** 2 + ego_decel_lat_max ** 2)
-        # TEMP_COMMENT: x_relative_start_dist 注释开始
-        x_relative_start_dist = abs(self.ego_df["posY"] - self.df['posY'])
-        # x_relative_start_dist = self.ego_df['x_relative_dist']
-
-        # 设置安全指标阈值
-        self.safety_thresholds = {
-            'TTC': {'min': 1.5, 'max': None},  # TTC小于1.5秒视为危险
-            'MTTC': {'min': 1.5, 'max': None},  # MTTC小于1.5秒视为危险
-            'THW': {'min': 1.0, 'max': None},  # THW小于1.0秒视为危险
-            'LonSD': {'min': None, 'max': None},  # 根据实际情况设置
-            'LatSD': {'min': 0.5, 'max': None},  # LatSD小于0.5米视为危险
-            'BTN': {'min': None, 'max': 0.8},  # BTN大于0.8视为危险
-            'collisionRisk': {'min': None, 'max': 30},  # 碰撞风险大于30%视为危险
-            'collisionSeverity': {'min': None, 'max': 30}  # 碰撞严重性大于30%视为危险
-        }
+    def _calculate_safety_parameters(self):
+        """核心安全参数计算方法"""
+        # 预处理:构建按帧组织的目标字典
+        obj_dict = self._preprocess_object_data()
+        df_list = []
+        EGO_PLAYER_ID = 1
+        found_valid_target = False
+
+        # 逐帧处理数据
+        for frame_num in self.frame_list:
+            try:
+                ego_data = obj_dict[frame_num][EGO_PLAYER_ID]
+            except KeyError:
+                continue
 
+            frame_targets = self._process_frame_targets(frame_num, ego_data, obj_dict, EGO_PLAYER_ID)
+
+            if frame_targets:
+                found_valid_target = True
+                df_fnum = pd.DataFrame(frame_targets)
+                df_list.append(df_fnum)
+
+        # 合并处理结果
+        self._postprocess_results(df_list, found_valid_target)
+
+    def _preprocess_object_data(self):
+        """预处理目标数据为按帧组织的字典"""
         obj_dict = defaultdict(dict)
         obj_data_dict = self.df.to_dict('records')
         for item in obj_data_dict:
             obj_dict[item['simFrame']][item['playerId']] = item
+        return obj_dict
 
-        df_list = []
-        EGO_PLAYER_ID = 1
+    def _process_frame_targets(self, frame_num, ego_data, obj_dict, ego_id):
+        """处理单帧内的所有目标物"""
+        frame_targets = []
+        vx_ego = ego_data.get('lon_v_vehicle', 0)
+        vy_ego = ego_data.get('lat_v_vehicle', 0)
+        self.lane_width = ego_data.get('lane_width', 3.75)
 
-        for frame_num in self.frame_list:
-            ego_data = obj_dict[frame_num][EGO_PLAYER_ID]
-            v1 = ego_data['v']
-            x1 = ego_data['posX']
-            y1 = ego_data['posY']
-            h1 = ego_data['posH']
-            laneOffset = ego_data["laneOffset"]
-
-            v_x1 = ego_data['speedX']
-            v_y1 = ego_data['speedY']
-            a_x1 = ego_data['accelX']
-            a_y1 = ego_data['accelY']
-            a1 = np.sqrt(a_x1 ** 2 + a_y1 ** 2)
-
-            for playerId in self.obj_id_list:
-                if playerId == EGO_PLAYER_ID:
-                    continue
-                try:
-                    obj_data = obj_dict[frame_num][playerId]
-                except KeyError:
-                    continue
-
-                x2 = obj_data['posX']
-                y2 = obj_data['posY']
-                dist = self.dist(x1, y1, x2, y2)
-                obj_data['dist'] = dist
-
-                v_x2 = obj_data['speedX']
-                v_y2 = obj_data['speedY']
-                v2 = obj_data['v']
-                a_x2 = obj_data['accelX']
-                a_y2 = obj_data['accelY']
-                a2 = np.sqrt(a_x2 ** 2 + a_y2 ** 2)
-
-                dx, dy = x2 - x1, y2 - y1
-
-                # 计算目标车相对于自车的方位角
-                # beta = math.atan2(dy, dx)
-
-                # 将全局坐标系下的相对位置向量转换到自车坐标系
-                # 自车坐标系:车头方向为x轴正方向,车辆左侧为y轴正方向
-                '''
-                你现在的 posH 是 正北为 0°,顺时针为正角度。
-                但是我们在做二维旋转时,Python/数学里默认是:
-
-                0° 是 X 轴(正东)
-
-                顺时针是负角,逆时针是正角
-
-                所以,要将你的 posH(正北为 0)转换成数学中以正东为 0° 的角度'''
-                h1_rad = math.radians(90 - h1)  # 转换为与x轴的夹角
-
-                # 坐标变换
-                lon_d = dx * math.cos(h1_rad) + dy * math.sin(h1_rad)  # 纵向距离(前为正,后为负)
-                lat_d = abs(-dx * math.sin(h1_rad) + dy * math.cos(h1_rad))  # 横向距离(取绝对值)
-
-                obj_dict[frame_num][playerId]['lon_d'] = lon_d
-                obj_dict[frame_num][playerId]['lat_d'] = lat_d
-
-                # if lon_d > 100 or lon_d < -5 or lat_d > 4:
-                #     continue
-
-                self.empty_flag = False
-
-                vx, vy = v_x1 - v_x2, v_y1 - v_y2
-                ax, ay = a_x2 - a_x1, a_y2 - a_y1
-                relative_v = np.sqrt(vx ** 2 + vy ** 2)
-
-                v_ego_p = self._cal_v_ego_projection(dx, dy, v_x1, v_y1)
-                v_obj_p = self._cal_v_ego_projection(dx, dy, v_x2, v_y2)
-                vrel_projection_in_dist = self._cal_v_projection(dx, dy, vx, vy)
-                arel_projection_in_dist = self._cal_a_projection(dx, dy, vx, vy, ax, ay, x1, y1, x2, y2, v_x1, v_y1,
-                                                                 v_x2, v_y2)
-
-                obj_dict[frame_num][playerId]['vrel_projection_in_dist'] = vrel_projection_in_dist
-                obj_dict[frame_num][playerId]['arel_projection_in_dist'] = arel_projection_in_dist
-                obj_dict[frame_num][playerId]['v_ego_projection_in_dist'] = v_ego_p
-                obj_dict[frame_num][playerId]['v_obj_projection_in_dist'] = v_obj_p
-
-                obj_type = obj_data['type']
-
-                TTC = self._cal_TTC(dist, vrel_projection_in_dist) if abs(vrel_projection_in_dist) > 1e-6 else None
-                MTTC = self._cal_MTTC(dist, vrel_projection_in_dist, arel_projection_in_dist)
-                THW = self._cal_THW(dist, v_ego_p) if abs(v_ego_p) > 1e-6 else None
-                TLC = self._cal_TLC(v1, h1, laneOffset)
-                TTB = self._cal_TTB(x_relative_start_dist, relative_v, ego_decel_max)
-                TM = self._cal_TM(x_relative_start_dist, v2, a2, v1, a1)
-                DTC = self._cal_DTC(vrel_projection_in_dist, arel_projection_in_dist, rho)
-                PSD = self._cal_PSD(x_relative_start_dist, v1, ego_decel_lon_max)
-
-                LonSD = self._cal_longitudinal_safe_dist(v_ego_p, v_obj_p, rho, ego_accel_max, ego_decel_min,
-                                                         obj_decel_max)
-
-                lat_dist = 0.5
-                v_right = v1
-                v_left = v2
-                a_right_lat_brake_min = 1
-                a_left_lat_brake_min = 1
-                a_lat_max = 5
-
-                LatSD = self._cal_lateral_safe_dist(lat_dist, v_right, v_left, rho, a_right_lat_brake_min,
-                                                    a_left_lat_brake_min, a_lat_max)
-
-                # 使用自车坐标系下的纵向加速度
-                lon_a1 = a_x1 * math.cos(h1_rad) + a_y1 * math.sin(h1_rad)
-                lon_a2 = a_x2 * math.cos(h1_rad) + a_y2 * math.sin(h1_rad)
-                lon_a = abs(lon_a1 - lon_a2)
-                lon_d = max(0.1, lon_d)  # 确保纵向距离为正
-                lon_v = v_x1 * math.cos(h1_rad) + v_y1 * math.sin(h1_rad)
-                BTN = self._cal_BTN_new(lon_a1, lon_a, lon_d, lon_v, ego_decel_lon_max)
-
-                # 使用自车坐标系下的横向加速度
-                lat_a1 = -a_x1 * math.sin(h1_rad) + a_y1 * math.cos(h1_rad)
-                lat_a2 = -a_x2 * math.sin(h1_rad) + a_y2 * math.cos(h1_rad)
-                lat_a = abs(lat_a1 - lat_a2)
-                lat_v = -v_x1 * math.sin(h1_rad) + v_y1 * math.cos(h1_rad)
-
-                obj_dict[frame_num][playerId]['lat_v_rel'] = lat_v - (
-                        -v_x2 * math.sin(h1_rad) + v_y2 * math.cos(h1_rad))
-                obj_dict[frame_num][playerId]['lon_v_rel'] = lon_v - (v_x2 * math.cos(h1_rad) + v_y2 * math.sin(h1_rad))
-
-                TTC = None if (TTC is None or TTC < 0) else TTC
-                MTTC = None if (MTTC is None or MTTC < 0) else MTTC
-                THW = None if (THW is None or THW < 0) else THW
-                TLC = None if (TLC is None or TLC < 0) else TLC
-                TTB = None if (TTB is None or TTB < 0) else TTB
-                TM = None if (TM is None or TM < 0) else TM
-                DTC = None if (DTC is None or DTC < 0) else DTC
-                PSD = None if (PSD is None or PSD < 0) else PSD
-
-                obj_dict[frame_num][playerId]['TTC'] = TTC
-                obj_dict[frame_num][playerId]['MTTC'] = MTTC
-                obj_dict[frame_num][playerId]['THW'] = THW
-                obj_dict[frame_num][playerId]['TLC'] = TLC
-                obj_dict[frame_num][playerId]['TTB'] = TTB
-                obj_dict[frame_num][playerId]['TM'] = TM
-                obj_dict[frame_num][playerId]['DTC'] = DTC
-                obj_dict[frame_num][playerId]['PSD'] = PSD
-                obj_dict[frame_num][playerId]['LonSD'] = LonSD
-                obj_dict[frame_num][playerId]['LatSD'] = LatSD
-                obj_dict[frame_num][playerId]['BTN'] = abs(BTN)
-
-                # TTC要进行筛选,否则会出现nan或者TTC过大的情况
-                if not TTC or TTC > 4000:  # threshold = 4258.41
-                    collisionSeverity = 0
-                    pr_death = 0
-                    collisionRisk = 0
-                else:
-                    result, error = spi.quad(self._normal_distribution, 0, TTC - Tc)
-                    collisionSeverity = 1 - result
-                    pr_death = self._death_pr(obj_type, vrel_projection_in_dist)
-                    collisionRisk = 0.4 * pr_death + 0.6 * collisionSeverity
-
-                obj_dict[frame_num][playerId]['collisionSeverity'] = collisionSeverity * 100
-                obj_dict[frame_num][playerId]['pr_death'] = pr_death * 100
-                obj_dict[frame_num][playerId]['collisionRisk'] = collisionRisk * 100
-
-            df_fnum = pd.DataFrame(obj_dict[frame_num].values())
-            df_list.append(df_fnum)
-
-        df_safe = pd.concat(df_list)
-        col_list = ['simTime', 'simFrame', 'playerId',
-                    'TTC', 'MTTC', 'THW', 'TLC', 'TTB', 'TM', 'DTC', 'PSD', 'LonSD', 'LatSD', 'BTN',
-                    'collisionSeverity', 'pr_death', 'collisionRisk']
-        self.df_safe = df_safe[col_list].reset_index(drop=True)
-
-    def _cal_v_ego_projection(self, dx, dy, v_x1, v_y1):
-        # 计算 AB 连线的向量 AB
-        # dx = x2 - x1
-        # dy = y2 - y1
-
-        # 计算 AB 连线的模长 |AB|
-        AB_mod = math.sqrt(dx ** 2 + dy ** 2)
-
-        # 计算 AB 连线的单位向量 U_AB
-        U_ABx = dx / AB_mod
-        U_ABy = dy / AB_mod
-
-        # 计算 A 在 AB 连线上的速度 V1_on_AB
-        V1_on_AB = v_x1 * U_ABx + v_y1 * U_ABy
-
-        return V1_on_AB
-
-    def _cal_v_projection(self, dx, dy, vx, vy):
-        # 计算 AB 连线的向量 AB
-        # dx = x2 - x1
-        # dy = y2 - y1
-
-        # 计算 AB 连线的模长 |AB|
-        AB_mod = math.sqrt(dx ** 2 + dy ** 2)
-
-        # 计算 AB 连线的单位向量 U_AB
-        U_ABx = dx / AB_mod
-        U_ABy = dy / AB_mod
-
-        # 计算 A 相对于 B 的速度 V_relative
-        # vx = vx1 - vx2
-        # vy = vy1 - vy2
-
-        # 计算 A 相对于 B 在 AB 连线上的速度 V_on_AB
-        V_on_AB = vx * U_ABx + vy * U_ABy
-
-        return V_on_AB
-
-    def _cal_a_projection(self, dx, dy, vx, vy, ax, ay, x1, y1, x2, y2, v_x1, v_y1, v_x2, v_y2):
-        # 计算 AB 连线的向量 AB
-        # dx = x2 - x1
-        # dy = y2 - y1
-
-        # 计算 θ
-        V_mod = math.sqrt(vx ** 2 + vy ** 2)
-        AB_mod = math.sqrt(dx ** 2 + dy ** 2)
-        if V_mod == 0 or AB_mod == 0:
-            return 0
-
-        cos_theta = (vx * dx + vy * dy) / (V_mod * AB_mod)
-        theta = math.acos(cos_theta)
-
-        # 计算 AB 连线的模长 |AB|
-        AB_mod = math.sqrt(dx ** 2 + dy ** 2)
-
-        # 计算 AB 连线的单位向量 U_AB
-        U_ABx = dx / AB_mod
-        U_ABy = dy / AB_mod
-
-        # 计算 A 相对于 B 的加速度 a_relative
-        # ax = ax1 - ax2
-        # ay = ay1 - ay2
-
-        # 计算 A 相对于 B 在 AB 连线上的加速度 a_on_AB
-        a_on_AB = ax * U_ABx + ay * U_ABy
-
-        VA = np.array([v_x1, v_y1])
-        VB = np.array([v_x2, v_y2])
-        D_A = np.array([x1, y1])
-        D_B = np.array([x2, y2])
-        V_r = VA - VB
-        V = np.linalg.norm(V_r)
-        w = self._cal_relative_angular_v(theta, D_A, D_B, VA, VB)
-        a_on_AB_back = self._calculate_derivative(a_on_AB, w, V, theta)
-        return a_on_AB_back
-
-    # 计算相对加速度
-    def _calculate_derivative(self, a, w, V, theta):
-        # 计算(V×cos(θ))'的值
-        # derivative = a * math.cos(theta) - w * V * math.sin(theta)theta
-        derivative = a - w * V * math.sin(theta)
-        return derivative
-
-    def _cal_relative_angular_v(self, theta, A, B, VA, VB):
-        dx = A[0] - B[0]
-        dy = A[1] - B[1]
-        dvx = VA[0] - VB[0]
-        dvy = VA[1] - VB[1]
-        # (dx * dvy - dy * dvx)
-        angular_velocity = math.sqrt(dvx ** 2 + dvy ** 2) * math.sin(theta) / math.sqrt(dx ** 2 + dy ** 2)
-        return angular_velocity
+        for player_id in self.obj_id_list:
+            if player_id == ego_id:
+                continue
 
-    def _death_pr(self, obj_type, v_relative):
-        if obj_type == 5:
-            p_death = 1 / (1 + np.exp(7.723 - 0.15 * v_relative))
+            try:
+                obj_data = obj_dict[frame_num][player_id]
+            except KeyError:
+                continue
+
+            # 计算目标物安全指标
+            result = self._calculate_target_metrics(ego_data, obj_data, vx_ego, vy_ego)
+            obj_data.update(result)
+
+            frame_targets.append(obj_data)
+
+        return frame_targets
+
+    def _calculate_target_metrics(self, ego_data, obj_data, vx_ego, vy_ego):
+        """计算单个目标物的安全指标"""
+        # 提取基本参数
+        obj_type = obj_data.get('type', 0)
+        lon_d = obj_data.get('x_relative_start_dist', 0)
+        lat_d = obj_data.get('y_relative_start_dist', 0)
+        vx_obj = obj_data.get('lon_v_vehicle', 0)
+        vy_obj = obj_data.get('lat_v_vehicle', 0)
+
+        # 计算相对运动参数
+        vx_rel = vx_obj - vx_ego
+        vy_rel = vy_obj - vy_ego
+        dist = math.sqrt(lon_d ** 2 + lat_d ** 2)
+
+        # 计算航向角差异
+        h1 = ego_data['posH']
+        h2 = obj_data.get('posH', h1)
+        heading_diff = abs(h1 - h2)
+        if heading_diff > 180:
+            heading_diff = 360 - heading_diff
+
+        # 计算相对加速度
+        ax_ego = ego_data.get('lon_acc_vehicle', 0)
+        ay_ego = ego_data.get('lat_acc_vehicle', 0)
+        ax_obj = obj_data.get('lon_acc_vehicle', 0)
+        ay_obj = obj_data.get('lat_acc_vehicle', 0)
+        ax_rel_ego = ax_obj - ax_ego
+        ay_rel_ego = ay_obj - ay_ego
+
+        # 计算投影
+        vrel_projection = self._cal_v_projection_using_relative(lon_d, lat_d, vx_rel, vy_rel)
+        accl_projection = self._cal_a_projection_using_relative(lon_d, lat_d, ax_rel_ego, ay_rel_ego)
+
+        # 检查目标相关性
+        is_relevant = self._is_relevant_target(lat_d, lon_d, vy_rel, heading_diff, obj_type)
+
+        # 检查过滤条件
+        passed_conflict = self._has_passed_conflict_point(lon_d, lat_d, obj_type, heading_diff)
+        moving_away = self._is_moving_away(
+            (vx_ego, vy_ego), (vx_obj, vy_obj), lon_d, lat_d, obj_type, heading_diff
+        )
+        skip_risk_calculation = passed_conflict and (moving_away or obj_data.get('v', 0) < 0.05)
+
+        # 计算指标
+        if not is_relevant or skip_risk_calculation:
+            return self._get_default_metrics(obj_type)
         else:
-            p_death = 1 / (1 + np.exp(8.192 - 0.12 * v_relative))
-        return p_death
+            return self._calculate_relevant_metrics(
+                dist, vrel_projection, accl_projection,
+                lon_d, lat_d, vx_ego, vy_ego, vx_rel, ax_rel_ego,
+                heading_diff, obj_type, ego_data, obj_data
+            )
+
+    def _get_default_metrics(self, obj_type):
+        """获取默认指标值"""
+        return {
+            'TTC': self.DEFAULT_VALUES['TTC'],
+            'MTTC': self.DEFAULT_VALUES['MTTC'],
+            'THW': self.DEFAULT_VALUES['THW'] if obj_type != 5 else None,
+            'TTB': self.DEFAULT_VALUES['TTB'] if obj_type != 5 else None,
+            'TM': self.DEFAULT_VALUES['TM'] if obj_type != 5 else None,
+            'PSD': self.DEFAULT_VALUES['PSD'],
+            'DTC': self.DEFAULT_VALUES['DTC'],
+            'LonSD': self.DEFAULT_VALUES['LonSD'] if obj_type != 5 else None,
+            'LatSD': self.DEFAULT_VALUES['LatSD'] if obj_type != 5 else None,
+            'BTN': self.DEFAULT_VALUES['BTN'] if obj_type != 5 else None,
+            'collisionSeverity': self.DEFAULT_VALUES['collisionSeverity'],
+            'pr_death': 0,
+            'collisionRisk': self.DEFAULT_VALUES['collisionRisk']
+        }
 
-    def _cal_collisionRisk_level(self, obj_type, v_relative, collisionSeverity):
-        if obj_type == 5:
-            p_death = 1 / (1 + np.exp(7.723 - 0.15 * v_relative))
+    def _calculate_relevant_metrics(self, dist, vrel_projection, accl_projection,
+                                    lon_d, lat_d, vx_ego, vy_ego, vx_rel, ax_rel_ego,
+                                    heading_diff, obj_type, ego_data, obj_data):
+        """计算相关目标的指标"""
+        Tc = 0.3  # 反应时间
+
+        # 核心指标计算
+        metrics = {
+            'TTC': self._cal_TTC(dist, vrel_projection),
+            'MTTC': self._cal_MTTC(dist, vrel_projection, ego_data.get('lon_acc_vehicle', 0)),
+            'THW': self._cal_THW(lon_d, vx_ego),
+            'TTB': self._cal_TTB(dist, vrel_projection, self.ego_decel_max) if obj_type != 5 else None,
+            'TM': self._cal_TM(vrel_projection, obj_data.get('v', 0),
+                               obj_data.get('lon_acc_vehicle', 0),
+                               ego_data.get('v', 0),
+                               ego_data.get('lon_acc_vehicle', 0)) if obj_type != 5 else None,
+            'PSD': self._cal_PSD(dist, vrel_projection, self.max_deceleration,
+                                 heading_diff, vx_ego, vy_ego, obj_type, lon_d, lat_d),
+            'DTC': self._cal_DTC(vx_rel, ax_rel_ego, self.rho) if abs(heading_diff) < 30 else self.DEFAULT_VALUES[
+                'DTC'],
+            'LonSD': self._cal_longitudinal_safe_dist(vx_ego, vx_rel, self.rho,
+                                                      self.ego_decel_min,
+                                                      self.obj_decel_max) if obj_type != 5 else None,
+            'LatSD': self._cal_lateral_safe_dist(abs(lat_d), vy_ego, self.ego_width,
+                                                 self.lane_width, self.ego_decel_lat_max) if obj_type != 5 else None,
+            'BTN': self._cal_BTN_new(ego_data.get('lon_acc_vehicle', 0),
+                                     ego_data.get('lon_acc_vehicle', 0),
+                                     abs(lon_d), vx_ego, self.ego_decel_lon_max) if obj_type != 5 else None
+        }
+
+        # 处理碰撞风险相关指标
+        if metrics['TTC'] is None or metrics['TTC'] > 4000:
+            metrics.update({
+                'collisionSeverity': 0,
+                'pr_death': 0,
+                'collisionRisk': 0
+            })
         else:
-            p_death = 1 / (1 + np.exp(8.192 - 0.12 * v_relative))
-        collisionRisk = 0.4 * p_death + 0.6 * collisionSeverity
-        return collisionRisk
+            try:
+                result, _ = spi.quad(self._normal_distribution, 0, metrics['TTC'] - Tc)
+                collisionSeverity = 1 - result
+                pr_death = self._death_pr(obj_type, vrel_projection)
+                collisionRisk = self._cal_collisionRisk_level(obj_type, vrel_projection, collisionSeverity)
+                metrics.update({
+                    'collisionSeverity': collisionSeverity * 100,
+                    'pr_death': pr_death * 100,
+                    'collisionRisk': collisionRisk * 100
+                })
+            except Exception as e:
+                self.logger.error(f"碰撞风险计算错误: {e}")
+                metrics.update({
+                    'collisionSeverity': 0,
+                    'pr_death': 0,
+                    'collisionRisk': 0
+                })
 
-    # 求两车之间当前距离
-    def dist(self, x1, y1, x2, y2):
-        dist = np.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
-        return dist
+        return metrics
 
-    def generate_metric_chart(self, metric_name: str, plot_path: Path) -> None:
-        """生成指标图表
+    def _postprocess_results(self, df_list, found_valid_target):
+        """后处理计算结果"""
+        self.empty_flag = not found_valid_target
+
+        # 合并所有帧数据
+        if df_list:
+            self.df_safe = pd.concat(df_list)
+            col_list = ['simTime', 'simFrame', 'playerId',
+                        'TTC', 'MTTC', 'THW', 'TTB', 'TM', 'DTC', 'PSD', 'LonSD', 'LatSD', 'BTN',
+                        'collisionSeverity', 'pr_death', 'collisionRisk']
+            self.df_safe = self.df_safe[col_list].reset_index(drop=True)
+        else:
+            self.df_safe = pd.DataFrame()
+            self.empty_flag = True
+
+        self.logger.info(f"处理完成,找到有效目标: {not self.empty_flag}")
+
+    # ==================== 核心计算方法 ====================
+
+    def _cal_v_projection_using_relative(self, lon_d, lat_d, vx_rel, vy_rel):
+        """计算速度投影(自车坐标系)"""
+        dist = math.sqrt(lon_d ** 2 + lat_d ** 2)
+        if dist < 1e-6:
+            return 0.0
+        U_ABx = lon_d / dist
+        U_ABy = lat_d / dist
+        return vx_rel * U_ABx + vy_rel * U_ABy
+
+    def _cal_a_projection_using_relative(self, lon_d, lat_d, ax_rel, ay_rel):
+        """计算加速度投影(自车坐标系)"""
+        dist = math.sqrt(lon_d ** 2 + lat_d ** 2)
+        if dist < 1e-6:
+            return 0.0
+        U_ABx = lon_d / dist
+        U_ABy = lat_d / dist
+        return ax_rel * U_ABx + ay_rel * U_ABy
+
+    def _cal_TTC(self, dist, vrel_projection):
+        """计算碰撞时间(TTC)"""
+        if vrel_projection >= 0:  # 不接近
+            return None
+        return dist / abs(vrel_projection)
+
+    def _cal_MTTC(self, dist, vrel_projection, a_ego):
+        """计算修正的碰撞时间(MTTC)"""
+        if vrel_projection >= 0 or dist <= 0 or a_ego >= 0:
+            return None
 
-        Args:
-            metric_name: 指标名称
-        """
         try:
-            # 确定输出目录
-            if plot_path is None:
-                self.output_dir = os.path.join(os.getcwd(), 'data')
-                os.makedirs(self.output_dir, exist_ok=True)
-            else:
-                self.output_dir = plot_path
+            if abs(a_ego) < 1e-6:
+                return dist / abs(vrel_projection) if abs(vrel_projection) > 1e-6 else None
 
-            # 调用图表生成函数
-            chart_path = generate_safety_chart_data(self, metric_name, self.output_dir)
-            if chart_path:
-                self.logger.info(f"{metric_name}图表已生成: {chart_path}")
-            else:
-                self.logger.warning(f"{metric_name}图表生成失败")
+            discriminant = vrel_projection ** 2 + 2 * a_ego * dist
+            if discriminant < 0:
+                return None
+
+            t1 = (-vrel_projection + math.sqrt(discriminant)) / a_ego
+            t2 = (-vrel_projection - math.sqrt(discriminant)) / a_ego
 
+            valid_times = [t for t in (t1, t2) if t > 0]
+            return min(valid_times) if valid_times else None
         except Exception as e:
-            self.logger.error(f"生成{metric_name}图表失败: {str(e)}", exc_info=True)
-
-    # TTC (time to collision)
-    def _cal_TTC(self, dist, vrel_projection_in_dist):
-        if vrel_projection_in_dist == 0:
-            return math.inf
-        TTC = dist / vrel_projection_in_dist
-        return TTC
-
-    def _cal_MTTC(self, dist, vrel_projection_in_dist, arel_projection_in_dist):
-        MTTC = math.nan
-        if arel_projection_in_dist != 0:
-            tmp = vrel_projection_in_dist ** 2 + 2 * arel_projection_in_dist * dist
-            if tmp < 0:
-                return math.nan
-            t1 = (-1 * vrel_projection_in_dist - math.sqrt(tmp)) / arel_projection_in_dist
-            t2 = (-1 * vrel_projection_in_dist + math.sqrt(tmp)) / arel_projection_in_dist
-            if t1 > 0 and t2 > 0:
-                if t1 >= t2:
-                    MTTC = t2
-                elif t1 < t2:
-                    MTTC = t1
-            elif t1 > 0 and t2 <= 0:
-                MTTC = t1
-            elif t1 <= 0 and t2 > 0:
-                MTTC = t2
-        if arel_projection_in_dist == 0 and vrel_projection_in_dist > 0:
-            MTTC = dist / vrel_projection_in_dist
-        return MTTC
-
-    # THW (time headway)
-    def _cal_THW(self, dist, v_ego_projection_in_dist):
-        if not v_ego_projection_in_dist:
-            THW = None
-        else:
-            THW = dist / v_ego_projection_in_dist
-        return THW
-
-    # TLC (time to line crossing)
-    def _cal_TLC(self, ego_v, ego_yaw, laneOffset):
-        TLC = laneOffset / ego_v / np.sin(ego_yaw) if ((ego_v != 0) and (np.sin(ego_yaw) != 0)) else 10.0
-        if TLC < 0:
-            TLC = None
-        return TLC
-
-    def _cal_TTB(self, x_relative_start_dist, relative_v, ego_decel_max):
-        if len(x_relative_start_dist):
+            self.logger.warning(f"MTTC计算错误: {e}")
             return None
-        if (ego_decel_max == 0) or (relative_v == 0):
-            return self.calculated_value["TTB"]
-        else:
-            x_relative_start_dist0 = x_relative_start_dist.tolist()[0]
-            TTB = (x_relative_start_dist0 + relative_v * relative_v / 2 / ego_decel_max) / relative_v
-            return TTB
 
-    def _cal_TM(self, x_relative_start_dist, v2, a2, v1, a1):
-        if len(x_relative_start_dist):
+    def _cal_THW(self, lon_d, vx_ego):
+        """计算车头时距(THW)"""
+        if vx_ego is None or vx_ego <= 0 or lon_d is None or lon_d <= 0:
             return None
-        if (a2 == 0) or (v1 == 0):
-            return self.calculated_value["TM"]
-        if a1 == 0:
+        return lon_d / vx_ego
+
+    def _cal_TTB(self, dist, vrel_projection, ego_decel_max):
+        """计算制动时间(TTB)"""
+        if vrel_projection is None or ego_decel_max is None or vrel_projection >= 0:
             return None
-        x_relative_start_dist0 = x_relative_start_dist.tolist()[0]
-        TM = (x_relative_start_dist0 + v2 ** 2 / (2 * a2) - v1 ** 2 / (2 * a1)) / v1
-        return TM
-
-    def velocity(self, v_x, v_y):
-        v = math.sqrt(v_x ** 2 + v_y ** 2) * 3.6
-        return v
-
-    def _cal_longitudinal_safe_dist(self, v_ego_p, v_obj_p, rho, ego_accel_max, ego_decel_min, ego_decel_max):
-        lon_dist_min = v_ego_p * rho + ego_accel_max * (rho ** 2) / 2 + (v_ego_p + rho * ego_accel_max) ** 2 / (
-                2 * ego_decel_min) - v_obj_p ** 2 / (2 * ego_decel_max)
-        return lon_dist_min
-
-    def _cal_lateral_safe_dist(self, lat_dist, v_right, v_left, rho, a_right_lat_brake_min,
-                               a_left_lat_brake_min, a_lat_max):
-        # 检查除数是否为零
-        if a_right_lat_brake_min == 0 or a_left_lat_brake_min == 0:
-            return self._default_value('LatSD')  # 返回默认值
-
-        v_right_rho = v_right + rho * a_lat_max
-        v_left_rho = v_left + rho * a_lat_max
-        dist_min = lat_dist + (
-                (v_right + v_right_rho) * rho / 2
-                + v_right_rho ** 2 / a_right_lat_brake_min / 2
-                + ((v_left + v_right_rho) * rho / 2)
-                + v_left_rho ** 2 / a_left_lat_brake_min / 2
-        )
-        return dist_min
 
-    def _cal_DTC(self, v_on_dist, a_on_dist, t):
-        if a_on_dist == 0:
+        try:
+            TTB = (dist + vrel_projection ** 2 / (2 * ego_decel_max) / vrel_projection)
+            return TTB if TTB > 0 and not (np.isinf(TTB) or np.isnan(TTB)) else None
+        except Exception as e:
+            self.logger.warning(f"计算TTB时出错: {e}")
             return None
-        DTC = v_on_dist * t + v_on_dist ** 2 / a_on_dist
-        return DTC
 
-    def _cal_PSD(self, x_relative_start_dist, v1, ego_decel_lon_max):
-        if v1 == 0:
+    def _cal_TM(self, vrel_projection, v2, a2, v1, a1):
+        """计算时间裕度(TM)"""
+        if (vrel_projection is None or v2 is None or a2 is None or a2 <= 0 or
+                v1 is None or a1 is None or a1 <= 0 or vrel_projection >= 0):
             return None
-        else:
-            if len(x_relative_start_dist) > 0:
-                x_relative_start_dist0 = x_relative_start_dist.tolist()[0]
-                PSD = x_relative_start_dist0 * 2 * ego_decel_lon_max / v1
-                return PSD
-            else:
-                return None
 
-    # DRAC (decelerate required avoid collision)
-    def _cal_DRAC(self, dist, vrel_projection_in_dist, len1, len2, width1, width2, o_x1, o_x2):
-        dist_length = dist - (len2 / 2 - o_x2 + len1 / 2 + o_x1)  # 4.671
-        if dist_length < 0:
-            dist_width = dist - (width2 / 2 + width1 / 2)
-            if dist_width < 0:
-                return math.inf
-            else:
-                d = dist_width
-        else:
-            d = dist_length
-        DRAC = vrel_projection_in_dist ** 2 / (2 * d)
-        return DRAC
+        try:
+            TM = (v2 ** 2 / (2 * a2) - v1 ** 2 / (2 * a1)) / v1
+            return TM if TM > 0 and not (np.isinf(TM) or np.isnan(TM)) else None
+        except Exception as e:
+            self.logger.warning(f"计算TM时出错: {e}")
+            return None
+
+    def _cal_PSD(self, dist, vrel_projection, max_decel, heading_diff, vx_ego, vy_ego, obj_type, relative_x,
+                 relative_y):
+        """计算预测安全距离比(PSD)"""
+        abs_heading_diff = abs(heading_diff)
+
+        # 根据航向差确定场景类型
+        if abs_heading_diff < 45:
+            # 纵向场景
+            D = abs(relative_x)
+            v_dir = abs(vx_ego)
+            min_stopping_dist = (v_dir ** 2) / (2 * self.ego_decel_lon_max)
+        elif 45 <= abs_heading_diff <= 135:
+            # 横向场景
+            D = abs(relative_y)
+            v_dir = abs(vy_ego)
+            if obj_type == 5:  # 行人
+                reaction_dist = abs(relative_y) * self.rho
+                braking_dist = (v_dir ** 2) / (2 * self.ego_decel_lat_max)
+                min_stopping_dist = reaction_dist + braking_dist
+            else:  # 车辆
+                min_stopping_dist = (v_dir ** 2) / (2 * self.ego_decel_lat_max)
+        else:  # 对向场景
+            return self.DEFAULT_VALUES['PSD']
+
+        # 处理特殊情况
+        if min_stopping_dist < 1e-3:
+            return float('inf') if D > 0.1 else 0.0
+
+        return D / min_stopping_dist
+
+    def _cal_DTC(self, vx_rel, ax_rel_ego, t):
+        """计算碰撞距离(DTC)"""
+        if vx_rel >= 0:  # 没有接近风险
+            return self.DEFAULT_VALUES['DTC']
+        try:
+            speed = abs(vx_rel)
+            reaction_distance = speed * t
+            braking_distance = (speed ** 2) / (2 * self.ego_decel_lon_max)
+            dtc = reaction_distance + braking_distance
+            return max(0, dtc)
+        except Exception as e:
+            self.logger.warning(f"DTC计算错误: {e}")
+            return self.DEFAULT_VALUES['DTC']
+
+    def _cal_longitudinal_safe_dist(self, v_ego, v_lead_rel, rho, decel_ego, decel_lead):
+        """计算纵向安全距离(LonSD)"""
+        try:
+            v_lead = v_ego + v_lead_rel
+            reaction_distance = v_ego * rho
+            braking_distance_ego = v_ego ** 2 / (2 * decel_ego) if v_ego > 0 else 0
+            braking_distance_lead = v_lead ** 2 / (2 * decel_lead) if v_lead > 0 else 0
+            safe_dist = reaction_distance + braking_distance_ego - braking_distance_lead
+            return max(2.0, safe_dist)
+        except Exception as e:
+            self.logger.warning(f"计算纵向安全距离错误: {e}")
+            return 25.0
+
+    def _cal_lateral_safe_dist(self, lat_dist, v_ego_lat, vehicle_width, lane_width, decel_lat_max):
+        """计算横向安全距离(LatSD)"""
+        try:
+            available_space = lane_width - vehicle_width
+            base_margin = 0.5
+            speed_margin = 0.1 * abs(v_ego_lat)
+            safe_dist = min(base_margin + speed_margin, available_space / 2)
+            return max(base_margin, safe_dist)
+        except Exception as e:
+            self.logger.warning(f"计算横向安全距离错误: {e}")
+            return 1.0
 
-    # BTN (brake threat number)
     def _cal_BTN_new(self, lon_a1, lon_a, lon_d, lon_v, ego_decel_lon_max):
-        BTN = (lon_a1 + lon_a - lon_v ** 2 / (2 * lon_d)) / ego_decel_lon_max  # max_ay为此车可实现的最大纵向加速度,目前为本次实例里的最大值
-        return BTN
-
-    # STN (steer threat number)
-    def _cal_STN_new(self, ttc, lat_a1, lat_a, lat_d, lat_v, ego_decel_lat_max, width1, width2):
-        STN = (lat_a1 + lat_a + 2 / ttc ** 2 * (lat_d + abs(ego_decel_lat_max * lat_v) * (
-                width1 + width2) / 2 + abs(lat_v * ttc))) / ego_decel_lat_max
-        return STN
-
-    # BTN (brake threat number)
-    def cal_BTN(self, a_y1, ay, dy, vy, max_ay):
-        BTN = (a_y1 + ay - vy ** 2 / (2 * dy)) / max_ay  # max_ay为此车可实现的最大纵向加速度,目前为本次实例里的最大值
-        return BTN
-
-    # STN (steer threat number)
-    def cal_STN(self, ttc, a_x1, ax, dx, vx, max_ax, width1, width2):
-        STN = (a_x1 + ax + 2 / ttc ** 2 * (dx + np.sign(max_ax * vx) * (width1 + width2) / 2 + vx * ttc)) / max_ax
-        return STN
-
-    # 追尾碰撞风险
-    def _normal_distribution(self, x):
-        mean = 1.32
-        std_dev = 0.26
-        return (1 / (math.sqrt(std_dev * 2 * math.pi))) * math.exp(-0.5 * (x - mean) ** 2 / std_dev)
+        """计算制动威胁数(BTN)"""
+        if lon_d is None or lon_d <= 0 or ego_decel_lon_max is None or ego_decel_lon_max <= 0:
+            return None
+        try:
+            return (lon_a1 + lon_a - (lon_v ** 2) / (2 * lon_d)) / ego_decel_lon_max
+        except:
+            return None
 
-    def continuous_group(self, df):
-        time_list = df['simTime'].values.tolist()
-        frame_list = df['simFrame'].values.tolist()
+    def _death_pr(self, obj_type, v_relative):
+        """计算死亡概率"""
+        if obj_type == 5:  # 行人
+            return 1 / (1 + np.exp(7.723 - 0.15 * v_relative))
+        else:  # 车辆
+            return 1 / (1 + np.exp(8.192 - 0.12 * v_relative))
 
-        group_time = []
-        group_frame = []
-        sub_group_time = []
-        sub_group_frame = []
+    def _cal_collisionRisk_level(self, obj_type, v_relative, collisionSeverity):
+        """计算碰撞风险等级"""
+        p_death = self._death_pr(obj_type, v_relative)
+        return 0.4 * p_death + 0.6 * collisionSeverity
 
-        for i in range(len(frame_list)):
-            if not sub_group_time or frame_list[i] - frame_list[i - 1] <= 1:
-                sub_group_time.append(time_list[i])
-                sub_group_frame.append(frame_list[i])
-            else:
-                group_time.append(sub_group_time)
-                group_frame.append(sub_group_frame)
-                sub_group_time = [time_list[i]]
-                sub_group_frame = [frame_list[i]]
-
-        group_time.append(sub_group_time)
-        group_frame.append(sub_group_frame)
-        group_time = [g for g in group_time if len(g) >= 2]
-        group_frame = [g for g in group_frame if len(g) >= 2]
-
-        # 输出图表值
-        time = [[g[0], g[-1]] for g in group_time]
-        frame = [[g[0], g[-1]] for g in group_frame]
-
-        unfunc_time_df = pd.DataFrame(time, columns=['start_time', 'end_time'])
-        unfunc_frame_df = pd.DataFrame(frame, columns=['start_frame', 'end_frame'])
-
-        unfunc_df = pd.concat([unfunc_time_df, unfunc_frame_df], axis=1)
-        return unfunc_df
-        # 统计最危险的指标
-
-    def _safe_statistic_most_dangerous(self):
-        min_list = ['TTC', 'MTTC', 'THW', 'TLC', 'TTB', 'LonSD', 'LatSD', 'TM', 'PSD']
-        max_list = ['DTC', 'BTN', 'collisionRisk', 'collisionSeverity']
-        result = {}
-        for metric in min_list:
-            if metric in self.metric_list:
-                if metric in self.df.columns:
-                    val = self.df[metric].min()
-                    result[metric] = float(val) if pd.notnull(val) else self._default_value(metric)
-                else:
-                    result[metric] = self._default_value(metric)
-        for metric in max_list:
-            if metric in self.metric_list:
-                if metric in self.df.columns:
-                    val = self.df[metric].max()
-                    result[metric] = float(val) if pd.notnull(val) else self._default_value(metric)
-                else:
-                    result[metric] = self._default_value(metric)
-        return result
-
-    def _safe_no_obj_statistic(self):
-        # 仅有自车时的默认值
-        result = {metric: self._default_value(metric) for metric in self.metric_list}
-        return result
-
-    def _default_value(self, metric):
-        # 统一默认值
-        defaults = {
-            'TTC': 10.0,
-            'MTTC': 4.2,
-            'THW': 2.1,
-            'TLC': 10.0,
-            'TTB': 10.0,
-            'TM': 10.0,
-            'DTC': 10.0,
-            'PSD': 10.0,
-            'LonSD': 10.0,
-            'LatSD': 2.0,
-            'BTN': 1.0,
-            'collisionRisk': 0.0,
-            'collisionSeverity': 0.0
-        }
-        return defaults.get(metric, None)
+    def _normal_distribution(self, x, mean=1.32, std_dev=0.26):
+        """正态分布函数用于碰撞严重性计算"""
+        return (1 / (math.sqrt(std_dev * 2 * math.pi))) * math.exp(-0.5 * (x - mean) ** 2 / std_dev)
 
-    def report_statistic(self):
-        if len(self.obj_id_list) == 1:
-            safety_result = self._safe_no_obj_statistic()
-        else:
-            safety_result = self._safe_statistic_most_dangerous()
-        evaluator = Score(self.data_processed.safety_config)
-        result = evaluator.evaluate(safety_result)
-        print("\n[安全性表现及得分情况]")
-        return result
+    # ==================== 目标物过滤方法 ====================
 
-    def get_ttc_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('TTC')
-        ttc_values = self.df_safe['TTC'].dropna()
-        ttc_value = float(ttc_values.min()) if not ttc_values.empty else self._default_value('TTC')
+    def _is_relevant_target(self, lat_d, lon_d, v_lat, heading_diff, obj_type):
+        """判断目标物是否相关"""
+        abs_lat_d = abs(lat_d)
+        abs_heading_diff = abs(heading_diff)
+        lane_half_width = self.lane_width / 2
+        ego_half_width = self.ego_width / 2
+        ped_half_width = self.ped_width / 2
+        safe_width = ego_half_width + ped_half_width + 0.2
 
-        # 收集TTC数据
-        if not ttc_values.empty:
-            self.ttc_data = []
-            for time, frame, ttc in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['TTC']):
-                if pd.notnull(ttc):
-                    self.ttc_data.append({'simTime': time, 'simFrame': frame, 'TTC': ttc})
+        # 行人目标处理
+        if obj_type == 5:
+            if abs_heading_diff >= 60:  # 横穿行人
+                return (0 < lon_d < 50) and (abs_lat_d < safe_width or abs_lat_d < lane_half_width)
+            else:  # 沿路行人
+                return (0 < lon_d < 30) and (abs_lat_d < safe_width) and abs(v_lat) < 1.0
 
-        return ttc_value
+        # 车辆类目标处理
+        if abs_heading_diff < 45:  # 纵向目标
+            return abs_lat_d < lane_half_width + 0.1 and abs(lon_d) < 80
 
-    def get_mttc_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('MTTC')
-        mttc_values = self.df_safe['MTTC'].dropna()
-        mttc_value = float(mttc_values.min()) if not mttc_values.empty else self._default_value('MTTC')
+        if 45 <= abs_heading_diff <= 135:  # 横向目标
+            return 0 < lon_d < 30 and abs_lat_d < ego_half_width + 0.1
 
-        # 收集MTTC数据
-        if not mttc_values.empty:
-            self.mttc_data = []
-            for time, frame, mttc in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['MTTC']):
-                if pd.notnull(mttc):
-                    self.mttc_data.append({'simTime': time, 'simFrame': frame, 'MTTC': mttc})
+        return False  # 反向目标
 
-        return mttc_value
+    def _has_passed_conflict_point(self, relative_x, relative_y, obj_type, heading_diff):
+        """判断目标是否已越过冲突点"""
+        if obj_type == 5:  # 行人
+            obj_length = self.pedestrian_length
+            obj_width = self.pedestrian_width
+        else:  # 车辆
+            obj_length = self.vehicle_length
+            obj_width = self.vehicle_width
 
-    def get_thw_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('THW')
-        thw_values = self.df_safe['THW'].dropna()
-        thw_value = float(thw_values.min()) if not thw_values.empty else self._default_value('THW')
-
-        # 收集THW数据
-        if not thw_values.empty:
-            self.thw_data = []
-            for time, frame, thw in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['THW']):
-                if pd.notnull(thw):
-                    self.thw_data.append({'simTime': time, 'simFrame': frame, 'THW': thw})
-
-        return thw_value
-
-    def get_tlc_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('TLC')
-        tlc_values = self.df_safe['TLC'].dropna()
-        tlc_value = float(tlc_values.min()) if not tlc_values.empty else self._default_value('TLC')
-
-        # 收集TLC数据
-        if not tlc_values.empty:
-            self.tlc_data = []
-            for time, frame, tlc in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['TLC']):
-                if pd.notnull(tlc):
-                    self.tlc_data.append({'simTime': time, 'simFrame': frame, 'TLC': tlc})
-
-        return tlc_value
+        length_safe_dist = (self.ego_length + obj_length) / 2
+        width_safe_dist = (self.ego_width + obj_width) / 2
 
-    def get_ttb_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('TTB')
-        ttb_values = self.df_safe['TTB'].dropna()
-        ttb_value = float(ttb_values.min()) if not ttb_values.empty else self._default_value('TTB')
+        if obj_type == 5:
+            return relative_x < -length_safe_dist or abs(relative_y) > width_safe_dist * 2
+        else:
+            if abs(heading_diff) < 45:  # 同向
+                return relative_x < -length_safe_dist or abs(relative_y) > width_safe_dist * 1.5
+            elif 45 <= abs(heading_diff) <= 135:  # 横向
+                return relative_x < -length_safe_dist or relative_x > length_safe_dist
+            else:  # 对向
+                return ((relative_x < -length_safe_dist and relative_y < -width_safe_dist) or
+                        (relative_x < -length_safe_dist and relative_y > width_safe_dist) or
+                        (relative_x > length_safe_dist and relative_y < -width_safe_dist) or
+                        (relative_x > length_safe_dist and relative_y > width_safe_dist))
+
+    def _is_moving_away(self, ego_vel, target_vel, relative_x, relative_y, obj_type, heading_diff):
+        """判断目标是否正在远离"""
+        vx_rel = target_vel[0] - ego_vel[0]
+        vy_rel = target_vel[1] - ego_vel[1]
+
+        dist = math.sqrt(relative_x ** 2 + relative_y ** 2)
+        if dist < 1e-6:
+            return False
+
+        v_projection = (vx_rel * relative_x + vy_rel * relative_y) / dist
+
+        if obj_type == 5:  # 行人
+            if relative_x < 0:  # 后方
+                return v_projection > 0.5
+            else:  # 前方
+                return v_projection < -0.5
+        else:
+            abs_heading_diff = abs(heading_diff)
+
+            if abs_heading_diff < 45:  # 同向
+                if relative_x < 0:  # 后方
+                    return v_projection > 0
+                else:  # 前方
+                    return v_projection < 0
+            elif 45 <= abs_heading_diff <= 135:  # 横向
+                if relative_x > 0:  # 前方
+                    return v_projection < 0
+                else:  # 后方
+                    return v_projection > 0
+            else:  # 对向
+                return False
+
+    # ==================== 指标获取方法 ====================
+
+    def _get_metric_value(self, metric_name, aggregation='min'):
+        """通用指标值获取方法"""
+        if self.empty_flag or self.df_safe is None or self.df_safe.empty:
+            return self.DEFAULT_VALUES.get(metric_name, None)
+
+        values = self.df_safe[metric_name].dropna()
+        if values.empty:
+            return self.DEFAULT_VALUES.get(metric_name, None)
+
+        # 根据聚合类型获取值
+        if aggregation == 'min':
+            metric_value = float(values.min())
+        elif aggregation == 'max':
+            metric_value = float(values.max())
+        elif aggregation == 'mean':
+            metric_value = float(values.mean())
+        else:
+            metric_value = float(values.min())
+
+        # 存储指标数据
+        metric_data = []
+        for _, row in self.df_safe.iterrows():
+            if pd.notnull(row[metric_name]):
+                metric_data.append({
+                    'simTime': row['simTime'],
+                    'simFrame': row['simFrame'],
+                    'playerId': row['playerId'],
+                    metric_name: row[metric_name]
+                })
+        setattr(self, f'{metric_name.lower()}_data', metric_data)
+
+        return metric_value
 
-        # 收集TTB数据
-        if not ttb_values.empty:
-            self.ttb_data = []
-            for time, frame, ttb in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['TTB']):
-                if pd.notnull(ttb):
-                    self.ttb_data.append({'simTime': time, 'simFrame': frame, 'TTB': ttb})
+    def get_ttc_value(self) -> float:
+        return self._get_metric_value('TTC')
 
-        return ttb_value
+    def get_mttc_value(self) -> float:
+        return self._get_metric_value('MTTC')
 
-    def get_tm_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('TM')
-        tm_values = self.df_safe['TM'].dropna()
-        tm_value = float(tm_values.min()) if not tm_values.empty else self._default_value('TM')
+    def get_thw_value(self) -> float:
+        return self._get_metric_value('THW')
 
-        # 收集TM数据
-        if not tm_values.empty:
-            self.tm_data = []
-            for time, frame, tm in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['TM']):
-                if pd.notnull(tm):
-                    self.tm_data.append({'simTime': time, 'simFrame': frame, 'TM': tm})
+    def get_ttb_value(self) -> float:
+        return self._get_metric_value('TTB')
 
-        return tm_value
+    def get_tm_value(self) -> float:
+        return self._get_metric_value('TM')
 
     def get_dtc_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('DTC')
-        dtc_values = self.df_safe['DTC'].dropna()
-        return float(dtc_values.min()) if not dtc_values.empty else self._default_value('DTC')
+        return self._get_metric_value('DTC', 'min')
 
     def get_psd_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('PSD')
-        psd_values = self.df_safe['PSD'].dropna()
-        return float(psd_values.min()) if not psd_values.empty else self._default_value('PSD')
+        return self._get_metric_value('PSD', 'min')
 
     def get_lonsd_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('LonSD')
-        lonsd_values = self.df_safe['LonSD'].dropna()
-        lonsd_value = float(lonsd_values.mean()) if not lonsd_values.empty else self._default_value('LonSD')
-
-        # 收集LonSD数据
-        if not lonsd_values.empty:
-            self.lonsd_data = []
-            for time, frame, lonsd in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['LonSD']):
-                if pd.notnull(lonsd):
-                    self.lonsd_data.append({'simTime': time, 'simFrame': frame, 'LonSD': lonsd})
-
-        return lonsd_value
+        return self._get_metric_value('LonSD', 'mean')
 
     def get_latsd_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('LatSD')
-        latsd_values = self.df_safe['LatSD'].dropna()
-        # 使用最小值而非平均值,与safety1.py保持一致
-        latsd_value = float(latsd_values.min()) if not latsd_values.empty else self._default_value('LatSD')
-
-        # 收集LatSD数据
-        if not latsd_values.empty:
-            self.latsd_data = []
-            for time, frame, latsd in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['LatSD']):
-                if pd.notnull(latsd):
-                    self.latsd_data.append({'simTime': time, 'simFrame': frame, 'LatSD': latsd})
-
-        return latsd_value
+        return self._get_metric_value('LatSD', 'min')
 
     def get_btn_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('BTN')
-        btn_values = self.df_safe['BTN'].dropna()
-        btn_value = float(btn_values.max()) if not btn_values.empty else self._default_value('BTN')
-
-        # 收集BTN数据
-        if not btn_values.empty:
-            self.btn_data = []
-            for time, frame, btn in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['BTN']):
-                if pd.notnull(btn):
-                    self.btn_data.append({'simTime': time, 'simFrame': frame, 'BTN': btn})
-
-        return btn_value
+        return self._get_metric_value('BTN', 'max')
 
     def get_collision_risk_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('collisionRisk')
-        risk_values = self.df_safe['collisionRisk'].dropna()
-        risk_value = float(risk_values.max()) if not risk_values.empty else self._default_value('collisionRisk')
+        return self._get_metric_value('collisionRisk', 'max')
 
-        # 收集碰撞风险数据
-        if not risk_values.empty:
-            self.collision_risk_data = []
-            for time, frame, risk in zip(self.df_safe['simTime'], self.df_safe['simFrame'],
-                                         self.df_safe['collisionRisk']):
-                if pd.notnull(risk):
-                    self.collision_risk_data.append({'simTime': time, 'simFrame': frame, 'collisionRisk': risk})
+    def get_collision_severity_value(self) -> float:
+        return self._get_metric_value('collisionSeverity', 'max')
 
-        return risk_value
+    # ==================== 辅助方法 ====================
 
-    def get_collision_severity_value(self) -> float:
-        if self.empty_flag or self.df_safe is None:
-            return self._default_value('collisionSeverity')
-        severity_values = self.df_safe['collisionSeverity'].dropna()
-        severity_value = float(severity_values.max()) if not severity_values.empty else self._default_value(
-            'collisionSeverity')
-
-        # 收集碰撞严重性数据
-        if not severity_values.empty:
-            self.collision_severity_data = []
-            for time, frame, severity in zip(self.df_safe['simTime'], self.df_safe['simFrame'],
-                                             self.df_safe['collisionSeverity']):
-                if pd.notnull(severity):
-                    self.collision_severity_data.append(
-                        {'simTime': time, 'simFrame': frame, 'collisionSeverity': severity})
-
-        return severity_value
+    def generate_metric_chart(self, metric_name: str, plot_path: Path) -> None:
+        """生成指标图表"""
+        try:
+            self.output_dir = plot_path if plot_path else os.path.join(os.getcwd(), 'data')
+            os.makedirs(self.output_dir, exist_ok=True)
 
+            chart_path = generate_safety_chart_data(self, metric_name, self.output_dir)
+            if chart_path:
+                self.logger.info(f"{metric_name}图表已生成: {chart_path}")
+            else:
+                self.logger.warning(f"{metric_name}图表生成失败")
+        except Exception as e:
+            self.logger.error(f"生成{metric_name}图表失败: {str(e)}", exc_info=True)

+ 48 - 19
modules/metric/traffic.py

@@ -789,6 +789,10 @@ class OvertakingViolation(object):
         crossroad_objstate = self.obj_data[
             self.obj_data["simTime"].isin(crossroad_simTime)
         ]
+        if crossroad_objstate.empty:
+            print("超车环境一定范围内没有其他车辆数据,无法检测不同场景超车违规,默认为0")
+            self._calculated["different_senerios"] = True
+            return
         # 读取前后的laneId
         lane_id = crossroad_ego["lane_id"].tolist()
 
@@ -945,7 +949,7 @@ class SlowdownViolation(object):
             # start_time, end_time = crosswalk_simtime
             start_time = crosswalk_simtime[0]
             end_time = crosswalk_simtime[-1]
-            print(f"当前时间段:{start_time} - {end_time}")
+            # print(f"当前时间段:{start_time} - {end_time}")
             crosswalk_objstate = self.ego_data[
                 (self.ego_data["simTime"] >= start_time)
                 & (self.ego_data["simTime"] <= end_time)
@@ -1674,8 +1678,7 @@ class WarningViolation(object):
         lane_width = self.data["lane_width"]
 
         # 计算阈值
-        # threshold = (lane_width - car_width) / 2
-        threshold = (lane_width - car_width)
+        threshold = (lane_width - car_width) / 2
 
         # 找到满足条件的行
         self.data["is_violation"] = self.data["laneOffset"] > threshold
@@ -1689,16 +1692,15 @@ class WarningViolation(object):
         self.violation_counts["urbanExpresswayOrHighwayRideLaneDivider"] = len(
             violation_segments
         )
-
     def count_continuous_violations(self, violation_series, time_series):
-        """统计连续违规的时间段数量
+        """统计连续违规的时间段数量(每段需 >6.5s)
 
         Args:
             violation_series: 表示是否违规的布尔序列
-            time_series: 对应的时间序列
+            time_series: 对应的时间序列(单位:秒)
 
         Returns:
-            list: 连续违规时间段列表
+            list: 连续违规时间段列表,每项为 [start_time, end_time]
         """
         continuous_segments = []
         current_segment = []
@@ -1710,16 +1712,50 @@ class WarningViolation(object):
             else:
                 if current_segment:  # 连续段结束
                     current_segment.append(time)  # 添加结束时间
-                    continuous_segments.append(current_segment)
+                    start_time, end_time = current_segment
+                    if end_time - start_time > 6.5:
+                        continuous_segments.append(current_segment)
                     current_segment = []
 
-        # 检查是否有一个未结束的连续段在最后
+        # 检查是否有未结束的段在最后
         if current_segment:
-            current_segment.append(time_series.iloc[-1])  # 使用最后的时间作为结束时间
-            continuous_segments.append(current_segment)
+            current_segment.append(time_series.iloc[-1])
+            start_time, end_time = current_segment
+            if end_time - start_time > 6.5:
+                continuous_segments.append(current_segment)
 
         return continuous_segments
 
+    # def count_continuous_violations(self, violation_series, time_series):
+    #     """统计连续违规的时间段数量
+
+    #     Args:
+    #         violation_series: 表示是否违规的布尔序列
+    #         time_series: 对应的时间序列
+
+    #     Returns:
+    #         list: 连续违规时间段列表
+    #     """
+    #     continuous_segments = []
+    #     current_segment = []
+
+    #     for is_violation, time in zip(violation_series, time_series):
+    #         if is_violation:
+    #             if not current_segment:  # 新的连续段开始
+    #                 current_segment.append(time)
+    #         else:
+    #             if current_segment:  # 连续段结束
+    #                 current_segment.append(time)  # 添加结束时间
+    #                 continuous_segments.append(current_segment)
+    #                 current_segment = []
+
+    #     # 检查是否有一个未结束的连续段在最后
+    #     if current_segment:
+    #         current_segment.append(time_series.iloc[-1])  # 使用最后的时间作为结束时间
+    #         continuous_segments.append(current_segment)
+
+    #     return continuous_segments
+
     def calculate_generalRoadIrregularLaneUse_count(self):
         """计算普通道路不按规定车道行驶违规次数"""
         # 只处理普通道路不按规定车道行驶违规
@@ -1837,11 +1873,4 @@ class TrafficSignViolation:
 
         if not violation_df.empty:
             mask = compare_op(violation_df['v'], violation_df['sign_speed'])
-            self._violation_counts[count_key] = mask.sum()
-
-
-
-
-
-
-
+            self._violation_counts[count_key] = mask.sum()

+ 181 - 0
scripts/banch.py

@@ -0,0 +1,181 @@
+# Author: XGJ2024  
+# Date: 2025/8/14 10:32
+# !/usr/bin/env python3
+# batch_evaluator.py
+import os
+import subprocess
+import argparse
+import time
+import logging
+from pathlib import Path
+
+# 配置日志
+logging.basicConfig(
+    level=logging.INFO,
+    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+    handlers=[
+        logging.FileHandler("batch_evaluator.log"),
+        logging.StreamHandler()
+    ]
+)
+logger = logging.getLogger("batch_evaluator")
+
+# 固定参数配置
+DEFAULT_CONFIG = {
+    "allConfigPath": r"D:\Kevin\zhaoyuan\code\zhaoyuan-master_v2.0\zhaoyuan_new\zhaoyuan\config\LST_1_all_metrics_config.yaml",
+    "baseConfigPath": r"D:\Kevin\zhaoyuan\code\zhaoyuan-master_v2.0\zhaoyuan_new\zhaoyuan\config\LST_1_builtin_metrics_config.yaml",
+    "customConfigPath": r"D:\Kevin\zhaoyuan\code\zhaoyuan-master_v2.0\zhaoyuan_new\zhaoyuan\config\LST_1_custom_metrics_config.yaml",
+    "logDir": r"D:\Kevin\zhaoyuan\code\zhaoyuan-master_v2.0\zhaoyuan_new\zhaoyuan\logs",
+    "reportPath": r"D:\Kevin\zhaoyuan\code\zhaoyuan-master_v2.0\zhaoyuan_new\zhaoyuan\reports",
+    "plotPath": r"D:\Kevin\zhaoyuan\code\zhaoyuan-master_v2.0\zhaoyuan_new\zhaoyuan\plots",
+    "customMetricsPath": r"D:\Kevin\zhaoyuan\code\zhaoyuan-master_v2.0\zhaoyuan_new\zhaoyuan\custom_metrics"
+}
+
+
+def run_evaluation(data_path: Path):
+    """执行单个测试用例的评估"""
+    try:
+        # 构建命令行参数
+        cmd = [
+            "python",
+            r"D:\Kevin\zhaoyuan\code\zhaoyuan-master_v2.0\zhaoyuan_new\zhaoyuan\scripts\evaluator_enhanced.py",
+            "--dataPath", str(data_path),
+            "--allConfigPath", DEFAULT_CONFIG["allConfigPath"],
+            "--baseConfigPath", DEFAULT_CONFIG["baseConfigPath"],
+            "--customConfigPath", DEFAULT_CONFIG["customConfigPath"],
+            "--logDir", DEFAULT_CONFIG["logDir"],
+            "--reportPath", DEFAULT_CONFIG["reportPath"],
+            "--plotPath", DEFAULT_CONFIG["plotPath"],
+            "--customMetricsPath", DEFAULT_CONFIG["customMetricsPath"]
+        ]
+
+        logger.info(f"开始评估用例: {data_path.name}")
+        logger.debug(f"执行命令: {' '.join(cmd)}")
+
+        # 执行评估
+        start_time = time.time()
+        # 修改这里:使用 errors='replace' 或 'ignore' 来处理编码错误,或使用 'gbk' 等其他编码
+        result = subprocess.run(cmd, check=True, capture_output=True, text=True)
+        elapsed_time = time.time() - start_time
+
+        # 处理结果
+        logger.info(f"用例评估完成: {data_path.name} (耗时: {elapsed_time:.1f}秒)")
+        logger.debug(f"评估输出:\n{result.stdout}")
+
+        return True
+
+    except subprocess.CalledProcessError as e:
+        logger.error(f"评估失败: {data_path.name}")
+        logger.error(f"错误代码: {e.returncode}")
+        logger.error(f"错误输出:\n{e.stderr}")
+        return False
+    except Exception as e:
+        logger.error(f"执行异常: {str(e)}")
+        return False
+
+
+def find_test_cases(parent_dir: Path):
+    """在父目录中查找所有测试用例目录"""
+    test_cases = []
+
+    # 确保父目录存在
+    if not parent_dir.exists():
+        logger.error(f"父目录不存在: {parent_dir}")
+        return test_cases
+
+    # 扫描所有直接子目录
+    for item in parent_dir.iterdir():
+        if item.is_dir():
+            # 检查是否包含必要的评估数据
+            # 这里可以添加更复杂的检查逻辑
+            if any(item.glob("*")):  # 简单检查目录是否非空
+                test_cases.append(item)
+            else:
+                logger.warning(f"跳过空目录: {item.name}")
+
+    # 按名称排序
+    test_cases.sort(key=lambda x: x.name)
+    return test_cases
+
+
+def main():
+    """主函数"""
+    parser = argparse.ArgumentParser(description="批量评估系统 - 处理父目录下的所有测试用例")
+    # parser.add_argument(
+    #     "parentDir",
+    #     default=r"D:\Kevin\zhaoyuan\data_new\LST1\二轮评测数据\单车",
+    #     type=str,
+    #     help="包含多个测试用例目录的父目录路径"
+    # )
+    parser.add_argument(
+        "--parentDir",  # 添加 -- 前缀改为可选参数
+        default=r"D:\Kevin\zhaoyuan\data_new\LST1\二轮评测数据\单车",
+        type=str,
+        required=False,  # 明确标记为非必填
+        help="包含多个测试用例目录的父目录路径"
+    )
+    parser.add_argument(
+        "--verbose", "-v",
+        action="store_true",
+        help="显示详细输出"
+    )
+    args = parser.parse_args()
+
+    # 设置详细日志
+    if args.verbose:
+        logger.setLevel(logging.DEBUG)
+
+    # 确保输出目录存在
+    Path(DEFAULT_CONFIG["logDir"]).mkdir(parents=True, exist_ok=True)
+    Path(DEFAULT_CONFIG["reportPath"]).mkdir(parents=True, exist_ok=True)
+    Path(DEFAULT_CONFIG["plotPath"]).mkdir(parents=True, exist_ok=True)
+
+    # 获取父目录路径
+    parent_dir = Path(args.parentDir).resolve()
+
+    logger.info("=" * 80)
+    logger.info("开始批量评估")
+    logger.info(f"父目录: {parent_dir}")
+    logger.info(f"日志目录: {DEFAULT_CONFIG['logDir']}")
+    logger.info(f"报告目录: {DEFAULT_CONFIG['reportPath']}")
+    logger.info(f"图表目录: {DEFAULT_CONFIG['plotPath']}")
+    logger.info("=" * 80)
+
+    # 获取所有测试用例
+    test_cases = find_test_cases(parent_dir)
+
+    if not test_cases:
+        logger.error(f"在 {parent_dir} 中未找到有效的测试用例目录")
+        return
+
+    logger.info(f"找到 {len(test_cases)} 个测试用例:")
+    for i, case in enumerate(test_cases, 1):
+        logger.info(f"{i}. {case.name}")
+
+    # 执行评估
+    total_count = len(test_cases)
+    success_count = 0
+    failed_count = 0
+    start_time = time.time()
+
+    for i, test_case in enumerate(test_cases, 1):
+        logger.info(f"\n[处理 {i}/{total_count}] {test_case.name}")
+        if run_evaluation(test_case):
+            success_count += 1
+        else:
+            failed_count += 1
+
+    # 统计结果
+    elapsed_time = time.time() - start_time
+    logger.info("\n" + "=" * 80)
+    logger.info(f"批量评估完成")
+    logger.info(f"总用例数: {total_count}")
+    logger.info(f"成功: {success_count}")
+    logger.info(f"失败: {failed_count}")
+    logger.info(f"总耗时: {elapsed_time:.1f} 秒")
+    logger.info(f"平均每用例: {elapsed_time / total_count if total_count > 0 else 0:.1f} 秒")
+    logger.info("=" * 80)
+
+
+if __name__ == "__main__":
+    main()

+ 146 - 48
scripts/evaluator_enhanced.py

@@ -16,11 +16,14 @@ import logging
 import traceback
 import json
 import inspect
+import os
+import glob
 
 # 常量定义
 DEFAULT_WORKERS = 4
 CUSTOM_METRIC_PREFIX = "metric_"
 CUSTOM_METRIC_FILE_PATTERN = "*.py"
+MAX_LOG_SIZE_MB = 300  # 最大日志文件夹大小
 
 # 安全设置根目录路径
 if hasattr(sys, "_MEIPASS"):
@@ -127,17 +130,38 @@ class ConfigManager:
             return self.merged_config
         return {}
 
-    @lru_cache(maxsize=16)
+    @lru_cache(maxsize=32)
     def _safe_load_config(self, config_path: Path) -> Dict[str, Any]:
-        """安全加载YAML配置,使用lru_cache减少重复读取"""
+        """安全加载YAML配置,使用增强的lru_cache减少重复读取"""
         try:
             if not config_path or not config_path.exists():
                 self.logger.warning(f"Config file not found: {config_path}")
                 return {}
+
+            # 检查文件修改时间,避免使用过期缓存
+            file_mtime = config_path.stat().st_mtime
+            cache_key = f"{config_path}_{file_mtime}"
+
+            # 使用类级别缓存存储解析后的配置
+            if not hasattr(self.__class__, '_config_file_cache'):
+                self.__class__._config_file_cache = {}
+
+            if cache_key in self.__class__._config_file_cache:
+                self.logger.info(f"Using cached config: {config_path}")
+                return self.__class__._config_file_cache[cache_key]
+
             with config_path.open('r', encoding='utf-8') as f:
                 config_dict = yaml.safe_load(f) or {}
-                self.logger.info(f"Loaded config: {config_path}")
-                return config_dict
+
+            # 限制缓存大小
+            if len(self.__class__._config_file_cache) > 20:
+                # 移除最早添加的项
+                self.__class__._config_file_cache.pop(next(iter(self.__class__._config_file_cache)))
+
+            # 存储到缓存
+            self.__class__._config_file_cache[cache_key] = config_dict
+            self.logger.info(f"Loaded config: {config_path}")
+            return config_dict
         except Exception as err:
             self.logger.error(f"Failed to load config {config_path}: {str(err)}")
             return {}
@@ -308,35 +332,37 @@ class EvaluationEngine:
         return self._process_merged_results(raw_results, custom_results)
 
     def _collect_builtin_metrics(self, data: Any) -> Dict[str, Any]:
-        """收集内置指标结果"""
+        """收集内置指标结果 - 优化版本"""
         metric_modules = self.metric_loader.get_builtin_metrics()
-        x = metric_modules.items()
         raw_results: Dict[str, Any] = {}
 
-        # 获取配置中实际存在的指标
+        # 获取配置中实际存在的指标 - 使用更高效的字典推导
         config = self.config_manager.get_config()
-        available_metrics = {
-            metric_name for metric_name in metric_modules.keys()
-            if metric_name in config and isinstance(config[metric_name], dict)
-        }
-
-        # 只处理配置中存在的指标
         filtered_modules = {
             name: module for name, module in metric_modules.items()
-            if name in available_metrics
+            if name in config and isinstance(config[name], dict)
         }
 
-        # 优化线程池大小,避免创建过多线程
-        max_workers = min(len(filtered_modules), DEFAULT_WORKERS)
+        if not filtered_modules:
+            return raw_results
+
+        # 优化线程池大小,根据CPU核心数和任务数量动态调整
+        import multiprocessing
+        cpu_count = multiprocessing.cpu_count()
+        max_workers = min(len(filtered_modules), max(DEFAULT_WORKERS, cpu_count))
 
+        # 使用上下文管理器确保线程池正确关闭
         with ThreadPoolExecutor(max_workers=max_workers) as executor:
-            futures = {
-                executor.submit(self._run_module, module, data, module_name, self.plot_path): module_name for
-                module_name, module in filtered_modules.items()
+            # 预先创建所有任务
+            futures_to_names = {
+                executor.submit(self._run_module, module, data, module_name, self.plot_path): module_name
+                for module_name, module in filtered_modules.items()
             }
 
-            for future in futures:
-                module_name = futures[future]
+            # 使用as_completed获取结果,避免阻塞
+            from concurrent.futures import as_completed
+            for future in as_completed(futures_to_names):
+                module_name = futures_to_names[future]
                 try:
                     result = future.result()
                     raw_results[module_name] = result[module_name]
@@ -457,25 +483,83 @@ class EvaluationEngine:
 class LoggingManager:
     """日志管理组件"""
 
-    def __init__(self, log_path: Path):
-        self.log_path = log_path
+    def __init__(self, log_dir: Path, case_name: str):
+        self.log_dir = log_dir
+        self.case_name = case_name
         self.logger = self._init_logger()
 
     def _init_logger(self) -> logging.Logger:
         """初始化日志系统"""
         try:
+            # 确保日志目录存在
+            self.log_dir.mkdir(parents=True, exist_ok=True)
+
+            # 创建以用例名命名的日志文件
+            log_file = self.log_dir / f"{self.case_name}.log"
+
+            # 检查日志文件夹大小并清理
+            self._cleanup_old_logs()
+
             from modules.lib.log_manager import LogManager
-            log_manager = LogManager(self.log_path)
+            log_manager = LogManager(log_file)
             return log_manager.get_logger()
         except (ImportError, PermissionError, IOError) as e:
+            # 回退到基础日志配置
             logger = logging.getLogger("evaluator")
             logger.setLevel(logging.INFO)
+
+            # 创建控制台处理器
             console_handler = logging.StreamHandler()
-            console_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
+            console_handler.setFormatter(
+                logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
             logger.addHandler(console_handler)
+
+            # 尝试创建文件处理器
+            try:
+                self.log_dir.mkdir(parents=True, exist_ok=True)
+                log_file = self.log_dir / f"{self.case_name}.log"
+                file_handler = logging.FileHandler(log_file, encoding='utf-8')
+                file_handler.setFormatter(
+                    logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
+                logger.addHandler(file_handler)
+            except Exception as file_err:
+                logger.warning(f"无法创建文件日志: {str(file_err)}")
+
             logger.warning(f"Failed to init standard logger: {str(e)}, using fallback logger")
             return logger
 
+    def _cleanup_old_logs(self):
+        """当日志文件夹超过300MB时删除最早的日志文件"""
+        try:
+            # 获取所有日志文件
+            log_files = list(self.log_dir.glob("*.log"))
+
+            # 计算总大小 (MB)
+            total_size = sum(f.stat().st_size for f in log_files) / (1024 * 1024)
+
+            if total_size > MAX_LOG_SIZE_MB:
+                # 使用基础日志记录警告(因为标准日志器可能尚未初始化)
+                logging.warning(f"日志文件夹大小 {total_size:.2f}MB > {MAX_LOG_SIZE_MB}MB, 开始清理...")
+
+                # 按修改时间排序 (旧文件在前)
+                log_files.sort(key=lambda f: f.stat().st_mtime)
+
+                # 删除文件直到总大小低于阈值
+                while total_size > MAX_LOG_SIZE_MB and log_files:
+                    oldest_file = log_files.pop(0)
+                    file_size_mb = oldest_file.stat().st_size / (1024 * 1024)
+                    try:
+                        oldest_file.unlink()
+                        total_size -= file_size_mb
+                        logging.info(f"已删除旧日志文件: {oldest_file.name} ({file_size_mb:.2f}MB)")
+                    except Exception as e:
+                        logging.error(f"删除日志文件失败 {oldest_file.name}: {str(e)}")
+
+                logging.info(f"清理完成, 当前日志文件夹大小: {total_size:.2f}MB")
+        except Exception as e:
+            # 使用基础日志记录错误
+            logging.error(f"清理日志时出错: {str(e)}")
+
     def get_logger(self) -> logging.Logger:
         return self.logger
 
@@ -498,11 +582,26 @@ class DataProcessor:
         return self._processor
 
     def _load_processor(self) -> Any:
-        """加载数据处理器"""
+        """加载数据处理器,优化版本"""
         try:
             start_time = time.perf_counter()
             from modules.lib import data_process
-            processor = data_process.DataPreprocessing(self.data_path, self.config_path)
+            # 使用缓存机制,避免重复加载相同数据
+            cache_key = f"{self.data_path}_{self.config_path}"
+            if hasattr(self.__class__, '_processor_cache') and cache_key in self.__class__._processor_cache:
+                processor = self.__class__._processor_cache[cache_key]
+                self.logger.info(f"Using cached data processor for {self.data_path.name}")
+            else:
+                processor = data_process.DataPreprocessing(self.data_path, self.config_path)
+                # 初始化类级别缓存
+                if not hasattr(self.__class__, '_processor_cache'):
+                    self.__class__._processor_cache = {}
+                # 限制缓存大小,避免内存泄漏
+                if len(self.__class__._processor_cache) > 5:
+                    # 移除最早添加的缓存项
+                    self.__class__._processor_cache.pop(next(iter(self.__class__._processor_cache)))
+                self.__class__._processor_cache[cache_key] = processor
+
             elapsed_time = time.perf_counter() - start_time
             self.logger.info(f"Data processor loaded in {elapsed_time:.2f}s")
             return processor
@@ -521,7 +620,7 @@ class DataProcessor:
 class EvaluationPipeline:
     """评估流水线控制器"""
 
-    def __init__(self, all_config_path: str, base_config_path: str, log_path: str, data_path: str, report_path: str,
+    def __init__(self, all_config_path: str, base_config_path: str, log_dir: str, data_path: str, report_path: str,
                  plot_path: str,
                  custom_metrics_path: Optional[str] = None, custom_config_path: Optional[str] = None):
         # 路径初始化
@@ -532,10 +631,15 @@ class EvaluationPipeline:
         self.report_path = Path(report_path)
         self.plot_path = Path(plot_path)
         self.custom_metrics_path = Path(custom_metrics_path) if custom_metrics_path else None
+        self.log_dir = Path(log_dir)
 
-        # 日志
-        self.logging_manager = LoggingManager(Path(log_path))
+        # 获取用例名
+        self.case_name = self.data_path.name
+
+        # 日志管理 (使用用例名)
+        self.logging_manager = LoggingManager(self.log_dir, self.case_name)
         self.logger = self.logging_manager.get_logger()
+
         # 配置
         self.config_manager = ConfigManager(self.logger)
         self.config = self.config_manager.load_configs(
@@ -661,14 +765,9 @@ def main():
     # 必要参数
     parser.add_argument(
         "--dataPath",
+        default=r"D:\Cicv\招远\测试数据\二轮评测数据\单车\AD_GBT41798-2022_RoadTraInfraObstRR_LST_010",
         type=str,
-        # default=r"D:\Cicv\招远\V2V_CSAE53-2020_ForwardCollision_LST_01-02_new",
-        # default=r"D:\Cicv\招远\AD_GBT41798-2022_TrafficSignalRecognitionAndResponse_LST_01",
-        # default=r"/home/server/桌面/XGJ/zhaoyuan_DataPreProcess/output/AD_GBT41798-2022_TrafficSignalRecognitionAndResponse_LST_02",
-        # default=r"/home/server/桌面/XGJ/zhaoyuan_DataPreProcess/output/V2I_CSAE53-2020_LeftTurnAssist_PGVIL_demo",
-        # default=r"D:\Cicv\招远\delivery_car_data\V2I_CSAE53-2020_HazardousLocationW_LST_02",
-        # default=r"D:\Cicv\招远\PGVILdata\AD_GBT41798-2022_AutoEmergencyEvacuation_PGVL_05", # 紧急避险未通过
-        default=r"D:\Cicv\招远\PGVILdata\AD_GBT41798-2022_RoadTraInfraObstRR_PGVL_25", # 急刹
+        # required=True,
         help="Input data directory",
     )
 
@@ -677,15 +776,14 @@ def main():
     config_group.add_argument(
         "--allConfigPath",
         type=str,
-        # default=r"D:\Cicv\招远\zhaoyuan\config\all_metrics_config_delivery.yaml",
-        default=r"D:\Cicv\招远\zhaoyuan\config\all_metrics_config.yaml",
+        default=r"D:\Cicv\招远\评测代码\zhaoyuan_0808new\zhaoyuan\config\LST_1_all_metrics_config.yaml",
         help="Full metrics config file path (built-in + custom)",
     )
     config_group.add_argument(
         "--baseConfigPath",
         type=str,
-        # default=r"D:\Cicv\招远\zhaoyuan\config\all_metrics_config_delivery.yaml",
-        default=r"D:\Cicv\招远\zhaoyuan\config\all_metrics_config.yaml",
+        default=r"D:\Cicv\招远\评测代码\zhaoyuan_0808new\zhaoyuan\config\LST_1_builtin_metrics_config.yaml",
+        # required=True,
         help="Built-in metrics config file path",
     )
     config_group.add_argument(
@@ -698,10 +796,10 @@ def main():
     # 输出参数
     output_group = parser.add_argument_group('Output')
     output_group.add_argument(
-        "--logPath",
+        "--logDir",
         type=str,
-        default="test.log",
-        help="Log file path",
+        default="logs",
+        help="Log directory path",
     )
     output_group.add_argument(
         "--reportPath",
@@ -712,7 +810,8 @@ def main():
     output_group.add_argument(
         "--plotPath",
         type=str,
-        default=r"D:\Cicv\招远\zhaoyuan\scripts\reports\datas",
+        default=r"D:\Cicv\招远\评测代码\zhaoyuan_0808new\zhaoyuan\plots",
+        # required=True,
         help="Output plot csv directory",
     )
 
@@ -721,7 +820,6 @@ def main():
     ext_group.add_argument(
         "--customMetricsPath",
         type=str,
-        default="custom_metrics",
         help="Custom metrics scripts directory (optional)",
     )
 
@@ -731,7 +829,7 @@ def main():
         pipeline = EvaluationPipeline(
             all_config_path=args.allConfigPath,
             base_config_path=args.baseConfigPath,
-            log_path=args.logPath,
+            log_dir=args.logDir,
             data_path=args.dataPath,
             report_path=args.reportPath,
             plot_path=args.plotPath,
@@ -761,4 +859,4 @@ def main():
 
 if __name__ == "__main__":
     warnings.filterwarnings("ignore")
-    main()
+    main()

+ 16 - 15
templates/custom_metric_template.py

@@ -18,23 +18,24 @@ from modules.lib.metric_registry import BaseMetric
 # 可选值: safety, comfort, traffic, efficient, function, custom
 METRIC_CATEGORY = "custom"
 
+
 class CustomMetricExample(BaseMetric):
     """自定义指标示例 - 计算平均速度"""
-    
+
     def __init__(self, data: Any):
         """
         初始化指标
-        
+
         Args:
             data: 输入数据
         """
         super().__init__(data)
         # 在这里添加自定义初始化代码
-        
+
     def calculate(self) -> Dict[str, Any]:
         """
         计算指标
-        
+
         Returns:
             计算结果字典
         """
@@ -44,7 +45,7 @@ class CustomMetricExample(BaseMetric):
             "score": 100,  # 评分
             "details": {}  # 详细信息
         }
-        
+
         # 示例:计算平均速度
         try:
             if hasattr(self.data, 'velocities') and self.data.velocities:
@@ -53,12 +54,12 @@ class CustomMetricExample(BaseMetric):
                     # 计算合速度
                     vx = np.array(velocities['vx'])
                     vy = np.array(velocities['vy'])
-                    speeds = np.sqrt(vx**2 + vy**2)
-                    
+                    speeds = np.sqrt(vx ** 2 + vy ** 2)
+
                     # 计算平均速度
                     avg_speed = np.mean(speeds)
                     result['value'] = float(avg_speed)
-                    
+
                     # 简单评分逻辑
                     if avg_speed < 10:
                         result['score'] = 60  # 速度过低
@@ -66,7 +67,7 @@ class CustomMetricExample(BaseMetric):
                         result['score'] = 70  # 速度过高
                     else:
                         result['score'] = 100  # 速度适中
-                    
+
                     # 添加详细信息
                     result['details'] = {
                         "max_speed": float(np.max(speeds)),
@@ -78,29 +79,29 @@ class CustomMetricExample(BaseMetric):
             result['value'] = 0.0
             result['score'] = 0
             result['details'] = {"error": str(e)}
-                
+
         return result
-    
+
     def report_statistic(self) -> Dict[str, Any]:
         """
         报告统计结果
         可以在这里自定义结果格式
         """
         result = self.calculate()
-        
+
         # 可以在这里添加额外的处理逻辑
         # 例如:添加时间戳、格式化结果等
-        
+
         return result
 
 
 # 可以在同一文件中定义多个指标类
 class AnotherCustomMetric(BaseMetric):
     """另一个自定义指标示例 - 计算加速度变化率"""
-    
+
     def __init__(self, data: Any):
         super().__init__(data)
-    
+
     def calculate(self) -> Dict[str, Any]:
         # 实现您的计算逻辑
         return {"value": 0.0, "score": 100, "details": {}}

+ 25 - 24
templates/unified_custom_metric_template.py

@@ -24,6 +24,7 @@ from modules.lib.metric_registry import BaseMetric
 # 可选值: safety, comfort, traffic, efficient, function, custom
 METRIC_CATEGORY = "custom"
 
+
 #############################################################
 # 方式一:基于类继承的实现方式(推荐)
 # 优点:结构清晰,易于扩展,支持复杂指标计算
@@ -32,21 +33,21 @@ METRIC_CATEGORY = "custom"
 
 class CustomMetricExample(BaseMetric):
     """自定义指标示例 - 计算平均速度"""
-    
+
     def __init__(self, data: Any):
         """
         初始化指标
-        
+
         Args:
             data: 输入数据,通常包含场景、轨迹等信息
         """
         super().__init__(data)
         # 在这里添加自定义初始化代码
-        
+
     def calculate(self) -> Dict[str, Any]:
         """
         计算指标
-        
+
         Returns:
             计算结果字典,包含以下字段:
             - value: 指标值
@@ -59,17 +60,17 @@ class CustomMetricExample(BaseMetric):
             "score": 100,  # 评分
             "details": {}  # 详细信息
         }
-        
+
         # 示例:计算平均速度
         try:
             if hasattr(self.data, 'ego_data') and hasattr(self.data.ego_data, 'v'):
                 # 获取速度数据
                 speeds = self.data.ego_data['v'].values
-                
+
                 # 计算平均速度
                 avg_speed = np.mean(speeds)
                 result['value'] = float(avg_speed)
-                
+
                 # 简单评分逻辑
                 if avg_speed < 10:
                     result['score'] = 60  # 速度过低
@@ -77,7 +78,7 @@ class CustomMetricExample(BaseMetric):
                     result['score'] = 70  # 速度过高
                 else:
                     result['score'] = 100  # 速度适中
-                
+
                 # 添加详细信息
                 result['details'] = {
                     "max_speed": float(np.max(speeds)),
@@ -90,22 +91,22 @@ class CustomMetricExample(BaseMetric):
             result['value'] = 0.0
             result['score'] = 0
             result['details'] = {"error": str(e)}
-                
+
         return result
-    
+
     def report_statistic(self) -> Dict[str, Any]:
         """
         报告统计结果
         可以在这里自定义结果格式
-        
+
         Returns:
             统计结果字典
         """
         result = self.calculate()
-        
+
         # 可以在这里添加额外的处理逻辑
         # 例如:添加时间戳、格式化结果等
-        
+
         return result
 
 
@@ -118,10 +119,10 @@ class CustomMetricExample(BaseMetric):
 def evaluate(data) -> Dict[str, Any]:
     """
     评测自定义指标
-    
+
     Args:
         data: 评测数据,包含场景、轨迹等信息
-        
+
     Returns:
         评测结果,包含指标值、分数、详情等
     """
@@ -129,12 +130,12 @@ def evaluate(data) -> Dict[str, Any]:
     try:
         # 计算指标值
         result = calculate_metric(data)
-        
+
         # 可以使用Score类评估结果
-        # evaluator = Score(config)   
+        # evaluator = Score(config)
         # result = evaluator.evaluate(result)
         return result
-        
+
     except Exception as e:
         logging.error(f"评测指标失败: {str(e)}")
         # 发生异常时返回错误信息
@@ -145,31 +146,31 @@ def evaluate(data) -> Dict[str, Any]:
                 "error": str(e)
             }
         }
-    
+
 
 def calculate_metric(data) -> Dict[str, Any]:
     """
     计算指标值
-    
+
     Args:
         data: 输入数据
-        
+
     Returns:
         指标计算结果
     """
     # 这里是计算指标的具体逻辑
     # 以下是一个简化的示例
-    
+
     if data is None:
         raise ValueError("输入数据不能为空")
-    
+
     try:
         # 示例:计算TTC (Time To Collision)
         if hasattr(data, 'ego_data'):
             # 这里应该实现实际的指标计算逻辑
             # 临时使用固定值代替实际计算
             metric_value = 1.5
-            
+
             # 返回结果
             return {
                 "value": metric_value,

+ 17 - 13
test/split.py

@@ -1,11 +1,12 @@
 import yaml
 from pathlib import Path
 
+
 def ensure_structure(metrics_dict, full_dict, path):
     """确保每一级都包含name和priority字段"""
     if not isinstance(metrics_dict, dict):
         return metrics_dict
-    
+
     # 从完整配置中获取当前路径的结构
     current = full_dict
     for key in path.split('.'):
@@ -13,7 +14,7 @@ def ensure_structure(metrics_dict, full_dict, path):
             current = current[key]
         else:
             break
-    
+
     # 如果原结构中有name和priority,就保留它们
     result = {}
     if isinstance(current, dict):
@@ -21,18 +22,19 @@ def ensure_structure(metrics_dict, full_dict, path):
             result['name'] = current['name']
         if 'priority' in current:
             result['priority'] = current['priority']
-    
+
     # 添加自定义内容
     for key, value in metrics_dict.items():
         if key not in ['name', 'priority']:
             result[key] = ensure_structure(value, full_dict, f"{path}.{key}" if path else key)
-    
+
     return result
 
+
 def find_custom_metrics(all_metrics, builtin_metrics, current_path=""):
     """递归比较两个配置,找出自定义指标"""
     custom_metrics = {}
-    
+
     if isinstance(all_metrics, dict) and isinstance(builtin_metrics, dict):
         for key in all_metrics:
             if key not in builtin_metrics:
@@ -41,7 +43,7 @@ def find_custom_metrics(all_metrics, builtin_metrics, current_path=""):
             else:
                 # 递归比较子结构
                 child_custom = find_custom_metrics(
-                    all_metrics[key], 
+                    all_metrics[key],
                     builtin_metrics[key],
                     f"{current_path}.{key}" if current_path else key
                 )
@@ -50,29 +52,30 @@ def find_custom_metrics(all_metrics, builtin_metrics, current_path=""):
     elif all_metrics != builtin_metrics:
         # 值不同的情况
         return all_metrics
-    
+
     # 对结果进行结构调整,确保每层都有name和priority
     if custom_metrics:
         return ensure_structure(custom_metrics, all_metrics, current_path)
     return None
 
+
 def split_metrics_config(all_metrics_path, builtin_metrics_path, custom_metrics_path):
     # 加载完整的指标配置
     with open(all_metrics_path, 'r', encoding='utf-8') as f:
         all_metrics = yaml.safe_load(f) or {}
-    
+
     # 加载内置指标配置作为基准
     with open(builtin_metrics_path, 'r', encoding='utf-8') as f:
         builtin_metrics = yaml.safe_load(f) or {}
-    
+
     # 找出自定义指标
     custom_metrics = find_custom_metrics(all_metrics, builtin_metrics)
-    
+
     # 保存自定义指标
     if custom_metrics:
         with open(custom_metrics_path, 'w', encoding='utf-8') as f:
             yaml.dump(custom_metrics, f, allow_unicode=True, sort_keys=False, indent=2)
-        
+
         print(f"成功拆分指标配置:")
         print(f"- 内置指标已保存到: {builtin_metrics_path}")
         print(f"- 自定义指标已保存到: {custom_metrics_path}")
@@ -81,17 +84,18 @@ def split_metrics_config(all_metrics_path, builtin_metrics_path, custom_metrics_
     else:
         print("未发现自定义指标")
 
+
 if __name__ == "__main__":
     # 配置文件路径
     all_metrics_path = '/home/kevin/kevin/zhaoyuan/zhaoyuan_v2.0/zhaoyuan_new/config/all_metrics_config.yaml'
     builtin_metrics_path = '/home/kevin/kevin/zhaoyuan/zhaoyuan_v2.0/zhaoyuan_new/config/metrics_config.yaml'
     custom_metrics_path = '/home/kevin/kevin/zhaoyuan/zhaoyuan_v2.0/zhaoyuan_new/config/custom_metrics_config.yaml'
-    
+
     # 确保文件存在
     if not Path(all_metrics_path).exists():
         raise FileNotFoundError(f"{all_metrics_path} 文件不存在")
     if not Path(builtin_metrics_path).exists():
         raise FileNotFoundError(f"{builtin_metrics_path} 文件不存在")
-    
+
     # 执行拆分
     split_metrics_config(all_metrics_path, builtin_metrics_path, custom_metrics_path)

Some files were not shown because too many files changed in this diff