XGJ2024 6 сар өмнө
commit
ece7da87c0
94 өөрчлөгдсөн 6286 нэмэгдсэн , 0 устгасан
  1. BIN
      __pycache__/cicv_LKA_01_distance_nearby_lane.cpython-310.pyc
  2. BIN
      __pycache__/cicv_LKA_03_heading_deviation_max.cpython-310.pyc
  3. BIN
      __pycache__/cicv_acc_01_delay_time_cruise_new.cpython-310.pyc
  4. BIN
      __pycache__/cicv_acc_01_delay_time_cruise_new2.cpython-310.pyc
  5. BIN
      __pycache__/cicv_acc_02_rise_time_cruise_new.cpython-310.pyc
  6. BIN
      __pycache__/cicv_acc_02_rise_time_cruise_new2.cpython-310.pyc
  7. BIN
      __pycache__/cicv_acc_03_peak_time_cruise_new.cpython-310.pyc
  8. BIN
      __pycache__/cicv_acc_03_peak_time_cruise_new2.cpython-310.pyc
  9. BIN
      __pycache__/cicv_acc_04_overshoot_cruise_new.cpython-310.pyc
  10. BIN
      __pycache__/cicv_acc_04_overshoot_cruise_new2.cpython-310.pyc
  11. BIN
      __pycache__/cicv_acc_05_steady_error_cruise_new.cpython-310.pyc
  12. BIN
      __pycache__/cicv_acc_06_delay_time_THW_new.cpython-310.pyc
  13. BIN
      __pycache__/cicv_acc_06_delay_time_THW_new2.cpython-310.pyc
  14. BIN
      __pycache__/cicv_acc_07_rise_time_THW_new.cpython-310.pyc
  15. BIN
      __pycache__/cicv_acc_07_rise_time_THW_new2.cpython-310.pyc
  16. BIN
      __pycache__/cicv_acc_08_peak_time_THW_new.cpython-310.pyc
  17. BIN
      __pycache__/cicv_acc_08_peak_time_THW_new2.cpython-310.pyc
  18. BIN
      __pycache__/cicv_acc_09_overshoot_THW_new.cpython-310.pyc
  19. BIN
      __pycache__/cicv_acc_09_overshoot_THW_new2.cpython-310.pyc
  20. BIN
      __pycache__/cicv_acc_10_steady_error_THW_new.cpython-310.pyc
  21. BIN
      __pycache__/cicv_acc_11_reasonable_acceleration_percentage.cpython-310.pyc
  22. BIN
      __pycache__/cicv_acc_11_reasonable_acceleration_percentage2.cpython-310.pyc
  23. BIN
      __pycache__/cicv_acc_12_reasonable_acceleration_change_rate_percentage.cpython-310.pyc
  24. BIN
      __pycache__/cicv_acc_12_reasonable_acceleration_change_rate_percentage2.cpython-310.pyc
  25. BIN
      __pycache__/cicv_ica_lateral_control07_center_distance_max_new.cpython-310.pyc
  26. BIN
      __pycache__/cicv_ica_lateral_control11_heading_deviation_max_new.cpython-310.pyc
  27. BIN
      __pycache__/cicv_ica_longitudinal_control01_delay_time_cruise_new.cpython-310.pyc
  28. BIN
      __pycache__/cicv_ica_longitudinal_control02_rise_time_cruise_new.cpython-310.pyc
  29. BIN
      __pycache__/cicv_ica_longitudinal_control03_peak_time_cruise_new.cpython-310.pyc
  30. BIN
      __pycache__/cicv_ica_longitudinal_control04_overshoot_cruise_new.cpython-310.pyc
  31. BIN
      __pycache__/cicv_ica_longitudinal_control05_steady_error_cruise_new.cpython-310.pyc
  32. BIN
      __pycache__/cicv_ica_longitudinal_control06_delay_time_THW_new.cpython-310.pyc
  33. BIN
      __pycache__/cicv_ica_longitudinal_control07_rise_time_THW_new.cpython-310.pyc
  34. BIN
      __pycache__/cicv_ica_longitudinal_control08_peak_time_THW_new.cpython-310.pyc
  35. BIN
      __pycache__/cicv_ica_longitudinal_control09_overshoot_THW_new.cpython-310.pyc
  36. BIN
      __pycache__/cicv_ica_longitudinal_control10_steady_error_THW_new.cpython-310.pyc
  37. BIN
      __pycache__/cicv_ica_longitudinal_control11_reasonable_acceleration_percentage_new.cpython-310.pyc
  38. BIN
      __pycache__/cicv_ica_longitudinal_control12_reasonable_acceleration_change_rate_percentage_new.cpython-310.pyc
  39. 24 0
      cicv_LKA_01_distance_nearby_lane.json
  40. 170 0
      cicv_LKA_01_distance_nearby_lane.py
  41. 24 0
      cicv_LKA_03_heading_deviation_max.json
  42. 160 0
      cicv_LKA_03_heading_deviation_max.py
  43. 21 0
      cicv_acc_01_delay_time_cruise_new.json
  44. 200 0
      cicv_acc_01_delay_time_cruise_new.py
  45. 21 0
      cicv_acc_02_rise_time_cruise_new.json
  46. 230 0
      cicv_acc_02_rise_time_cruise_new.py
  47. 21 0
      cicv_acc_03_peak_time_cruise_new.json
  48. 153 0
      cicv_acc_03_peak_time_cruise_new.py
  49. 21 0
      cicv_acc_04_overshoot_cruise_new.json
  50. 213 0
      cicv_acc_04_overshoot_cruise_new.py
  51. 21 0
      cicv_acc_05_steady_error_cruise_new.json
  52. 205 0
      cicv_acc_05_steady_error_cruise_new.py
  53. 21 0
      cicv_acc_06_delay_time_THW_new.json
  54. 224 0
      cicv_acc_06_delay_time_THW_new.py
  55. 21 0
      cicv_acc_07_rise_time_THW_new.json
  56. 261 0
      cicv_acc_07_rise_time_THW_new.py
  57. 21 0
      cicv_acc_08_peak_time_THW_new.json
  58. 175 0
      cicv_acc_08_peak_time_THW_new.py
  59. 21 0
      cicv_acc_09_overshoot_THW_new.json
  60. 232 0
      cicv_acc_09_overshoot_THW_new.py
  61. 21 0
      cicv_acc_10_steady_error_THW_new.json
  62. 237 0
      cicv_acc_10_steady_error_THW_new.py
  63. 21 0
      cicv_acc_11_reasonable_acceleration_percentage.json
  64. 166 0
      cicv_acc_11_reasonable_acceleration_percentage.py
  65. 21 0
      cicv_acc_12_reasonable_acceleration_change_rate_percentage.json
  66. 167 0
      cicv_acc_12_reasonable_acceleration_change_rate_percentage.py
  67. 22 0
      cicv_ica_lateral_control07_center_distance_max_new.json
  68. 185 0
      cicv_ica_lateral_control07_center_distance_max_new.py
  69. 22 0
      cicv_ica_lateral_control11_heading_deviation_max_new.json
  70. 189 0
      cicv_ica_lateral_control11_heading_deviation_max_new.py
  71. 21 0
      cicv_ica_longitudinal_control01_delay_time_cruise_new.json
  72. 206 0
      cicv_ica_longitudinal_control01_delay_time_cruise_new.py
  73. 21 0
      cicv_ica_longitudinal_control02_rise_time_cruise_new.json
  74. 236 0
      cicv_ica_longitudinal_control02_rise_time_cruise_new.py
  75. 21 0
      cicv_ica_longitudinal_control03_peak_time_cruise_new.json
  76. 159 0
      cicv_ica_longitudinal_control03_peak_time_cruise_new.py
  77. 21 0
      cicv_ica_longitudinal_control04_overshoot_cruise_new.json
  78. 217 0
      cicv_ica_longitudinal_control04_overshoot_cruise_new.py
  79. 21 0
      cicv_ica_longitudinal_control05_steady_error_cruise_new.json
  80. 214 0
      cicv_ica_longitudinal_control05_steady_error_cruise_new.py
  81. 21 0
      cicv_ica_longitudinal_control06_delay_time_THW_new.json
  82. 229 0
      cicv_ica_longitudinal_control06_delay_time_THW_new.py
  83. 21 0
      cicv_ica_longitudinal_control07_rise_time_THW_new.json
  84. 267 0
      cicv_ica_longitudinal_control07_rise_time_THW_new.py
  85. 21 0
      cicv_ica_longitudinal_control08_peak_time_THW_new.json
  86. 182 0
      cicv_ica_longitudinal_control08_peak_time_THW_new.py
  87. 21 0
      cicv_ica_longitudinal_control09_overshoot_THW_new.json
  88. 233 0
      cicv_ica_longitudinal_control09_overshoot_THW_new.py
  89. 21 0
      cicv_ica_longitudinal_control10_steady_error_THW_new.json
  90. 233 0
      cicv_ica_longitudinal_control10_steady_error_THW_new.py
  91. 21 0
      cicv_ica_longitudinal_control11_reasonable_acceleration_percentage_new.json
  92. 170 0
      cicv_ica_longitudinal_control11_reasonable_acceleration_percentage_new.py
  93. 21 0
      cicv_ica_longitudinal_control12_reasonable_acceleration_change_rate_percentage_new.json
  94. 177 0
      cicv_ica_longitudinal_control12_reasonable_acceleration_change_rate_percentage_new.py

BIN
__pycache__/cicv_LKA_01_distance_nearby_lane.cpython-310.pyc


BIN
__pycache__/cicv_LKA_03_heading_deviation_max.cpython-310.pyc


BIN
__pycache__/cicv_acc_01_delay_time_cruise_new.cpython-310.pyc


BIN
__pycache__/cicv_acc_01_delay_time_cruise_new2.cpython-310.pyc


BIN
__pycache__/cicv_acc_02_rise_time_cruise_new.cpython-310.pyc


BIN
__pycache__/cicv_acc_02_rise_time_cruise_new2.cpython-310.pyc


BIN
__pycache__/cicv_acc_03_peak_time_cruise_new.cpython-310.pyc


BIN
__pycache__/cicv_acc_03_peak_time_cruise_new2.cpython-310.pyc


BIN
__pycache__/cicv_acc_04_overshoot_cruise_new.cpython-310.pyc


BIN
__pycache__/cicv_acc_04_overshoot_cruise_new2.cpython-310.pyc


BIN
__pycache__/cicv_acc_05_steady_error_cruise_new.cpython-310.pyc


BIN
__pycache__/cicv_acc_06_delay_time_THW_new.cpython-310.pyc


BIN
__pycache__/cicv_acc_06_delay_time_THW_new2.cpython-310.pyc


BIN
__pycache__/cicv_acc_07_rise_time_THW_new.cpython-310.pyc


BIN
__pycache__/cicv_acc_07_rise_time_THW_new2.cpython-310.pyc


BIN
__pycache__/cicv_acc_08_peak_time_THW_new.cpython-310.pyc


BIN
__pycache__/cicv_acc_08_peak_time_THW_new2.cpython-310.pyc


BIN
__pycache__/cicv_acc_09_overshoot_THW_new.cpython-310.pyc


BIN
__pycache__/cicv_acc_09_overshoot_THW_new2.cpython-310.pyc


BIN
__pycache__/cicv_acc_10_steady_error_THW_new.cpython-310.pyc


BIN
__pycache__/cicv_acc_11_reasonable_acceleration_percentage.cpython-310.pyc


BIN
__pycache__/cicv_acc_11_reasonable_acceleration_percentage2.cpython-310.pyc


BIN
__pycache__/cicv_acc_12_reasonable_acceleration_change_rate_percentage.cpython-310.pyc


BIN
__pycache__/cicv_acc_12_reasonable_acceleration_change_rate_percentage2.cpython-310.pyc


BIN
__pycache__/cicv_ica_lateral_control07_center_distance_max_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_lateral_control11_heading_deviation_max_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control01_delay_time_cruise_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control02_rise_time_cruise_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control03_peak_time_cruise_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control04_overshoot_cruise_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control05_steady_error_cruise_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control06_delay_time_THW_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control07_rise_time_THW_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control08_peak_time_THW_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control09_overshoot_THW_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control10_steady_error_THW_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control11_reasonable_acceleration_percentage_new.cpython-310.pyc


BIN
__pycache__/cicv_ica_longitudinal_control12_reasonable_acceleration_change_rate_percentage_new.cpython-310.pyc


+ 24 - 0
cicv_LKA_01_distance_nearby_lane.json

@@ -0,0 +1,24 @@
+{
+  "priority": "0",
+  "paramList": [
+    {
+      "kind": "1",
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ],
+      "optimal": "0.1",
+      "multiple": [
+        "0.2",
+        "2"
+      ]
+    }
+  ],
+  "weight": null,
+  "unit": "m",
+  "name": "离近侧车道线距离"
+}

+ 170 - 0
cicv_LKA_01_distance_nearby_lane.py

@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhangyu
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+车道宽度:3.75m
+车宽:1.8m
+车的一边距离车道边界线为0.975m为最佳,值越小,分值越低
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ego = pd.DataFrame()
+        self.roadMark_df = pd.DataFrame()
+
+        self.time_list_follow = list()
+        self.frame_list_follow = list()
+        self.dist_list = list()
+        self.dist_deviation_list = list()
+        self.dist_deviation_list_full_time = list()
+
+        self.result = {
+            "name": "离近侧车道线最小距离",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "离近侧车道线最小距离(m)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"指标01: 离近侧车道线最小距离: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.object_df[self.data.object_df.playerId == 1]
+
+        # new active get code
+        active_time_ranges = self.status_trigger_dict['LKA']['LKA_active_time']
+        self.df_ego = get_status_active_data(active_time_ranges, self.ego_df)
+        self.roadMark_df = get_status_active_data(active_time_ranges, self.data.road_mark_df)
+
+        # self.df_ego = self.df[self.df['LKA_status'] == "Active"].copy()  # 数字3对应LKA的Active
+        # self.roadMark_df = self.data.road_mark_df
+
+        if self.df_ego.empty:
+            self.result['statusFlag']['function_LKA'] = False
+        else:
+            self.result['statusFlag']['function_LKA'] = True
+
+    def dist(self, x1, y1, x2, y2):
+        dis = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
+        return dis
+
+    def Compute_nearby_distance_to_lane_boundary(self, x, width_ego):
+        if x.lateralDist < abs(x.right_lateral_distance):
+            return x.lateralDist - width_ego / 2
+        else:
+            return abs(x.right_lateral_distance) - width_ego / 2
+
+    def data_analyze(self):
+        # 提取自车宽度
+        roadMark_df = self.roadMark_df
+        ego_df = self.df_ego
+        # ego_df = player_df[player_df.playerId == 1]
+        width_ego = ego_df['dimY'].values.tolist()[0]
+        # 提取距离左车道线和右车道线距离
+        # roadMark_df['nearby_distance']
+        roadMark_left_df = roadMark_df[roadMark_df.id == 0].reset_index(drop=True)
+        roadMark_right_df = roadMark_df[roadMark_df.id == 2].reset_index(drop=True)
+        roadMark_left_df['right_lateral_distance'] = roadMark_right_df['lateralDist']
+        # 计算到车道边界线距离
+        roadMark_left_df['nearby_distance_to_lane_boundary'] = roadMark_left_df.apply(
+            lambda x: self.Compute_nearby_distance_to_lane_boundary(x, width_ego), axis=1)
+        nearby_distance_to_lane_boundary = min(roadMark_left_df['nearby_distance_to_lane_boundary'])
+
+        self.result['value'] = [round(nearby_distance_to_lane_boundary, 3)]
+
+        self.time_list_follow = roadMark_left_df['simTime'].values.tolist()
+        self.frame_list_follow = roadMark_left_df['simFrame'].values.tolist()
+        self.dist_deviation_list = roadMark_left_df['nearby_distance_to_lane_boundary'].values.tolist()
+        # print("hello world")
+
+    def markline_statistic(self):
+        unfunc_df = pd.DataFrame({'simTime': self.time_list_follow, 'simFrame': self.frame_list_follow,
+                                  'dist_deviation': self.dist_deviation_list})
+        unfunc_df = unfunc_df[unfunc_df['simFrame'] > 1]
+
+        v_df = unfunc_df[unfunc_df['dist_deviation'] > 0]
+        v_df = v_df[['simTime', 'simFrame', 'dist_deviation']]
+        v_follow_df = continuous_group(v_df)
+        v_follow_df['type'] = "LKA"
+        self.markline_df = pd.concat([self.markline_df, v_follow_df], ignore_index=True)
+
+    def report_data_statistic(self):
+        time_list = self.ego_df['simTime'].values.tolist()
+        graph_list = [x for x in self.dist_deviation_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = f'{np.mean(graph_list):.3f}' if graph_list else 0
+        self.result['tableData']['max'] = f'{max(graph_list):.3f}' if graph_list else 0
+        self.result['tableData']['min'] = f'{min(graph_list):.3f}' if graph_list else 0
+
+        zip_vs_time = zip_time_pairs(time_list, self.dist_deviation_list)
+        self.result['reportData']['data'] = zip_vs_time
+
+        self.markline_statistic()
+        markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = markline_slices
+
+        self.result['reportData']['range'] = f"[0, 0.975]"
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[ica_distance_deviation:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 24 - 0
cicv_LKA_03_heading_deviation_max.json

@@ -0,0 +1,24 @@
+{
+  "priority": "0",
+  "paramList": [
+    {
+      "kind": "-1",
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ],
+      "optimal": "0.175",
+      "multiple": [
+        "0.5",
+        "3"
+      ]
+    }
+  ],
+  "weight": null,
+  "unit": "rad",
+  "name": "最大航向偏差"
+}

+ 160 - 0
cicv_LKA_03_heading_deviation_max.py

@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhangyu
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+最大航向偏差角
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ego = pd.DataFrame()
+        self.roadMark_df = pd.DataFrame()
+        # self.roadPos_df = pd.DataFrame()
+
+        self.time_list_follow = list()
+        self.frame_list_follow = list()
+        self.dist_list = list()
+        self.dist_deviation_list = list()
+        self.dist_deviation_list_full_time = list()
+
+        self.result = {
+            "name": "最大航向角偏差",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "最大航向角偏差(rad)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"指标03: LKA最大航向角偏差: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.object_df[self.data.object_df.playerId == 1]
+
+        # new active get code
+        active_time_ranges = self.status_trigger_dict['LKA']['LKA_active_time']
+        self.df_ego = get_status_active_data(active_time_ranges, self.ego_df)
+        self.roadMark_df = get_status_active_data(active_time_ranges, self.data.road_mark_df)
+
+        if self.df_ego.empty:
+            self.result['statusFlag']['function_LKA'] = False
+        else:
+            self.result['statusFlag']['function_LKA'] = True
+
+    def dist(self, x1, y1, x2, y2):
+        dis = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
+        return dis
+
+    def Compute_nearby_distance_to_lane_boundary(self, x, width_ego):
+        if x.lateralDist < abs(x.right_lateral_distance):
+            return x.lateralDist - width_ego / 2
+        else:
+            return abs(x.right_lateral_distance) - width_ego / 2
+
+    def data_analyze(self):
+        # 提取自车宽度
+        ego_df = self.df_ego.reset_index(drop=True)
+        road_mark_df = self.roadMark_df
+
+        # 左车道线曲率,右车道线曲率,求二者平均值,计算车道线曲率,再与自车朝向相减
+        road_mark_left_df = road_mark_df[road_mark_df.id == 0].reset_index(drop=True)
+        road_mark_right_df = road_mark_df[road_mark_df.id == 2].reset_index(drop=True)
+        road_mark_left_df['curvHor_left'] = road_mark_left_df['curvHor']
+        road_mark_left_df['curvHor_right'] = road_mark_right_df['curvHor']
+        road_mark_left_df['curvHor_middle'] = road_mark_left_df[['curvHor_left', 'curvHor_right']].apply( \
+            lambda x: (x['curvHor_left'] + x['curvHor_right']) / 2, axis=1)
+        ego_df['curvHor_middle'] = road_mark_left_df['curvHor_middle']
+        ego_df['heading_deviation_abs'] = ego_df[['curvHor_middle', 'posH']].apply( \
+            lambda x: abs(x['posH'] - x['curvHor_middle']), axis=1)
+        row_with_max_value = max(ego_df['heading_deviation_abs'])
+        self.result['value'] = [row_with_max_value]
+        self.time_list_follow = ego_df['simTime'].values.tolist()
+        self.frame_list_follow = ego_df['simFrame'].values.tolist()
+        self.dist_deviation_list = ego_df['heading_deviation_abs'].values.tolist()
+
+    def markline_statistic(self):
+        unfunc_df = pd.DataFrame({'simTime': self.time_list_follow, 'simFrame': self.frame_list_follow,
+                                  'dist_deviation': self.dist_deviation_list})
+        unfunc_df = unfunc_df[unfunc_df['simFrame'] > 1]
+        # v_df = unfunc_df[unfunc_df['dist_deviation'] > 0]
+        v_df = unfunc_df
+        v_df = v_df[['simTime', 'simFrame', 'dist_deviation']]
+        v_follow_df = continuous_group(v_df)
+        v_follow_df['type'] = "LKA"
+        self.markline_df = pd.concat([self.markline_df, v_follow_df], ignore_index=True)
+
+    def report_data_statistic(self):
+        time_list = self.ego_df['simTime'].values.tolist()
+        graph_list = [x for x in self.dist_deviation_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = f'{np.mean(graph_list):.3f}' if graph_list else 0
+        self.result['tableData']['max'] = f'{max(graph_list):.3f}' if graph_list else 0
+        self.result['tableData']['min'] = f'{min(graph_list):.3f}' if graph_list else 0
+
+        zip_vs_time = zip_time_pairs(time_list, self.dist_deviation_list)
+        self.result['reportData']['data'] = zip_vs_time
+
+        self.markline_statistic()
+        markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['range'] = f"[-1.875, 1.875]"
+
+    def run(self):
+        logger.info(f"[case:{self.case_name}] Custom metric:[cicv_LKA_03_heading_deviation_max:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_01_delay_time_cruise_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "5.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 200 - 0
cicv_acc_01_delay_time_cruise_new.py

@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+Max_Time = 1000
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_acc = pd.DataFrame()
+
+        # self.stable_start_time_cruise = None
+        self.stable_average_speed = None
+        self.delay_time_cruise = None
+
+        self.result = {
+            "name": "定速巡航延迟时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "定速巡航延迟时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"定速巡航延迟时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.ego_df)
+        # self.df_acc = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的 LLC_Follow_Line
+        # self.df_acc = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+
+    def _find_stable_speed_cruise(self, window_size, percent_deviation, set_value):
+        """
+        在给定的速度数据中查找稳定的巡航速度段,并计算该段的平均速度。
+
+        Args:
+            window_size (int): 滑动窗口的大小,表示用于计算平均速度的速度数据点数量。
+            percent_deviation (float): 设定值的允许偏差百分比。
+            set_value (float): 期望的稳定速度设定值。
+
+        Returns:
+            None
+
+        """
+        # speed_data = self.df['speedX'].values
+        speed_data = self.df_acc['speedX'].values  # .tolist()
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_speed = None
+
+        for i in range(len(speed_data) - window_size + 1):
+            window_data = speed_data[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_speed = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(speed_data) - window_size + 1:
+                    next_window_data = speed_data[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_speed = (stable_average_speed * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_speed = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_cruise = self.df['simTime'].iloc[stable_start]
+        self.stable_average_speed = stable_average_speed
+
+    def data_analyze(self):
+        change_indices = self.df_acc[self.df_acc['set_cruise_speed'] != self.df_acc['set_cruise_speed'].shift()].index
+        print(f"Change indices of set speed: {change_indices}")
+
+        set_cruise_speed = self.df_acc.loc[change_indices[0], 'set_cruise_speed']
+        self._find_stable_speed_cruise(window_size=4, percent_deviation=5, set_value=set_cruise_speed)
+
+        if not change_indices.empty:
+            first_change_index = change_indices[change_indices != 0].min()
+            set_cruise_speed_at_change = self.df_acc.loc[first_change_index, 'set_cruise_speed']
+            timestamp_at_change = self.df_acc.loc[first_change_index, 'simTime']
+            print(f"Set speed at first change: {set_cruise_speed_at_change}, Timestamp: {timestamp_at_change}")
+            if self.stable_average_speed:
+                target_speed = (self.stable_average_speed + self.df_acc.loc[first_change_index, 'speedX']) / 2
+                closest_index = (self.df_acc['speedX'] - target_speed).abs().idxmin()
+                closest_current_speed = self.df_acc.loc[closest_index, 'speedX']
+                closest_timestamp = self.df_acc.loc[closest_index, 'simTime']
+                print(f"Closest speed: {closest_current_speed} at time: {closest_timestamp}")
+
+                self.delay_time_cruise = closest_timestamp - timestamp_at_change
+                self.result['value'] = [round(self.delay_time_cruise, 3)]
+                print(f"Delay time: {self.delay_time_cruise}")
+            else:
+                self.delay_time_cruise = Max_Time
+                self.result['value'] = [round(self.delay_time_cruise, 3)]
+                print("No valid stable speed found.")
+
+        else:
+            self.delay_time_cruise = Max_Time
+            self.result['value'] = [round(self.delay_time_cruise, 3)]
+            print("No valid change point for further calculation.")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_acc.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[delay_time_cruise:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_02_rise_time_cruise_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "5.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 230 - 0
cicv_acc_02_rise_time_cruise_new.py

@@ -0,0 +1,230 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+Max_Time = 1000
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_acc = pd.DataFrame()
+
+        self.stable_average_speed = None
+        self.rise_time_cruise = None
+
+        self.result = {
+            "name": "定速巡航上升时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "定速巡航上升时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"定速巡航上升时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.ego_df)
+        # self.df_acc = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+
+    def _get_first_change_index_cruise(self):
+        """
+        获取数据集中第一次巡航速度发生变化的索引。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在巡航速度发生变化的索引,则返回第一个发生变化的索引(int类型);
+            如果不存在,则返回None。
+
+        """
+        change_indices = self.df_acc[self.df_acc['set_cruise_speed'] != self.df_acc['set_cruise_speed'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_speed_cruise(self, window_size, percent_deviation, set_value):
+        """
+        在给定的速度数据中查找稳定的巡航速度段,并计算该段的平均速度。
+
+        Args:
+            window_size (int): 滑动窗口的大小,表示用于计算平均速度的速度数据点数量。
+            percent_deviation (float): 设定值的允许偏差百分比。
+            set_value (float): 期望的稳定速度设定值。
+
+        Returns:
+            None
+
+        """
+        # speed_data = self.df['speedX'].values
+        speed_data = self.df_acc['speedX'].values   #.tolist()
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_speed = None
+
+        for i in range(len(speed_data) - window_size + 1):
+            window_data = speed_data[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_speed = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(speed_data) - window_size + 1:
+                    next_window_data = speed_data[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_speed = (stable_average_speed * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_speed = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_cruise = self.df['simTime'].iloc[stable_start]
+        self.stable_average_speed = stable_average_speed
+
+    def _find_closest_time_stamp_cruise(self, df, target_speed, start_index):
+        """
+        在给定的数据帧df中,从start_index索引位置开始,查找与目标速度target_speed最接近的时间戳。
+
+        Args:
+            df (pd.DataFrame): 包含速度和时间戳等信息的数据帧,需要至少包含'speedX'和'simTime'两列。
+            target_speed (float): 目标速度值,用于在数据帧中查找最接近此值的时间戳。
+            start_index (int): 开始查找的索引位置,即在数据帧df中从该索引位置开始向后查找。
+
+        Returns:
+            pd.Timestamp: 与目标速度最接近的时间戳。
+
+        """
+        subset = df.loc[start_index + 1:]
+        speed_diff = np.abs(subset['speedX'] - target_speed)
+        closest_index = speed_diff.idxmin()
+        closest_timestamp = subset.loc[closest_index, 'simTime']
+        return closest_timestamp
+
+    def data_analyze(self):
+        first_change_index = self._get_first_change_index_cruise()
+
+        set_cruise_speed = self.df_acc.loc[first_change_index, 'set_cruise_speed']
+        self._find_stable_speed_cruise(window_size=4, percent_deviation=5, set_value=set_cruise_speed)
+
+        initial_speed = self.df_acc.loc[first_change_index, 'speedX']
+        if self.stable_average_speed:
+            
+            target_speed_90 = initial_speed + (self.stable_average_speed - initial_speed) * 0.9
+            target_speed_10 = initial_speed + (self.stable_average_speed - initial_speed) * 0.1
+
+            timestamp_at_10 = self._find_closest_time_stamp_cruise(self.df_acc, target_speed_10, first_change_index)
+            timestamp_at_90 = self._find_closest_time_stamp_cruise(self.df_acc, target_speed_90, first_change_index)
+
+            print(f"Closest speed at 10% range from set speed: {timestamp_at_10}")
+            print(f"Closest speed at 90% range from set speed: {timestamp_at_90}")
+
+            self.rise_time_cruise = timestamp_at_90 - timestamp_at_10
+            self.result['value'] = [round(self.rise_time_cruise, 3)]
+            print(f"Rise time: {self.rise_time_cruise}")
+        else:
+            self.rise_time_cruise = Max_Time
+            self.result['value'] = [round(self.rise_time_cruise, 3)]
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_acc.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[rise_time_cruise:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_03_peak_time_cruise_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "5.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 153 - 0
cicv_acc_03_peak_time_cruise_new.py

@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_acc = pd.DataFrame()
+
+        self.peak_time_cruise = None
+
+        self.result = {
+            "name": "定速巡航峰值时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "定速巡航峰值时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"定速巡航峰值时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.ego_df)
+        # self.df_acc = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+
+    def _get_first_change_index_cruise(self):
+        """
+        获取数据集中第一次巡航速度发生变化的索引。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在巡航速度发生变化的索引,则返回第一个发生变化的索引(int类型);
+            如果不存在,则返回None。
+
+        """
+        change_indices = self.df_acc[self.df_acc['set_cruise_speed'] != self.df_acc['set_cruise_speed'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def data_analyze(self):
+        first_change_index = self._get_first_change_index_cruise()
+
+        if not first_change_index:
+            self.peak_time_cruise = 0
+            self.result['value'] = [round(self.peak_time_cruise, 3)]
+            print(f"peak_time_cruise: {self.peak_time_cruise}")
+        else:
+            start_time = self.df_acc.loc[first_change_index, 'simTime']
+            peak_time = self.df_acc.loc[self.df_acc['speedX'].idxmax(), 'simTime']
+            self.peak_time_cruise = peak_time - start_time
+            self.result['value'] = [round(self.peak_time_cruise, 3)]
+            print(f"peak_time_cruise: {self.peak_time_cruise}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_acc.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[peak_time_cruise:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_04_overshoot_cruise_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "5.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 213 - 0
cicv_acc_04_overshoot_cruise_new.py

@@ -0,0 +1,213 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+Max_Time = 1000
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_acc = pd.DataFrame()
+
+        self.stable_average_speed = None
+        self.overshoot_cruise = None
+
+        self.result = {
+            "name": "定速巡航超调量",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "定速巡航超调量(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"定速巡航超调量: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.ego_df)
+        # self.df_acc = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+
+    def _get_first_change_index_cruise(self):
+        """
+        获取数据集中第一次巡航速度发生变化的索引。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在巡航速度发生变化的索引,则返回第一个发生变化的索引(int类型);
+            如果不存在,则返回None。
+
+        """
+        change_indices = self.df_acc[self.df_acc['set_cruise_speed'] != self.df_acc['set_cruise_speed'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_speed_cruise(self, window_size, percent_deviation, set_value):
+        """
+        在给定的速度数据中查找稳定的巡航速度段,并计算该段的平均速度。
+
+        Args:
+            window_size (int): 滑动窗口的大小,表示用于计算平均速度的速度数据点数量。
+            percent_deviation (float): 设定值的允许偏差百分比。
+            set_value (float): 期望的稳定速度设定值。
+
+        Returns:
+            None
+
+        """
+        # speed_data = self.df['speedX'].values
+        speed_data = self.df_acc['speedX'].values  # .tolist()
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_speed = None
+
+        for i in range(len(speed_data) - window_size + 1):
+            window_data = speed_data[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_speed = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(speed_data) - window_size + 1:
+                    next_window_data = speed_data[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_speed = (stable_average_speed * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_speed = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_cruise = self.df['simTime'].iloc[stable_start]
+        self.stable_average_speed = stable_average_speed
+        print("self.stable_average_speed is", self.stable_average_speed)
+
+    def data_analyze(self):
+        first_change_index = self._get_first_change_index_cruise()
+
+        set_cruise_speed = self.df_acc.loc[first_change_index, 'set_cruise_speed']
+        self._find_stable_speed_cruise(window_size=4, percent_deviation=5, set_value=set_cruise_speed)
+        if self.stable_average_speed:
+            if not first_change_index:
+                self.overshoot_cruise = 0
+            else:
+                initial_speed = self.df.loc[first_change_index, 'speedX']
+                print("initial_speed is", initial_speed)
+                print("self.df['speedX'].min() is", self.df['speedX'].min())
+                if initial_speed > self.stable_average_speed:
+                    self.overshoot_cruise = (self.stable_average_speed - self.df[
+                        'speedX'].min()) * 100 / self.stable_average_speed
+                elif initial_speed < self.stable_average_speed:
+                    self.overshoot_cruise = (self.df[
+                                                'speedX'].max() - self.stable_average_speed) * 100 / self.stable_average_speed
+                else:
+                    self.overshoot_cruise = 0
+
+            self.result['value'] = [round(self.overshoot_cruise, 3)]
+            print(f"overshoot_cruise: {self.overshoot_cruise}")
+        else:
+            self.overshoot_cruise = Max_Time
+            self.result['value'] = [round(self.overshoot_cruise, 3)]
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_acc.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[overshoot_cruise:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_05_steady_error_cruise_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "5.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 205 - 0
cicv_acc_05_steady_error_cruise_new.py

@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+Max = 100
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_acc = pd.DataFrame()
+
+        self.stable_average_speed = None
+        self.steady_error_cruise = None
+
+        self.result = {
+            "name": "定速巡航速度稳态误差",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "定速巡航速度稳态误差(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"定速巡航速度稳态误差: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.ego_df)
+        # self.df_acc = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+
+    def _get_first_change_index_cruise(self):
+        """
+        获取数据集中第一次巡航速度发生变化的索引。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在巡航速度发生变化的索引,则返回第一个发生变化的索引(int类型);
+            如果不存在,则返回None。
+
+        """
+        change_indices = self.df_acc[self.df_acc['set_cruise_speed'] != self.df_acc['set_cruise_speed'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_speed_cruise(self, window_size, percent_deviation, set_value):
+        """
+        在给定的速度数据中查找稳定的巡航速度段,并计算该段的平均速度。
+
+        Args:
+            window_size (int): 滑动窗口的大小,表示用于计算平均速度的速度数据点数量。
+            percent_deviation (float): 设定值的允许偏差百分比。
+            set_value (float): 期望的稳定速度设定值。
+
+        Returns:
+            None
+
+        """
+        # speed_data = self.df['speedX'].values
+        speed_data = self.df_acc['speedX'].values  # .tolist()
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_speed = None
+
+        for i in range(len(speed_data) - window_size + 1):
+            window_data = speed_data[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_speed = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(speed_data) - window_size + 1:
+                    next_window_data = speed_data[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_speed = (stable_average_speed * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_speed = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_cruise = self.df['simTime'].iloc[stable_start]
+        self.stable_average_speed = stable_average_speed
+
+    def data_analyze(self):
+        first_change_index = self._get_first_change_index_cruise()
+
+        if not first_change_index:
+            self.steady_error_cruise = 0
+            self.result['value'] = [abs(round(self.steady_error_cruise, 3))]
+            print(f"steady_error_cruise: {abs(self.steady_error_cruise)}")
+        else:
+            set_cruise_speed = self.df_acc.loc[first_change_index, 'set_cruise_speed']
+            self._find_stable_speed_cruise(window_size=4, percent_deviation=5, set_value=set_cruise_speed)
+
+            if self.stable_average_speed:
+                self.steady_error_cruise = (self.stable_average_speed - set_cruise_speed) * 100 / self.stable_average_speed
+            else:
+                self.steady_error_cruise = Max
+                
+
+            self.result['value'] = [abs(round(self.steady_error_cruise, 3))]
+            print(f"steady_error_cruise: {abs(self.steady_error_cruise)}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_acc.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[steady_error_cruise:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_06_delay_time_THW_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "5.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 224 - 0
cicv_acc_06_delay_time_THW_new.py

@@ -0,0 +1,224 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+from log import logger
+
+"""import functions"""
+
+Max_Time = 1000
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_acc = pd.DataFrame()
+
+        # self.stable_start_time_THW = None
+        self.stable_average_THW = None
+        self.delay_time_THW = Max_Time 
+
+        self.result = {
+            "name": "跟车延迟时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "跟车延迟时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"跟车延迟时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.df)
+        # self.df_acc = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else: 
+            self.result['statusFlag']['function_ACC'] = True
+
+    def _find_stable_THW(self, window_size, percent_deviation, set_value):
+        """
+        在给定的数据窗口中查找稳定跟车时距离THW,并计算该段内THW的平均值。
+
+        Args:
+            window_size (int): 窗口大小,表示在数据中寻找稳定段时考虑的连续数据点数量。
+            percent_deviation (float): THW值相对于设定值的允许偏差百分比。
+            set_value (float): THW的设定值。
+
+        Returns:
+            None
+
+        """
+        ego_x = self.df_acc[self.df_acc['playerId'] == 1]['posX'].reset_index(drop = True)
+        ego_y = self.df_acc[self.df_acc['playerId'] == 1]['posY'].reset_index(drop = True)
+        obj_x = self.df_acc[self.df_acc['playerId'] == 2]['posX'].reset_index(drop = True)
+        obj_y = self.df_acc[self.df_acc['playerId'] == 2]['posY'].reset_index(drop = True)
+
+        ego_speedx = self.df_acc[self.df_acc['playerId'] == 1]['speedX'].reset_index(drop = True)
+        ego_speedy = self.df_acc[self.df_acc['playerId'] == 1]['speedY'].reset_index(drop = True)
+        obj_speedx = self.df_acc[self.df_acc['playerId'] == 2]['speedX'].reset_index(drop = True)
+        obj_speedy = self.df_acc[self.df_acc['playerId'] == 2]['speedY'].reset_index(drop = True)
+
+        dx = obj_x - ego_x
+        dy = obj_y - ego_y
+        vx = obj_speedx - ego_speedx
+        vy = obj_speedy - ego_speedy
+        dist = np.sqrt(dx**2 + dy**2)
+        ego_v_projection_in_dist = _cal_v_ego_projection(dx, dy, ego_speedx, ego_speedy)
+        thw1 = _cal_THW(dist, ego_v_projection_in_dist)
+        thw = thw1.tolist()
+        THW = []
+        for item in thw:
+            THW.append(item)
+            THW.append(item)
+        self.df_acc['THW'] = THW
+
+        THW = self.df_acc['THW'].values
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_THW = None
+
+        for i in range(len(THW) - window_size + 1):
+            window_data = THW[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_THW = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(THW) - window_size + 1:
+                    next_window_data = THW[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_THW = (stable_average_THW * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_THW = np.mean(window_data)
+                        break
+
+        
+        self.stable_average_THW = stable_average_THW
+
+    def data_analyze(self):
+        change_indices = self.df_acc[self.df_acc['set_headway_time'] != self.df_acc['set_headway_time'].shift()].index
+        # print("change_indices is", change_indices)
+        # print(f"Change indices of set speed: {change_indices}")
+
+        set_headway_time = self.df_acc.loc[change_indices[0], 'set_headway_time']
+        self._find_stable_THW(window_size=25, percent_deviation=10, set_value=set_headway_time)
+
+        if not change_indices.empty:
+            first_change_index = change_indices[change_indices != 0].min()
+            set_THW_at_change = self.df_acc.loc[first_change_index, 'set_headway_time']
+            timestamp_at_change = self.df_acc.loc[first_change_index, 'simTime']
+            print(f"Set THW at first change: {set_THW_at_change}, Timestamp: {timestamp_at_change}")
+
+            if self.stable_average_THW:
+               
+                target_THW = (self.stable_average_THW + self.df_acc.loc[first_change_index, 'set_headway_time']) / 2
+                print("target_THW is", target_THW)
+                closest_index = (pd.to_numeric(self.df_acc['set_headway_time']) - target_THW).abs().idxmin()
+                closest_current_THW = self.df_acc.loc[closest_index, 'set_headway_time']
+                closest_timestamp = self.df_acc.loc[closest_index, 'simTime']
+                print(f"Closest speed: {closest_current_THW} at time: {closest_timestamp}")
+
+                self.delay_time_THW = closest_timestamp - timestamp_at_change
+            else:
+                self.delay_time_THW = Max_Time
+            self.result['value'] = [round(self.delay_time_THW, 3)]
+            print(f"Delay time: {self.delay_time_THW}")
+
+        else:
+            self.delay_time_THW = 0
+            self.result['value'] = [round(self.delay_time_THW, 3)]
+            print("No valid change point for further calculation.")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_acc.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[delay_time_THW:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_07_rise_time_THW_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "5.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 261 - 0
cicv_acc_07_rise_time_THW_new.py

@@ -0,0 +1,261 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+from log import logger
+
+"""import functions"""
+
+Max_Time = 1000
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_acc = pd.DataFrame()
+
+        # self.stable_start_time_THW = None
+        self.stable_average_THW = None
+        self.rise_time_THW = None
+
+        self.result = {
+            "name": "跟车上升时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "跟车上升时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"跟车上升时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.df)
+        # self.df_acc = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+
+    def _get_first_change_index_THW(self):
+        """
+        获取DataFrame中'set_headway_time'列首次发生变化的索引值。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在变化,则返回首次发生变化的索引值(int类型),否则返回None。
+
+        """
+        change_indices = self.df_acc[self.df_acc['set_headway_time'] != self.df_acc['set_headway_time'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_THW(self, window_size, percent_deviation, set_value):
+        """
+        在给定的数据窗口中查找稳定跟车时距离THW,并计算该段内THW的平均值。
+
+        Args:
+            window_size (int): 窗口大小,表示在数据中寻找稳定段时考虑的连续数据点数量。
+            percent_deviation (float): THW值相对于设定值的允许偏差百分比。
+            set_value (float): THW的设定值。
+
+        Returns:
+            None
+
+        """
+        ego_x = self.df_acc[self.df_acc['playerId'] == 1]['posX'].reset_index(drop = True)
+        ego_y = self.df_acc[self.df_acc['playerId'] == 1]['posY'].reset_index(drop = True)
+        obj_x = self.df_acc[self.df_acc['playerId'] == 2]['posX'].reset_index(drop = True)
+        obj_y = self.df_acc[self.df_acc['playerId'] == 2]['posY'].reset_index(drop = True)
+
+        ego_speedx = self.df_acc[self.df_acc['playerId'] == 1]['speedX'].reset_index(drop = True)
+        ego_speedy = self.df_acc[self.df_acc['playerId'] == 1]['speedY'].reset_index(drop = True)
+        obj_speedx = self.df_acc[self.df_acc['playerId'] == 2]['speedX'].reset_index(drop = True)
+        obj_speedy = self.df_acc[self.df_acc['playerId'] == 2]['speedY'].reset_index(drop = True)
+
+        dx = obj_x - ego_x
+        dy = obj_y - ego_y
+        vx = obj_speedx - ego_speedx
+        vy = obj_speedy - ego_speedy
+        dist = np.sqrt(dx**2 + dy**2)
+        ego_v_projection_in_dist = _cal_v_ego_projection(dx, dy, ego_speedx, ego_speedy)
+        thw1 = _cal_THW(dist, ego_v_projection_in_dist)
+        thw = thw1.tolist()
+        THW = []
+        for item in thw:
+            THW.append(item)
+            THW.append(item)
+        self.df_acc['THW'] = THW
+
+        THW = self.df_acc['THW'].values
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_THW = None
+
+        for i in range(len(THW) - window_size + 1):
+            window_data = THW[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_THW = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(THW) - window_size + 1:
+                    next_window_data = THW[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_THW = (stable_average_THW * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_THW = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_THW = self.df_acc['simTime'].iloc[stable_start]
+        self.stable_average_THW = stable_average_THW
+
+    def _find_closest_time_stamp_THW(self, df, target_THW, start_index):
+        """
+        在DataFrame中找到与目标THW值最接近的时间戳。
+
+        Args:
+            df (pandas.DataFrame): 包含'THW'和'simTime'列的DataFrame,其中'THW'表示目标变量,'simTime'表示时间戳。
+            target_THW (float): 目标THW值。
+            start_index (int): 开始搜索的索引位置(不包含)。
+
+        Returns:
+            pandas.Timestamp: 与目标THW值最接近的时间戳。
+
+        """
+        subset = df.loc[start_index + 1:]
+        THW_diff = np.abs(subset['THW'] - target_THW)
+        closest_index = THW_diff.idxmin()
+        closest_timestamp = subset.loc[closest_index, 'simTime']
+        return closest_timestamp
+
+    def data_analyze(self):
+        """
+        计算巡航时从初THW到达稳定THW的90%的所需时间(rise time)。
+
+        Args:
+            无。
+
+        Returns:
+            无返回值,但会设置实例属性self.rise_time_THW为计算得到的rise time。
+
+        """
+        first_change_index = self._get_first_change_index_THW()
+
+        set_headway_time = self.df_acc.loc[first_change_index, 'set_headway_time']
+        self._find_stable_THW(window_size=25, percent_deviation=10, set_value=set_headway_time)
+
+        initial_THW = self.df_acc.loc[first_change_index, 'THW']
+        if self.stable_average_THW is not None:
+            
+            target_THW_90 = pd.to_numeric(initial_THW) + (pd.to_numeric(self.stable_average_THW) - pd.to_numeric(initial_THW)) * 0.9
+            target_THW_10 = pd.to_numeric(initial_THW) + (pd.to_numeric(self.stable_average_THW) - pd.to_numeric(initial_THW)) * 0.1
+
+            timestamp_at_10 = self._find_closest_time_stamp_THW(self.df_acc, target_THW_10, first_change_index)
+            timestamp_at_90 = self._find_closest_time_stamp_THW(self.df_acc, target_THW_90, first_change_index)
+
+            print(f"Closest speed at 10% range from set speed: {timestamp_at_10}")
+            print(f"Closest speed at 90% range from set speed: {timestamp_at_90}")
+
+            self.rise_time_THW = timestamp_at_90 - timestamp_at_10
+        else:
+            self.rise_time_THW = Max_Time
+        self.result['value'] = [round(self.rise_time_THW, 3)]
+        print(f"Rise time: {self.rise_time_THW}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_acc.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[rise_time_THW:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_08_peak_time_THW_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "10.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 175 - 0
cicv_acc_08_peak_time_THW_new.py

@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_acc = pd.DataFrame()
+
+        self.peak_time_THW = None
+
+        self.result = {
+            "name": "跟车峰值时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "跟车峰值时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"跟车峰值时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.df)
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+
+    def _get_first_change_index_THW(self):
+        """
+        获取DataFrame中'set_headway_time'列首次发生变化的索引值。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在变化,则返回首次发生变化的索引值(int类型),否则返回None。
+
+        """
+        ego_x = self.df_acc[self.df_acc['playerId'] == 1]['posX'].reset_index(drop=True)
+        ego_y = self.df_acc[self.df_acc['playerId'] == 1]['posY'].reset_index(drop=True)
+        obj_x = self.df_acc[self.df_acc['playerId'] == 2]['posX'].reset_index(drop=True)
+        obj_y = self.df_acc[self.df_acc['playerId'] == 2]['posY'].reset_index(drop=True)
+
+        ego_speedx = self.df_acc[self.df_acc['playerId'] == 1]['speedX'].reset_index(drop=True)
+        ego_speedy = self.df_acc[self.df_acc['playerId'] == 1]['speedY'].reset_index(drop=True)
+        obj_speedx = self.df_acc[self.df_acc['playerId'] == 2]['speedX'].reset_index(drop=True)
+        obj_speedy = self.df_acc[self.df_acc['playerId'] == 2]['speedY'].reset_index(drop=True)
+
+        dx = obj_x - ego_x
+        dy = obj_y - ego_y
+        # vx = obj_speedx - ego_speedx
+        # vy = obj_speedy - ego_speedy
+        dist = np.sqrt(dx ** 2 + dy ** 2)
+        ego_v_projection_in_dist = _cal_v_ego_projection(dx, dy, ego_speedx, ego_speedy)
+        thw1 = _cal_THW(dist, ego_v_projection_in_dist)
+        thw = thw1.tolist()
+        THW = []
+        for item in thw:
+            THW.append(item)
+            THW.append(item)
+        self.df_acc['THW'] = THW
+
+        change_indices = self.df_acc[self.df_acc['set_headway_time'] != self.df_acc['set_headway_time'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def data_analyze(self):
+        first_change_index = self._get_first_change_index_THW()
+
+        if not first_change_index:
+            self.peak_time_THW = 0
+            self.result['value'] = [round(self.peak_time_THW, 3)]
+            print(f"peak_time_THW: {self.peak_time_THW}")
+        else:
+            start_time = self.df_acc.loc[first_change_index, 'simTime']
+            peak_time = self.df_acc.loc[self.df_acc['THW'].idxmax(), 'simTime']
+            self.peak_time_THW = peak_time - start_time
+            self.result['value'] = [round(self.peak_time_THW, 3)]
+            print(f"peak_time_THW: {self.peak_time_THW}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_acc.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[peak_time_THW:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_09_overshoot_THW_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "5.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 232 - 0
cicv_acc_09_overshoot_THW_new.py

@@ -0,0 +1,232 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+from log import logger
+
+"""import functions"""
+Max = 100
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_acc = pd.DataFrame()
+
+        self.stable_average_THW = None
+        self.overshoot_THW = None
+
+        self.result = {
+            "name": "跟车超调量",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "跟车超调量(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"跟车超调量: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.df)
+
+        # self.df_acc = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+
+    def _get_first_change_index_THW(self):
+        """
+        获取DataFrame中'set_headway_time'列首次发生变化的索引值。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在变化,则返回首次发生变化的索引值(int类型),否则返回None。
+
+        """
+        change_indices = self.df_acc[self.df_acc['set_headway_time'] != self.df_acc['set_headway_time'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_THW(self, window_size, percent_deviation, set_value):
+        """
+        在给定的数据窗口中查找稳定跟车时距离THW,并计算该段内THW的平均值。
+
+        Args:
+            window_size (int): 窗口大小,表示在数据中寻找稳定段时考虑的连续数据点数量。
+            percent_deviation (float): THW值相对于设定值的允许偏差百分比。
+            set_value (float): THW的设定值。
+
+        Returns:
+            None
+
+        """
+        ego_x = self.df_acc[self.df_acc['playerId'] == 1]['posX'].reset_index(drop = True)
+        ego_y = self.df_acc[self.df_acc['playerId'] == 1]['posY'].reset_index(drop = True)
+        obj_x = self.df_acc[self.df_acc['playerId'] == 2]['posX'].reset_index(drop = True)
+        obj_y = self.df_acc[self.df_acc['playerId'] == 2]['posY'].reset_index(drop = True)
+        
+
+        ego_speedx = self.df_acc[self.df_acc['playerId'] == 1]['speedX'].reset_index(drop = True)
+        ego_speedy = self.df_acc[self.df_acc['playerId'] == 1]['speedY'].reset_index(drop = True)
+        obj_speedx = self.df_acc[self.df_acc['playerId'] == 2]['speedX'].reset_index(drop = True)
+        obj_speedy = self.df_acc[self.df_acc['playerId'] == 2]['speedY'].reset_index(drop = True)
+
+        dx = obj_x - ego_x
+        dy = obj_y - ego_y
+        # vx = obj_speedx - ego_speedx
+        # vy = obj_speedy - ego_speedy
+        dist = np.sqrt(dx**2 + dy**2)
+        ego_v_projection_in_dist = _cal_v_ego_projection(dx, dy, ego_speedx, ego_speedy)
+        thw1 = _cal_THW(dist, ego_v_projection_in_dist)
+        thw = thw1.tolist()
+        THW = []
+        for item in thw:
+            THW.append(item)
+            THW.append(item)
+        self.df_acc['THW'] = THW
+
+        THW = self.df_acc['THW'].values
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_THW = None
+        for i in range(len(THW) - window_size + 1):
+            window_data = THW[i:i + window_size]
+            
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_THW = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(THW) - window_size + 1:
+                    next_window_data = THW[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_THW = (stable_average_THW * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_THW = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_THW = self.df_acc['simTime'].iloc[stable_start]
+        self.stable_average_THW = stable_average_THW
+
+    def data_analyze(self):
+        first_change_index = self._get_first_change_index_THW()
+
+        set_headway_time = self.df_acc.loc[first_change_index, 'set_headway_time']
+        self._find_stable_THW(window_size=25, percent_deviation=10, set_value=set_headway_time)
+
+        if not first_change_index:
+            self.overshoot_THW = 0
+        else:
+            initial_THW = self.df_acc.loc[first_change_index, 'THW']
+            print("initial_THW is", initial_THW)
+            if self.stable_average_THW is None:
+                self.overshoot_THW = Max
+            else:
+                if initial_THW > self.stable_average_THW:
+                    self.overshoot_THW = (self.stable_average_THW - self.df_acc['THW'].min()) * 100 / self.stable_average_THW
+                elif initial_THW < self.stable_average_THW:
+                    self.overshoot_THW = (self.df_acc['THW'].max() - self.stable_average_THW) * 100 / self.stable_average_THW
+                else:
+                    self.overshoot_THW = 0
+
+        self.result['value'] = [round(self.overshoot_THW, 3)]
+        print(f"overshoot_THW: {self.overshoot_THW}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_acc.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[overshoot_THW:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_10_steady_error_THW_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "10.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 237 - 0
cicv_acc_10_steady_error_THW_new.py

@@ -0,0 +1,237 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+#from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+from log import logger
+import pandas as pd  
+import matplotlib.pyplot as plt  
+import seaborn as sns
+
+
+
+
+"""import functions"""
+Max_error = 100
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_acc = pd.DataFrame()
+
+        self.stable_average_THW = None
+        self.steady_error_THW = Max_error
+
+        self.result = {
+            "name": "跟车速度稳态误差",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "跟车速度稳态误差(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"跟车速度稳态误差: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.df)
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+
+    def _get_first_change_index_THW(self):
+        """
+        获取DataFrame中'set_headway_time'列首次发生变化的索引值。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在变化,则返回首次发生变化的索引值(int类型),否则返回None。
+
+        """
+        change_indices = self.df_acc[self.df_acc['set_headway_time'] != self.df_acc['set_headway_time'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_THW(self, window_size, percent_deviation, set_value):
+        """
+        在给定的数据窗口中查找稳定跟车时距离THW,并计算该段内THW的平均值。
+
+        Args:
+            window_size (int): 窗口大小,表示在数据中寻找稳定段时考虑的连续数据点数量。
+            percent_deviation (float): THW值相对于设定值的允许偏差百分比。
+            set_value (float): THW的设定值。
+
+        Returns:
+            None
+
+        """
+        ego_x = self.df_acc[self.df_acc['playerId'] == 1]['posX'].reset_index(drop=True)
+        ego_y = self.df_acc[self.df_acc['playerId'] == 1]['posY'].reset_index(drop=True)
+        obj_x = self.df_acc[self.df_acc['playerId'] == 2]['posX'].reset_index(drop=True)
+        obj_y = self.df_acc[self.df_acc['playerId'] == 2]['posY'].reset_index(drop=True)
+
+        ego_speedx = self.df_acc[self.df_acc['playerId'] == 1]['speedX'].reset_index(drop=True)
+        ego_speedy = self.df_acc[self.df_acc['playerId'] == 1]['speedY'].reset_index(drop=True)
+        obj_speedx = self.df_acc[self.df_acc['playerId'] == 2]['speedX'].reset_index(drop=True)
+        obj_speedy = self.df_acc[self.df_acc['playerId'] == 2]['speedY'].reset_index(drop=True)
+
+        dx = obj_x - ego_x
+        dy = obj_y - ego_y
+        # vx = obj_speedx - ego_speedx
+        # vy = obj_speedy - ego_speedy
+        dist = np.sqrt(dx ** 2 + dy ** 2)
+        ego_v_projection_in_dist = _cal_v_ego_projection(dx, dy, ego_speedx, ego_speedy)
+        thw1 = _cal_THW(dist, ego_v_projection_in_dist)
+
+        thw = thw1.tolist()
+        
+        THW = []
+        for item in thw:
+            THW.append(item)
+            THW.append(item)
+        self.df_acc['THW'] = THW
+
+        THW = self.df_acc['THW'].values
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_THW = None
+
+        for i in range(len(THW) - window_size + 1):
+            window_data = THW[i:i + window_size]
+            
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_THW = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(THW) - window_size + 1:
+                    next_window_data = THW[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_THW = (stable_average_THW * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_THW = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_THW = self.df_ica['simTime'].iloc[stable_start]
+        self.stable_average_THW = stable_average_THW
+
+    def data_analyze(self):
+        first_change_index = self._get_first_change_index_THW()
+
+
+        if not first_change_index:
+            self.steady_error_THW = 0
+            self.result['value'] = [abs(round(self.steady_error_THW, 3))]
+            print(f"steady_error_THW: {abs(self.steady_error_THW)}")
+        else:
+            set_headway_time = self.df_acc.loc[first_change_index, 'set_headway_time']
+            self._find_stable_THW(window_size=25, percent_deviation=10, set_value=set_headway_time)
+
+            if self.stable_average_THW:
+                self.steady_error_THW = (self.stable_average_THW - set_headway_time) * 100 / self.stable_average_THW
+            else:
+                self.steady_error_THW = Max_error
+                
+
+            self.result['value'] = [abs(round(self.steady_error_THW, 3))]
+            print(f"steady_error_THW: {abs(self.steady_error_THW)}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_acc.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[steady_error_THW:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_11_reasonable_acceleration_percentage.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "1",
+      "optimal": "95",
+      "multiple": [
+        "0.6",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 166 - 0
cicv_acc_11_reasonable_acceleration_percentage.py

@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.percentage_accel = 0
+
+        self.result = {
+            "name": "合理加减速度占比",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "合理加减速度占比(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"合理加减速度占比: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.ego_df)
+        # self.df_acc = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+    def data_analyze(self):
+        col_list = ['simTime', 'simFrame', 'playerId', 'v', 'accel', 'lon_acc_roc']  # 列表指定了要分析的列  
+        # df = self.df_acc[col_list].copy()  # 从原始 DataFrame 中选择列并复制  
+
+        try:  
+            df = self.df_acc[col_list].copy()  
+        except KeyError as e:  
+            print(f"Error: {e}. Column '{col_list}' not found in DataFrame.")  
+    # 这里可以添加更多的错误处理逻辑,比如退出程序或者设置默认值等
+    
+        # 将速度从km/h 转换为 m/s
+        df['v_ms'] = df['v'] / 3.6  
+    
+        # 初始化计数器和阈值  
+        count_in_range = 0  
+        total_frames = len(df)  
+    
+        # 遍历DataFrame的每一行  
+        for index, row in df.iterrows():  
+            v_ms = row['v_ms']  
+            accel = row['accel']  
+    
+            # 根据车速范围设置减速度和加速度的阈值  
+            if v_ms > 20:  # 车速大于20km/h  
+                decel_threshold = -3.5  
+                accel_threshold = 2  
+            elif v_ms < 5:  # 车速低于5km/h  
+                decel_threshold = 5  
+                accel_threshold = 4  # 注意:这里假设是4而不是44,因为44对于加速度来说是一个很大的值  
+            else:  # 车速在5到20km/h之间  
+                # 对减速度和加速度的阈值进行线性插值  
+                # 假设在5km/h时decel_threshold=5, accel_threshold=4  
+                # 在20km/h时decel_threshold=-3.5, accel_threshold=2  
+                decel_threshold = 5 - (5 + 3.5) * ((v_ms - 5) / (20 - 5))  
+                accel_threshold = 4 - (4 - 2) * ((v_ms - 5) / (20 - 5))  
+    
+            # 检查加速度是否在允许的范围内  
+            if accel >= decel_threshold and accel <= accel_threshold:  
+                count_in_range += 1  
+    
+        # 计算百分比  
+        percentage_in_range = (count_in_range / total_frames) * 100  
+    
+        # 存储结果并打印  
+        self.result['value'] = [round(percentage_in_range, 3)]  
+        print(f"Percentage of frames with acceleration in range: {percentage_in_range}%")  
+
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        time_list = self.ego_df['simTime'].values.tolist()
+        graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = f'{np.mean(graph_list):.2f}' if graph_list else 0
+        self.result['tableData']['max'] = f'{max(graph_list):.2f}' if graph_list else 0
+        self.result['tableData']['min'] = f'{min(graph_list):.2f}' if graph_list else 0
+
+        zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = zip_vs_time
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 100]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[percentage_accel:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_acc_12_reasonable_acceleration_change_rate_percentage.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "1",
+      "optimal": "95",
+      "multiple": [
+        "0.6",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 167 - 0
cicv_acc_12_reasonable_acceleration_change_rate_percentage.py

@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.ego_df = self.data.ego_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.percentage_lon_acc_roc = 0
+
+        self.result = {
+            "name": "合理加减速度变化率占比",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "合理加减速度变化率占比(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"合理加减速度变化率占比: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+        active_time_ranges = self.status_trigger_dict['ACC']['ACC_active_time']
+        self.df_acc = get_status_active_data(active_time_ranges, self.ego_df)
+        # self.df_acc = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_acc = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_acc.empty:
+            self.result['statusFlag']['function_ACC'] = False
+        else:
+            self.result['statusFlag']['function_ACC'] = True
+
+    def data_analyze(self):
+        # 指定要分析的列  
+        col_list = ['simTime', 'simFrame', 'playerId', 'v', 'accel', 'lon_acc_roc']  
+        # 从原始 DataFrame 中选择列并复制  
+        df = self.df_acc[col_list].copy()  
+    
+        # 将速度从km/h转换为m/s  
+        df['v_ms'] = df['v'] / 3.6  
+    
+        # 初始化计数器  
+        count_in_range = 0  
+        total_frames = len(df)  
+    
+        # 遍历DataFrame的每一行  
+        for index, row in df.iterrows():  
+            v_ms = row['v_ms']  
+            lon_acc_roc = row['lon_acc_roc']  
+    
+            # 根据车速范围设置减速度和加速度的阈值(这里假设减速度就是加速度的相反数,用于判断)  
+            if v_ms > 20:  # 车速大于20m/s  
+                decel_threshold = -2.5  # 减速度不小于-2.5 m/s^2(注意是减速度,所以是负数)  
+                accel_threshold_max = 2.5  # 这里我们也需要一个加速度的最大值阈值,但原题没有给出,这里假设和减速度绝对值相同  
+            elif v_ms < 5:  # 车速低于5m/s(这里已经是m/s,无需转换)  
+                decel_threshold = -5  # 减速度不小于5 m/s^2(注意是减速度,所以是负数)  
+                accel_threshold_max = 5  # 加速度不大于5 m/s^2(注意这里是正值,因为加速度是正值)  
+            else:  # 车速在5到20m/s之间  
+                # 对减速度和加速度的阈值进行线性插值  
+                # 假设在5m/s时decel_threshold=-5 m/s^2, accel_threshold_max=5 m/s^2  
+                # 在20m/s时decel_threshold=-2.5 m/s^2, accel_threshold_max=2.5 m/s^2(这里假设加速度和减速度绝对值相等)  
+                decel_threshold = -5 + (5 - 2.5) * ((v_ms - 5) / (20 - 5))  # 注意这里计算出来的是减速度,所以是负数  
+                accel_threshold_max = 5 - (5 - 2.5) * ((v_ms - 5) / (20 - 5))  # 加速度的最大值  
+    
+            # 检查加速度是否在允许的范围内(注意这里我们假设加速度是正值,减速度是负值)  
+            # 由于我们只有一个加速度值(accel),并且没有直接的减速度值,  
+            # 我们只能根据加速度的值和符号来判断它是否满足减速度的要求。  
+            # 如果accel是负数,并且其绝对值小于等于decel_threshold的绝对值,则满足减速度要求。  
+            # 如果accel是正数,并且小于等于accel_threshold_max,则满足加速度要求。  
+            if (lon_acc_roc < 0 and abs(lon_acc_roc) <= abs(decel_threshold)) or (lon_acc_roc >= 0 and lon_acc_roc <= accel_threshold_max):  
+                count_in_range += 1  
+    
+        # 计算百分比  
+        percentage_in_range = (count_in_range / total_frames) * 100  
+    
+        # 存储结果并打印  
+        self.result['value'] = [round(percentage_in_range, 3)]  
+        print(f"Percentage of frames with acceleration/deceleration in range: {percentage_in_range}%")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        time_list = self.ego_df['simTime'].values.tolist()
+        graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = f'{np.mean(graph_list):.2f}' if graph_list else 0
+        self.result['tableData']['max'] = f'{max(graph_list):.2f}' if graph_list else 0
+        self.result['tableData']['min'] = f'{min(graph_list):.2f}' if graph_list else 0
+
+        zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = zip_vs_time
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 100]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[percentage_lon_acc_roc:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 22 - 0
cicv_ica_lateral_control07_center_distance_max_new.json

@@ -0,0 +1,22 @@
+
+{
+  "priority": "0",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.6",
+      "multiple": [
+        "0.5",
+        "1.2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 185 - 0
cicv_ica_lateral_control07_center_distance_max_new.py

@@ -0,0 +1,185 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhangyu
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+最大横向偏移量
+zy_center_distance_expectation
+"""
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_follow = pd.DataFrame()
+        self.roadMark_df = pd.DataFrame()
+        self.roadPos_df = pd.DataFrame()
+
+
+        self.time_list_follow = list()
+        self.frame_list_follow = list()
+        self.dist_list = list()
+        self.dist_deviation_list = list()
+        self.dist_deviation_list_full_time = list()
+
+        self.result = {
+            "name": "横向距离极大值",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "横向距离极大值(m)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"指标07: 横向距离极大值: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.object_df[self.data.object_df.playerId == 1]
+        # new active get code
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_Lateral_time']
+        self.roadPos_df = get_status_active_data(active_time_ranges, self.data.road_pos_df)
+
+        # self.df_ego = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ego = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+        # self.roadMark_df = self.data.road_mark_df
+        # self.roadPos_df = self.data.road_pos_df
+
+        if self.roadPos_df.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def dist(self, x1, y1, x2, y2):
+        dis = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
+        return dis
+
+    def Compute_nearby_distance_to_lane_boundary(self, x, width_ego):
+        if x.lateralDist < abs(x.right_lateral_distance):
+            return x.lateralDist - width_ego/2
+        else:
+            return abs(x.right_lateral_distance) - width_ego/2
+
+    def func_laneOffset_abs(self, x):
+        return abs(x.laneOffset)
+
+    def data_analyze(self):
+        
+        if self.roadPos_df.empty:
+            row_with_max_value = 0.0
+            self.result['value'] = [0.0]
+        else:
+            # 提取自车宽度
+            roadPos_df = self.roadPos_df
+            # player_df = self.df
+            # ego_df = player_df[player_df.playerId == 1]
+            # width_ego = ego_df['dimY'].values.tolist()[0]
+
+            # 提取距离左车道线和右车道线距离
+            roadPos_ego_df = roadPos_df[roadPos_df.playerId == 1].reset_index(drop=True)
+            # # 计算到车道边界线距离
+            roadPos_ego_df['laneOffset_abs'] = roadPos_ego_df.apply(lambda x: self.func_laneOffset_abs(x), axis=1)
+            max_laneOffset_abs_index = roadPos_ego_df['laneOffset_abs'].idxmax()
+            row_with_max_value = roadPos_ego_df.iloc[max_laneOffset_abs_index].laneOffset
+            self.result['value'] = [row_with_max_value]
+            self.time_list_follow = roadPos_ego_df['simTime'].values.tolist()
+            self.frame_list_follow = roadPos_ego_df['simFrame'].values.tolist()
+            self.dist_deviation_list = roadPos_ego_df['laneOffset'].values.tolist()
+
+    def markline_statistic(self):
+        if not self.roadPos_df.empty:
+            unfunc_df = pd.DataFrame({'simTime': self.time_list_follow, 'simFrame': self.frame_list_follow,
+                                    'dist_deviation': self.dist_deviation_list})
+            unfunc_df = unfunc_df[unfunc_df['simFrame'] > 1]
+            # v_df = unfunc_df[unfunc_df['dist_deviation'] > 0]
+            v_df = unfunc_df
+            v_df = v_df[['simTime', 'simFrame', 'dist_deviation']]
+            v_follow_df = continuous_group(v_df)
+            v_follow_df['type'] = "ICA"
+            self.markline_df = pd.concat([self.markline_df, v_follow_df], ignore_index=True)
+
+    def report_data_statistic(self):
+        if not self.roadPos_df.empty:
+            time_list = self.ego_df['simTime'].values.tolist()
+            graph_list = [x for x in self.dist_deviation_list if not np.isnan(x)]
+            self.result['tableData']['avg'] = f'{np.mean(graph_list):.2f}' if graph_list else 0
+            self.result['tableData']['max'] = f'{max(graph_list):.2f}' if graph_list else 0
+            self.result['tableData']['min'] = f'{min(graph_list):.2f}' if graph_list else 0
+
+            zip_vs_time = zip_time_pairs(time_list, self.dist_deviation_list)
+            self.result['reportData']['data'] = zip_vs_time
+
+            self.markline_statistic()
+            markline_slices = self.markline_df.to_dict('records')
+            self.result['reportData']['markLine'] = markline_slices
+            self.result['reportData']['range'] = f"[-1.875, 1.875]"
+        else:
+            self.result['tableData']['avg'] = 0
+            self.result['tableData']['max'] = 0
+            self.result['tableData']['min'] = 0
+
+            zip_vs_time = []
+            self.result['reportData']['data'] = zip_vs_time
+
+            self.markline_statistic()
+            markline_slices = self.markline_df.to_dict('records')
+            self.result['reportData']['markLine'] = markline_slices
+            self.result['reportData']['range'] = f"[-1.875, 1.875]"
+
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[ica_distance_deviation:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 22 - 0
cicv_ica_lateral_control11_heading_deviation_max_new.json

@@ -0,0 +1,22 @@
+
+{
+  "priority": "0",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "0.175",
+      "multiple": [
+        "0.5",
+        "3"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 189 - 0
cicv_ica_lateral_control11_heading_deviation_max_new.py

@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhangyu
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+最大航向偏差角
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ego = pd.DataFrame()
+        self.roadMark_df = pd.DataFrame()
+        self.roadPos_df = pd.DataFrame()
+
+
+        self.time_list_follow = list()
+        self.frame_list_follow = list()
+        self.dist_list = list()
+        self.dist_deviation_list = list()
+        self.dist_deviation_list_full_time = list()
+
+        self.result = {
+            "name": "最大航向角偏差",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "最大航向角偏差(rad)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"指标11: ICA最大航向角偏差: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.object_df[self.data.object_df.playerId == 1]
+        # new active get code
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_Lateral_time']
+        self.df_ego = get_status_active_data(active_time_ranges, self.ego_df)
+        self.roadMark_df = get_status_active_data(active_time_ranges, self.data.road_mark_df)
+
+        # self.df_ego = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # # self.df_ego = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+        # self.roadMark_df = self.data.road_mark_df
+        # self.roadPos_df = self.data.road_pos_df
+
+        if self.df_ego.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def dist(self, x1, y1, x2, y2):
+        dis = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
+        return dis
+
+    def Compute_nearby_distance_to_lane_boundary(self, x, width_ego):
+        if x.lateralDist < abs(x.right_lateral_distance):
+            return x.lateralDist - width_ego/2
+        else:
+            return abs(x.right_lateral_distance) - width_ego/2
+
+
+    def data_analyze(self):
+        if not self.df_ego.empty:
+            # 提取自车宽度
+            road_mark_df = self.roadMark_df
+            ego_df = self.ego_df.reset_index(drop=True)
+            # player_df = self.df
+            # road_mark_df = self.roadMark_df
+            # ego_df = player_df[player_df.playerId == 1].reset_index(drop=True)
+            # width_ego = ego_df['dimY'].values.tolist()[0]
+
+            # 左车道线曲率,右车道线曲率,求二者平均值,计算车道线曲率,再与自车朝向相减
+            road_mark_left_df = road_mark_df[road_mark_df.id == 0].reset_index(drop=True)
+            road_mark_right_df = road_mark_df[road_mark_df.id == 2].reset_index(drop=True)
+            road_mark_left_df['curvHor_left'] = road_mark_left_df['curvHor']
+            road_mark_left_df['curvHor_right'] = road_mark_right_df['curvHor']
+            road_mark_left_df['curvHor_middle'] = road_mark_left_df[['curvHor_left', 'curvHor_right']].apply( \
+                lambda x: (x['curvHor_left'] + x['curvHor_right'])/2, axis=1)
+            ego_df['curvHor_middle'] = road_mark_left_df['curvHor_middle']
+            ego_df['heading_deviation_abs'] = ego_df[['curvHor_middle', 'posH']].apply( \
+                lambda x: abs(x['posH'] - x['curvHor_middle']), axis=1)
+            row_with_max_value = max(ego_df['heading_deviation_abs'])
+
+            self.result['value'] = [row_with_max_value]
+            self.time_list_follow = ego_df['simTime'].values.tolist()
+            self.frame_list_follow = ego_df['simFrame'].values.tolist()
+            self.dist_deviation_list = ego_df['heading_deviation_abs'].values.tolist()
+        else:
+            self.result['value'] = [0.0]
+
+    def markline_statistic(self):
+        if not self.df_ego.empty:
+            unfunc_df = pd.DataFrame({'simTime': self.time_list_follow, 'simFrame': self.frame_list_follow,
+                                    'dist_deviation': self.dist_deviation_list})
+            unfunc_df = unfunc_df[unfunc_df['simFrame'] > 1]
+            # v_df = unfunc_df[unfunc_df['dist_deviation'] > 0]
+            v_df = unfunc_df
+            v_df = v_df[['simTime', 'simFrame', 'dist_deviation']]
+            v_follow_df = continuous_group(v_df)
+            v_follow_df['type'] = "ICA"
+            self.markline_df = pd.concat([self.markline_df, v_follow_df], ignore_index=True)
+
+    def report_data_statistic(self):
+
+        if not self.roadPos_df.empty:
+            time_list = self.ego_df['simTime'].values.tolist()
+            graph_list = [x for x in self.dist_deviation_list if not np.isnan(x)]
+            self.result['tableData']['avg'] = f'{np.mean(graph_list):.2f}' if graph_list else 0
+            self.result['tableData']['max'] = f'{max(graph_list):.2f}' if graph_list else 0
+            self.result['tableData']['min'] = f'{min(graph_list):.2f}' if graph_list else 0
+
+            zip_vs_time = zip_time_pairs(time_list, self.dist_deviation_list)
+            self.result['reportData']['data'] = zip_vs_time
+
+            self.markline_statistic()
+            markline_slices = self.markline_df.to_dict('records')
+            self.result['reportData']['markLine'] = markline_slices
+            self.result['reportData']['range'] = f"[-1.875, 1.875]"
+        else:
+            self.result['tableData']['avg'] = 0
+            self.result['tableData']['max'] = 0
+            self.result['tableData']['min'] = 0
+
+            zip_vs_time = []
+            self.result['reportData']['data'] = zip_vs_time
+
+            self.markline_statistic()
+            markline_slices = self.markline_df.to_dict('records')
+            self.result['reportData']['markLine'] = markline_slices
+            self.result['reportData']['range'] = f"[-1.875, 1.875]"
+
+    def run(self):
+        logger.info(f"[case:{self.case_name}] Custom metric:[ica_distance_deviation:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control01_delay_time_cruise_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 206 - 0
cicv_ica_longitudinal_control01_delay_time_cruise_new.py

@@ -0,0 +1,206 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+Max_Time = 1000
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        # self.stable_start_time_cruise = None
+        self.stable_average_speed = None
+        self.delay_time_cruise = Max_Time
+
+        self.result = {
+            "name": "定速巡航延迟时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "定速巡航延迟时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"定速巡航延迟时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_cruise_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的 LLC_Follow_Line
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def _find_stable_speed_cruise(self, window_size, percent_deviation, set_value):
+        """
+        在给定的速度数据中查找稳定的巡航速度段,并计算该段的平均速度。
+
+        Args:
+            window_size (int): 滑动窗口的大小,表示用于计算平均速度的速度数据点数量。
+            percent_deviation (float): 设定值的允许偏差百分比。
+            set_value (float): 期望的稳定速度设定值。
+
+        Returns:
+            None
+
+        """
+        # speed_data = self.df['speedX'].values
+        speed_data = self.df_ica['speedX'].values  # .tolist()
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_speed = None
+
+        for i in range(len(speed_data) - window_size + 1):
+            window_data = speed_data[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_speed = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(speed_data) - window_size + 1:
+                    next_window_data = speed_data[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_speed = (stable_average_speed * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_speed = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_cruise = self.df['simTime'].iloc[stable_start]
+        self.stable_average_speed = stable_average_speed
+
+    def data_analyze(self):
+
+        if not self.df_ica.empty:
+            change_indices = self.df_ica[self.df_ica['set_cruise_speed'] != self.df_ica['set_cruise_speed'].shift()].index
+            print(f"Change indices of set speed: {change_indices}")
+            print(change_indices[0])
+            set_cruise_speed = self.df_ica.loc[change_indices[0], 'set_cruise_speed']
+            self._find_stable_speed_cruise(window_size=4, percent_deviation=5, set_value=set_cruise_speed)
+
+            if not change_indices.empty:
+                first_change_index = change_indices[change_indices != 0].min()
+                set_cruise_speed_at_change = self.df_ica.loc[first_change_index, 'set_cruise_speed']
+                timestamp_at_change = self.df_ica.loc[first_change_index, 'simTime']
+                print(f"Set speed at first change: {set_cruise_speed_at_change}, Timestamp: {timestamp_at_change}")
+                if self.stable_average_speed:
+                    target_speed = (self.stable_average_speed + self.df_ica.loc[first_change_index, 'speedX']) / 2
+                    closest_index = (self.df_ica['speedX'] - target_speed).abs().idxmin()
+                    closest_current_speed = self.df_ica.loc[closest_index, 'speedX']
+                    closest_timestamp = self.df_ica.loc[closest_index, 'simTime']
+                    print(f"Closest speed: {closest_current_speed} at time: {closest_timestamp}")
+
+                    self.delay_time_cruise = closest_timestamp - timestamp_at_change
+                else:
+                    self.delay_time_cruise = Max_Time
+
+                self.result['value'] = [round(self.delay_time_cruise, 3)]
+                print(f"Delay time: {self.delay_time_cruise}")
+
+            else:
+                self.delay_time_cruise = 0
+                self.result['value'] = [round(self.delay_time_cruise, 3)]
+                print("No valid change point for further calculation.")
+        else:
+            self.delay_time_cruise = Max_Time
+            self.result['value'] = [Max_Time]
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_ica.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[delay_time_cruise:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control02_rise_time_cruise_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 236 - 0
cicv_ica_longitudinal_control02_rise_time_cruise_new.py

@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+Max_Time = 1000
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.stable_average_speed = None
+        self.rise_time_cruise = Max_Time
+
+        self.result = {
+            "name": "定速巡航上升时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "定速巡航上升时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"定速巡航上升时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_cruise_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def _get_first_change_index_cruise(self):
+        """
+        获取数据集中第一次巡航速度发生变化的索引。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在巡航速度发生变化的索引,则返回第一个发生变化的索引(int类型);
+            如果不存在,则返回None。
+
+        """
+        change_indices = self.df_ica[self.df_ica['set_cruise_speed'] != self.df_ica['set_cruise_speed'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_speed_cruise(self, window_size, percent_deviation, set_value):
+        """
+        在给定的速度数据中查找稳定的巡航速度段,并计算该段的平均速度。
+
+        Args:
+            window_size (int): 滑动窗口的大小,表示用于计算平均速度的速度数据点数量。
+            percent_deviation (float): 设定值的允许偏差百分比。
+            set_value (float): 期望的稳定速度设定值。
+
+        Returns:
+            None
+
+        """
+        # speed_data = self.df['speedX'].values
+        speed_data = self.df_ica['speedX'].values   #.tolist()
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_speed = None
+
+        for i in range(len(speed_data) - window_size + 1):
+            window_data = speed_data[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_speed = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(speed_data) - window_size + 1:
+                    next_window_data = speed_data[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_speed = (stable_average_speed * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_speed = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_cruise = self.df['simTime'].iloc[stable_start]
+        self.stable_average_speed = stable_average_speed
+
+    def _find_closest_time_stamp_cruise(self, df, target_speed, start_index):
+        """
+        在给定的数据帧df中,从start_index索引位置开始,查找与目标速度target_speed最接近的时间戳。
+
+        Args:
+            df (pd.DataFrame): 包含速度和时间戳等信息的数据帧,需要至少包含'speedX'和'simTime'两列。
+            target_speed (float): 目标速度值,用于在数据帧中查找最接近此值的时间戳。
+            start_index (int): 开始查找的索引位置,即在数据帧df中从该索引位置开始向后查找。
+
+        Returns:
+            pd.Timestamp: 与目标速度最接近的时间戳。
+
+        """
+        subset = df.loc[start_index + 1:]
+        speed_diff = np.abs(subset['speedX'] - target_speed)
+        closest_index = speed_diff.idxmin()
+        closest_timestamp = subset.loc[closest_index, 'simTime']
+        return closest_timestamp
+
+    def data_analyze(self):
+
+        if not self.df_ica.empty:
+            first_change_index = self._get_first_change_index_cruise()
+
+            set_cruise_speed = self.df_ica.loc[first_change_index, 'set_cruise_speed']
+            self._find_stable_speed_cruise(window_size=40, percent_deviation=20, set_value=set_cruise_speed)
+
+            initial_speed = self.df_ica.loc[first_change_index, 'speedX']
+            if self.stable_average_speed:
+                target_speed_90 = initial_speed + (self.stable_average_speed - initial_speed) * 0.9
+                target_speed_10 = initial_speed + (self.stable_average_speed - initial_speed) * 0.1
+
+                timestamp_at_10 = self._find_closest_time_stamp_cruise(self.df_ica, target_speed_10, first_change_index)
+                timestamp_at_90 = self._find_closest_time_stamp_cruise(self.df_ica, target_speed_90, first_change_index)
+
+                print(f"Closest speed at 10% range from set speed: {timestamp_at_10}")
+                print(f"Closest speed at 90% range from set speed: {timestamp_at_90}")
+
+                self.rise_time_cruise = timestamp_at_90 - timestamp_at_10
+                self.result['value'] = [round(self.rise_time_cruise, 3)]
+                print(f"Rise time: {self.rise_time_cruise}")
+            else:
+                self.rise_time_cruise = Max_Time
+                self.result['value'] = [round(self.rise_time_cruise, 3)]
+        else:
+            self.delay_time_cruise = Max_Time
+            self.result['value'] = [Max_Time]
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_ica.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[rise_time_cruise:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control03_peak_time_cruise_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 159 - 0
cicv_ica_longitudinal_control03_peak_time_cruise_new.py

@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.peak_time_cruise = None
+
+        self.result = {
+            "name": "定速巡航峰值时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "定速巡航峰值时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"定速巡航峰值时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_cruise_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def _get_first_change_index_cruise(self):
+        """
+        获取数据集中第一次巡航速度发生变化的索引。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在巡航速度发生变化的索引,则返回第一个发生变化的索引(int类型);
+            如果不存在,则返回None。
+
+        """
+        change_indices = self.df_ica[self.df_ica['set_cruise_speed'] != self.df_ica['set_cruise_speed'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def data_analyze(self):
+        if not self.df_ica.empty:
+            first_change_index = self._get_first_change_index_cruise()
+
+            if not first_change_index:
+                self.peak_time_cruise = 0
+                self.result['value'] = [round(self.peak_time_cruise, 3)]
+                print(f"peak_time_cruise: {self.peak_time_cruise}")
+            else:
+                start_time = self.df_ica.loc[first_change_index, 'simTime']
+                peak_time = self.df_ica.loc[self.df_ica['speedX'].idxmax(), 'simTime']
+                self.peak_time_cruise = peak_time - start_time
+                self.result['value'] = [round(self.peak_time_cruise, 3)]
+                print(f"peak_time_cruise: {self.peak_time_cruise}")
+        else:
+            self.peak_time_cruise = 1000
+            self.result['value'] = [1000]
+            print(f"peak_time_cruise: {self.peak_time_cruise}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_ica.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[peak_time_cruise:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control04_overshoot_cruise_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 217 - 0
cicv_ica_longitudinal_control04_overshoot_cruise_new.py

@@ -0,0 +1,217 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+Max_Time = 1000
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.stable_average_speed = None
+        self.overshoot_cruise = None
+
+        self.result = {
+            "name": "定速巡航超调量",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "定速巡航超调量(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"定速巡航超调量: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_cruise_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def _get_first_change_index_cruise(self):
+        """
+        获取数据集中第一次巡航速度发生变化的索引。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在巡航速度发生变化的索引,则返回第一个发生变化的索引(int类型);
+            如果不存在,则返回None。
+
+        """
+        change_indices = self.df_ica[self.df_ica['set_cruise_speed'] != self.df_ica['set_cruise_speed'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_speed_cruise(self, window_size, percent_deviation, set_value):
+        """
+        在给定的速度数据中查找稳定的巡航速度段,并计算该段的平均速度。
+
+        Args:
+            window_size (int): 滑动窗口的大小,表示用于计算平均速度的速度数据点数量。
+            percent_deviation (float): 设定值的允许偏差百分比。
+            set_value (float): 期望的稳定速度设定值。
+
+        Returns:
+            None
+
+        """
+        # speed_data = self.df['speedX'].values
+        speed_data = self.df_ica['speedX'].values  # .tolist()
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_speed = None
+
+        for i in range(len(speed_data) - window_size + 1):
+            window_data = speed_data[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_speed = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(speed_data) - window_size + 1:
+                    next_window_data = speed_data[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_speed = (stable_average_speed * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_speed = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_cruise = self.df['simTime'].iloc[stable_start]
+        self.stable_average_speed = stable_average_speed
+
+    def data_analyze(self):
+        if self.df_ica.empty:
+            self.result['value'] = [Max_Time]
+            print("No ICA status found.")
+        else:
+            first_change_index = self._get_first_change_index_cruise()
+
+            set_cruise_speed = self.df_ica.loc[first_change_index, 'set_cruise_speed']
+            self._find_stable_speed_cruise(window_size=4, percent_deviation=5, set_value=set_cruise_speed)
+            if self.stable_average_speed:
+                if not first_change_index:
+                    self.overshoot_cruise = 0
+                else:
+                    initial_speed = self.df.loc[first_change_index, 'speedX']
+
+                    if initial_speed > self.stable_average_speed:
+                        self.overshoot_cruise = (self.stable_average_speed - self.df[
+                            'speedX'].min()) * 100 / self.stable_average_speed
+                    elif initial_speed < self.stable_average_speed:
+                        self.overshoot_cruise = (self.df[
+                                                    'speedX'].max() - self.stable_average_speed) * 100 / self.stable_average_speed
+                    else:
+                        self.overshoot_cruise = 0
+            else:
+                self.overshoot_cruise = Max_Time
+            self.result['value'] = [round(self.overshoot_cruise, 3)]
+            print(f"overshoot_cruise: {self.overshoot_cruise}")
+        
+            
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_ica.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[overshoot_cruise:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control05_steady_error_cruise_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 214 - 0
cicv_ica_longitudinal_control05_steady_error_cruise_new.py

@@ -0,0 +1,214 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+Max = 100
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.stable_average_speed = None
+        self.steady_error_cruise = None
+
+        self.result = {
+            "name": "定速巡航速度稳态误差",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "定速巡航速度稳态误差(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"定速巡航速度稳态误差: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_cruise_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Line"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def _get_first_change_index_cruise(self):
+        """
+        获取数据集中第一次巡航速度发生变化的索引。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在巡航速度发生变化的索引,则返回第一个发生变化的索引(int类型);
+            如果不存在,则返回None。
+
+        """
+        change_indices = self.df_ica[self.df_ica['set_cruise_speed'] != self.df_ica['set_cruise_speed'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_speed_cruise(self, window_size, percent_deviation, set_value):
+        """
+        在给定的速度数据中查找稳定的巡航速度段,并计算该段的平均速度。
+
+        Args:
+            window_size (int): 滑动窗口的大小,表示用于计算平均速度的速度数据点数量。
+            percent_deviation (float): 设定值的允许偏差百分比。
+            set_value (float): 期望的稳定速度设定值。
+
+        Returns:
+            None
+
+        """
+        # speed_data = self.df['speedX'].values
+        speed_data = self.df_ica['speedX'].values  # .tolist()
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_speed = None
+
+        for i in range(len(speed_data) - window_size + 1):
+            window_data = speed_data[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_speed = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(speed_data) - window_size + 1:
+                    next_window_data = speed_data[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_speed = (stable_average_speed * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_speed = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_cruise = self.df['simTime'].iloc[stable_start]
+        self.stable_average_speed = stable_average_speed
+
+    def data_analyze(self):
+
+        if not self.df_ica.empty:
+            first_change_index = self._get_first_change_index_cruise()
+
+            if not first_change_index:
+                self.steady_error_cruise = 0
+                self.result['value'] = [abs(round(self.steady_error_cruise, 3))]
+                print(f"steady_error_cruise: {self.steady_error_cruise}")
+            else:
+                set_cruise_speed = self.df_ica.loc[first_change_index, 'set_cruise_speed']
+                self._find_stable_speed_cruise(window_size=4, percent_deviation=5, set_value=set_cruise_speed)
+
+                if self.stable_average_speed:
+                    self.steady_error_cruise = (self.stable_average_speed - set_cruise_speed) * 100 / self.stable_average_speed
+                else:
+                    self.steady_error_cruise = Max
+                    # raise ValueError("Stable average speed is None.")
+
+                self.result['value'] = [abs(round(self.steady_error_cruise, 3))]
+                print(f"steady_error_cruise: {self.steady_error_cruise}")
+        else:
+            self.steady_error_cruise = Max
+            self.result['value'] = [1000]
+            print(f"steady_error_cruise: {self.steady_error_cruise}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_ica.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[steady_error_cruise:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control06_delay_time_THW_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 229 - 0
cicv_ica_longitudinal_control06_delay_time_THW_new.py

@@ -0,0 +1,229 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+Max_Time = 1000
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        # self.stable_start_time_THW = None
+        self.stable_average_THW = None
+        self.delay_time_THW = Max_Time
+
+        self.result = {
+            "name": "跟车延迟时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "跟车延迟时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"跟车延迟时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_follow_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Vehicle"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def _find_stable_THW(self, window_size, percent_deviation, set_value):
+        """
+        在给定的数据窗口中查找稳定跟车时距离THW,并计算该段内THW的平均值。
+
+        Args:
+            window_size (int): 窗口大小,表示在数据中寻找稳定段时考虑的连续数据点数量。
+            percent_deviation (float): THW值相对于设定值的允许偏差百分比。
+            set_value (float): THW的设定值。
+
+        Returns:
+            None
+
+        """
+        ego_x = self.df_ica[self.df_ica['playerId'] == 1]['posX'].reset_index(drop=True)
+        ego_y = self.df_ica[self.df_ica['playerId'] == 1]['posY'].reset_index(drop=True)
+        obj_x = self.df_ica[self.df_ica['playerId'] == 2]['posX'].reset_index(drop=True)
+        obj_y = self.df_ica[self.df_ica['playerId'] == 2]['posY'].reset_index(drop=True)
+
+        ego_speedx = self.df_ica[self.df_ica['playerId'] == 1]['speedX'].reset_index(drop=True)
+        ego_speedy = self.df_ica[self.df_ica['playerId'] == 1]['speedY'].reset_index(drop=True)
+        obj_speedx = self.df_ica[self.df_ica['playerId'] == 2]['speedX'].reset_index(drop=True)
+        obj_speedy = self.df_ica[self.df_ica['playerId'] == 2]['speedY'].reset_index(drop=True)
+
+        dx = obj_x - ego_x
+        dy = obj_y - ego_y
+        vx = obj_speedx - ego_speedx
+        vy = obj_speedy - ego_speedy
+        dist = np.sqrt(dx ** 2 + dy ** 2)
+        ego_v_projection_in_dist = _cal_v_ego_projection(dx, dy, ego_speedx, ego_speedy)
+        thw1 = _cal_THW(dist, ego_v_projection_in_dist)
+        thw = thw1.tolist()
+        THW = []
+        for item in thw:
+            THW.append(item)
+            THW.append(item)
+        self.df_ica['THW'] = THW
+
+        THW = self.df_ica['THW'].values
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_THW = None
+
+        for i in range(len(THW) - window_size + 1):
+            window_data = THW[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_THW = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(THW) - window_size + 1:
+                    next_window_data = THW[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_THW = (stable_average_THW * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_THW = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_THW = self.df_ica['simTime'].iloc[stable_start]
+        self.stable_average_THW = stable_average_THW
+
+    def data_analyze(self):
+        if self.df_ica.empty:
+            self.delay_time_THW = 0
+            self.result['value'] = [round(self.delay_time_THW, 3)]
+            print(f"Delay time: {self.delay_time_THW}")
+        else:
+
+            change_indices = self.df_ica[self.df_ica['set_headway_time'] != self.df_ica['set_headway_time'].shift()].index
+            print(f"Change indices of set speed: {change_indices}")
+
+            set_headway_time = self.df_ica.loc[change_indices[0], 'set_headway_time']
+            self._find_stable_THW(window_size=4, percent_deviation=5, set_value=set_headway_time)
+
+            if not change_indices.empty:
+                first_change_index = change_indices[change_indices != 0].min()
+                set_THW_at_change = self.df_ica.loc[first_change_index, 'set_headway_time']
+                timestamp_at_change = self.df_ica.loc[first_change_index, 'simTime']
+                print(f"Set THW at first change: {set_THW_at_change}, Timestamp: {timestamp_at_change}")
+                if self.stable_average_THW:
+                    target_THW = (self.stable_average_THW + self.df_ica.loc[first_change_index, 'set_headway_time']) / 2
+                    closest_index = (self.df_ica['set_headway_time'] - target_THW).abs().idxmin()
+                    closest_current_THW = self.df_ica.loc[closest_index, 'set_headway_time']
+                    closest_timestamp = self.df_ica.loc[closest_index, 'simTime']
+                    print(f"Closest speed: {closest_current_THW} at time: {closest_timestamp}")
+
+                    self.delay_time_THW = closest_timestamp - timestamp_at_change
+                else:
+                    self.delay_time_THW = Max_Time
+                self.result['value'] = [round(self.delay_time_THW, 3)]
+                print(f"Delay time: {self.delay_time_THW}")
+
+            else:
+                self.delay_time_THW = 0.0
+                self.result['value'] = [0.0]
+                print("No valid change point for further calculation.")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_ica.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[delay_time_THW:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control07_rise_time_THW_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 267 - 0
cicv_ica_longitudinal_control07_rise_time_THW_new.py

@@ -0,0 +1,267 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+Max_Time = 1000
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        # self.stable_start_time_THW = None
+        self.stable_average_THW = None
+        self.rise_time_THW = None
+
+        self.result = {
+            "name": "跟车上升时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "跟车上升时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"跟车上升时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_follow_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Vehicle"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def _get_first_change_index_THW(self):
+        """
+        获取DataFrame中'set_headway_time'列首次发生变化的索引值。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在变化,则返回首次发生变化的索引值(int类型),否则返回None。
+
+        """
+        change_indices = self.df_ica[self.df_ica['set_headway_time'] != self.df_ica['set_headway_time'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_THW(self, window_size, percent_deviation, set_value):
+        """
+        在给定的数据窗口中查找稳定跟车时距离THW,并计算该段内THW的平均值。
+
+        Args:
+            window_size (int): 窗口大小,表示在数据中寻找稳定段时考虑的连续数据点数量。
+            percent_deviation (float): THW值相对于设定值的允许偏差百分比。
+            set_value (float): THW的设定值。
+
+        Returns:
+            None
+
+        """
+        ego_x = self.df_ica[self.df_ica['playerId'] == 1]['posX'].reset_index(drop=True)
+        ego_y = self.df_ica[self.df_ica['playerId'] == 1]['posY'].reset_index(drop=True)
+        obj_x = self.df_ica[self.df_ica['playerId'] == 2]['posX'].reset_index(drop=True)
+        obj_y = self.df_ica[self.df_ica['playerId'] == 2]['posY'].reset_index(drop=True)
+
+        ego_speedx = self.df_ica[self.df_ica['playerId'] == 1]['speedX'].reset_index(drop=True)
+        ego_speedy = self.df_ica[self.df_ica['playerId'] == 1]['speedY'].reset_index(drop=True)
+        obj_speedx = self.df_ica[self.df_ica['playerId'] == 2]['speedX'].reset_index(drop=True)
+        obj_speedy = self.df_ica[self.df_ica['playerId'] == 2]['speedY'].reset_index(drop=True)
+
+        dx = obj_x - ego_x
+        dy = obj_y - ego_y
+        vx = obj_speedx - ego_speedx
+        vy = obj_speedy - ego_speedy
+        dist = np.sqrt(dx ** 2 + dy ** 2)
+        ego_v_projection_in_dist = _cal_v_ego_projection(dx, dy, ego_speedx, ego_speedy)
+        thw1 = _cal_THW(dist, ego_v_projection_in_dist)
+        thw = thw1.tolist()
+        THW = []
+        for item in thw:
+            THW.append(item)
+            THW.append(item)
+        self.df_ica['THW'] = THW
+
+        THW = self.df_ica['THW'].values
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_THW = None
+
+        for i in range(len(THW) - window_size + 1):
+            window_data = THW[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_THW = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(THW) - window_size + 1:
+                    next_window_data = THW[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_THW = (stable_average_THW * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_THW = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_THW = self.df_ica['simTime'].iloc[stable_start]
+        self.stable_average_THW = stable_average_THW
+
+    def _find_closest_time_stamp_THW(self, df, target_THW, start_index):
+        """
+        在DataFrame中找到与目标THW值最接近的时间戳。
+
+        Args:
+            df (pandas.DataFrame): 包含'THW'和'simTime'列的DataFrame,其中'THW'表示目标变量,'simTime'表示时间戳。
+            target_THW (float): 目标THW值。
+            start_index (int): 开始搜索的索引位置(不包含)。
+
+        Returns:
+            pandas.Timestamp: 与目标THW值最接近的时间戳。
+
+        """
+        subset = df.loc[start_index + 1:]
+        THW_diff = np.abs(subset['THW'] - target_THW)
+        closest_index = THW_diff.idxmin()
+        closest_timestamp = subset.loc[closest_index, 'simTime']
+        return closest_timestamp
+
+    def data_analyze(self):
+        """
+        计算巡航时从初THW到达稳定THW的90%的所需时间(rise time)。
+
+        Args:
+            无。
+
+        Returns:
+            无返回值,但会设置实例属性self.rise_time_THW为计算得到的rise time。
+
+        """
+        if self.df_ica.empty:
+            self.result['value'] = [0.0]
+            print(f"Rise time: 0")
+        else:
+            first_change_index = self._get_first_change_index_THW()
+
+            set_headway_time = self.df_ica.loc[first_change_index, 'set_headway_time']
+            self._find_stable_THW(window_size=4, percent_deviation=5, set_value=set_headway_time)
+
+            initial_THW = self.df_ica.loc[first_change_index, 'THW']
+            if self.stable_average_THW:
+                target_THW_90 = initial_THW + (self.stable_average_THW - initial_THW) * 0.9
+                target_THW_10 = initial_THW + (self.stable_average_THW - initial_THW) * 0.1
+
+                timestamp_at_10 = self._find_closest_time_stamp_THW(self.df_ica, target_THW_10, first_change_index)
+                timestamp_at_90 = self._find_closest_time_stamp_THW(self.df_ica, target_THW_90, first_change_index)
+
+                print(f"Closest speed at 10% range from set speed: {timestamp_at_10}")
+                print(f"Closest speed at 90% range from set speed: {timestamp_at_90}")
+
+                self.rise_time_THW = timestamp_at_90 - timestamp_at_10
+            else:
+                self.rise_time_THW = Max_Time
+            self.result['value'] = [round(self.rise_time_THW, 3)]
+            print(f"Rise time: {self.rise_time_THW}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_ica.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[rise_time_THW:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control08_peak_time_THW_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 182 - 0
cicv_ica_longitudinal_control08_peak_time_THW_new.py

@@ -0,0 +1,182 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+Max_Time = 1000
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.peak_time_THW = None
+
+        self.result = {
+            "name": "跟车峰值时间",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "跟车峰值时间(s)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"跟车峰值时间: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_follow_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Vehicle"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def _get_first_change_index_THW(self):
+        """
+        获取DataFrame中'set_headway_time'列首次发生变化的索引值。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在变化,则返回首次发生变化的索引值(int类型),否则返回None。
+
+        """
+        change_indices = self.df_ica[self.df_ica['set_headway_time'] != self.df_ica['set_headway_time'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def data_analyze(self):
+        if self.df_ica.empty:
+            self.peak_time_THW = 0.0
+            self.result['value'] = [0.0]
+            print(f"peak_time_THW: {self.peak_time_THW}")
+        else:
+            ego_x = self.df_ica[self.df_ica['playerId'] == 1]['posX'].reset_index(drop=True)
+            ego_y = self.df_ica[self.df_ica['playerId'] == 1]['posY'].reset_index(drop=True)
+            obj_x = self.df_ica[self.df_ica['playerId'] == 2]['posX'].reset_index(drop=True)
+            obj_y = self.df_ica[self.df_ica['playerId'] == 2]['posY'].reset_index(drop=True)
+
+            ego_speedx = self.df_ica[self.df_ica['playerId'] == 1]['speedX'].reset_index(drop=True)
+            ego_speedy = self.df_ica[self.df_ica['playerId'] == 1]['speedY'].reset_index(drop=True)
+            obj_speedx = self.df_ica[self.df_ica['playerId'] == 2]['speedX'].reset_index(drop=True)
+            obj_speedy = self.df_ica[self.df_ica['playerId'] == 2]['speedY'].reset_index(drop=True)
+
+            dx = obj_x - ego_x
+            dy = obj_y - ego_y
+            vx = obj_speedx - ego_speedx
+            vy = obj_speedy - ego_speedy
+            dist = np.sqrt(dx ** 2 + dy ** 2)
+            ego_v_projection_in_dist = _cal_v_ego_projection(dx, dy, ego_speedx, ego_speedy)
+            thw1 = _cal_THW(dist, ego_v_projection_in_dist)
+            thw = thw1.tolist()
+            THW = []
+            for item in thw:
+                THW.append(item)
+                THW.append(item)
+            self.df_ica['THW'] = THW
+            first_change_index = self._get_first_change_index_THW()
+
+            if not first_change_index:
+                self.peak_time_THW = 0
+                self.result['value'] = [round(self.peak_time_THW, 3)]
+                print(f"peak_time_THW: {self.peak_time_THW}")
+            else:
+                start_time = self.df_ica.loc[first_change_index, 'simTime']
+                peak_time = self.df_ica.loc[self.df_ica['THW'].idxmax(), 'simTime']
+                self.peak_time_THW = peak_time - start_time
+                self.result['value'] = [round(self.peak_time_THW, 3)]
+                print(f"peak_time_THW: {self.peak_time_THW}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_ica.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[peak_time_THW:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control09_overshoot_THW_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 233 - 0
cicv_ica_longitudinal_control09_overshoot_THW_new.py

@@ -0,0 +1,233 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.stable_average_THW = None
+        self.overshoot_THW = None
+
+        self.result = {
+            "name": "跟车超调量",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "跟车超调量(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"跟车超调量: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_follow_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Vehicle"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def _get_first_change_index_THW(self):
+        """
+        获取DataFrame中'set_headway_time'列首次发生变化的索引值。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在变化,则返回首次发生变化的索引值(int类型),否则返回None。
+
+        """
+        change_indices = self.df_ica[self.df_ica['set_headway_time'] != self.df_ica['set_headway_time'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_THW(self, window_size, percent_deviation, set_value):
+        """
+        在给定的数据窗口中查找稳定跟车时距离THW,并计算该段内THW的平均值。
+
+        Args:
+            window_size (int): 窗口大小,表示在数据中寻找稳定段时考虑的连续数据点数量。
+            percent_deviation (float): THW值相对于设定值的允许偏差百分比。
+            set_value (float): THW的设定值。
+
+        Returns:
+            None
+
+        """
+        ego_x = self.df_ica[self.df_ica['playerId'] == 1]['posX'].reset_index(drop=True)
+        ego_y = self.df_ica[self.df_ica['playerId'] == 1]['posY'].reset_index(drop=True)
+        obj_x = self.df_ica[self.df_ica['playerId'] == 2]['posX'].reset_index(drop=True)
+        obj_y = self.df_ica[self.df_ica['playerId'] == 2]['posY'].reset_index(drop=True)
+
+        ego_speedx = self.df_ica[self.df_ica['playerId'] == 1]['speedX'].reset_index(drop=True)
+        ego_speedy = self.df_ica[self.df_ica['playerId'] == 1]['speedY'].reset_index(drop=True)
+        obj_speedx = self.df_ica[self.df_ica['playerId'] == 2]['speedX'].reset_index(drop=True)
+        obj_speedy = self.df_ica[self.df_ica['playerId'] == 2]['speedY'].reset_index(drop=True)
+
+        dx = obj_x - ego_x
+        dy = obj_y - ego_y
+        vx = obj_speedx - ego_speedx
+        vy = obj_speedy - ego_speedy
+        dist = np.sqrt(dx ** 2 + dy ** 2)
+        ego_v_projection_in_dist = _cal_v_ego_projection(dx, dy, ego_speedx, ego_speedy)
+        thw1 = _cal_THW(dist, ego_v_projection_in_dist)
+        thw = thw1.tolist()
+        THW = []
+        for item in thw:
+            THW.append(item)
+            THW.append(item)
+        self.df_ica['THW'] = THW
+        THW = self.df_ica['THW'].values
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_THW = None
+
+        for i in range(len(THW) - window_size + 1):
+            window_data = THW[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_THW = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(THW) - window_size + 1:
+                    next_window_data = THW[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_THW = (stable_average_THW * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_THW = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_THW = self.df_ica['simTime'].iloc[stable_start]
+        self.stable_average_THW = stable_average_THW
+
+    def data_analyze(self):
+        if self.df_ica.empty:
+            self.result['value'] = [0.0]
+            print(f"overshoot_THW: 0")
+        else:
+            first_change_index = self._get_first_change_index_THW()
+
+            set_headway_time = self.df_ica.loc[first_change_index, 'set_headway_time']
+            self._find_stable_THW(window_size=4, percent_deviation=5, set_value=set_headway_time)
+
+            if not first_change_index:
+                self.overshoot_THW = 0
+            else:
+                initial_THW = self.df_ica.loc[first_change_index, 'THW']
+
+                if initial_THW > self.stable_average_THW:
+                    self.overshoot_THW = (self.stable_average_THW - self.df_ica['THW'].min()) * 100 / self.stable_average_THW
+                elif initial_THW < self.stable_average_THW:
+                    self.overshoot_THW = (self.df_ica['THW'].max() - self.stable_average_THW) * 100 / self.stable_average_THW
+                else:
+                    self.overshoot_THW = 0
+
+            self.result['value'] = [round(self.overshoot_THW, 3)]
+            print(f"overshoot_THW: {self.overshoot_THW}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_ica.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[overshoot_THW:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control10_steady_error_THW_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "-1",
+      "optimal": "1.0",
+      "multiple": [
+        "0.5",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 233 - 0
cicv_ica_longitudinal_control10_steady_error_THW_new.py

@@ -0,0 +1,233 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data, _cal_THW, _cal_v_ego_projection
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.stable_average_THW = None
+        self.steady_error_THW = None
+
+        self.result = {
+            "name": "跟车速度稳态误差",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "跟车速度稳态误差(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"跟车速度稳态误差: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_follow_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'] == "LLC_Follow_Vehicle"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def _get_first_change_index_THW(self):
+        """
+        获取DataFrame中'set_headway_time'列首次发生变化的索引值。
+
+        Args:
+            无参数。
+
+        Returns:
+            Union[int, None]: 如果存在变化,则返回首次发生变化的索引值(int类型),否则返回None。
+
+        """
+        change_indices = self.df_ica[self.df_ica['set_headway_time'] != self.df_ica['set_headway_time'].shift()].index
+        if not change_indices.empty:
+            first_change_index = change_indices.min()
+        else:
+            first_change_index = None
+        return first_change_index
+
+    def _find_stable_THW(self, window_size, percent_deviation, set_value):
+        """
+        在给定的数据窗口中查找稳定跟车时距离THW,并计算该段内THW的平均值。
+
+        Args:
+            window_size (int): 窗口大小,表示在数据中寻找稳定段时考虑的连续数据点数量。
+            percent_deviation (float): THW值相对于设定值的允许偏差百分比。
+            set_value (float): THW的设定值。
+
+        Returns:
+            None
+
+        """
+        ego_x = self.df_ica[self.df_ica['playerId'] == 1]['posX'].reset_index(drop=True)
+        ego_y = self.df_ica[self.df_ica['playerId'] == 1]['posY'].reset_index(drop=True)
+        obj_x = self.df_ica[self.df_ica['playerId'] == 2]['posX'].reset_index(drop=True)
+        obj_y = self.df_ica[self.df_ica['playerId'] == 2]['posY'].reset_index(drop=True)
+
+        ego_speedx = self.df_ica[self.df_ica['playerId'] == 1]['speedX'].reset_index(drop=True)
+        ego_speedy = self.df_ica[self.df_ica['playerId'] == 1]['speedY'].reset_index(drop=True)
+        obj_speedx = self.df_ica[self.df_ica['playerId'] == 2]['speedX'].reset_index(drop=True)
+        obj_speedy = self.df_ica[self.df_ica['playerId'] == 2]['speedY'].reset_index(drop=True)
+
+        dx = obj_x - ego_x
+        dy = obj_y - ego_y
+        vx = obj_speedx - ego_speedx
+        vy = obj_speedy - ego_speedy
+        dist = np.sqrt(dx ** 2 + dy ** 2)
+        ego_v_projection_in_dist = _cal_v_ego_projection(dx, dy, ego_speedx, ego_speedy)
+        thw1 = _cal_THW(dist, ego_v_projection_in_dist)
+        thw = thw1.tolist()
+        THW = []
+        for item in thw:
+            THW.append(item)
+            THW.append(item)
+        self.df_ica['THW'] = THW
+        THW = self.df_ica['THW'].values
+        deviation = set_value * (percent_deviation / 100)
+        stable_start = None
+        stable_average_THW = None
+
+        for i in range(len(THW) - window_size + 1):
+            window_data = THW[i:i + window_size]
+
+            if all(set_value - deviation <= s <= set_value + deviation for s in window_data):
+                if stable_start is None:
+                    stable_start = i
+                    stable_end = i + window_size - 1
+                    stable_average_THW = np.mean(window_data)
+
+                j = i + window_size
+                while j < len(THW) - window_size + 1:
+                    next_window_data = THW[j:j + window_size]
+
+                    if all(set_value - deviation <= s <= set_value + deviation for s in next_window_data):
+                        stable_end = j + window_size - 1
+                        stable_average_THW = (stable_average_THW * (j - stable_start) + sum(next_window_data)) / (
+                                j - stable_start + window_size)
+                        j += window_size
+                    else:
+                        stable_start = j + window_size - 1
+                        stable_end = i + window_size - 1
+                        stable_average_THW = np.mean(window_data)
+                        break
+
+        # self.stable_start_time_THW = self.df_ica['simTime'].iloc[stable_start]
+        self.stable_average_THW = stable_average_THW
+
+    def data_analyze(self):
+        if self.df_ica.empty:
+            self.steady_error_THW = 0.0
+            print(f"steady_error_THW: {self.steady_error_THW}")
+            self.result['value'] = [0.0]
+        else:
+            first_change_index = self._get_first_change_index_THW()
+
+            if not first_change_index:
+                self.steady_error_THW = 0
+                self.result['value'] = [abs(round(self.steady_error_THW, 3))]
+                print(f"steady_error_THW: {self.steady_error_THW}")
+            else:
+                set_headway_time = self.df_ica.loc[first_change_index, 'set_headway_time']
+                self._find_stable_THW(window_size=4, percent_deviation=5, set_value=set_headway_time)
+
+                if self.stable_average_THW:
+                    self.steady_error_THW = (self.stable_average_THW - set_headway_time) * 100 / self.stable_average_THW
+                else:
+                    self.steady_error_THW = 0
+                    raise ValueError("Stable average speed is None.")
+
+                self.result['value'] = [abs(round(self.steady_error_THW, 3))]
+                print(f"steady_error_THW: {self.steady_error_THW}")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        # time_list = self.ego_df['simTime'].values.tolist()
+        # graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = self.result['value'][0] if not self.df_ica.empty else '-'
+        self.result['tableData']['max'] = '-'
+        self.result['tableData']['min'] = '-'
+
+        # zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = []
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 1.2]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[steady_error_THW:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control11_reasonable_acceleration_percentage_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "1",
+      "optimal": "100",
+      "multiple": [
+        "0.6",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 170 - 0
cicv_ica_longitudinal_control11_reasonable_acceleration_percentage_new.py

@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.percentage_accel = 0
+
+        self.result = {
+            "name": "合理加减速度占比",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "合理加减速度占比(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"合理加减速度占比: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_active_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # active_status_list = ["LLC_Follow_Line", "LLC_Follow_Vehicle", "Only_Longitudinal_Control"]
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'].isin(active_status_list)].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def data_analyze(self):
+        if self.df_ica.empty:
+            percentage_in_range = 100
+            # 存储结果并打印  
+            self.result['value'] = [100]  
+            print(f"Percentage of frames with acceleration in range: {percentage_in_range}%") 
+        else:
+            col_list = ['simTime', 'simFrame', 'playerId', 'v', 'accel', 'lon_acc_roc']  # 列表指定了要分析的列  
+            df = self.df_ica[col_list].copy()  # 从原始 DataFrame 中选择列并复制  
+        
+            # 将速度从km/h 转换为 m/s
+            df['v_ms'] = df['v'] / 3.6  
+        
+            # 初始化计数器和阈值  
+            count_in_range = 0  
+            total_frames = len(df)  
+        
+            # 遍历DataFrame的每一行  
+            for index, row in df.iterrows():  
+                v_ms = row['v_ms']  
+                accel = row['accel']  
+        
+                # 根据车速范围设置减速度和加速度的阈值  
+                if v_ms > 20:  # 车速大于20km/h  
+                    decel_threshold = -3.5  
+                    accel_threshold = 2  
+                elif v_ms < 5:  # 车速低于5km/h  
+                    decel_threshold = 5  
+                    accel_threshold = 4  # 注意:这里假设是4而不是44,因为44对于加速度来说是一个很大的值  
+                else:  # 车速在5到20km/h之间  
+                    # 对减速度和加速度的阈值进行线性插值  
+                    # 假设在5km/h时decel_threshold=5, accel_threshold=4  
+                    # 在20km/h时decel_threshold=-3.5, accel_threshold=2  
+                    decel_threshold = 5 - (5 + 3.5) * ((v_ms - 5) / (20 - 5))  
+                    accel_threshold = 4 - (4 - 2) * ((v_ms - 5) / (20 - 5))  
+        
+                # 检查加速度是否在允许的范围内  
+                if accel >= decel_threshold and accel <= accel_threshold:  
+                    count_in_range += 1  
+        
+            # 计算百分比  
+            percentage_in_range = (count_in_range / total_frames) * 100  
+        
+            # 存储结果并打印  
+            self.result['value'] = [round(percentage_in_range, 3)]  
+            print(f"Percentage of frames with acceleration in range: {percentage_in_range}%")  
+    
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        time_list = self.ego_df['simTime'].values.tolist()
+        graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = f'{np.mean(graph_list):.2f}' if graph_list else 0
+        self.result['tableData']['max'] = f'{max(graph_list):.2f}' if graph_list else 0
+        self.result['tableData']['min'] = f'{min(graph_list):.2f}' if graph_list else 0
+
+        zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = zip_vs_time
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 100]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[percentage_accel:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass

+ 21 - 0
cicv_ica_longitudinal_control12_reasonable_acceleration_change_rate_percentage_new.json

@@ -0,0 +1,21 @@
+{
+  "priority": "1",
+  "paramList": [
+    {
+      "kind": "1",
+      "optimal": "100",
+      "multiple": [
+        "0.6",
+        "2"
+      ],
+      "spare": [
+        {
+          "param": null
+        },
+        {
+          "param": null
+        }
+      ]
+    }
+  ]
+}

+ 177 - 0
cicv_ica_longitudinal_control12_reasonable_acceleration_change_rate_percentage_new.py

@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##################################################################
+#
+# Copyright (c) 2023 CICV, Inc. All Rights Reserved
+#
+##################################################################
+"""
+@Authors:           zhanghaiwen, yangzihao
+@Data:              2024/02/21
+@Last Modified:     2024/02/21
+@Summary:           The template of custom indicator.
+"""
+
+"""
+设计思路:
+
+"""
+
+import math
+import pandas as pd
+import numpy as np
+from common import zip_time_pairs, continuous_group, get_status_active_data
+from log import logger
+
+"""import functions"""
+
+
+# custom metric codes
+class CustomMetric(object):
+    def __init__(self, all_data, case_name):
+        self.data = all_data
+        self.optimal_dict = self.data.config
+        self.status_trigger_dict = self.data.status_trigger_dict
+        self.case_name = case_name
+        self.markline_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
+        self.graph_list = []
+
+        self.df = pd.DataFrame()
+        self.ego_df = pd.DataFrame()
+        self.df_ica = pd.DataFrame()
+
+        self.percentage_lon_acc_roc = 0
+
+        self.result = {
+            "name": "合理加减速度变化率占比",
+            "value": [],
+            # "weight": [],
+            "tableData": {
+                "avg": "",  # 平均值,或指标值
+                "max": "",
+                "min": ""
+            },
+            "reportData": {
+                "name": "合理加减速度变化率占比(%)",
+                # "legend": [], # 如果有多个data,则需要增加data对应的说明,如:["横向加速度", "纵向加速度"]
+                "data": [],
+                "markLine": [],
+                "range": [],
+            },
+            "statusFlag": {}
+        }
+        self.run()
+        print(f"合理加减速度变化率占比: {self.result['value']}")
+
+    def data_extract(self):
+        self.df = self.data.object_df
+        self.ego_df = self.data.ego_data
+
+        active_time_ranges = self.status_trigger_dict['ICA']['ICA_active_time']
+        self.df_ica = get_status_active_data(active_time_ranges, self.ego_df)
+
+        # active_status_list = ["LLC_Follow_Line", "LLC_Follow_Vehicle", "Only_Longitudinal_Control"]
+        # self.df_ica = self.ego_df[self.ego_df['ICA_status'].isin(active_status_list)].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Shut_off"].copy()  # 数字3对应ICA的Active
+        # self.df_ica = self.df[self.df['ACC_status'] == "Active"].copy()  # 数字3对应ICA的Active
+
+        if self.df_ica.empty:
+            self.result['statusFlag']['function_ICA'] = False
+        else:
+            self.result['statusFlag']['function_ICA'] = True
+
+    def data_analyze(self):
+         
+        if self.df_ica.empty:
+            percentage_in_range = 100  
+    
+            # 存储结果并打印  
+            self.result['value'] = [100]  
+            print(f"Percentage of frames with acceleration/deceleration in range: {percentage_in_range}%")
+        else:
+            # 指定要分析的列  
+            col_list = ['simTime', 'simFrame', 'playerId', 'v', 'accel', 'lon_acc_roc']  
+            # 从原始 DataFrame 中选择列并复制  
+            df = self.df_ica[col_list].copy()  
+        
+            # 将速度从km/h转换为m/s  
+            df['v_ms'] = df['v'] / 3.6  
+        
+            # 初始化计数器  
+            
+            total_frames = len(df)  
+        
+            # 遍历DataFrame的每一行  
+            for index, row in df.iterrows():  
+                v_ms = row['v_ms']  
+                lon_acc_roc = row['lon_acc_roc']  
+        
+                # 根据车速范围设置减速度和加速度的阈值(这里假设减速度就是加速度的相反数,用于判断)  
+                if v_ms > 20:  # 车速大于20m/s  
+                    decel_threshold = -2.5  # 减速度不小于-2.5 m/s^2(注意是减速度,所以是负数)  
+                    accel_threshold_max = 2.5  # 这里我们也需要一个加速度的最大值阈值,但原题没有给出,这里假设和减速度绝对值相同  
+                elif v_ms < 5:  # 车速低于5m/s(这里已经是m/s,无需转换)  
+                    decel_threshold = -5  # 减速度不小于5 m/s^2(注意是减速度,所以是负数)  
+                    accel_threshold_max = 5  # 加速度不大于5 m/s^2(注意这里是正值,因为加速度是正值)  
+                else:  # 车速在5到20m/s之间  
+                    # 对减速度和加速度的阈值进行线性插值  
+                    # 假设在5m/s时decel_threshold=-5 m/s^2, accel_threshold_max=5 m/s^2  
+                    # 在20m/s时decel_threshold=-2.5 m/s^2, accel_threshold_max=2.5 m/s^2(这里假设加速度和减速度绝对值相等)  
+                    decel_threshold = -5 + (5 - 2.5) * ((v_ms - 5) / (20 - 5))  # 注意这里计算出来的是减速度,所以是负数  
+                    accel_threshold_max = 5 - (5 - 2.5) * ((v_ms - 5) / (20 - 5))  # 加速度的最大值  
+        
+                # 检查加速度是否在允许的范围内(注意这里我们假设加速度是正值,减速度是负值)  
+                # 由于我们只有一个加速度值(accel),并且没有直接的减速度值,  
+                # 我们只能根据加速度的值和符号来判断它是否满足减速度的要求。  
+                # 如果accel是负数,并且其绝对值小于等于decel_threshold的绝对值,则满足减速度要求。  
+                # 如果accel是正数,并且小于等于accel_threshold_max,则满足加速度要求。  
+                if (lon_acc_roc < 0 and abs(lon_acc_roc) <= abs(decel_threshold)) or (lon_acc_roc >= 0 and lon_acc_roc <= accel_threshold_max):  
+                    count_in_range += 1  
+        
+            # 计算百分比  
+            percentage_in_range = (count_in_range / total_frames) * 100  
+        
+            # 存储结果并打印  
+            self.result['value'] = [round(percentage_in_range, 3)]  
+            print(f"Percentage of frames with acceleration/deceleration in range: {percentage_in_range}%")
+
+    def markline_statistic(self):
+        pass
+
+    def report_data_statistic(self):
+        time_list = self.ego_df['simTime'].values.tolist()
+        graph_list = [x for x in self.graph_list if not np.isnan(x)]
+        self.result['tableData']['avg'] = f'{np.mean(graph_list):.2f}' if graph_list else 0
+        self.result['tableData']['max'] = f'{max(graph_list):.2f}' if graph_list else 0
+        self.result['tableData']['min'] = f'{min(graph_list):.2f}' if graph_list else 0
+
+        zip_vs_time = zip_time_pairs(time_list, self.graph_list)
+        self.result['reportData']['data'] = zip_vs_time
+
+        # self.markline_statistic()
+        # markline_slices = self.markline_df.to_dict('records')
+        self.result['reportData']['markLine'] = []
+
+        self.result['reportData']['range'] = [0, 100]
+
+    def run(self):
+        # logger.info(f"Custom metric run:[{self.result['name']}].")
+        logger.info(f"[case:{self.case_name}] Custom metric:[percentage_lon_acc_roc:{self.result['name']}] evaluate.")
+
+        try:
+            self.data_extract()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data extract ERROR!", e)
+
+        try:
+            self.data_analyze()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} data analyze ERROR!", e)
+
+        try:
+            self.report_data_statistic()
+        except Exception as e:
+            logger.error(f"[case:{self.case_name}] Custom metric:{self.result['name']} report data statistic ERROR!", e)
+
+# if __name__ == "__main__":
+#     pass