cicv 3 settimane fa
parent
commit
a1a20dda6d

+ 30 - 31
config/all_metrics_config.yaml

@@ -1,20 +1,22 @@
 vehicle:
-  CAR_WIDTH: 1.872       
-  CAR_LENGTH: 4.924      
-  CAR_HEIGHT: 1.3        
-  CAR_OFFX: 1.321        
-  RHO: 0.3               
-  EGO_ACCEL_MAX: 6       
-  OBJ_DECEL_MAX: 8       
-  EGO_DECEL_MIN: 1       
-  EGO_DECEL_LON_MAX: 8   
-  EGO_DECEL_LAT_MAX: 1   
-  EGO_WHEELBASS: 2.8     
+  # 车辆参数配置
+  CAR_WIDTH: 2.065       # 车体宽度(单位:米)
+  CAR_LENGTH: 5.990      # 车体长度(单位:米)
+  CAR_HEIGHT: 2.820        # 车体高度(单位:米)新增
+  CAR_OFFX: 1.321        # 车辆长度方向参考点到几何中心点的距离(单位:米)新增
+  RHO: 0.3               # 驾驶员制动反应时间(单位:秒)
+  EGO_ACCEL_MAX: 2.268       # 自车油门最大加速度(单位:m/s²)
+  OBJ_DECEL_MAX: 8       # 前车刹车最大减速度(单位:m/s²)
+  EGO_DECEL_MIN: 0.01       # 自车刹车最小减速度(需确认单位:m/s²)
+  EGO_DECEL_LON_MAX: 5.7869   # 自车纵向刹车最大减速度(单位:m/s²)
+  EGO_DECEL_LAT_MAX: 0   # 自车横向刹车最大减速度(单位:m/s²)
+  EGO_WHEELBASS: 3.8     # 自车轮距(单位:米)
 
 T_threshold:
-  T0_threshold: 0  
-  T1_threshold: 2  
-  T2_threshold: 5  
+  T0_threshold: 0  # 表示T0等级的通过阈值
+  T1_threshold: 2  # 表示T1等级的通过阈值
+  T2_threshold: 5  # 表示T2等级的通过阈值
+
 
 safety:
   name: safety
@@ -180,7 +182,7 @@ function:
       priority: 0
       max: 17.29
       min: 10.51
-
+      
 traffic:
   name: traffic
   priority: 0
@@ -192,17 +194,6 @@ traffic:
       priority: 0
       max: 0
       min: 0
-    urbanExpresswayOrHighwayReverse:
-      name: urbanExpresswayOrHighwayReverse
-      priority: 0
-      max: 0
-      min: 0
-    urbanExpresswayOrHighwayDrivingAgainst:
-      name: urbanExpresswayOrHighwayDrivingAgainst
-      priority: 0
-      max: 0
-      min: 0
-
   seriousViolation:
     name: seriousViolation
     priority: 0
@@ -240,7 +231,6 @@ traffic:
       priority: 0
       max: 0
       min: 0
-
   generalViolation:
     name: generalViolation
     priority: 0
@@ -343,12 +333,21 @@ traffic:
   minorViolation:
     name: minorViolation
     priority: 0
-    noUTurnViolation:
-      name: noUTurnViolation
+    turn_in_forbiden_turn_left_sign:
+      name: turn_in_forbiden_turn_left_sign
+      priority: 0
+      max: 0
+      min: 0
+    turn_in_forbiden_turn_back_sign:
+      name: turn_in_forbiden_turn_back_sign
+      priority: 0
+      max: 0
+      min: 0
+    avoid_pedestrian_when_turn_back:
+      name: avoid_pedestrian_when_turn_back
       priority: 0
       max: 0
       min: 0
-
   warningViolation:
     name: warningViolation
     priority: 0
@@ -366,4 +365,4 @@ traffic:
       name: generalRoadIrregularLaneUse
       priority: 0
       max: 0
-      min: 0
+      min: 0

File diff suppressed because it is too large
+ 109 - 0
logs/test.log


BIN
modules/lib/__pycache__/data_process.cpython-313.pyc


+ 81 - 19
modules/lib/data_process.py

@@ -33,7 +33,7 @@ class DataPreprocessing:
         # self.logger = log.get_logger()
         
         self.data_path = data_path
-        self.case_name = os.path.basename(os.path.dirname(data_path))
+        self.case_name = os.path.basename(os.path.normpath(data_path))
 
         self.config_path = config_path
 
@@ -117,26 +117,36 @@ class DataPreprocessing:
         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(
-                merged_csv_path, dtype={"simTime": float}
+                merged_csv_path,
+                dtype={"simTime": float},
+                engine="python",
+                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()
 
-            # Calculate common parameters
+            # 使用向量化操作计算速度和加速度,提高性能
             data["lat_v"] = data["speedY"] * 1
             data["lon_v"] = data["speedX"] * 1
-            data["v"] = data.apply(
-                lambda row: self.cal_velocity(row["lat_v"], row["lon_v"]), axis=1
-            )
-            data["v"] = data["v"]  # km/h
+            # 使用向量化操作代替 apply
+            data["v"] = np.sqrt(data["lat_v"]**2 + data["lon_v"]**2)
 
-            # Calculate acceleration components
+            # 计算加速度分量
             data["lat_acc"] = data["accelY"] * 1
             data["lon_acc"] = data["accelX"] * 1
-            data["accel"] = data.apply(
-                lambda row: self.cal_velocity(row["lat_acc"], row["lon_acc"]), axis=1
-            )
+            # 使用向量化操作代替 apply
+            data["accel"] = np.sqrt(data["lat_acc"]**2 + data["lon_acc"]**2)
+
 
             # Drop rows with missing 'type' and reset index
             data = data.dropna(subset=["type"])
@@ -152,14 +162,31 @@ class DataPreprocessing:
             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)
+
         except Exception as e:
-            # self.logger.error(f"Error processing object DataFrame: {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()
+        
+        # 确保数据按时间排序
+        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()
@@ -212,16 +239,51 @@ class DataPreprocessing:
 
     def _mileage_cal(self, df):
         """Calculate mileage based on the driving data."""
+        if len(df) < 2:
+            return 0.0  # 数据不足,无法计算里程
+            
         if df["travelDist"].nunique() == 1:
-            df["time_diff"] = df["simTime"].diff().fillna(0)
-            df["avg_speed"] = (df["v"] + df["v"].shift()).fillna(0) / 2
-            df["distance_increment"] = df["avg_speed"] * df["time_diff"] / 3.6
-            df["travelDist"] = df["distance_increment"].cumsum().fillna(0)
-
-            mileage = round(df["travelDist"].iloc[-1] - df["travelDist"].iloc[0], 2)
+            # 创建临时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
+        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]
+        return df["simTime"].iloc[-1] - df["simTime"].iloc[0]
+
+    def process_ego_data(self, ego_data):
+        """处理自车数据,包括坐标系转换等"""
+        if ego_data is None or len(ego_data) == 0:
+            logger = LogManager().get_logger()
+            logger.warning("自车数据为空,无法进行坐标系转换")
+            return ego_data
+            
+        # 创建副本避免修改原始数据
+        ego_data = ego_data.copy()
+        
+        # 添加坐标系转换:将东北天坐标系下的加速度转换为车辆坐标系下的加速度
+        # 使用车辆航向角进行转换
+        # 注意:与safety.py保持一致,使用(90 - heading)作为与x轴的夹角
+        ego_data['heading_rad'] = np.deg2rad(90 - ego_data['posH'])  # 转换为与x轴的夹角
+        
+        # 使用向量化操作计算车辆坐标系下的纵向和横向加速度
+        ego_data['lon_acc_vehicle'] = ego_data['accelX'] * np.cos(ego_data['heading_rad']) + \
+                                     ego_data['accelY'] * np.sin(ego_data['heading_rad'])
+        ego_data['lat_acc_vehicle'] = -ego_data['accelX'] * np.sin(ego_data['heading_rad']) + \
+                                     ego_data['accelY'] * np.cos(ego_data['heading_rad'])
+        
+        # 将原始的东北天坐标系加速度保留,但在comfort.py中使用车辆坐标系加速度
+        ego_data['lon_acc'] = ego_data['lon_acc_vehicle']
+        ego_data['lat_acc'] = ego_data['lat_acc_vehicle']
+        
+        return ego_data

BIN
modules/metric/__pycache__/comfort.cpython-313.pyc


BIN
modules/metric/__pycache__/efficient.cpython-313.pyc


BIN
modules/metric/__pycache__/function.cpython-313.pyc


BIN
modules/metric/__pycache__/safety.cpython-313.pyc


BIN
modules/metric/__pycache__/traffic.cpython-313.pyc


+ 106 - 62
modules/metric/comfort.py

@@ -8,7 +8,7 @@
 """
 @Authors:           zhanghaiwen(zhanghaiwen@china-icv.cn), yangzihao(yangzihao@china-icv.cn)
 @Data:              2023/06/25
-@Last Modified:     2023/06/25
+@Last Modified:     2025/04/25
 @Summary:           Comfort metrics
 """
 
@@ -168,6 +168,15 @@ class ComfortCalculator:
         self.ego_df = pd.DataFrame()
         self.discomfort_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
         
+        # 统计指标
+        self.calculated_value = {
+            'weaving': 0, 
+            'shake': 0, 
+            'cadence': 0,
+            'slamBrake': 0, 
+            'slamAccelerate': 0
+        }
+        
         self.time_list = self.data['simTime'].values.tolist()
         self.frame_list = self.data['simFrame'].values.tolist()
         
@@ -179,8 +188,6 @@ class ComfortCalculator:
         
         self.zigzag_time_list = []
         self.zigzag_stre_list = []
-        self.cur_ego_path_list = []
-        self.curvature_list = []
         
         self._initialize_data()
     
@@ -202,16 +209,6 @@ class ComfortCalculator:
         self.ego_df['cadence'] = self.ego_df.apply(
             lambda row: self._cadence_process_new(row['lon_acc'], row['ip_acc'], row['ip_dec']), axis=1)
 
-        # 计算曲率相关参数
-        self.ego_df['cur_ego_path'] = self.ego_df.apply(self._cal_cur_ego_path, axis=1)
-        self.ego_df['curvHor'] = self.ego_df['curvHor'].astype('float')
-        self.ego_df['cur_diff'] = (self.ego_df['cur_ego_path'] - self.ego_df['curvHor']).abs()
-        self.ego_df['R'] = self.ego_df['curvHor'].apply(lambda x: 10000 if x == 0 else 1 / x)
-        self.ego_df['R_ego'] = self.ego_df['cur_ego_path'].apply(lambda x: 10000 if x == 0 else 1 / x)
-        self.ego_df['R_diff'] = (self.ego_df['R_ego'] - self.ego_df['R']).abs()
-        
-        self.cur_ego_path_list = self.ego_df['cur_ego_path'].values.tolist()
-        self.curvature_list = self.ego_df['curvHor'].values.tolist()
     
     def _cal_cur_ego_path(self, row):
         """计算车辆轨迹曲率"""
@@ -227,21 +224,25 @@ class ComfortCalculator:
     
     def _peak_valley_determination(self, df):
         """确定角速度的峰谷"""
-        peaks, _ = scipy.signal.find_peaks(df['speedH'], height=0.01, distance=1, prominence=0.01)
-        valleys, _ = scipy.signal.find_peaks(-df['speedH'], height=0.01, distance=1, prominence=0.01)
-        peak_valley = sorted(list(peaks) + list(valleys))
-        return peak_valley
+        peaks, _ = scipy.signal.find_peaks(
+            df['speedH'], height=2.3, distance=3, 
+            prominence=2.3, width=1)
+        valleys, _ = scipy.signal.find_peaks(
+            -df['speedH'], height=2.3, distance=3, 
+            prominence=2.3, width=1)
+        return sorted(list(peaks) + list(valleys))
     
-    def _peak_valley_judgment(self, p_last, p_curr, tw=10000, avg=0.02):
+    def _peak_valley_judgment(self, p_last, p_curr, tw=100, avg=4.6):
         """判断峰谷是否满足蛇行条件"""
         t_diff = p_curr[0] - p_last[0]
         v_diff = abs(p_curr[1] - p_last[1])
         s = p_curr[1] * p_last[1]
 
-        zigzag_flag = t_diff < tw and v_diff > avg and s < 0
-        if zigzag_flag and ([p_last[0], p_curr[0]] not in self.zigzag_time_list):
-            self.zigzag_time_list.append([p_last[0], p_curr[0]])
-        return zigzag_flag
+        if t_diff < tw and v_diff > avg and s < 0:
+            if [p_last[0], p_curr[0]] not in self.zigzag_time_list:
+                self.zigzag_time_list.append([p_last[0], p_curr[0]])
+            return True
+        return False
     
     def _cadence_process_new(self, lon_acc, ip_acc, ip_dec):
         """处理顿挫数据"""
@@ -270,10 +271,22 @@ class ComfortCalculator:
         if flag:
             v_diff = abs(p_curr[1] - p_last[1])
             t_diff = p_curr[0] - p_last[0]
-            self.zigzag_stre_list.append(v_diff / t_diff)  # 平均角加速度
+            if t_diff > 0:
+                self.zigzag_stre_list.append(v_diff / t_diff)  # 平均角加速度
         else:
             self.zigzag_stre_list = []
     
+    def _get_zigzag_times(self):
+        """获取所有蛇行时间点"""
+        all_times = []
+        for time_range in self.zigzag_time_list:
+            start, end = time_range
+            # 获取这个时间范围内的所有时间点
+            times_in_range = self.ego_df[(self.ego_df['simTime'] >= start) & 
+                                         (self.ego_df['simTime'] <= end)]['simTime'].tolist()
+            all_times.extend(times_in_range)
+        return all_times
+    
     def calculate_zigzag_count(self):
         """计算蛇行指标"""
         self._zigzag_count_func()
@@ -299,49 +312,80 @@ class ComfortCalculator:
         self._slam_accel_detector()
         return self.slam_accel_count
     
-    def _shake_detector(self, Cr_diff=0.05, T_diff=0.39):
-        """晃动检测器"""
+    def _shake_detector(self, T_diff=0.5):
+        """检测晃动事件 - 改进版本(不使用车辆轨迹曲率)"""
+        # lat_acc已经是车辆坐标系下的横向加速度,由data_process.py计算
         time_list = []
         frame_list = []
 
+        # 复制数据以避免修改原始数据
         df = self.ego_df.copy()
-        df = df[df['cur_diff'] > Cr_diff]
-        df['frame_ID_diff'] = df['simFrame'].diff()
-        filtered_df = df[df.frame_ID_diff > T_diff]
-
-        row_numbers = filtered_df.index.tolist()
-        cut_column = pd.cut(df.index, bins=row_numbers)
-
-        grouped = df.groupby(cut_column)
-        dfs = {}
-        for name, group in grouped:
-            dfs[name] = group.reset_index(drop=True)
-
-        for name, df_group in dfs.items():
-            # 直道,未主动换道
-            df_group['curvHor'] = df_group['curvHor'].abs()
-            df_group_straight = df_group[(df_group.lightMask == 0) & (df_group.curvHor < 0.001)]
-            if not df_group_straight.empty:
-                time_list.extend(df_group_straight['simTime'].values)
-                frame_list.extend(df_group_straight['simFrame'].values)
-                self.shake_count = self.shake_count + 1
-
-            # 打转向灯,道路为直道
-            df_group_change_lane = df_group[(df_group['lightMask'] != 0) & (df_group['curvHor'] < 0.001)]
-            df_group_change_lane_data = df_group_change_lane[df_group_change_lane.cur_diff > Cr_diff + 0.2]
-            if not df_group_change_lane_data.empty:
-                time_list.extend(df_group_change_lane_data['simTime'].values)
-                frame_list.extend(df_group_change_lane_data['simFrame'].values)
-                self.shake_count = self.shake_count + 1
-
-            # 转弯,打转向灯
-            df_group_turn = df_group[(df_group['lightMask'] != 0) & (df_group['curvHor'].abs() > 0.001)]
-            df_group_turn_data = df_group_turn[df_group_turn.cur_diff.abs() > Cr_diff + 0.1]
-            if not df_group_turn_data.empty:
-                time_list.extend(df_group_turn_data['simTime'].values)
-                frame_list.extend(df_group_turn_data['simFrame'].values)
-                self.shake_count = self.shake_count + 1
-
+        
+        # 1. 计算横向加速度变化率
+        df['lat_acc_rate'] = df['lat_acc'].diff() / df['simTime'].diff()
+        
+        # 2. 计算横摆角速度变化率
+        df['speedH_rate'] = df['speedH'].diff() / df['simTime'].diff()
+        
+        # 3. 计算横摆角速度的短期变化特性
+        window_size = 5  # 5帧窗口
+        df['speedH_std'] = df['speedH'].rolling(window=window_size, min_periods=2).std()
+        
+        # 4. 基于车速的动态阈值
+        v0 = 20 * 5/18        # ≈5.56 m/s
+        # 递减系数
+        k  = 0.008 * 3.6      # =0.0288 per m/s
+        df['lat_acc_threshold'] = df['v'].apply(
+            lambda speed: max(
+                1.0,                                   # 下限 1.0 m/s²
+                min(
+                    1.8,                               # 上限 1.8 m/s²
+                    1.8 - k * (speed - v0)             # 线性递减
+                )
+            )
+        )
+        
+        df['speedH_threshold'] = df['v'].apply(
+            lambda speed: max(1.5, min(3.0, 2.0 * (1 + (speed - 20) / 60)))
+        )
+        # 将计算好的阈值和中间变量保存到self.ego_df中,供其他函数使用
+        self.ego_df['lat_acc_threshold'] = df['lat_acc_threshold']
+        self.ego_df['speedH_threshold'] = df['speedH_threshold']
+        self.ego_df['lat_acc_rate'] = df['lat_acc_rate']
+        self.ego_df['speedH_rate'] = df['speedH_rate']
+        self.ego_df['speedH_std'] = df['speedH_std']
+        
+        # 5. 综合判断晃动条件
+        # 条件A: 横向加速度超过阈值
+        condition_A = df['lat_acc'].abs() > df['lat_acc_threshold']
+        
+        # 条件B: 横向加速度变化率超过阈值
+        lat_acc_rate_threshold = 0.5  # 横向加速度变化率阈值 (m/s³)
+        condition_B = df['lat_acc_rate'].abs() > lat_acc_rate_threshold
+        
+        # 条件C: 横摆角速度有明显变化但不呈现周期性
+        condition_C = (df['speedH_std'] > df['speedH_threshold']) & (~df['simTime'].isin(self._get_zigzag_times()))
+        
+        # 综合条件: 满足条件A,且满足条件B或条件C
+        shake_condition = condition_A & (condition_B | condition_C)
+        
+        # 筛选满足条件的数据
+        shake_df = df[shake_condition].copy()
+        
+        # 按照连续帧号分组,确保只有连续帧超过阈值的才被认为是晃动
+        if not shake_df.empty:
+            shake_df['frame_diff'] = shake_df['simFrame'].diff().fillna(0)
+            shake_df['group'] = (shake_df['frame_diff'] > T_diff).cumsum()
+            
+            # 分组统计
+            shake_groups = shake_df.groupby('group')
+            
+            for _, group in shake_groups:
+                if len(group) >= 2:  # 至少2帧才算一次晃动
+                    time_list.extend(group['simTime'].values)
+                    frame_list.extend(group['simFrame'].values)
+                    self.shake_count += 1
+        
         # 分组处理
         TIME_RANGE = 1
         t_list = time_list

+ 140 - 49
modules/metric/efficient.py

@@ -19,69 +19,161 @@ from typing import Dict, Tuple, Optional, Callable, Any
 import pandas as pd
 
 
+class Efficient:
+    """高效性指标计算类"""
+    
+    def __init__(self, data_processed):
+        """初始化高效性指标计算类
+        
+        Args:
+            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()  # 浅拷贝
+        
+        # 配置参数
+        self.STOP_SPEED_THRESHOLD = 0.05  # 停车速度阈值 (m/s)
+        self.STOP_TIME_THRESHOLD = 0.5    # 停车时间阈值 (秒)
+        self.FRAME_RANGE = 13             # 停车帧数阈值
+        
+        # 初始化结果变量
+        self.stop_count = 0     # 停车次数
+        self.stop_duration = 0  # 平均停车时长
+        self.average_v = 0      # 平均速度
+        
+    def _max_speed(self):
+        """计算最大速度
+        
+        Returns:
+            float: 最大速度 (m/s)
+        """
+        return self.ego_df['v'].max()
+
+    def _deviation_speed(self):
+        """计算速度方差
+        
+        Returns:
+            float: 速度方差
+        """
+        return self.ego_df['v'].var()
+
+    def average_velocity(self):
+        """计算平均速度
+        
+        Returns:
+            float: 平均速度 (m/s)
+        """
+        self.average_v = self.ego_df['v'].mean()
+        return self.average_v
+
+    def stop_duration_and_count(self):
+        """计算停车次数和平均停车时长
+        
+        Returns:
+            float: 平均停车时长 (秒)
+        """
+        # 获取速度低于阈值的时间和帧号
+        stop_mask = self.ego_df['v'] <= self.STOP_SPEED_THRESHOLD
+        if not any(stop_mask):
+            return 0  # 如果没有停车,直接返回0
+            
+        stop_time_list = self.ego_df.loc[stop_mask, 'simTime'].values.tolist()
+        stop_frame_list = self.ego_df.loc[stop_mask, 'simFrame'].values.tolist()
+        
+        if not stop_frame_list:
+            return 0  # 防止空列表导致的索引错误
+            
+        stop_frame_group = []
+        stop_time_group = []
+        sum_stop_time = 0
+        f1, t1 = stop_frame_list[0], stop_time_list[0]
+        
+        # 检测停车段
+        for i in range(1, len(stop_frame_list)):
+            if stop_frame_list[i] - stop_frame_list[i - 1] != 1:  # 帧不连续
+                f2, t2 = stop_frame_list[i - 1], stop_time_list[i - 1]
+                # 如果停车有效(帧数差 >= FRAME_RANGE)
+                if f2 - f1 >= self.FRAME_RANGE:
+                    stop_frame_group.append((f1, f2))
+                    stop_time_group.append((t1, t2))
+                    sum_stop_time += (t2 - t1)
+                    self.stop_count += 1
+                # 更新起始点
+                f1, t1 = stop_frame_list[i], stop_time_list[i]
+        
+        # 检查最后一段停车
+        if len(stop_frame_list) > 0:
+            f2, t2 = stop_frame_list[-1], stop_time_list[-1]
+            last_frame = self.ego_df['simFrame'].values[-1]
+            # 确保不是因为数据结束导致的停车
+            if f2 - f1 >= self.FRAME_RANGE and f2 != last_frame:
+                stop_frame_group.append((f1, f2))
+                stop_time_group.append((t1, t2))
+                sum_stop_time += (t2 - t1)
+                self.stop_count += 1
+        
+        # 计算平均停车时长
+        self.stop_duration = sum_stop_time / self.stop_count if self.stop_count > 0 else 0
+        
+        self.logger.info(f"检测到停车次数: {self.stop_count}, 平均停车时长: {self.stop_duration:.2f}秒")
+        return self.stop_duration
+
+    def report_statistic(self):
+        """生成统计报告
+        
+        Returns:
+            dict: 高效性评估结果
+        """
+        # 计算各项指标
+        max_speed_ms = self._max_speed()
+        deviation_speed_ms = self._deviation_speed()
+        average_speed_ms = self.average_velocity()
+        
+        # 将 m/s 转换为 km/h 用于评分
+        max_speed_kmh = max_speed_ms * 3.6
+        deviation_speed_kmh = deviation_speed_ms * 3.6
+        average_speed_kmh = average_speed_ms * 3.6
+        
+        efficient_result = {
+            'maxSpeed': max_speed_kmh,        # 转换为 km/h
+            'deviationSpeed': deviation_speed_kmh,  # 转换为 km/h
+            'averagedSpeed': average_speed_kmh,     # 转换为 km/h
+            'stopDuration': self.stop_duration_and_count()
+        }
+        
+        self.logger.info(f"高效性指标计算完成,结果: {efficient_result}")
+        
+        return efficient_result
+
+
 # ----------------------
 # 基础指标计算函数
 # ----------------------
 def maxSpeed(data_processed) -> dict:
     """计算最大速度"""
-    max_speed = data_processed.ego_data['v'].max()
+    efficient = Efficient(data_processed)
+    max_speed = efficient._max_speed() * 3.6  # 转换为 km/h
     return {"maxSpeed": float(max_speed)}
 
 def deviationSpeed(data_processed) -> dict:
     """计算速度方差"""
-    deviation = data_processed.ego_data['v'].var()
+    efficient = Efficient(data_processed)
+    deviation = efficient._deviation_speed() * 3.6  # 转换为 km/h
     return {"deviationSpeed": float(deviation)}
 
 def averagedSpeed(data_processed) -> dict:
     """计算平均速度"""
-    avg_speed = data_processed.ego_data['v'].mean()
+    efficient = Efficient(data_processed)
+    avg_speed = efficient.average_velocity() * 3.6  # 转换为 km/h
     return {"averagedSpeed": float(avg_speed)}
 
 def stopDuration(data_processed) -> dict:
     """计算停车持续时间和次数"""
-    STOP_SPEED_THRESHOLD = 0.05  # 停车速度阈值
-    FRAME_RANGE = 13  # 停车帧数阈值
-    
-    ego_df = data_processed.ego_data
-    
-    stop_time_list = ego_df[ego_df['v'] <= STOP_SPEED_THRESHOLD]['simTime'].values.tolist()
-    stop_frame_list = ego_df[ego_df['v'] <= STOP_SPEED_THRESHOLD]['simFrame'].values.tolist()
-
-    stop_frame_group = []
-    stop_time_group = []
-    sum_stop_time = 0
-    stop_count = 0
-    
-    if not stop_frame_list:
-        return {"stopDuration": 0.0, "stopCount": 0}
-        
-    f1, t1 = stop_frame_list[0], stop_time_list[0]
-
-    for i in range(1, len(stop_frame_list)):
-        if stop_frame_list[i] - stop_frame_list[i - 1] != 1:  # 帧不连续
-            f2, t2 = stop_frame_list[i - 1], stop_time_list[i - 1]
-            # 如果停车有效(帧间隔 >= FRAME_RANGE)
-            if f2 - f1 >= FRAME_RANGE:
-                stop_frame_group.append((f1, f2))
-                stop_time_group.append((t1, t2))
-                sum_stop_time += (t2 - t1)
-                stop_count += 1
-            # 更新 f1, t1
-            f1, t1 = stop_frame_list[i], stop_time_list[i]
-
-    # 检查最后一段停车
-    if len(stop_frame_list) > 0:
-        f2, t2 = stop_frame_list[-1], stop_time_list[-1]
-        if f2 - f1 >= FRAME_RANGE and f2 != ego_df['simFrame'].values[-1]:
-            stop_frame_group.append((f1, f2))
-            stop_time_group.append((t1, t2))
-            sum_stop_time += (t2 - t1)
-            stop_count += 1
-
-    # 计算停车持续时间
-    stop_duration = sum_stop_time / stop_count if stop_count != 0 else 0
-    
-    return {"stopDuration": float(stop_duration), "stopCount": stop_count}
+    efficient = Efficient(data_processed)
+    stop_duration = efficient.stop_duration_and_count()
+    return {"stopDuration": float(stop_duration)}
 
 
 class EfficientRegistry:
@@ -124,6 +216,8 @@ class EfficientRegistry:
             try:
                 result = func(self.data)
                 results.update(result)
+                # 新增:将每个指标的结果写入日志
+                self.logger.info(f'高效性指标[{name}]计算结果: {result}')
             except Exception as e:
                 self.logger.error(f"{name} 执行失败: {str(e)}", exc_info=True)
                 results[name] = None
@@ -141,8 +235,5 @@ class EfficientManager:
         """Generate the statistics and report the results."""
         # 使用注册表批量执行指标计算
         efficient_result = self.efficient.batch_execute()
-        # evaluator = Score(self.data.efficient_config)
-        # result = evaluator.evaluate(efficient_result) 
-        # return result
         return efficient_result
         

+ 18 - 6
modules/metric/function.py

@@ -92,30 +92,33 @@ def get_first_warning(data_processed) -> Optional[pd.DataFrame]:
 def latestWarningDistance_LST(data) -> dict:
     """预警距离计算流水线"""
     scenario_name = data.function_config["function"]["scenario"]["name"]
+    value = data.function_config["function"]["scenario"]["latestWarningDistance_LST"]["max"]
     correctwarning = scenario_sign_dict[scenario_name]
     ego_df = data.ego_data
     warning_dist = calculate_distance(ego_df, correctwarning)
     if warning_dist.empty:
         return {"latestWarningDistance_LST": 0.0}
 
-    return {"latestWarningDistance_LST": float(warning_dist.iloc[-1])}
+    return {"latestWarningDistance_LST": float(warning_dist.iloc[-1]) if len(warning_dist) > 0 else value}
 
 
 def earliestWarningDistance_LST(data) -> dict:
     """预警距离计算流水线"""
     scenario_name = data.function_config["function"]["scenario"]["name"]
+    value = data.function_config["function"]["scenario"]["earliestWarningDistance_LST"]["max"]
     correctwarning = scenario_sign_dict[scenario_name]
     ego_df = data.ego_data
     warning_dist = calculate_distance(ego_df, correctwarning)
     if warning_dist.empty:
         return {"earliestWarningDistance_LST": 0.0}
 
-    return {"earliestWarningDistance_LST": float(warning_dist.iloc[0]) if len(warning_dist) > 0 else np.inf}
+    return {"earliestWarningDistance_LST": float(warning_dist.iloc[0]) if len(warning_dist) > 0 else value}
 
 
 def latestWarningDistance_TTC_LST(data) -> dict:
     """TTC计算流水线"""
     scenario_name = data.function_config["function"]["scenario"]["name"]
+    value = data.function_config["function"]["scenario"]["latestWarningDistance_TTC_LST"]["max"]
     correctwarning = scenario_sign_dict[scenario_name]
     ego_df = data.ego_data
     warning_dist = calculate_distance(ego_df, correctwarning)
@@ -126,13 +129,18 @@ def latestWarningDistance_TTC_LST(data) -> dict:
 
     with np.errstate(divide='ignore', invalid='ignore'):
         ttc = np.where(warning_speed != 0, warning_dist / warning_speed, np.inf)
-
-    return {"latestWarningDistance_TTC_LST": float(ttc[-1]) if len(ttc) > 0 else np.inf}
+    
+    # 处理无效的TTC值
+    for i in range(len(ttc)):
+        ttc[i] = float(value) if (not ttc[i] or ttc[i] < 0) else ttc[i]
+        
+    return {"latestWarningDistance_TTC_LST": float(ttc[-1]) if len(ttc) > 0 else value}
 
 
 def earliestWarningDistance_TTC_LST(data) -> dict:
     """TTC计算流水线"""
     scenario_name = data.function_config["function"]["scenario"]["name"]
+    value = data.function_config["function"]["scenario"]["earliestWarningDistance_TTC_LST"]["max"]
     correctwarning = scenario_sign_dict[scenario_name]
     ego_df = data.ego_data
     warning_dist = calculate_distance(ego_df, correctwarning)
@@ -143,8 +151,12 @@ def earliestWarningDistance_TTC_LST(data) -> dict:
 
     with np.errstate(divide='ignore', invalid='ignore'):
         ttc = np.where(warning_speed != 0, warning_dist / warning_speed, np.inf)
-
-    return {"earliestWarningDistance_TTC_LST": float(ttc[0]) if len(ttc) > 0 else np.inf}
+    
+    # 处理无效的TTC值
+    for i in range(len(ttc)):
+        ttc[i] = float(value) if (not ttc[i] or ttc[i] < 0) else ttc[i]
+        
+    return {"earliestWarningDistance_TTC_LST": float(ttc[0]) if len(ttc) > 0 else value}
 
 
 def warningDelayTime_LST(data):

+ 58 - 40
modules/metric/safety.py

@@ -29,20 +29,6 @@ SAFETY_INFO = [
     "type"
 ]
 
-# ----------------------
-# SafetyCalculator单例管理
-# ----------------------
-# class SafetyCalculatorManager:
-#     """SafetyCalculator单例管理器"""
-#     _instance = None
-    
-#     @classmethod
-#     def get_instance(cls, data_processed):
-#         """获取SafetyCalculator实例,如果不存在则创建"""
-#         if cls._instance is None or cls._instance.data_processed != data_processed:
-#             cls._instance = SafetyCalculator(data_processed)
-#         return cls._instance
-
 # ----------------------
 # 独立指标计算函数
 # ----------------------
@@ -53,6 +39,7 @@ def calculate_ttc(data_processed) -> dict:
     try:
         safety = SafetyCalculator(data_processed)
         ttc_value = safety.get_ttc_value()
+        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)
@@ -65,6 +52,7 @@ def calculate_mttc(data_processed) -> dict:
     try:
         safety = SafetyCalculator(data_processed)
         mttc_value = safety.get_mttc_value()
+        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)
@@ -77,6 +65,7 @@ def calculate_thw(data_processed) -> dict:
     try:
         safety = SafetyCalculator(data_processed)
         thw_value = safety.get_thw_value()
+        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)
@@ -86,30 +75,35 @@ def calculate_collisionrisk(data_processed) -> dict:
     """计算碰撞风险"""
     safety = SafetyCalculator(data_processed)
     collision_risk_value = safety.get_collision_risk_value()
+    LogManager().get_logger().info(f"安全指标[collisionRisk]计算结果: {collision_risk_value}")
     return {"collisionRisk": collision_risk_value}
 
 def calculate_lonsd(data_processed) -> dict:
     """计算纵向安全距离"""
     safety = SafetyCalculator(data_processed)
     lonsd_value = safety.get_lonsd_value()
+    LogManager().get_logger().info(f"安全指标[LonSD]计算结果: {lonsd_value}")
     return {"LonSD": lonsd_value}
 
 def calculate_latsd(data_processed) -> dict:
     """计算横向安全距离"""
     safety = SafetyCalculator(data_processed)
     latsd_value = safety.get_latsd_value()
+    LogManager().get_logger().info(f"安全指标[LatSD]计算结果: {latsd_value}")
     return {"LatSD": latsd_value}
 
 def calculate_btn(data_processed) -> dict:
     """计算制动威胁数"""
     safety = SafetyCalculator(data_processed)
     btn_value = safety.get_btn_value()
+    LogManager().get_logger().info(f"安全指标[BTN]计算结果: {btn_value}")
     return {"BTN": btn_value}
 
 def calculate_collisionseverity(data_processed) -> dict:
     """计算碰撞严重性"""
     safety = SafetyCalculator(data_processed)
     collision_severity_value = safety.get_collision_severity_value()
+    LogManager().get_logger().info(f"安全指标[collisionSeverity]计算结果: {collision_severity_value}")
     return {"collisionSeverity": collision_severity_value}
 
 
@@ -183,18 +177,29 @@ class SafetyCalculator:
         self.data_processed = data_processed
 
         self.df = data_processed.object_df.copy()
-        self.ego_df = data_processed.ego_data
+        self.ego_df = data_processed.ego_data.copy()  # 使用copy()避免修改原始数据
         self.obj_id_list = data_processed.obj_id_list
         self.metric_list = [
             'TTC', 'MTTC', 'THW', 'LonSD', 'LatSD', 'BTN', 'collisionRisk', 'collisionSeverity'
         ]
 
+        # 初始化默认值
+        self.calculated_value = {
+            "TTC": 10.0,
+            "MTTC": 10.0,
+            "THW": 10.0,
+            "LatSD": 3.0,
+            "BTN": 1.0,
+            "collisionRisk": 0.0,
+            "collisionSeverity": 0.0,
+        }
+
         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.logger.info("SafetyCalculator初始化完成,obj_id_list长度: %d", len(self.obj_id_list))
+        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'])
@@ -262,15 +267,18 @@ class SafetyCalculator:
                 a_y2 = obj_data['accelY']
 
                 dx, dy = x2 - x1, y2 - y1
-                A = np.array([dx, dy])
-                x = np.array([1, 0])
-                dot_product = np.dot(A, x)
-                vector_length_A = np.linalg.norm(A)
-                vector_length_x = np.linalg.norm(x)
-                cos_theta = dot_product / (vector_length_A * vector_length_x)
-                beta = np.arccos(cos_theta)
-                lon_d = dist * math.cos(beta - h1)
-                lat_d = abs(dist * math.sin(beta - h1))
+                
+                # 计算目标车相对于自车的方位角
+                beta = math.atan2(dy, dx)
+                
+                # 将全局坐标系下的相对位置向量转换到自车坐标系
+                # 自车坐标系:车头方向为x轴正方向,车辆左侧为y轴正方向
+                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
 
@@ -311,21 +319,22 @@ class SafetyCalculator:
                 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) + a_y1 * math.sin(h1)
-                lon_a2 = a_x2 * math.cos(h1) + a_y2 * math.sin(h1)
+                # 使用自车坐标系下的纵向加速度
+                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, dist * abs(math.cos(beta - h1)))
-                lon_v = v_x1 * math.cos(h1) + v_y1 * math.sin(h1)
+                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) * -1 + a_y1 * math.cos(h1)
-                lat_a2 = a_x2 * math.sin(h1) * -1 + a_y2 * math.cos(h1)
+                # 使用自车坐标系下的横向加速度
+                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_d = dist * abs(math.sin(beta - h1))
-                lat_v = v_x1 * math.sin(h1) * -1 + v_y1 * math.cos(h1)
+                lat_v = -v_x1 * math.sin(h1_rad) + v_y1 * math.cos(h1_rad)
 
-                obj_dict[frame_num][playerId]['lat_v_rel'] = v_x1 - v_x2
-                obj_dict[frame_num][playerId]['lon_v_rel'] = v_y1 - v_y2
+                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
@@ -512,12 +521,20 @@ class SafetyCalculator:
                 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):
+    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)
+        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
 
     # DRAC (decelerate required avoid collision)
@@ -673,7 +690,8 @@ class SafetyCalculator:
         if self.empty_flag or self.df_safe is None:
             return self._default_value('LatSD')
         latsd_values = self.df_safe['LatSD'].dropna()
-        return float(latsd_values.mean()) if not latsd_values.empty else self._default_value('LatSD')
+        # 使用最小值而非平均值,与safety1.py保持一致
+        return float(latsd_values.min()) if not latsd_values.empty else self._default_value('LatSD')
 
     def get_btn_value(self) -> float:
         if self.empty_flag or self.df_safe is None:

+ 416 - 147
modules/metric/traffic.py

@@ -1,6 +1,7 @@
 # ... 保留原有导入和常量定义 ...
 import math
-import operator
+import operator 
+import copy
 import numpy as np
 import pandas as pd
 from typing import Dict, Any, List, Optional
@@ -9,6 +10,7 @@ from modules.lib.score import Score
 from modules.lib.log_manager import LogManager
 from modules.lib import data_process
 
+
 OVERTAKE_INFO = [
     "simTime",
     "simFrame",
@@ -49,7 +51,7 @@ TURNAROUND_INFO = [
 
 TRFFICSIGN_INFO = [
     "simTime",
-    "simFrame",
+    "simFrame",  
     "playerId",
     "speedX",
     "speedY",
@@ -261,7 +263,7 @@ def calculate_generalroadirregularlaneuse(data_processed):
 def calculate_urbanexpresswayorhighwayridelanedivider(data_processed):
     """计算城市快速路或高速公路骑车道线行驶指标"""
     warningviolation = WarningViolation(data_processed)
-    urbanExpresswayOrHighwayRideLaneDivider_count = warningviolation.calculate_urbanExpresswayOrHighwayRideLaneDivider()
+    urbanExpresswayOrHighwayRideLaneDivider_count = warningviolation.calculate_urbanExpresswayOrHighwayRideLaneDivider_count()
     return {"urbanExpresswayOrHighwayRideLaneDivider": urbanExpresswayOrHighwayRideLaneDivider_count}
 
 def calculate_nostraightthrough(data_processed):
@@ -283,7 +285,7 @@ def calculate_minimumspeedlimitviolation(data_processed):
     trafficsignviolation = TrafficSignViolation(data_processed)
     calculate_MinimumSpeedLimitViolation_count = trafficsignviolation.calculate_MinimumSpeedLimitViolation_count()
     return {"MinimumSpeedLimitViolation": calculate_MinimumSpeedLimitViolation_count}
-# ... 保留原有类定义 ...
+
 
 # 修改 TrafficRegistry 类的 _build_registry 方法
 class TrafficRegistry:
@@ -349,7 +351,6 @@ class TrafficManager:
         traffic_result = self.registry.batch_execute()
         return traffic_result
 
-# ... 保留原有类定义和实现 ...
 
 class OvertakingViolation(object):
     """超车违规类"""
@@ -358,36 +359,78 @@ class OvertakingViolation(object):
         print("超车违规类初始化中...")
         self.traffic_violations_type = "超车违规类"
 
-        # self.logger = log.get_logger()  # 使用时再初始化
-
-        self.data = df_data.obj_data[1]
-        self.ego_data = (
-            self.data[OVERTAKE_INFO].copy().reset_index(drop=True)
-        )  # Copy to avoid modifying the original DataFrame
-        header = self.ego_data.columns
+        # 存储原始数据引用,不进行拷贝
+        self._raw_data = df_data.obj_data[1]  # 自车数据
+        
+        # 安全获取其他车辆数据
+        self._data_obj = None
+        self._other_obj_data1 = None
+        
+        # 检查是否存在ID为2的对象数据
         if 2 in df_data.obj_id_list:
-            self.data_obj = df_data.obj_data[2]
-            self.obj_data = (
-                self.data_obj[OVERTAKE_INFO].copy().reset_index(drop=True)
-            )  # Copy to avoid modifying the original DataFrame
-        else:
-            self.obj_data = pd.DataFrame(columns=header)
+            self._data_obj = df_data.obj_data[2]
+        
+        # 检查是否存在ID为3的对象数据
         if 3 in df_data.obj_id_list:
-            self.other_obj_data1 = df_data.obj_data[3]
-            self.other_obj_data = (
-                self.other_obj_data1[OVERTAKE_INFO].copy().reset_index(drop=True)
-            )
-        else:
-            self.other_obj_data = pd.DataFrame(columns=header)
-        self.overtake_on_right_count = 0
-        self.overtake_when_turn_around_count = 0
-        self.overtake_when_passing_car_count = 0
-        self.overtake_in_forbid_lane_count = 0
-        self.overtake_in_ramp_count = 0
-        self.overtake_in_tunnel_count = 0
-        self.overtake_on_accelerate_lane_count = 0
-        self.overtake_on_decelerate_lane_count = 0
-        self.overtake_in_different_senerios_count = 0
+            self._other_obj_data1 = df_data.obj_data[3]
+        
+        # 初始化属性,但不立即创建数据副本
+        self._ego_data = None
+        self._obj_data = None
+        self._other_obj_data = None
+        
+        # 使用字典统一管理违规计数器
+        self.violation_counts = {
+            "overtake_on_right": 0,
+            "overtake_when_turn_around": 0,
+            "overtake_when_passing_car": 0,
+            "overtake_in_forbid_lane": 0,
+            "overtake_in_ramp": 0,
+            "overtake_in_tunnel": 0,
+            "overtake_on_accelerate_lane": 0,
+            "overtake_on_decelerate_lane": 0,
+            "overtake_in_different_senerios": 0
+        }
+        
+        # 标记计算状态
+        self._calculated = {
+            "illegal_overtake": False,
+            "forbid_lane": False,
+            "ramp_area": False,
+            "tunnel_area": False,
+            "accelerate_lane": False,
+            "decelerate_lane": False,
+            "different_senerios": False
+        }
+     
+    @property
+    def ego_data(self):
+        """懒加载方式获取ego数据,只在首次访问时创建副本"""
+        if self._ego_data is None:
+            self._ego_data = self._raw_data[OVERTAKE_INFO].copy().reset_index(drop=True)
+        return self._ego_data
+    
+    @property
+    def obj_data(self):
+        """懒加载方式获取obj数据"""
+        if self._obj_data is None:
+            if self._data_obj is not None:
+                self._obj_data = self._data_obj[OVERTAKE_INFO].copy().reset_index(drop=True)
+            else:
+                # 如果没有数据,创建一个空的DataFrame,列名与ego_data相同
+                self._obj_data = pd.DataFrame(columns=OVERTAKE_INFO)
+        return self._obj_data
+    
+    @property
+    def other_obj_data(self):
+        """懒加载方式获取other_obj数据"""
+        if self._other_obj_data is None:
+            if self._other_obj_data1 is not None:
+                self._other_obj_data = self._other_obj_data1[OVERTAKE_INFO].copy().reset_index(drop=True)
+            else:
+                # 如果没有数据,创建一个空的DataFrame,列名与ego_data相同
+                self._other_obj_data = pd.DataFrame(columns=OVERTAKE_INFO)
+        return self._other_obj_data
 
     def different_road_area_simtime(self, df, threshold=0.5):
         if not df:
@@ -425,9 +468,17 @@ class OvertakingViolation(object):
 
         return car_dx, car_dy
 
-        # 在前车右侧超车、会车时超车、前车掉头时超车
-
     def illegal_overtake_with_car_detector(self, window_width=250):
+        """检测超车违规"""
+        # 如果已经计算过,直接返回
+        if self._calculated["illegal_overtake"]:
+            return
+            
+        # 如果没有其他车辆数据,直接返回,保持默认值0
+        if self.obj_data.empty:
+            print("没有其他车辆数据,无法检测超车违规,默认为0")
+            self._calculated["illegal_overtake"] = True
+            return
 
         # 获取csv文件中最短的帧数
         frame_id_length = len(self.ego_data["simFrame"])
@@ -442,28 +493,47 @@ class OvertakingViolation(object):
             ego_data_frames = self.ego_data[
                 self.ego_data["simFrame"].isin(simframe_window)
             ]
+            
+            # 确保有足够的数据进行处理
+            if len(ego_data_frames) == 0:
+                start_frame_id += 1
+                continue
+                
             obj_data_frames = self.obj_data[
                 self.obj_data["simFrame"].isin(simframe_window)
             ]
+            
+            # 如果没有其他车辆数据,跳过当前窗口
+            if len(obj_data_frames) == 0:
+                start_frame_id += 1
+                continue
+                
             other_data_frames = self.other_obj_data[
                 self.other_obj_data["simFrame"].isin(simframe_window)
             ]
+            
             # 读取前后的laneId
             lane_id = ego_data_frames["lane_id"].tolist()
-            # 读取前后方向盘转角steeringWheel,
+            
+            # 读取前后方向盘转角steeringWheel
             driverctrl_start_state = ego_data_frames["posH"].iloc[0]
             driverctrl_end_state = ego_data_frames["posH"].iloc[-1]
+            
             # 读取车辆前后的位置信息
             dx, dy = self._is_dxy_of_car(ego_data_frames, obj_data_frames)
             ego_speedx = ego_data_frames["speedX"].tolist()
             ego_speedy = ego_data_frames["speedY"].tolist()
 
-            obj_speedx = obj_data_frames[obj_data_frames["playerId"] == 2][
-                "speedX"
-            ].tolist()
-            obj_speedy = obj_data_frames[obj_data_frames["playerId"] == 2][
-                "speedY"
-            ].tolist()
+            # 安全获取obj_speedx和obj_speedy
+            obj_with_id_2 = obj_data_frames[obj_data_frames["playerId"] == 2]
+            if not obj_with_id_2.empty:
+                obj_speedx = obj_with_id_2["speedX"].tolist()
+                obj_speedy = obj_with_id_2["speedY"].tolist()
+            else:
+                obj_speedx = []
+                obj_speedy = []
+                
+            # 检查会车时超车
             if len(other_data_frames) > 0:
                 other_start_speedx = other_data_frames["speedX"].iloc[0]
                 other_start_speedy = other_data_frames["speedY"].iloc[0]
@@ -472,31 +542,47 @@ class OvertakingViolation(object):
                         + ego_speedy[0] * other_start_speedy
                         < 0
                 ):
-                    self.overtake_when_passing_car_count += self._is_overtake(
+                    self.violation_counts["overtake_when_passing_car"] += self._is_overtake(
                         lane_id, dx, dy, ego_speedx, ego_speedy
                     )
                     start_frame_id += window_width
-            """
-            如果滑动窗口开始和最后的laneid一致;
-            方向盘转角前后方向相反(开始方向盘转角向右后来方向盘转角向左);
-            自车和前车的位置发生的交换;
-            则认为右超车
-            """
+                    continue
+            
+            # 检查右侧超车
             if driverctrl_start_state > 0 and driverctrl_end_state < 0:
-                self.overtake_on_right_count += self._is_overtake(
-                    lane_id, dx, dy, ego_speedx, ego_speedy
-                )
-                start_frame_id += window_width
-            elif ego_speedx[0] * obj_speedx[0] + ego_speedy[0] * obj_speedy[0] < 0:
-                self.overtake_when_turn_around_count += self._is_overtake(
+                self.violation_counts["overtake_on_right"] += self._is_overtake(
                     lane_id, dx, dy, ego_speedx, ego_speedy
                 )
                 start_frame_id += window_width
-            else:
-                start_frame_id += 1
+                continue
+            
+            # 检查掉头时超车
+            if obj_speedx and obj_speedy:  # 确保列表不为空
+                if ego_speedx[0] * obj_speedx[0] + ego_speedy[0] * obj_speedy[0] < 0:
+                    self.violation_counts["overtake_when_turn_around"] += self._is_overtake(
+                        lane_id, dx, dy, ego_speedx, ego_speedy
+                    )
+                    start_frame_id += window_width
+                    continue
+            
+            # 如果没有检测到任何违规,移动窗口
+            start_frame_id += 1
+            
+        self._calculated["illegal_overtake"] = True
 
     # 借道超车场景
     def overtake_in_forbid_lane_detector(self):
+        """检测借道超车违规"""
+        # 如果已经计算过,直接返回
+        if self._calculated["forbid_lane"]:
+            return
+            
+        # 如果没有其他车辆数据,直接返回,保持默认值0
+        if self.obj_data.empty:
+            print("没有其他车辆数据,无法检测借道超车违规,默认为0")
+            self._calculated["forbid_lane"] = True
+            return
+            
         simTime = self.obj_data["simTime"].tolist()
         simtime_devide = self.different_road_area_simtime(simTime)
         for simtime in simtime_devide:
@@ -506,13 +592,25 @@ class OvertakingViolation(object):
                 if (50002 in lane_type and len(set(lane_type)) > 2) or (
                         50002 not in lane_type and len(set(lane_type)) > 1
                 ):
-                    self.overtake_in_forbid_lane_count += 1
+                    self.violation_counts["overtake_in_forbid_lane"] += 1
             except Exception as e:
                 print("数据缺少lane_type信息")
-        # print(f"在不该占用车道超车{self.overtake_in_forbid_lane_count}次")
+                
+        self._calculated["forbid_lane"] = True
 
     # 在匝道超车
     def overtake_in_ramp_area_detector(self):
+        """检测匝道超车违规"""
+        # 如果已经计算过,直接返回
+        if self._calculated["ramp_area"]:
+            return
+            
+        # 如果没有其他车辆数据,直接返回,保持默认值0
+        if self.obj_data.empty:
+            print("没有其他车辆数据,无法检测匝道超车违规,默认为0")
+            self._calculated["ramp_area"] = True
+            return
+            
         ramp_simtime_list = self.ego_data[(self.ego_data["road_type"] == 19)][
             "simTime"
         ].tolist()
@@ -527,14 +625,26 @@ class OvertakingViolation(object):
             ego_speedx = ego_in_ramp["speedX"].tolist()
             ego_speedy = ego_in_ramp["speedY"].tolist()
             if len(lane_id) > 0:
-                self.overtake_in_ramp_count += self._is_overtake(
+                self.violation_counts["overtake_in_ramp"] += self._is_overtake(
                     lane_id, dx, dy, ego_speedx, ego_speedy
                 )
             else:
                 continue
-        # print(f"在匝道超车{self.overtake_in_ramp_count}次")
+                
+        self._calculated["ramp_area"] = True
 
     def overtake_in_tunnel_area_detector(self):
+        """检测隧道超车违规"""
+        # 如果已经计算过,直接返回
+        if self._calculated["tunnel_area"]:
+            return
+            
+        # 如果没有其他车辆数据,直接返回,保持默认值0
+        if self.obj_data.empty:
+            print("没有其他车辆数据,无法检测隧道超车违规,默认为0")
+            self._calculated["tunnel_area"] = True
+            return
+            
         tunnel_simtime_list = self.ego_data[(self.ego_data["road_type"] == 15)][
             "simTime"
         ].tolist()
@@ -549,15 +659,27 @@ class OvertakingViolation(object):
             ego_speedx = ego_in_tunnel["speedX"].tolist()
             ego_speedy = ego_in_tunnel["speedY"].tolist()
             if len(lane_id) > 0:
-                self.overtake_in_tunnel_count += self._is_overtake(
+                self.violation_counts["overtake_in_tunnel"] += self._is_overtake(
                     lane_id, dx, dy, ego_speedx, ego_speedy
                 )
             else:
                 continue
-        # print(f"在隧道超车{self.overtake_in_tunnel_count}次")
+                
+        self._calculated["tunnel_area"] = True
 
     # 加速车道超车
     def overtake_on_accelerate_lane_detector(self):
+        """检测加速车道超车违规"""
+        # 如果已经计算过,直接返回
+        if self._calculated["accelerate_lane"]:
+            return
+            
+        # 如果没有其他车辆数据,直接返回,保持默认值0
+        if self.obj_data.empty:
+            print("没有其他车辆数据,无法检测加速车道超车违规,默认为0")
+            self._calculated["accelerate_lane"] = True
+            return
+            
         accelerate_simtime_list = self.ego_data[self.ego_data["lane_type"] == 2][
             "simTime"
         ].tolist()
@@ -576,13 +698,25 @@ class OvertakingViolation(object):
             ego_speedx = ego_in_accelerate["speedX"].tolist()
             ego_speedy = ego_in_accelerate["speedY"].tolist()
 
-            self.overtake_on_accelerate_lane_count += self._is_overtake(
+            self.violation_counts["overtake_on_accelerate_lane"] += self._is_overtake(
                 lane_id, dx, dy, ego_speedx, ego_speedy
             )
-        # print(f"在加速车道超车{self.overtake_on_accelerate_lane_count}次")
+            
+        self._calculated["accelerate_lane"] = True
 
     # 减速车道超车
     def overtake_on_decelerate_lane_detector(self):
+        """检测减速车道超车违规"""
+        # 如果已经计算过,直接返回
+        if self._calculated["decelerate_lane"]:
+            return
+            
+        # 如果没有其他车辆数据,直接返回,保持默认值0
+        if self.obj_data.empty:
+            print("没有其他车辆数据,无法检测减速车道超车违规,默认为0")
+            self._calculated["decelerate_lane"] = True
+            return
+            
         decelerate_simtime_list = self.ego_data[(self.ego_data["lane_type"] == 3)][
             "simTime"
         ].tolist()
@@ -601,13 +735,25 @@ class OvertakingViolation(object):
             ego_speedx = ego_in_decelerate["speedX"].tolist()
             ego_speedy = ego_in_decelerate["speedY"].tolist()
 
-            self.overtake_on_decelerate_lane_count += self._is_overtake(
+            self.violation_counts["overtake_on_decelerate_lane"] += self._is_overtake(
                 lane_id, dx, dy, ego_speedx, ego_speedy
             )
-        # print(f"在减速车道超车{self.overtake_on_decelerate_lane_count}次")
+            
+        self._calculated["decelerate_lane"] = True
 
     # 在交叉路口
     def overtake_in_different_senerios_detector(self):
+        """检测不同场景超车违规"""
+        # 如果已经计算过,直接返回
+        if self._calculated["different_senerios"]:
+            return
+            
+        # 如果没有其他车辆数据,直接返回,保持默认值0
+        if self.obj_data.empty:
+            print("没有其他车辆数据,无法检测不同场景超车违规,默认为0")
+            self._calculated["different_senerios"] = True
+            return
+            
         crossroad_simTime = self.ego_data[self.ego_data["interid"] != 10000][
             "simTime"
         ].tolist()  # 判断是路口或者隧道区域
@@ -629,47 +775,56 @@ class OvertakingViolation(object):
         则认为发生超车
         """
         if len(lane_id) > 0:
-            self.overtake_in_different_senerios_count += self._is_overtake(
+            self.violation_counts["overtake_in_different_senerios"] += self._is_overtake(
                 lane_id, dx, dy, ego_speedx, ego_speedy
             )
-        else:
-            pass
+            
+        self._calculated["different_senerios"] = True
+
     def calculate_overtake_when_passing_car_count(self):
+        """计算会车时超车违规次数"""
         self.illegal_overtake_with_car_detector()
-        return self.overtake_when_passing_car_count
+        return self.violation_counts["overtake_when_passing_car"]
 
     def calculate_overtake_on_right_count(self):
+        """计算右侧超车违规次数"""
         self.illegal_overtake_with_car_detector()
-        return self.overtake_on_right_count
+        return self.violation_counts["overtake_on_right"]
 
     def calculate_overtake_when_turn_around_count(self):
+        """计算掉头时超车违规次数"""
         self.illegal_overtake_with_car_detector()
-        return self.overtake_when_turn_around_count
+        return self.violation_counts["overtake_when_turn_around"]
 
     def calculate_overtake_in_forbid_lane_count(self):
+        """计算借道超车违规次数"""
         self.overtake_in_forbid_lane_detector()
-        return self.overtake_in_forbid_lane_count
+        return self.violation_counts["overtake_in_forbid_lane"]
 
     def calculate_overtake_in_ramp_area_count(self):
+        """计算匝道超车违规次数"""
         self.overtake_in_ramp_area_detector()
-        return self.overtake_in_ramp_count
+        return self.violation_counts["overtake_in_ramp"]
 
     def calculate_overtake_in_tunnel_area_count(self):
+        """计算隧道超车违规次数"""
         self.overtake_in_tunnel_area_detector()
-        return self.overtake_in_tunnel_count
+        return self.violation_counts["overtake_in_tunnel"]
 
     def calculate_overtake_on_accelerate_lane_count(self):
+        """计算加速车道超车违规次数"""
         self.overtake_on_accelerate_lane_detector()
-        return self.overtake_on_accelerate_lane_count
+        return self.violation_counts["overtake_on_accelerate_lane"]
 
     def calculate_overtake_on_decelerate_lane_count(self):
+        """计算减速车道超车违规次数"""
         self.overtake_on_decelerate_lane_detector()
-        return self.overtake_on_decelerate_lane_count
+        return self.violation_counts["overtake_on_decelerate_lane"]
 
     def calculate_overtake_in_different_senerios_count(self):
+        """计算不同场景超车违规次数"""
         self.overtake_in_different_senerios_detector()
-        return self.overtake_in_different_senerios_count
-
+        return self.violation_counts["overtake_in_different_senerios"]
 
 
 class SlowdownViolation(object):
@@ -678,24 +833,43 @@ class SlowdownViolation(object):
     def __init__(self, df_data):
         print("减速让行违规类-------------------------")
         self.traffic_violations_type = "减速让行违规类"
-        self.object_items = []
-        self.data = df_data.obj_data[1]
-        self.ego_data = (
-            self.data[SLOWDOWN_INFO].copy().reset_index(drop=True)
-        )  # Copy to avoid modifying the original DataFrame
-        self.pedestrian_data = pd.DataFrame()
-
+        
+        # 存储原始数据引用
+        self._raw_data = df_data.obj_data[1]
         self.object_items = set(df_data.object_df.type.tolist())
+        
+        # 存储行人数据引用
+        self._pedestrian_df = None
         if 13 in self.object_items:  # 行人的type是13
-            self.pedestrian_df = df_data.object_df[df_data.object_df.type == 13]
-            self.pedestrian_data = (
-                self.pedestrian_df[SLOWDOWN_INFO].copy().reset_index(drop=True)
-            )
-
+            self._pedestrian_df = df_data.object_df[df_data.object_df.type == 13]
+        
+        # 初始化属性,但不立即创建数据副本
+        self._ego_data = None
+        self._pedestrian_data = None
+        
+        # 初始化计数器
         self.slow_down_in_crosswalk_count = 0
         self.avoid_pedestrian_in_crosswalk_count = 0
         self.avoid_pedestrian_in_the_road_count = 0
         self.aviod_pedestrian_when_turning_count = 0
+    
+    @property
+    def ego_data(self):
+        """懒加载方式获取ego数据"""
+        if self._ego_data is None:
+            self._ego_data = self._raw_data[SLOWDOWN_INFO].copy().reset_index(drop=True)
+        return self._ego_data
+    
+    @property
+    def pedestrian_data(self):
+        """懒加载方式获取行人数据"""
+        if self._pedestrian_data is None:
+            if self._pedestrian_df is not None:
+                # 使用浅拷贝代替深拷贝
+                self._pedestrian_data = self._pedestrian_df[SLOWDOWN_INFO].copy().reset_index(drop=True)
+            else:
+                self._pedestrian_data = pd.DataFrame()
+        return self._pedestrian_data
 
     def pedestrian_in_front_of_car(self):
         if len(self.pedestrian_data) == 0:
@@ -875,22 +1049,40 @@ class TurnaroundViolation(object):
         print("掉头违规类初始化中...")
         self.traffic_violations_type = "掉头违规类"
 
-        self.data = df_data.obj_data[1]
-        self.ego_data = (
-            self.data[TURNAROUND_INFO].copy().reset_index(drop=True)
-        )  # Copy to avoid modifying the original DataFrame
-        self.pedestrian_data = pd.DataFrame()
-
+        # 存储原始数据引用
+        self._raw_data = df_data.obj_data[1]
         self.object_items = set(df_data.object_df.type.tolist())
+        
+        # 存储行人数据引用
+        self._pedestrian_df = None
         if 13 in self.object_items:  # 行人的type是13
-            self.pedestrian_df = df_data.object_df[df_data.object_df.type == 13]
-            self.pedestrian_data = (
-                self.pedestrian_df[SLOWDOWN_INFO].copy().reset_index(drop=True)
-            )
-
+            self._pedestrian_df = df_data.object_df[df_data.object_df.type == 13]
+        
+        # 初始化属性,但不立即创建数据副本
+        self._ego_data = None
+        self._pedestrian_data = None
+        
+        # 初始化计数器
         self.turning_in_forbiden_turn_back_sign_count = 0
         self.turning_in_forbiden_turn_left_sign_count = 0
         self.avoid_pedestrian_when_turn_back_count = 0
+    
+    @property
+    def ego_data(self):
+        """懒加载方式获取ego数据"""
+        if self._ego_data is None:
+            self._ego_data = self._raw_data[TURNAROUND_INFO].copy().reset_index(drop=True)
+        return self._ego_data
+    
+    @property
+    def pedestrian_data(self):
+        """懒加载方式获取行人数据"""
+        if self._pedestrian_data is None:
+            if self._pedestrian_df is not None:
+                self._pedestrian_data = self._pedestrian_df[SLOWDOWN_INFO].copy().reset_index(drop=True)
+            else:
+                self._pedestrian_data = pd.DataFrame()
+        return self._pedestrian_data
 
     def pedestrian_in_front_of_car(self):
         if len(self.pedestrian_data) == 0:
@@ -1021,20 +1213,33 @@ class TurnaroundViolation(object):
         self.avoid_pedestrian_when_turn_back_detector()
         return self.avoid_pedestrian_when_turn_back_count
 
-
-class WrongWayViolation:
+class WrongWayViolation(object):
     """停车违规类"""
 
     def __init__(self, df_data):
         print("停车违规类初始化中...")
         self.traffic_violations_type = "停车违规类"
-        self.data = df_data.obj_data[1]
+        
+        # 存储原始数据引用
+        self._raw_data = df_data.obj_data[1]
+        
+        # 初始化属性,但不立即创建数据副本
+        self._data = None
+        
         # 初始化违规统计
         self.violation_count = {
             "urbanExpresswayOrHighwayDrivingLaneStopped": 0,
             "urbanExpresswayOrHighwayEmergencyLaneStopped": 0,
             "urbanExpresswayEmergencyLaneDriving": 0,
         }
+    
+    @property
+    def data(self):
+        """懒加载方式获取数据"""
+        if self._data is None:
+            # 使用浅拷贝代替深拷贝
+            self._data = self._raw_data.copy()
+        return self._data
 
     def process_violations(self):
         """处理停车或者紧急车道行驶违规数据"""
@@ -1102,9 +1307,11 @@ class SpeedingViolation(object):
     def __init__(self, df_data):
         print("超速违规类初始化中...")
         self.traffic_violations_type = "超速违规类"
-        self.data = df_data.obj_data[
-            1
-        ]  # Copy to avoid modifying the original DataFrame
+        # 存储原始数据引用
+        self._raw_data = df_data.obj_data[1]
+        
+        # 初始化属性,但不立即创建数据副本
+        self._data = None
         # 初始化违规统计
         self.violation_counts = {
             "urbanExpresswayOrHighwaySpeedOverLimit50": 0,
@@ -1115,6 +1322,16 @@ class SpeedingViolation(object):
             "generalRoadSpeedOverLimit20to50": 0,
         }
 
+    @property
+    def data(self):
+        """懒加载方式获取数据"""
+        if self._data is None:
+            # 使用浅拷贝代替深拷贝
+            self._data = self._raw_data.copy()
+            # 预处理数据 - 转换速度单位
+            self._data["v"] *= 3.6  # 转换为 km/h
+        return self._data
+
     def process_violations(self):
         """处理数据帧,检查超速和其他违规行为"""
         # 提取有效道路类型
@@ -1365,61 +1582,92 @@ class WarningViolation(object):
     """警告性违规类"""
 
     def __init__(self, df_data):
+        print("警告性违规类初始化中...")
         self.traffic_violations_type = "警告性违规类"
-        print("警告性违规类 类初始化中...")
+        
+        # 存储原始数据引用
         self.config = df_data.vehicle_config
-        self.data_ego = df_data.obj_data[1]
-        self.data = self.data_ego.copy()  # 避免修改原始 DataFrame
+        self._raw_data = df_data.obj_data[1]
+        
+        # 初始化属性,但不立即创建数据副本
+        self._data = None
+        
+        # 初始化违规计数器
         self.violation_counts = {
             "generalRoadIrregularLaneUse": 0,  # 驾驶机动车在高速公路、城市快速路以外的道路上不按规定车道行驶
             "urbanExpresswayOrHighwayRideLaneDivider": 0,  # 机动车在高速公路或者城市快速路上骑、轧车行道分界线
         }
+    
+    @property
+    def data(self):
+        """懒加载方式获取数据"""
+        if self._data is None:
+            # 使用浅拷贝代替深拷贝
+            self._data = self._raw_data.copy()
+        return self._data
 
     def process_violations(self):
-        general_road = {3}  # 普通道路
-        lane_type = {11}  # 车道类型 # 10: 机动车道,11: 非机动车道
-        # with open(self.config_path / "vehicle_config.yaml", 'r') as f:
-        #     config = yaml.load(f, Loader=yaml.FullLoader)
-        car_width = self.config["CAR_WIDTH"]
-        lane_width = self.data["lane_width"]  # 假定 'lane_width' 在数据中存在
+        """处理所有违规类型"""
+        # 处理普通道路不按规定车道行驶违规
+        self._process_irregular_lane_use()
+        
+        # 处理骑、轧车行道分界线违规
+        self._process_lane_divider_violation()
 
-        # 驾驶机动车在高速公路、城市快速路以外的道路上不按规定车道行驶
+    def _process_irregular_lane_use(self):
+        """处理普通道路不按规定车道行驶违规"""
+        # 定义道路和车道类型
+        general_road = {3}  # 普通道路
+        lane_type = {11}    # 非机动车道
+        
         # 使用布尔索引来筛选满足条件的行
         condition = (self.data["road_fc"].isin(general_road)) & (
             self.data["lane_type"].isin(lane_type)
         )
-
+        
         # 创建一个新的列,并根据条件设置值
         self.data["is_violation"] = condition
-
+        
         # 统计满足条件的连续时间段
         violation_segments = self.count_continuous_violations(
             self.data["is_violation"], self.data["simTime"]
         )
+        
+        # 更新违规计数
+        self.violation_counts["generalRoadIrregularLaneUse"] = len(violation_segments)
 
-        # 更新骑行车道线违规计数
-        self.violation_counts["generalRoadIrregularLaneUse"] += len(violation_segments)
-
-        # 机动车在高速公路或者城市快速路上骑、轧车行道分界线
-
+    def _process_lane_divider_violation(self):
+        """处理骑、轧车行道分界线违规"""
+        # 获取车辆和车道宽度
+        car_width = self.config["CAR_WIDTH"]
+        lane_width = self.data["lane_width"]
+        
         # 计算阈值
         threshold = (lane_width - car_width) / 2
-
+        
         # 找到满足条件的行
         self.data["is_violation"] = self.data["laneOffset"] > threshold
-
+        
         # 统计满足条件的连续时间段
         violation_segments = self.count_continuous_violations(
             self.data["is_violation"], self.data["simTime"]
         )
-
-        # 更新骑行车道线违规计数
-        self.violation_counts["urbanExpresswayOrHighwayRideLaneDivider"] += len(
+        
+        # 更新违规计数
+        self.violation_counts["urbanExpresswayOrHighwayRideLaneDivider"] = len(
             violation_segments
         )
 
     def count_continuous_violations(self, violation_series, time_series):
-        """统计连续违规的时间段数量"""
+        """统计连续违规的时间段数量
+        
+        Args:
+            violation_series: 表示是否违规的布尔序列
+            time_series: 对应的时间序列
+            
+        Returns:
+            list: 连续违规时间段列表
+        """
         continuous_segments = []
         current_segment = []
 
@@ -1429,25 +1677,29 @@ class WarningViolation(object):
                     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):
-        self.process_violations()
+        """计算普通道路不按规定车道行驶违规次数"""
+        # 只处理普通道路不按规定车道行驶违规
+        self._process_irregular_lane_use()
         return self.violation_counts["generalRoadIrregularLaneUse"]
 
-    def calculate_urbanExpresswayOrHighwayRideLaneDivider(self):
-        self.process_violations()
+    def calculate_urbanExpresswayOrHighwayRideLaneDivider_count(self):
+        """计算骑、轧车行道分界线违规次数"""
+        # 只处理骑、轧车行道分界线违规
+        self._process_lane_divider_violation()
         return self.violation_counts["urbanExpresswayOrHighwayRideLaneDivider"]
 
-
 class TrafficSignViolation:
     """交通标志违规类"""
     
@@ -1457,12 +1709,14 @@ class TrafficSignViolation:
     SIGN_TYPE_MIN_SPEED_LIMIT = 13
 
     def __init__(self, df_data):
+        print("交通标志违规类初始化中...")
         self.traffic_violations_type = "交通标志违规类"
-        print("交通标志违规类 初始化中...")
         
-        # 数据预处理
-        self._raw_data = df_data.obj_data[1].copy()
-        self.data_ego = self._raw_data.sort_values('simTime').reset_index(drop=True)
+        # 存储原始数据引用
+        self._raw_data = df_data.obj_data[1]
+        
+        # 初始化属性,但不立即创建数据副本
+        self._data = None
         
         # 延迟计算标志
         self._calculated = False
@@ -1471,6 +1725,16 @@ class TrafficSignViolation:
             "SpeedLimitViolation": 0,
             "MinimumSpeedLimitViolation": 0
         }
+    
+    @property
+    def data(self):
+        """懒加载方式获取数据"""
+        if self._data is None:
+            # 使用浅拷贝代替深拷贝
+            self._data = self._raw_data.copy()
+            # 预处理数据 - 按时间排序
+            self._data = self._data.sort_values('simTime').reset_index(drop=True)
+        return self._data
 
     def _ensure_calculated(self):
         """保证计算只执行一次"""
@@ -1513,7 +1777,7 @@ class TrafficSignViolation:
 
     def _check_straight_violation(self):
         """检查禁止直行违规"""
-        straight_df = self.data_ego[self.data_ego["sign_type1"] == self.SIGN_TYPE_STRAIGHT_PROHIBITED]
+        straight_df = self.data[self.data["sign_type1"] == self.SIGN_TYPE_STRAIGHT_PROHIBITED]
         
         if not straight_df.empty:
             # 计算航向角变化并填充缺失值
@@ -1526,16 +1790,21 @@ class TrafficSignViolation:
                 (straight_df['v'] > 0)
             )
             
-            self.violation_counts["NoStraightThrough"] = mask.sum()
+            self._violation_counts["NoStraightThrough"] = mask.sum()
 
     def _check_speed_violation(self, sign_type, compare_op, count_key):
-        """通用速度违规检查方法"""
-        violation_df = self.data_ego[self.data_ego["sign_type1"] == sign_type]
+        """通用速度违规检查方法
+        
+        Args:
+            sign_type: 标志类型
+            compare_op: 比较操作符
+            count_key: 违规计数键名
+        """
+        violation_df = self.data[self.data["sign_type1"] == sign_type]
         
         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()
 
 
 

+ 400 - 0
reports/V2V_CSAE53-2020_ForwardCollisionW_LST_01-01_report.json

@@ -0,0 +1,400 @@
+{
+    "safety": {
+        "safetime": {
+            "CustomTTC": {
+                "result": false,
+                "priority": 0
+            },
+            "TTC": {
+                "result": true,
+                "priority": 0
+            },
+            "MTTC": {
+                "result": true,
+                "priority": 0
+            },
+            "THW": {
+                "result": true,
+                "priority": 0
+            },
+            "result": false,
+            "priority": 0,
+            "priority_0_count": 1,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "safeDistance": {
+            "LonSD": {
+                "result": true,
+                "priority": 0
+            },
+            "LatSD": {
+                "result": true,
+                "priority": 0
+            },
+            "result": true,
+            "priority": 0,
+            "priority_0_count": 0,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "safeAcceleration": {
+            "BTN": {
+                "result": false,
+                "priority": 0
+            },
+            "result": false,
+            "priority": 0,
+            "priority_0_count": 1,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "safeProbability": {
+            "collisionRisk": {
+                "result": true,
+                "priority": 0
+            },
+            "collisionSeverity": {
+                "result": true,
+                "priority": 0
+            },
+            "result": true,
+            "priority": 0,
+            "priority_0_count": 0,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "result": false,
+        "priority": 0,
+        "priority_0_count": 2,
+        "priority_1_count": 0,
+        "priority_2_count": 0
+    },
+    "comfort": {
+        "comfortLat": {
+            "Weaving": {
+                "result": true,
+                "priority": 0
+            },
+            "shake": {
+                "result": true,
+                "priority": 0
+            },
+            "result": true,
+            "priority": 0,
+            "priority_0_count": 0,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "comfortLon": {
+            "cadence": {
+                "result": true,
+                "priority": 0
+            },
+            "slamBrake": {
+                "result": true,
+                "priority": 0
+            },
+            "slamAccelerate": {
+                "result": false,
+                "priority": 0
+            },
+            "result": false,
+            "priority": 0,
+            "priority_0_count": 1,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "result": false,
+        "priority": 0,
+        "priority_0_count": 1,
+        "priority_1_count": 0,
+        "priority_2_count": 0
+    },
+    "traffic": {
+        "majorViolation": {
+            "urbanExpresswayOrHighwaySpeedOverLimit50": {
+                "result": true,
+                "priority": 0
+            },
+            "result": true,
+            "priority": 0,
+            "priority_0_count": 0,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "seriousViolation": {
+            "urbanExpresswayOrHighwayDrivingLaneStopped": {
+                "result": true,
+                "priority": 0
+            },
+            "urbanExpresswayOrHighwayEmergencyLaneStopped": {
+                "result": true,
+                "priority": 0
+            },
+            "result": true,
+            "priority": 0,
+            "priority_0_count": 0,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "dangerousViolation": {
+            "urbanExpresswayEmergencyLaneDriving": {
+                "result": true,
+                "priority": 0
+            },
+            "trafficSignalViolation": {
+                "result": true,
+                "priority": 0
+            },
+            "urbanExpresswayOrHighwaySpeedOverLimit20to50": {
+                "result": true,
+                "priority": 0
+            },
+            "generalRoadSpeedOverLimit50": {
+                "result": true,
+                "priority": 0
+            },
+            "result": true,
+            "priority": 0,
+            "priority_0_count": 0,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "generalViolation": {
+            "generalRoadSpeedOverLimit20to50": {
+                "result": false,
+                "priority": 0
+            },
+            "UrbanExpresswayOrHighwaySpeedUnderLimit": {
+                "result": true,
+                "priority": 0
+            },
+            "illegalDrivingOrParkingAtCrossroads": {
+                "result": true,
+                "priority": 0
+            },
+            "overtake_on_right": {
+                "result": true,
+                "priority": 0
+            },
+            "overtake_when_turn_around": {
+                "result": true,
+                "priority": 0
+            },
+            "overtake_when_passing_car": {
+                "result": true,
+                "priority": 0
+            },
+            "overtake_in_forbid_lane": {
+                "result": true,
+                "priority": 0
+            },
+            "overtake_in_ramp": {
+                "result": true,
+                "priority": 0
+            },
+            "overtake_in_tunnel": {
+                "result": true,
+                "priority": 0
+            },
+            "overtake_on_accelerate_lane": {
+                "result": true,
+                "priority": 0
+            },
+            "overtake_on_decelerate_lane": {
+                "result": true,
+                "priority": 0
+            },
+            "overtake_in_different_senerios": {
+                "result": true,
+                "priority": 0
+            },
+            "slow_down_in_crosswalk": {
+                "result": true,
+                "priority": 0
+            },
+            "avoid_pedestrian_in_crosswalk": {
+                "result": true,
+                "priority": 0
+            },
+            "avoid_pedestrian_in_the_road": {
+                "result": true,
+                "priority": 0
+            },
+            "avoid_pedestrian_when_turning": {
+                "result": true,
+                "priority": 0
+            },
+            "NoStraightThrough": {
+                "result": true,
+                "priority": 0
+            },
+            "SpeedLimitViolation": {
+                "result": true,
+                "priority": 0
+            },
+            "MinimumSpeedLimitViolation": {
+                "result": true,
+                "priority": 0
+            },
+            "aviod_pedestrian_when_turning": {
+                "result": true,
+                "priority": 0
+            },
+            "result": false,
+            "priority": 0,
+            "priority_0_count": 1,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "minorViolation": {
+            "turn_in_forbiden_turn_left_sign": {
+                "result": true,
+                "priority": 0
+            },
+            "turn_in_forbiden_turn_back_sign": {
+                "result": true,
+                "priority": 0
+            },
+            "avoid_pedestrian_when_turn_back": {
+                "result": true,
+                "priority": 0
+            },
+            "result": true,
+            "priority": 0,
+            "priority_0_count": 0,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "warningViolation": {
+            "urbanExpresswayOrHighwaySpeedOverLimit0to20": {
+                "result": true,
+                "priority": 0
+            },
+            "urbanExpresswayOrHighwayRideLaneDivider": {
+                "result": false,
+                "priority": 0
+            },
+            "generalRoadIrregularLaneUse": {
+                "result": true,
+                "priority": 0
+            },
+            "result": false,
+            "priority": 0,
+            "priority_0_count": 1,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "result": false,
+        "priority": 0,
+        "priority_0_count": 2,
+        "priority_1_count": 0,
+        "priority_2_count": 0
+    },
+    "efficient": {
+        "drivingMode": {
+            "maxSpeed": {
+                "result": false,
+                "priority": 0
+            },
+            "deviationSpeed": {
+                "result": false,
+                "priority": 0
+            },
+            "averagedSpeed": {
+                "result": false,
+                "priority": 0
+            },
+            "result": false,
+            "priority": 0,
+            "priority_0_count": 3,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "parkingMode": {
+            "stopDuration": {
+                "result": true,
+                "priority": 0
+            },
+            "result": true,
+            "priority": 0,
+            "priority_0_count": 0,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "result": false,
+        "priority": 0,
+        "priority_0_count": 1,
+        "priority_1_count": 0,
+        "priority_2_count": 0
+    },
+    "function": {
+        "ForwardCollision": {
+            "latestWarningDistance_TTC_LST": {
+                "result": false,
+                "priority": 0
+            },
+            "earliestWarningDistance_TTC_LST": {
+                "result": false,
+                "priority": 0
+            },
+            "latestWarningDistance_LST": {
+                "result": false,
+                "priority": 0
+            },
+            "earliestWarningDistance_LST": {
+                "result": false,
+                "priority": 0
+            },
+            "result": false,
+            "priority": 0,
+            "priority_0_count": 4,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "LKA": {
+            "latestWarningDistance_TTC": {
+                "result": true,
+                "priority": 0
+            },
+            "latestWarningDistance": {
+                "result": true,
+                "priority": 0
+            },
+            "result": true,
+            "priority": 0,
+            "priority_0_count": 0,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "result": false,
+        "priority": 0,
+        "priority_0_count": 1,
+        "priority_1_count": 0,
+        "priority_2_count": 0
+    },
+    "user": {
+        "safetime": {
+            "CustomTTC": {
+                "result": false,
+                "priority": 0
+            },
+            "result": false,
+            "priority": 0,
+            "priority_0_count": 1,
+            "priority_1_count": 0,
+            "priority_2_count": 0
+        },
+        "result": false,
+        "priority": 0,
+        "priority_0_count": 1,
+        "priority_1_count": 0,
+        "priority_2_count": 0
+    },
+    "metadata": {
+        "case_name": "V2V_CSAE53-2020_ForwardCollisionW_LST_01-01",
+        "timestamp": "2025-04-27T19:00:16.205739",
+        "version": "3.1.0"
+    }
+}

+ 1 - 1
scripts/evaluator_enhanced.py

@@ -560,7 +560,7 @@ def main():
     parser.add_argument(
         "--dataPath",
         type=str,
-        default="/home/kevin/kevin/zhaoyuan/sqlite3_demo/docker_build/data/V2V_CSAE53-2020_ForwardCollision_LST_02-03",
+        default="/home/kevin/kevin/zhaoyuan/sqlite3_demo/docker_build/data/V2V_CSAE53-2020_ForwardCollisionW_LST_01-01",
         help="Input data directory",
     )
     parser.add_argument(

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