1
0

5 Commitit 06379ef639 ... 74ba1234c7

Tekijä SHA1 Viesti Päivämäärä
  cicv 74ba1234c7 最新代码 3 viikkoa sitten
  cicv a1a20dda6d 最新代码 3 viikkoa sitten
  XGJ_zhaoyuan 06379ef639 Merge remote-tracking branch 'origin/master' 1 kuukausi sitten
  XGJ_zhaoyuan 5e19c75cef 修改yaml文件,和合规性指标名称对齐 1 kuukausi sitten
  XGJ_zhaoyuan 7bf6ce066c 补充yaml文件中合规性部分的指标名称(主要是轻度违规指标) 1 kuukausi sitten

+ 18 - 30
config/all_metrics_config.yaml

@@ -1,20 +1,22 @@
 vehicle:
 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:
 T_threshold:
-  T0_threshold: 0  
-  T1_threshold: 2  
-  T2_threshold: 5  
+  T0_threshold: 0  # 表示T0等级的通过阈值
+  T1_threshold: 2  # 表示T1等级的通过阈值
+  T2_threshold: 5  # 表示T2等级的通过阈值
+
 
 
 safety:
 safety:
   name: safety
   name: safety
@@ -180,7 +182,7 @@ function:
       priority: 0
       priority: 0
       max: 17.29
       max: 17.29
       min: 10.51
       min: 10.51
-
+      
 traffic:
 traffic:
   name: traffic
   name: traffic
   priority: 0
   priority: 0
@@ -192,18 +194,6 @@ traffic:
       priority: 0
       priority: 0
       max: 0
       max: 0
       min: 0
       min: 0
-
-#    urbanExpresswayOrHighwayReverse:
-#      name: urbanExpresswayOrHighwayReverse
-#      priority: 0
-#      max: 0
-#      min: 0
-#    urbanExpresswayOrHighwayDrivingAgainst:
-#      name: urbanExpresswayOrHighwayDrivingAgainst
-#      priority: 0
-#      max: 0
-#      min: 0
-
   seriousViolation:
   seriousViolation:
     name: seriousViolation
     name: seriousViolation
     priority: 0
     priority: 0
@@ -241,7 +231,6 @@ traffic:
       priority: 0
       priority: 0
       max: 0
       max: 0
       min: 0
       min: 0
-
   generalViolation:
   generalViolation:
     name: generalViolation
     name: generalViolation
     priority: 0
     priority: 0
@@ -359,7 +348,6 @@ traffic:
       priority: 0
       priority: 0
       max: 0
       max: 0
       min: 0
       min: 0
-
   warningViolation:
   warningViolation:
     name: warningViolation
     name: warningViolation
     priority: 0
     priority: 0
@@ -377,4 +365,4 @@ traffic:
       name: generalRoadIrregularLaneUse
       name: generalRoadIrregularLaneUse
       priority: 0
       priority: 0
       max: 0
       max: 0
-      min: 0
+      min: 0

+ 334 - 351
config/metrics_config.yaml

@@ -1,352 +1,335 @@
-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     
-
-T_threshold:
-  T0_threshold: 0  
-  T1_threshold: 2  
-  T2_threshold: 5  
-
-safety:
-  name: safety
-  priority: 0
-  safeTime:
-    name: safetime
-    priority: 0
-    TTC:
-      name: TTC
-      priority: 0
-      max: 2000.0
-      min: 2.86
-    MTTC:
-      name: MTTC
-      priority: 0
-      max: 2000.0
-      min: 3.0
-    THW:
-      name: THW
-      priority: 0
-      max: 2000.0
-      min: 1.5
-  safeDistance:
-    name: safeDistance
-    priority: 0
-    LonSD:
-      name: LonSD
-      priority: 0
-      max: 2000.0
-      min: 10.0
-    LatSD:
-      name: LatSD 
-      priority: 0
-      max: 2000.0
-      min: 2.0
-  safeAcceleration:
-    name: safeAcceleration
-    priority: 0
-    BTN:
-      name: BTN
-      priority: 0
-      max: 1.0
-      min: -2000.0
-  safeProbability:
-    name: safeProbability
-    priority: 0
-    collisionRisk:
-      name: collisionRisk
-      priority: 0
-      max: 10.0
-      min: 0.0
-    collisionSeverity:
-      name: collisionSeverity
-      priority: 0
-      max: 10.0
-      min: 0.0
-
-comfort:
-  name: comfort
-  priority: 0
-  comfortLat:
-    name: comfortLat
-    priority: 0
-    weaving:
-      name: weaving
-      priority: 0
-      max: 0
-      min: 0
-    shake:
-      name: shake
-      priority: 0
-      max: 0
-      min: 0
-  comfortLon:
-    name: comfortLon
-    priority: 0
-    cadence:
-      name: cadence
-      priority: 0
-      max: 0
-      min: 0
-    slamBrake:
-      name: slamBrake 
-      priority: 0
-      max: 0
-      min: 0
-    slamAccelerate:
-      name: slamAccelerate
-      priority: 0
-      max: 0
-      min: 0
-
-efficient:
-  name: efficient
-  priority: 0
-  drivingMode:
-    name: drivingMode
-    priority: 0
-    max_speed:
-      name: maxSpeed
-      priority: 0
-      max: 0.0
-      min: 0.0
-    devation_speed:
-      name: deviationSpeed
-      priority: 0
-      max: 0.0
-      min: 0.0
-    averagedSpeed:
-      name: averagedSpeed
-      priority: 0
-      max: 80.0
-      min: 30.0
-  parkingMode:
-    name: parkingMode
-    priority: 0
-    stopDuration:
-      name: stopDuration
-      priority: 0
-      max: 1
-      min: 0
-
-function:
-  name: function
-  priority: 0
-  scenario:
-    name: ForwardCollision
-    priority: 0
-    latestWarningDistance_TTC_LST:
-      name: latestWarningDistance_TTC_LST
-      priority: 0
-      max: 3.11
-      min: 1.89
-    earliestWarningDistance_TTC_LST:
-      name: earliestWarningDistance_TTC_LST
-      priority: 0
-      max: 3.11
-      min: 1.89
-    latestWarningDistance_LST:
-      name: latestWarningDistance_LST
-      priority: 0
-      max: 17.29
-      min: 10.51
-    earliestWarningDistance_LST:
-      name: earliestWarningDistance_LST
-      priority: 0
-      max: 17.29
-      min: 10.51
-
-traffic:
-  name: traffic
-  priority: 0
-  majorViolation:
-    name: majorViolation
-    priority: 0
-    urbanExpresswayOrHighwaySpeedOverLimit50:
-      name: urbanExpresswayOrHighwaySpeedOverLimit50
-      priority: 0
-      max: 0
-      min: 0
-    urbanExpresswayOrHighwayReverse:
-      name: higwayreverse
-      priority: 0
-      max: 0
-      min: 0
-    urbanExpresswayOrHighwayDrivingAgainst:
-      name: higwayDrivingAgainst
-      priority: 0
-      max: 0
-      min: 0
-
-  seriousViolation:
-    name: seriousViolation
-    priority: 0
-    urbanExpresswayOrHighwayDrivingLaneStopped:
-      name: urbanExpresswayOrHighwayDrivingLaneStopped
-      priority: 0
-      max: 0
-      min: 0
-    urbanExpresswayOrHighwayEmergencyLaneStopped:
-      name: highwayEmergencyLaneStopped
-      priority: 0
-      max: 0
-      min: 0
-
-  dangerousViolation:
-    name: dangerousViolation
-    priority: 0
-    urbanExpresswayEmergencyLaneDriving:
-      name: urbanExpresswayEmergencyLaneDriving
-      priority: 0
-      max: 0
-      min: 0
-    trafficSignalViolation:
-      name: trafficSignalViolation
-      priority: 0
-      max: 0
-      min: 0
-    urbanExpresswayOrHighwaySpeedOverLimit20to50:
-      name: urbanExpresswayOrHighwaySpeedOverLimit20to50
-      priority: 0
-      max: 0
-      min: 0
-    generalRoadSpeedOverLimit50:
-      name: generalRoadSpeedOverLimit50
-      priority: 0
-      max: 0
-      min: 0
-
-  generalViolation:
-    name: generalViolation
-    priority: 0
-    generalRoadSpeedOverLimit20to50:
-      name: generalRoadSpeedOverLimit20to50
-      priority: 0
-      max: 0
-      min: 0
-    urbanExpresswayOrHighwaySpeedUnderLimit:
-      name: UrbanExpresswayOrHighwaySpeedUnderLimit
-      priority: 0
-      max: 0
-      min: 0
-    illegalDrivingOrParkingAtCrossroads:
-      name: illegalDrivingOrParkingAtCrossroads
-      priority: 0
-      max: 0
-      min: 0
-    overtake_on_right:
-      name: overtake_on_right
-      priority: 0
-      max: 0
-      min: 0
-    overtake_when_turn_around:
-      name: overtake_when_turn_around
-      priority: 0
-      max: 0
-      min: 0
-    overtake_when_passing_car:
-      name: overtake_when_passing_car
-      priority: 0
-      max: 0
-      min: 0
-    overtake_in_forbid_lane:
-      name: overtake_in_forbid_lane
-      priority: 0
-      max: 0
-      min: 0
-    overtake_in_ramp:
-      name: overtake_in_ramp
-      priority: 0
-      max: 0
-      min: 0
-    overtake_in_tunnel:
-      name: overtake_in_tunnel
-      priority: 0
-      max: 0
-      min: 0
-    overtake_on_accelerate_lane:
-      name: overtake_on_accelerate_lane
-      priority: 0
-      max: 0
-      min: 0
-    overtake_on_decelerate_lane:
-      name: overtake_on_decelerate_lane
-      priority: 0
-      max: 0
-      min: 0
-    overtake_in_different_senerios:
-      name: overtake_in_different_senerios
-      priority: 0
-      max: 0
-      min: 0
-    slow_down_in_crosswalk:
-      name: slow_down_in_crosswalk
-      priority: 0
-      max: 0
-      min: 0
-    avoid_pedestrian_in_crosswalk:
-      name: avoid_pedestrian_in_crosswalk
-      priority: 0
-      max: 0
-      min: 0
-    avoid_pedestrian_in_the_road:
-      name: avoid_pedestrian_in_the_road
-      priority: 0
-      max: 0
-      min: 0
-    avoid_pedestrian_when_turning:
-      name: avoid_pedestrian_when_turning
-      priority: 0
-      max: 0
-      min: 0
-    NoStraightThrough:
-      name: NoStraightThrough
-      priority: 0
-      max: 0
-      min: 0
-    SpeedLimitViolation:
-      name: SpeedLimitViolation
-      priority: 0
-      max: 0
-      min: 0
-    MinimumSpeedLimitViolation:
-      name: MinimumSpeedLimitViolation
-      priority: 0
-      max: 0
-      min: 0
-
-  minorViolation:
-    name: minorViolation
-    priority: 0
-    noUTurnViolation:
-      name: noUTurnViolation
-      priority: 0
-      max: 0
-      min: 0
-
-  warningViolation:
-    name: warningViolation
-    priority: 0
-    urbanExpresswayOrHighwaySpeedOverLimit0to20:
-      name: urbanExpresswayOrHighwaySpeedOverLimit0to20
-      priority: 0
-      max: 0
-      min: 0
-    urbanExpresswayOrHighwayRideLaneDivider:
-      name: urbanExpresswayOrHighwayRideLaneDivider
-      priority: 0
-      max: 0
-      min: 0
-    generalRoadIrregularLaneUse:
-      name: generalRoadIrregularLaneUse
-      priority: 0
-      max: 0
+
+safety:
+  name: safety
+  priority: 0
+  safeTime:
+    name: safetime
+    priority: 0
+    TTC:
+      name: TTC
+      priority: 0
+      max: 2000.0
+      min: 2.86
+    MTTC:
+      name: MTTC
+      priority: 0
+      max: 2000.0
+      min: 3.0
+    THW:
+      name: THW
+      priority: 0
+      max: 2000.0
+      min: 1.5
+  safeDistance:
+    name: safeDistance
+    priority: 0
+    LonSD:
+      name: LonSD
+      priority: 0
+      max: 2000.0
+      min: 10.0
+    LatSD:
+      name: LatSD 
+      priority: 0
+      max: 2000.0
+      min: 2.0
+  safeAcceleration:
+    name: safeAcceleration
+    priority: 0
+    BTN:
+      name: BTN
+      priority: 0
+      max: 1.0
+      min: -2000.0
+  safeProbability:
+    name: safeProbability
+    priority: 0
+    collisionRisk:
+      name: collisionRisk
+      priority: 0
+      max: 10.0
+      min: 0.0
+    collisionSeverity:
+      name: collisionSeverity
+      priority: 0
+      max: 10.0
+      min: 0.0
+
+comfort:
+  name: comfort
+  priority: 0
+  comfortLat:
+    name: comfortLat
+    priority: 0
+    weaving:
+      name: weaving
+      priority: 0
+      max: 0
+      min: 0
+    shake:
+      name: shake
+      priority: 0
+      max: 0
+      min: 0
+  comfortLon:
+    name: comfortLon
+    priority: 0
+    cadence:
+      name: cadence
+      priority: 0
+      max: 0
+      min: 0
+    slamBrake:
+      name: slamBrake 
+      priority: 0
+      max: 0
+      min: 0
+    slamAccelerate:
+      name: slamAccelerate
+      priority: 0
+      max: 0
+      min: 0
+
+efficient:
+  name: efficient
+  priority: 0
+  drivingMode:
+    name: drivingMode
+    priority: 0
+    max_speed:
+      name: maxSpeed
+      priority: 0
+      max: 0.0
+      min: 0.0
+    devation_speed:
+      name: deviationSpeed
+      priority: 0
+      max: 0.0
+      min: 0.0
+    averagedSpeed:
+      name: averagedSpeed
+      priority: 0
+      max: 80.0
+      min: 30.0
+  parkingMode:
+    name: parkingMode
+    priority: 0
+    stopDuration:
+      name: stopDuration
+      priority: 0
+      max: 1
+      min: 0
+
+function:
+  name: function
+  priority: 0
+  scenario:
+    name: ForwardCollision
+    priority: 0
+    latestWarningDistance_TTC_LST:
+      name: latestWarningDistance_TTC_LST
+      priority: 0
+      max: 3.11
+      min: 1.89
+    earliestWarningDistance_TTC_LST:
+      name: earliestWarningDistance_TTC_LST
+      priority: 0
+      max: 3.11
+      min: 1.89
+    latestWarningDistance_LST:
+      name: latestWarningDistance_LST
+      priority: 0
+      max: 17.29
+      min: 10.51
+    earliestWarningDistance_LST:
+      name: earliestWarningDistance_LST
+      priority: 0
+      max: 17.29
+      min: 10.51
+
+traffic:
+  name: traffic
+  priority: 0
+  majorViolation:
+    name: majorViolation
+    priority: 0
+    urbanExpresswayOrHighwaySpeedOverLimit50:
+      name: urbanExpresswayOrHighwaySpeedOverLimit50
+      priority: 0
+      max: 0
+      min: 0
+    urbanExpresswayOrHighwayReverse:
+      name: higwayreverse
+      priority: 0
+      max: 0
+      min: 0
+    urbanExpresswayOrHighwayDrivingAgainst:
+      name: higwayDrivingAgainst
+      priority: 0
+      max: 0
+      min: 0
+
+  seriousViolation:
+    name: seriousViolation
+    priority: 0
+    urbanExpresswayOrHighwayDrivingLaneStopped:
+      name: urbanExpresswayOrHighwayDrivingLaneStopped
+      priority: 0
+      max: 0
+      min: 0
+    urbanExpresswayOrHighwayEmergencyLaneStopped:
+      name: highwayEmergencyLaneStopped
+      priority: 0
+      max: 0
+      min: 0
+
+  dangerousViolation:
+    name: dangerousViolation
+    priority: 0
+    urbanExpresswayEmergencyLaneDriving:
+      name: urbanExpresswayEmergencyLaneDriving
+      priority: 0
+      max: 0
+      min: 0
+    trafficSignalViolation:
+      name: trafficSignalViolation
+      priority: 0
+      max: 0
+      min: 0
+    urbanExpresswayOrHighwaySpeedOverLimit20to50:
+      name: urbanExpresswayOrHighwaySpeedOverLimit20to50
+      priority: 0
+      max: 0
+      min: 0
+    generalRoadSpeedOverLimit50:
+      name: generalRoadSpeedOverLimit50
+      priority: 0
+      max: 0
+      min: 0
+
+  generalViolation:
+    name: generalViolation
+    priority: 0
+    generalRoadSpeedOverLimit20to50:
+      name: generalRoadSpeedOverLimit20to50
+      priority: 0
+      max: 0
+      min: 0
+    urbanExpresswayOrHighwaySpeedUnderLimit:
+      name: UrbanExpresswayOrHighwaySpeedUnderLimit
+      priority: 0
+      max: 0
+      min: 0
+    illegalDrivingOrParkingAtCrossroads:
+      name: illegalDrivingOrParkingAtCrossroads
+      priority: 0
+      max: 0
+      min: 0
+    overtake_on_right:
+      name: overtake_on_right
+      priority: 0
+      max: 0
+      min: 0
+    overtake_when_turn_around:
+      name: overtake_when_turn_around
+      priority: 0
+      max: 0
+      min: 0
+    overtake_when_passing_car:
+      name: overtake_when_passing_car
+      priority: 0
+      max: 0
+      min: 0
+    overtake_in_forbid_lane:
+      name: overtake_in_forbid_lane
+      priority: 0
+      max: 0
+      min: 0
+    overtake_in_ramp:
+      name: overtake_in_ramp
+      priority: 0
+      max: 0
+      min: 0
+    overtake_in_tunnel:
+      name: overtake_in_tunnel
+      priority: 0
+      max: 0
+      min: 0
+    overtake_on_accelerate_lane:
+      name: overtake_on_accelerate_lane
+      priority: 0
+      max: 0
+      min: 0
+    overtake_on_decelerate_lane:
+      name: overtake_on_decelerate_lane
+      priority: 0
+      max: 0
+      min: 0
+    overtake_in_different_senerios:
+      name: overtake_in_different_senerios
+      priority: 0
+      max: 0
+      min: 0
+    slow_down_in_crosswalk:
+      name: slow_down_in_crosswalk
+      priority: 0
+      max: 0
+      min: 0
+    avoid_pedestrian_in_crosswalk:
+      name: avoid_pedestrian_in_crosswalk
+      priority: 0
+      max: 0
+      min: 0
+    avoid_pedestrian_in_the_road:
+      name: avoid_pedestrian_in_the_road
+      priority: 0
+      max: 0
+      min: 0
+    avoid_pedestrian_when_turning:
+      name: avoid_pedestrian_when_turning
+      priority: 0
+      max: 0
+      min: 0
+    NoStraightThrough:
+      name: NoStraightThrough
+      priority: 0
+      max: 0
+      min: 0
+    SpeedLimitViolation:
+      name: SpeedLimitViolation
+      priority: 0
+      max: 0
+      min: 0
+    MinimumSpeedLimitViolation:
+      name: MinimumSpeedLimitViolation
+      priority: 0
+      max: 0
+      min: 0
+
+  minorViolation:
+    name: minorViolation
+    priority: 0
+    noUTurnViolation:
+      name: noUTurnViolation
+      priority: 0
+      max: 0
+      min: 0
+
+  warningViolation:
+    name: warningViolation
+    priority: 0
+    urbanExpresswayOrHighwaySpeedOverLimit0to20:
+      name: urbanExpresswayOrHighwaySpeedOverLimit0to20
+      priority: 0
+      max: 0
+      min: 0
+    urbanExpresswayOrHighwayRideLaneDivider:
+      name: urbanExpresswayOrHighwayRideLaneDivider
+      priority: 0
+      max: 0
+      min: 0
+    generalRoadIrregularLaneUse:
+      name: generalRoadIrregularLaneUse
+      priority: 0
+      max: 0
       min: 0
       min: 0

BIN
custom_metrics/__pycache__/metric_safety_safeTime_CustomTTC.cpython-313.pyc


BIN
custom_metrics/__pycache__/metric_user_safeTime_CustomTTC.cpython-313.pyc


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


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


BIN
modules/lib/__pycache__/log_manager.cpython-310.pyc


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


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


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


+ 81 - 19
modules/lib/data_process.py

@@ -33,7 +33,7 @@ class DataPreprocessing:
         # self.logger = log.get_logger()
         # self.logger = log.get_logger()
         
         
         self.data_path = data_path
         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
         self.config_path = config_path
 
 
@@ -117,26 +117,36 @@ class DataPreprocessing:
         try:
         try:
             # 读取 CSV 文件
             # 读取 CSV 文件
             merged_csv_path = os.path.join(self.data_path, "merged_ObjState.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(
             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"])
             ).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 = self.object_df.copy()
 
 
-            # Calculate common parameters
+            # 使用向量化操作计算速度和加速度,提高性能
             data["lat_v"] = data["speedY"] * 1
             data["lat_v"] = data["speedY"] * 1
             data["lon_v"] = data["speedX"] * 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["lat_acc"] = data["accelY"] * 1
             data["lon_acc"] = data["accelX"] * 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
             # Drop rows with missing 'type' and reset index
             data = data.dropna(subset=["type"])
             data = data.dropna(subset=["type"])
@@ -152,14 +162,31 @@ class DataPreprocessing:
             self.obj_id_list = list(self.obj_data.keys())
             self.obj_id_list = list(self.obj_data.keys())
             self.ego_data = self.obj_data[EGO_PLAYER_ID]
             self.ego_data = self.obj_data[EGO_PLAYER_ID]
 
 
+            # 添加这一行:处理自车数据,进行坐标系转换
+            self.ego_data = self.process_ego_data(self.ego_data)
+
         except Exception as e:
         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
             raise
 
 
     def _calculate_object_parameters(self, obj_data):
     def _calculate_object_parameters(self, obj_data):
         """Calculate additional parameters for a single object."""
         """Calculate additional parameters for a single object."""
         obj_data = obj_data.copy()
         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()
         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["lat_acc_diff"] = obj_data["lat_acc"].diff()
         obj_data["lon_acc_diff"] = obj_data["lon_acc"].diff()
         obj_data["lon_acc_diff"] = obj_data["lon_acc"].diff()
@@ -212,16 +239,51 @@ class DataPreprocessing:
 
 
     def _mileage_cal(self, df):
     def _mileage_cal(self, df):
         """Calculate mileage based on the driving data."""
         """Calculate mileage based on the driving data."""
+        if len(df) < 2:
+            return 0.0  # 数据不足,无法计算里程
+            
         if df["travelDist"].nunique() == 1:
         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
             return mileage
+        else:
+            # 如果travelDist已经有多个值,直接计算最大值和最小值的差
+            return round(df["travelDist"].max() - df["travelDist"].min(), 2)
         return 0.0  # Return 0 if travelDist is not valid
         return 0.0  # Return 0 if travelDist is not valid
 
 
     def _duration_cal(self, df):
     def _duration_cal(self, df):
         """Calculate duration of the driving data."""
         """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-310.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)
 @Authors:           zhanghaiwen(zhanghaiwen@china-icv.cn), yangzihao(yangzihao@china-icv.cn)
 @Data:              2023/06/25
 @Data:              2023/06/25
-@Last Modified:     2023/06/25
+@Last Modified:     2025/04/25
 @Summary:           Comfort metrics
 @Summary:           Comfort metrics
 """
 """
 
 
@@ -168,6 +168,15 @@ class ComfortCalculator:
         self.ego_df = pd.DataFrame()
         self.ego_df = pd.DataFrame()
         self.discomfort_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
         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.time_list = self.data['simTime'].values.tolist()
         self.frame_list = self.data['simFrame'].values.tolist()
         self.frame_list = self.data['simFrame'].values.tolist()
         
         
@@ -179,8 +188,6 @@ class ComfortCalculator:
         
         
         self.zigzag_time_list = []
         self.zigzag_time_list = []
         self.zigzag_stre_list = []
         self.zigzag_stre_list = []
-        self.cur_ego_path_list = []
-        self.curvature_list = []
         
         
         self._initialize_data()
         self._initialize_data()
     
     
@@ -202,16 +209,6 @@ class ComfortCalculator:
         self.ego_df['cadence'] = self.ego_df.apply(
         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)
             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):
     def _cal_cur_ego_path(self, row):
         """计算车辆轨迹曲率"""
         """计算车辆轨迹曲率"""
@@ -227,21 +224,25 @@ class ComfortCalculator:
     
     
     def _peak_valley_determination(self, df):
     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]
         t_diff = p_curr[0] - p_last[0]
         v_diff = abs(p_curr[1] - p_last[1])
         v_diff = abs(p_curr[1] - p_last[1])
         s = 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):
     def _cadence_process_new(self, lon_acc, ip_acc, ip_dec):
         """处理顿挫数据"""
         """处理顿挫数据"""
@@ -270,10 +271,22 @@ class ComfortCalculator:
         if flag:
         if flag:
             v_diff = abs(p_curr[1] - p_last[1])
             v_diff = abs(p_curr[1] - p_last[1])
             t_diff = p_curr[0] - p_last[0]
             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:
         else:
             self.zigzag_stre_list = []
             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):
     def calculate_zigzag_count(self):
         """计算蛇行指标"""
         """计算蛇行指标"""
         self._zigzag_count_func()
         self._zigzag_count_func()
@@ -299,49 +312,80 @@ class ComfortCalculator:
         self._slam_accel_detector()
         self._slam_accel_detector()
         return self.slam_accel_count
         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 = []
         time_list = []
         frame_list = []
         frame_list = []
 
 
+        # 复制数据以避免修改原始数据
         df = self.ego_df.copy()
         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
         TIME_RANGE = 1
         t_list = time_list
         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
 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:
 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)}
     return {"maxSpeed": float(max_speed)}
 
 
 def deviationSpeed(data_processed) -> dict:
 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)}
     return {"deviationSpeed": float(deviation)}
 
 
 def averagedSpeed(data_processed) -> dict:
 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)}
     return {"averagedSpeed": float(avg_speed)}
 
 
 def stopDuration(data_processed) -> dict:
 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:
 class EfficientRegistry:
@@ -124,6 +216,8 @@ class EfficientRegistry:
             try:
             try:
                 result = func(self.data)
                 result = func(self.data)
                 results.update(result)
                 results.update(result)
+                # 新增:将每个指标的结果写入日志
+                self.logger.info(f'高效性指标[{name}]计算结果: {result}')
             except Exception as e:
             except Exception as e:
                 self.logger.error(f"{name} 执行失败: {str(e)}", exc_info=True)
                 self.logger.error(f"{name} 执行失败: {str(e)}", exc_info=True)
                 results[name] = None
                 results[name] = None
@@ -141,8 +235,5 @@ class EfficientManager:
         """Generate the statistics and report the results."""
         """Generate the statistics and report the results."""
         # 使用注册表批量执行指标计算
         # 使用注册表批量执行指标计算
         efficient_result = self.efficient.batch_execute()
         efficient_result = self.efficient.batch_execute()
-        # evaluator = Score(self.data.efficient_config)
-        # result = evaluator.evaluate(efficient_result) 
-        # return result
         return efficient_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:
 def latestWarningDistance_LST(data) -> dict:
     """预警距离计算流水线"""
     """预警距离计算流水线"""
     scenario_name = data.function_config["function"]["scenario"]["name"]
     scenario_name = data.function_config["function"]["scenario"]["name"]
+    value = data.function_config["function"]["scenario"]["latestWarningDistance_LST"]["max"]
     correctwarning = scenario_sign_dict[scenario_name]
     correctwarning = scenario_sign_dict[scenario_name]
     ego_df = data.ego_data
     ego_df = data.ego_data
     warning_dist = calculate_distance(ego_df, correctwarning)
     warning_dist = calculate_distance(ego_df, correctwarning)
     if warning_dist.empty:
     if warning_dist.empty:
         return {"latestWarningDistance_LST": 0.0}
         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:
 def earliestWarningDistance_LST(data) -> dict:
     """预警距离计算流水线"""
     """预警距离计算流水线"""
     scenario_name = data.function_config["function"]["scenario"]["name"]
     scenario_name = data.function_config["function"]["scenario"]["name"]
+    value = data.function_config["function"]["scenario"]["earliestWarningDistance_LST"]["max"]
     correctwarning = scenario_sign_dict[scenario_name]
     correctwarning = scenario_sign_dict[scenario_name]
     ego_df = data.ego_data
     ego_df = data.ego_data
     warning_dist = calculate_distance(ego_df, correctwarning)
     warning_dist = calculate_distance(ego_df, correctwarning)
     if warning_dist.empty:
     if warning_dist.empty:
         return {"earliestWarningDistance_LST": 0.0}
         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:
 def latestWarningDistance_TTC_LST(data) -> dict:
     """TTC计算流水线"""
     """TTC计算流水线"""
     scenario_name = data.function_config["function"]["scenario"]["name"]
     scenario_name = data.function_config["function"]["scenario"]["name"]
+    value = data.function_config["function"]["scenario"]["latestWarningDistance_TTC_LST"]["max"]
     correctwarning = scenario_sign_dict[scenario_name]
     correctwarning = scenario_sign_dict[scenario_name]
     ego_df = data.ego_data
     ego_df = data.ego_data
     warning_dist = calculate_distance(ego_df, correctwarning)
     warning_dist = calculate_distance(ego_df, correctwarning)
@@ -126,13 +129,18 @@ def latestWarningDistance_TTC_LST(data) -> dict:
 
 
     with np.errstate(divide='ignore', invalid='ignore'):
     with np.errstate(divide='ignore', invalid='ignore'):
         ttc = np.where(warning_speed != 0, warning_dist / warning_speed, np.inf)
         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:
 def earliestWarningDistance_TTC_LST(data) -> dict:
     """TTC计算流水线"""
     """TTC计算流水线"""
     scenario_name = data.function_config["function"]["scenario"]["name"]
     scenario_name = data.function_config["function"]["scenario"]["name"]
+    value = data.function_config["function"]["scenario"]["earliestWarningDistance_TTC_LST"]["max"]
     correctwarning = scenario_sign_dict[scenario_name]
     correctwarning = scenario_sign_dict[scenario_name]
     ego_df = data.ego_data
     ego_df = data.ego_data
     warning_dist = calculate_distance(ego_df, correctwarning)
     warning_dist = calculate_distance(ego_df, correctwarning)
@@ -143,8 +151,12 @@ def earliestWarningDistance_TTC_LST(data) -> dict:
 
 
     with np.errstate(divide='ignore', invalid='ignore'):
     with np.errstate(divide='ignore', invalid='ignore'):
         ttc = np.where(warning_speed != 0, warning_dist / warning_speed, np.inf)
         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):
 def warningDelayTime_LST(data):

+ 58 - 40
modules/metric/safety.py

@@ -29,20 +29,6 @@ SAFETY_INFO = [
     "type"
     "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:
     try:
         safety = SafetyCalculator(data_processed)
         safety = SafetyCalculator(data_processed)
         ttc_value = safety.get_ttc_value()
         ttc_value = safety.get_ttc_value()
+        LogManager().get_logger().info(f"安全指标[TTC]计算结果: {ttc_value}")
         return {"TTC": ttc_value}
         return {"TTC": ttc_value}
     except Exception as e:
     except Exception as e:
         LogManager().get_logger().error(f"TTC计算异常: {str(e)}", exc_info=True)
         LogManager().get_logger().error(f"TTC计算异常: {str(e)}", exc_info=True)
@@ -65,6 +52,7 @@ def calculate_mttc(data_processed) -> dict:
     try:
     try:
         safety = SafetyCalculator(data_processed)
         safety = SafetyCalculator(data_processed)
         mttc_value = safety.get_mttc_value()
         mttc_value = safety.get_mttc_value()
+        LogManager().get_logger().info(f"安全指标[MTTC]计算结果: {mttc_value}")
         return {"MTTC": mttc_value}
         return {"MTTC": mttc_value}
     except Exception as e:
     except Exception as e:
         LogManager().get_logger().error(f"MTTC计算异常: {str(e)}", exc_info=True)
         LogManager().get_logger().error(f"MTTC计算异常: {str(e)}", exc_info=True)
@@ -77,6 +65,7 @@ def calculate_thw(data_processed) -> dict:
     try:
     try:
         safety = SafetyCalculator(data_processed)
         safety = SafetyCalculator(data_processed)
         thw_value = safety.get_thw_value()
         thw_value = safety.get_thw_value()
+        LogManager().get_logger().info(f"安全指标[THW]计算结果: {thw_value}")
         return {"THW": thw_value}
         return {"THW": thw_value}
     except Exception as e:
     except Exception as e:
         LogManager().get_logger().error(f"THW计算异常: {str(e)}", exc_info=True)
         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)
     safety = SafetyCalculator(data_processed)
     collision_risk_value = safety.get_collision_risk_value()
     collision_risk_value = safety.get_collision_risk_value()
+    LogManager().get_logger().info(f"安全指标[collisionRisk]计算结果: {collision_risk_value}")
     return {"collisionRisk": collision_risk_value}
     return {"collisionRisk": collision_risk_value}
 
 
 def calculate_lonsd(data_processed) -> dict:
 def calculate_lonsd(data_processed) -> dict:
     """计算纵向安全距离"""
     """计算纵向安全距离"""
     safety = SafetyCalculator(data_processed)
     safety = SafetyCalculator(data_processed)
     lonsd_value = safety.get_lonsd_value()
     lonsd_value = safety.get_lonsd_value()
+    LogManager().get_logger().info(f"安全指标[LonSD]计算结果: {lonsd_value}")
     return {"LonSD": lonsd_value}
     return {"LonSD": lonsd_value}
 
 
 def calculate_latsd(data_processed) -> dict:
 def calculate_latsd(data_processed) -> dict:
     """计算横向安全距离"""
     """计算横向安全距离"""
     safety = SafetyCalculator(data_processed)
     safety = SafetyCalculator(data_processed)
     latsd_value = safety.get_latsd_value()
     latsd_value = safety.get_latsd_value()
+    LogManager().get_logger().info(f"安全指标[LatSD]计算结果: {latsd_value}")
     return {"LatSD": latsd_value}
     return {"LatSD": latsd_value}
 
 
 def calculate_btn(data_processed) -> dict:
 def calculate_btn(data_processed) -> dict:
     """计算制动威胁数"""
     """计算制动威胁数"""
     safety = SafetyCalculator(data_processed)
     safety = SafetyCalculator(data_processed)
     btn_value = safety.get_btn_value()
     btn_value = safety.get_btn_value()
+    LogManager().get_logger().info(f"安全指标[BTN]计算结果: {btn_value}")
     return {"BTN": btn_value}
     return {"BTN": btn_value}
 
 
 def calculate_collisionseverity(data_processed) -> dict:
 def calculate_collisionseverity(data_processed) -> dict:
     """计算碰撞严重性"""
     """计算碰撞严重性"""
     safety = SafetyCalculator(data_processed)
     safety = SafetyCalculator(data_processed)
     collision_severity_value = safety.get_collision_severity_value()
     collision_severity_value = safety.get_collision_severity_value()
+    LogManager().get_logger().info(f"安全指标[collisionSeverity]计算结果: {collision_severity_value}")
     return {"collisionSeverity": collision_severity_value}
     return {"collisionSeverity": collision_severity_value}
 
 
 
 
@@ -183,18 +177,29 @@ class SafetyCalculator:
         self.data_processed = data_processed
         self.data_processed = data_processed
 
 
         self.df = data_processed.object_df.copy()
         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.obj_id_list = data_processed.obj_id_list
         self.metric_list = [
         self.metric_list = [
             'TTC', 'MTTC', 'THW', 'LonSD', 'LatSD', 'BTN', 'collisionRisk', 'collisionSeverity'
             '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.time_list = self.ego_df['simTime'].values.tolist()
         self.frame_list = self.ego_df['simFrame'].values.tolist()
         self.frame_list = self.ego_df['simFrame'].values.tolist()
         self.collisionRisk = 0
         self.collisionRisk = 0
         self.empty_flag = True
         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:
         if len(self.obj_id_list) > 1:
             self.unsafe_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
             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']
                 a_y2 = obj_data['accelY']
 
 
                 dx, dy = x2 - x1, y2 - y1
                 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]['lon_d'] = lon_d
                 obj_dict[frame_num][playerId]['lat_d'] = lat_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,
                 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)
                                                     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_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)
                 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_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
                 TTC = None if (TTC is None or TTC < 0) else TTC
                 MTTC = None if (MTTC is None or MTTC < 0) else MTTC
                 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)
                 2 * ego_decel_min) - v_obj_p ** 2 / (2 * ego_decel_max)
         return lon_dist_min
         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_right_rho = v_right + rho * a_lat_max
         v_left_rho = v_left + 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
         return dist_min
 
 
     # DRAC (decelerate required avoid collision)
     # DRAC (decelerate required avoid collision)
@@ -673,7 +690,8 @@ class SafetyCalculator:
         if self.empty_flag or self.df_safe is None:
         if self.empty_flag or self.df_safe is None:
             return self._default_value('LatSD')
             return self._default_value('LatSD')
         latsd_values = self.df_safe['LatSD'].dropna()
         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:
     def get_btn_value(self) -> float:
         if self.empty_flag or self.df_safe is None:
         if self.empty_flag or self.df_safe is None:

+ 416 - 147
modules/metric/traffic.py

@@ -1,6 +1,7 @@
 # ... 保留原有导入和常量定义 ...
 # ... 保留原有导入和常量定义 ...
 import math
 import math
-import operator
+import operator 
+import copy
 import numpy as np
 import numpy as np
 import pandas as pd
 import pandas as pd
 from typing import Dict, Any, List, Optional
 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.log_manager import LogManager
 from modules.lib import data_process
 from modules.lib import data_process
 
 
+
 OVERTAKE_INFO = [
 OVERTAKE_INFO = [
     "simTime",
     "simTime",
     "simFrame",
     "simFrame",
@@ -49,7 +51,7 @@ TURNAROUND_INFO = [
 
 
 TRFFICSIGN_INFO = [
 TRFFICSIGN_INFO = [
     "simTime",
     "simTime",
-    "simFrame",
+    "simFrame",  
     "playerId",
     "playerId",
     "speedX",
     "speedX",
     "speedY",
     "speedY",
@@ -261,7 +263,7 @@ def calculate_generalroadirregularlaneuse(data_processed):
 def calculate_urbanexpresswayorhighwayridelanedivider(data_processed):
 def calculate_urbanexpresswayorhighwayridelanedivider(data_processed):
     """计算城市快速路或高速公路骑车道线行驶指标"""
     """计算城市快速路或高速公路骑车道线行驶指标"""
     warningviolation = WarningViolation(data_processed)
     warningviolation = WarningViolation(data_processed)
-    urbanExpresswayOrHighwayRideLaneDivider_count = warningviolation.calculate_urbanExpresswayOrHighwayRideLaneDivider()
+    urbanExpresswayOrHighwayRideLaneDivider_count = warningviolation.calculate_urbanExpresswayOrHighwayRideLaneDivider_count()
     return {"urbanExpresswayOrHighwayRideLaneDivider": urbanExpresswayOrHighwayRideLaneDivider_count}
     return {"urbanExpresswayOrHighwayRideLaneDivider": urbanExpresswayOrHighwayRideLaneDivider_count}
 
 
 def calculate_nostraightthrough(data_processed):
 def calculate_nostraightthrough(data_processed):
@@ -283,7 +285,7 @@ def calculate_minimumspeedlimitviolation(data_processed):
     trafficsignviolation = TrafficSignViolation(data_processed)
     trafficsignviolation = TrafficSignViolation(data_processed)
     calculate_MinimumSpeedLimitViolation_count = trafficsignviolation.calculate_MinimumSpeedLimitViolation_count()
     calculate_MinimumSpeedLimitViolation_count = trafficsignviolation.calculate_MinimumSpeedLimitViolation_count()
     return {"MinimumSpeedLimitViolation": calculate_MinimumSpeedLimitViolation_count}
     return {"MinimumSpeedLimitViolation": calculate_MinimumSpeedLimitViolation_count}
-# ... 保留原有类定义 ...
+
 
 
 # 修改 TrafficRegistry 类的 _build_registry 方法
 # 修改 TrafficRegistry 类的 _build_registry 方法
 class TrafficRegistry:
 class TrafficRegistry:
@@ -349,7 +351,6 @@ class TrafficManager:
         traffic_result = self.registry.batch_execute()
         traffic_result = self.registry.batch_execute()
         return traffic_result
         return traffic_result
 
 
-# ... 保留原有类定义和实现 ...
 
 
 class OvertakingViolation(object):
 class OvertakingViolation(object):
     """超车违规类"""
     """超车违规类"""
@@ -358,36 +359,78 @@ class OvertakingViolation(object):
         print("超车违规类初始化中...")
         print("超车违规类初始化中...")
         self.traffic_violations_type = "超车违规类"
         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:
         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:
         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):
     def different_road_area_simtime(self, df, threshold=0.5):
         if not df:
         if not df:
@@ -425,9 +468,17 @@ class OvertakingViolation(object):
 
 
         return car_dx, car_dy
         return car_dx, car_dy
 
 
-        # 在前车右侧超车、会车时超车、前车掉头时超车
-
     def illegal_overtake_with_car_detector(self, window_width=250):
     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文件中最短的帧数
         # 获取csv文件中最短的帧数
         frame_id_length = len(self.ego_data["simFrame"])
         frame_id_length = len(self.ego_data["simFrame"])
@@ -442,28 +493,47 @@ class OvertakingViolation(object):
             ego_data_frames = self.ego_data[
             ego_data_frames = self.ego_data[
                 self.ego_data["simFrame"].isin(simframe_window)
                 self.ego_data["simFrame"].isin(simframe_window)
             ]
             ]
+            
+            # 确保有足够的数据进行处理
+            if len(ego_data_frames) == 0:
+                start_frame_id += 1
+                continue
+                
             obj_data_frames = self.obj_data[
             obj_data_frames = self.obj_data[
                 self.obj_data["simFrame"].isin(simframe_window)
                 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[
             other_data_frames = self.other_obj_data[
                 self.other_obj_data["simFrame"].isin(simframe_window)
                 self.other_obj_data["simFrame"].isin(simframe_window)
             ]
             ]
+            
             # 读取前后的laneId
             # 读取前后的laneId
             lane_id = ego_data_frames["lane_id"].tolist()
             lane_id = ego_data_frames["lane_id"].tolist()
-            # 读取前后方向盘转角steeringWheel,
+            
+            # 读取前后方向盘转角steeringWheel
             driverctrl_start_state = ego_data_frames["posH"].iloc[0]
             driverctrl_start_state = ego_data_frames["posH"].iloc[0]
             driverctrl_end_state = ego_data_frames["posH"].iloc[-1]
             driverctrl_end_state = ego_data_frames["posH"].iloc[-1]
+            
             # 读取车辆前后的位置信息
             # 读取车辆前后的位置信息
             dx, dy = self._is_dxy_of_car(ego_data_frames, obj_data_frames)
             dx, dy = self._is_dxy_of_car(ego_data_frames, obj_data_frames)
             ego_speedx = ego_data_frames["speedX"].tolist()
             ego_speedx = ego_data_frames["speedX"].tolist()
             ego_speedy = ego_data_frames["speedY"].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:
             if len(other_data_frames) > 0:
                 other_start_speedx = other_data_frames["speedX"].iloc[0]
                 other_start_speedx = other_data_frames["speedX"].iloc[0]
                 other_start_speedy = other_data_frames["speedY"].iloc[0]
                 other_start_speedy = other_data_frames["speedY"].iloc[0]
@@ -472,31 +542,47 @@ class OvertakingViolation(object):
                         + ego_speedy[0] * other_start_speedy
                         + ego_speedy[0] * other_start_speedy
                         < 0
                         < 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
                         lane_id, dx, dy, ego_speedx, ego_speedy
                     )
                     )
                     start_frame_id += window_width
                     start_frame_id += window_width
-            """
-            如果滑动窗口开始和最后的laneid一致;
-            方向盘转角前后方向相反(开始方向盘转角向右后来方向盘转角向左);
-            自车和前车的位置发生的交换;
-            则认为右超车
-            """
+                    continue
+            
+            # 检查右侧超车
             if driverctrl_start_state > 0 and driverctrl_end_state < 0:
             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
                     lane_id, dx, dy, ego_speedx, ego_speedy
                 )
                 )
                 start_frame_id += window_width
                 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):
     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 = self.obj_data["simTime"].tolist()
         simtime_devide = self.different_road_area_simtime(simTime)
         simtime_devide = self.different_road_area_simtime(simTime)
         for simtime in simtime_devide:
         for simtime in simtime_devide:
@@ -506,13 +592,25 @@ class OvertakingViolation(object):
                 if (50002 in lane_type and len(set(lane_type)) > 2) or (
                 if (50002 in lane_type and len(set(lane_type)) > 2) or (
                         50002 not in lane_type and len(set(lane_type)) > 1
                         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:
             except Exception as e:
                 print("数据缺少lane_type信息")
                 print("数据缺少lane_type信息")
-        # print(f"在不该占用车道超车{self.overtake_in_forbid_lane_count}次")
+                
+        self._calculated["forbid_lane"] = True
 
 
     # 在匝道超车
     # 在匝道超车
     def overtake_in_ramp_area_detector(self):
     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)][
         ramp_simtime_list = self.ego_data[(self.ego_data["road_type"] == 19)][
             "simTime"
             "simTime"
         ].tolist()
         ].tolist()
@@ -527,14 +625,26 @@ class OvertakingViolation(object):
             ego_speedx = ego_in_ramp["speedX"].tolist()
             ego_speedx = ego_in_ramp["speedX"].tolist()
             ego_speedy = ego_in_ramp["speedY"].tolist()
             ego_speedy = ego_in_ramp["speedY"].tolist()
             if len(lane_id) > 0:
             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
                     lane_id, dx, dy, ego_speedx, ego_speedy
                 )
                 )
             else:
             else:
                 continue
                 continue
-        # print(f"在匝道超车{self.overtake_in_ramp_count}次")
+                
+        self._calculated["ramp_area"] = True
 
 
     def overtake_in_tunnel_area_detector(self):
     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)][
         tunnel_simtime_list = self.ego_data[(self.ego_data["road_type"] == 15)][
             "simTime"
             "simTime"
         ].tolist()
         ].tolist()
@@ -549,15 +659,27 @@ class OvertakingViolation(object):
             ego_speedx = ego_in_tunnel["speedX"].tolist()
             ego_speedx = ego_in_tunnel["speedX"].tolist()
             ego_speedy = ego_in_tunnel["speedY"].tolist()
             ego_speedy = ego_in_tunnel["speedY"].tolist()
             if len(lane_id) > 0:
             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
                     lane_id, dx, dy, ego_speedx, ego_speedy
                 )
                 )
             else:
             else:
                 continue
                 continue
-        # print(f"在隧道超车{self.overtake_in_tunnel_count}次")
+                
+        self._calculated["tunnel_area"] = True
 
 
     # 加速车道超车
     # 加速车道超车
     def overtake_on_accelerate_lane_detector(self):
     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][
         accelerate_simtime_list = self.ego_data[self.ego_data["lane_type"] == 2][
             "simTime"
             "simTime"
         ].tolist()
         ].tolist()
@@ -576,13 +698,25 @@ class OvertakingViolation(object):
             ego_speedx = ego_in_accelerate["speedX"].tolist()
             ego_speedx = ego_in_accelerate["speedX"].tolist()
             ego_speedy = ego_in_accelerate["speedY"].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
                 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):
     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)][
         decelerate_simtime_list = self.ego_data[(self.ego_data["lane_type"] == 3)][
             "simTime"
             "simTime"
         ].tolist()
         ].tolist()
@@ -601,13 +735,25 @@ class OvertakingViolation(object):
             ego_speedx = ego_in_decelerate["speedX"].tolist()
             ego_speedx = ego_in_decelerate["speedX"].tolist()
             ego_speedy = ego_in_decelerate["speedY"].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
                 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):
     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][
         crossroad_simTime = self.ego_data[self.ego_data["interid"] != 10000][
             "simTime"
             "simTime"
         ].tolist()  # 判断是路口或者隧道区域
         ].tolist()  # 判断是路口或者隧道区域
@@ -629,47 +775,56 @@ class OvertakingViolation(object):
         则认为发生超车
         则认为发生超车
         """
         """
         if len(lane_id) > 0:
         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
                 lane_id, dx, dy, ego_speedx, ego_speedy
             )
             )
-        else:
-            pass
+            
+        self._calculated["different_senerios"] = True
+
     def calculate_overtake_when_passing_car_count(self):
     def calculate_overtake_when_passing_car_count(self):
+        """计算会车时超车违规次数"""
         self.illegal_overtake_with_car_detector()
         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):
     def calculate_overtake_on_right_count(self):
+        """计算右侧超车违规次数"""
         self.illegal_overtake_with_car_detector()
         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):
     def calculate_overtake_when_turn_around_count(self):
+        """计算掉头时超车违规次数"""
         self.illegal_overtake_with_car_detector()
         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):
     def calculate_overtake_in_forbid_lane_count(self):
+        """计算借道超车违规次数"""
         self.overtake_in_forbid_lane_detector()
         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):
     def calculate_overtake_in_ramp_area_count(self):
+        """计算匝道超车违规次数"""
         self.overtake_in_ramp_area_detector()
         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):
     def calculate_overtake_in_tunnel_area_count(self):
+        """计算隧道超车违规次数"""
         self.overtake_in_tunnel_area_detector()
         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):
     def calculate_overtake_on_accelerate_lane_count(self):
+        """计算加速车道超车违规次数"""
         self.overtake_on_accelerate_lane_detector()
         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):
     def calculate_overtake_on_decelerate_lane_count(self):
+        """计算减速车道超车违规次数"""
         self.overtake_on_decelerate_lane_detector()
         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):
     def calculate_overtake_in_different_senerios_count(self):
+        """计算不同场景超车违规次数"""
         self.overtake_in_different_senerios_detector()
         self.overtake_in_different_senerios_detector()
-        return self.overtake_in_different_senerios_count
-
+        return self.violation_counts["overtake_in_different_senerios"]
 
 
 
 
 class SlowdownViolation(object):
 class SlowdownViolation(object):
@@ -678,24 +833,43 @@ class SlowdownViolation(object):
     def __init__(self, df_data):
     def __init__(self, df_data):
         print("减速让行违规类-------------------------")
         print("减速让行违规类-------------------------")
         self.traffic_violations_type = "减速让行违规类"
         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.object_items = set(df_data.object_df.type.tolist())
+        
+        # 存储行人数据引用
+        self._pedestrian_df = None
         if 13 in self.object_items:  # 行人的type是13
         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.slow_down_in_crosswalk_count = 0
         self.avoid_pedestrian_in_crosswalk_count = 0
         self.avoid_pedestrian_in_crosswalk_count = 0
         self.avoid_pedestrian_in_the_road_count = 0
         self.avoid_pedestrian_in_the_road_count = 0
         self.aviod_pedestrian_when_turning_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):
     def pedestrian_in_front_of_car(self):
         if len(self.pedestrian_data) == 0:
         if len(self.pedestrian_data) == 0:
@@ -875,22 +1049,40 @@ class TurnaroundViolation(object):
         print("掉头违规类初始化中...")
         print("掉头违规类初始化中...")
         self.traffic_violations_type = "掉头违规类"
         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.object_items = set(df_data.object_df.type.tolist())
+        
+        # 存储行人数据引用
+        self._pedestrian_df = None
         if 13 in self.object_items:  # 行人的type是13
         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_back_sign_count = 0
         self.turning_in_forbiden_turn_left_sign_count = 0
         self.turning_in_forbiden_turn_left_sign_count = 0
         self.avoid_pedestrian_when_turn_back_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):
     def pedestrian_in_front_of_car(self):
         if len(self.pedestrian_data) == 0:
         if len(self.pedestrian_data) == 0:
@@ -1021,20 +1213,33 @@ class TurnaroundViolation(object):
         self.avoid_pedestrian_when_turn_back_detector()
         self.avoid_pedestrian_when_turn_back_detector()
         return self.avoid_pedestrian_when_turn_back_count
         return self.avoid_pedestrian_when_turn_back_count
 
 
-
-class WrongWayViolation:
+class WrongWayViolation(object):
     """停车违规类"""
     """停车违规类"""
 
 
     def __init__(self, df_data):
     def __init__(self, df_data):
         print("停车违规类初始化中...")
         print("停车违规类初始化中...")
         self.traffic_violations_type = "停车违规类"
         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 = {
         self.violation_count = {
             "urbanExpresswayOrHighwayDrivingLaneStopped": 0,
             "urbanExpresswayOrHighwayDrivingLaneStopped": 0,
             "urbanExpresswayOrHighwayEmergencyLaneStopped": 0,
             "urbanExpresswayOrHighwayEmergencyLaneStopped": 0,
             "urbanExpresswayEmergencyLaneDriving": 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):
     def process_violations(self):
         """处理停车或者紧急车道行驶违规数据"""
         """处理停车或者紧急车道行驶违规数据"""
@@ -1102,9 +1307,11 @@ class SpeedingViolation(object):
     def __init__(self, df_data):
     def __init__(self, df_data):
         print("超速违规类初始化中...")
         print("超速违规类初始化中...")
         self.traffic_violations_type = "超速违规类"
         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 = {
         self.violation_counts = {
             "urbanExpresswayOrHighwaySpeedOverLimit50": 0,
             "urbanExpresswayOrHighwaySpeedOverLimit50": 0,
@@ -1115,6 +1322,16 @@ class SpeedingViolation(object):
             "generalRoadSpeedOverLimit20to50": 0,
             "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):
     def process_violations(self):
         """处理数据帧,检查超速和其他违规行为"""
         """处理数据帧,检查超速和其他违规行为"""
         # 提取有效道路类型
         # 提取有效道路类型
@@ -1365,61 +1582,92 @@ class WarningViolation(object):
     """警告性违规类"""
     """警告性违规类"""
 
 
     def __init__(self, df_data):
     def __init__(self, df_data):
+        print("警告性违规类初始化中...")
         self.traffic_violations_type = "警告性违规类"
         self.traffic_violations_type = "警告性违规类"
-        print("警告性违规类 类初始化中...")
+        
+        # 存储原始数据引用
         self.config = df_data.vehicle_config
         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 = {
         self.violation_counts = {
             "generalRoadIrregularLaneUse": 0,  # 驾驶机动车在高速公路、城市快速路以外的道路上不按规定车道行驶
             "generalRoadIrregularLaneUse": 0,  # 驾驶机动车在高速公路、城市快速路以外的道路上不按规定车道行驶
             "urbanExpresswayOrHighwayRideLaneDivider": 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):
     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)) & (
         condition = (self.data["road_fc"].isin(general_road)) & (
             self.data["lane_type"].isin(lane_type)
             self.data["lane_type"].isin(lane_type)
         )
         )
-
+        
         # 创建一个新的列,并根据条件设置值
         # 创建一个新的列,并根据条件设置值
         self.data["is_violation"] = condition
         self.data["is_violation"] = condition
-
+        
         # 统计满足条件的连续时间段
         # 统计满足条件的连续时间段
         violation_segments = self.count_continuous_violations(
         violation_segments = self.count_continuous_violations(
             self.data["is_violation"], self.data["simTime"]
             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
         threshold = (lane_width - car_width) / 2
-
+        
         # 找到满足条件的行
         # 找到满足条件的行
         self.data["is_violation"] = self.data["laneOffset"] > threshold
         self.data["is_violation"] = self.data["laneOffset"] > threshold
-
+        
         # 统计满足条件的连续时间段
         # 统计满足条件的连续时间段
         violation_segments = self.count_continuous_violations(
         violation_segments = self.count_continuous_violations(
             self.data["is_violation"], self.data["simTime"]
             self.data["is_violation"], self.data["simTime"]
         )
         )
-
-        # 更新骑行车道线违规计数
-        self.violation_counts["urbanExpresswayOrHighwayRideLaneDivider"] += len(
+        
+        # 更新违规计数
+        self.violation_counts["urbanExpresswayOrHighwayRideLaneDivider"] = len(
             violation_segments
             violation_segments
         )
         )
 
 
     def count_continuous_violations(self, violation_series, time_series):
     def count_continuous_violations(self, violation_series, time_series):
-        """统计连续违规的时间段数量"""
+        """统计连续违规的时间段数量
+        
+        Args:
+            violation_series: 表示是否违规的布尔序列
+            time_series: 对应的时间序列
+            
+        Returns:
+            list: 连续违规时间段列表
+        """
         continuous_segments = []
         continuous_segments = []
         current_segment = []
         current_segment = []
 
 
@@ -1429,25 +1677,29 @@ class WarningViolation(object):
                     current_segment.append(time)
                     current_segment.append(time)
             else:
             else:
                 if current_segment:  # 连续段结束
                 if current_segment:  # 连续段结束
+                    current_segment.append(time)  # 添加结束时间
                     continuous_segments.append(current_segment)
                     continuous_segments.append(current_segment)
                     current_segment = []
                     current_segment = []
 
 
         # 检查是否有一个未结束的连续段在最后
         # 检查是否有一个未结束的连续段在最后
         if current_segment:
         if current_segment:
+            current_segment.append(time_series.iloc[-1])  # 使用最后的时间作为结束时间
             continuous_segments.append(current_segment)
             continuous_segments.append(current_segment)
 
 
         return continuous_segments
         return continuous_segments
 
 
-
     def calculate_generalRoadIrregularLaneUse_count(self):
     def calculate_generalRoadIrregularLaneUse_count(self):
-        self.process_violations()
+        """计算普通道路不按规定车道行驶违规次数"""
+        # 只处理普通道路不按规定车道行驶违规
+        self._process_irregular_lane_use()
         return self.violation_counts["generalRoadIrregularLaneUse"]
         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"]
         return self.violation_counts["urbanExpresswayOrHighwayRideLaneDivider"]
 
 
-
 class TrafficSignViolation:
 class TrafficSignViolation:
     """交通标志违规类"""
     """交通标志违规类"""
     
     
@@ -1457,12 +1709,14 @@ class TrafficSignViolation:
     SIGN_TYPE_MIN_SPEED_LIMIT = 13
     SIGN_TYPE_MIN_SPEED_LIMIT = 13
 
 
     def __init__(self, df_data):
     def __init__(self, df_data):
+        print("交通标志违规类初始化中...")
         self.traffic_violations_type = "交通标志违规类"
         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
         self._calculated = False
@@ -1471,6 +1725,16 @@ class TrafficSignViolation:
             "SpeedLimitViolation": 0,
             "SpeedLimitViolation": 0,
             "MinimumSpeedLimitViolation": 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):
     def _ensure_calculated(self):
         """保证计算只执行一次"""
         """保证计算只执行一次"""
@@ -1513,7 +1777,7 @@ class TrafficSignViolation:
 
 
     def _check_straight_violation(self):
     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:
         if not straight_df.empty:
             # 计算航向角变化并填充缺失值
             # 计算航向角变化并填充缺失值
@@ -1526,16 +1790,21 @@ class TrafficSignViolation:
                 (straight_df['v'] > 0)
                 (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):
     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:
         if not violation_df.empty:
             mask = compare_op(violation_df['v'], violation_df['sign_speed'])
             mask = compare_op(violation_df['v'], violation_df['sign_speed'])
-            self.violation_counts[count_key] = mask.sum()
-
+            self._violation_counts[count_key] = mask.sum()
 
 
 
 
 
 

+ 1 - 1
scripts/evaluator_enhanced.py

@@ -560,7 +560,7 @@ def main():
     parser.add_argument(
     parser.add_argument(
         "--dataPath",
         "--dataPath",
         type=str,
         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",
         help="Input data directory",
     )
     )
     parser.add_argument(
     parser.add_argument(