safety.py 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. 安全指标计算模块
  5. """
  6. import os
  7. import numpy as np
  8. import pandas as pd
  9. import math
  10. import matplotlib.pyplot as plt
  11. import scipy.integrate as spi
  12. from collections import defaultdict
  13. from typing import Dict, Any, List, Optional
  14. from pathlib import Path
  15. import ast
  16. from modules.lib.score import Score
  17. from modules.lib.log_manager import LogManager
  18. from modules.lib.chart_generator import generate_safety_chart_data
  19. # 安全指标相关常量
  20. SAFETY_INFO = [
  21. "simTime",
  22. "simFrame",
  23. "playerId",
  24. "posX",
  25. "posY",
  26. "posH",
  27. "speedX",
  28. "speedY",
  29. "accelX",
  30. "accelY",
  31. "v",
  32. "type"
  33. ]
  34. # ----------------------
  35. # 独立指标计算函数
  36. # ----------------------
  37. def calculate_ttc(data_processed) -> dict:
  38. """计算TTC (Time To Collision)"""
  39. if data_processed is None or not hasattr(data_processed, 'object_df'):
  40. return {"TTC": None}
  41. try:
  42. safety = SafetyCalculator(data_processed)
  43. ttc_value = safety.get_ttc_value()
  44. # 只生成图表,数据导出由chart_generator处理
  45. if safety.ttc_data:
  46. safety.generate_metric_chart('TTC')
  47. LogManager().get_logger().info(f"安全指标[TTC]计算结果: {ttc_value}")
  48. return {"TTC": ttc_value}
  49. except Exception as e:
  50. LogManager().get_logger().error(f"TTC计算异常: {str(e)}", exc_info=True)
  51. return {"TTC": None}
  52. def calculate_mttc(data_processed) -> dict:
  53. """计算MTTC (Modified Time To Collision)"""
  54. if data_processed is None or not hasattr(data_processed, 'object_df'):
  55. return {"MTTC": None}
  56. try:
  57. safety = SafetyCalculator(data_processed)
  58. mttc_value = safety.get_mttc_value()
  59. if safety.mttc_data:
  60. safety.generate_metric_chart('MTTC')
  61. LogManager().get_logger().info(f"安全指标[MTTC]计算结果: {mttc_value}")
  62. return {"MTTC": mttc_value}
  63. except Exception as e:
  64. LogManager().get_logger().error(f"MTTC计算异常: {str(e)}", exc_info=True)
  65. return {"MTTC": None}
  66. def calculate_thw(data_processed) -> dict:
  67. """计算THW (Time Headway)"""
  68. if data_processed is None or not hasattr(data_processed, 'object_df'):
  69. return {"THW": None}
  70. try:
  71. safety = SafetyCalculator(data_processed)
  72. thw_value = safety.get_thw_value()
  73. if safety.thw_data:
  74. safety.generate_metric_chart('THW')
  75. LogManager().get_logger().info(f"安全指标[THW]计算结果: {thw_value}")
  76. return {"THW": thw_value}
  77. except Exception as e:
  78. LogManager().get_logger().error(f"THW计算异常: {str(e)}", exc_info=True)
  79. return {"THW": None}
  80. def calculate_tlc(data_processed) -> dict:
  81. """计算TLC (Time to Line Crossing)"""
  82. if data_processed is None or not hasattr(data_processed, 'object_df'):
  83. return {"TLC": None}
  84. try:
  85. safety = SafetyCalculator(data_processed)
  86. tlc_value = safety.get_tlc_value()
  87. if safety.tlc_data:
  88. safety.generate_metric_chart('TLC')
  89. LogManager().get_logger().info(f"安全指标[TLC]计算结果: {tlc_value}")
  90. return {"TLC": tlc_value}
  91. except Exception as e:
  92. LogManager().get_logger().error(f"TLC计算异常: {str(e)}", exc_info=True)
  93. return {"TLC": None}
  94. def calculate_ttb(data_processed) -> dict:
  95. """计算TTB (Time to Brake)"""
  96. if data_processed is None or not hasattr(data_processed, 'object_df'):
  97. return {"TTB": None}
  98. try:
  99. safety = SafetyCalculator(data_processed)
  100. ttb_value = safety.get_ttb_value()
  101. if safety.ttb_data:
  102. safety.generate_metric_chart('TTB')
  103. LogManager().get_logger().info(f"安全指标[TTB]计算结果: {ttb_value}")
  104. return {"TTB": ttb_value}
  105. except Exception as e:
  106. LogManager().get_logger().error(f"TTB计算异常: {str(e)}", exc_info=True)
  107. return {"TTB": None}
  108. def calculate_tm(data_processed) -> dict:
  109. """计算TM (Time Margin)"""
  110. if data_processed is None or not hasattr(data_processed, 'object_df'):
  111. return {"TM": None}
  112. try:
  113. safety = SafetyCalculator(data_processed)
  114. tm_value = safety.get_tm_value()
  115. if safety.tm_data:
  116. safety.generate_metric_chart('TM')
  117. LogManager().get_logger().info(f"安全指标[TM]计算结果: {tm_value}")
  118. return {"TM": tm_value}
  119. except Exception as e:
  120. LogManager().get_logger().error(f"TM计算异常: {str(e)}", exc_info=True)
  121. return {"TM": None}
  122. def calculate_dtc(data_processed) -> dict:
  123. """计算DTC (Distance to Collision)"""
  124. if data_processed is None or not hasattr(data_processed, 'object_df'):
  125. return {"DTC": None}
  126. try:
  127. safety = SafetyCalculator(data_processed)
  128. dtc_value = safety.get_dtc_value()
  129. LogManager().get_logger().info(f"安全指标[DTC]计算结果: {dtc_value}")
  130. return {"DTC": dtc_value}
  131. except Exception as e:
  132. LogManager().get_logger().error(f"DTC计算异常: {str(e)}", exc_info=True)
  133. return {"DTC": None}
  134. def calculate_pet(data_processed) -> dict:
  135. """计算PET (Post Encroachment Time)"""
  136. if data_processed is None or not hasattr(data_processed, 'object_df'):
  137. return {"PET": None}
  138. try:
  139. safety = SafetyCalculator(data_processed)
  140. pet_value = safety.get_dtc_value()
  141. LogManager().get_logger().info(f"安全指标[PET]计算结果: {pet_value}")
  142. return {"PET": pet_value}
  143. except Exception as e:
  144. LogManager().get_logger().error(f"PET计算异常: {str(e)}", exc_info=True)
  145. return {"PET": None}
  146. def calculate_psd(data_processed) -> dict:
  147. """计算PSD (Potential Safety Distance)"""
  148. if data_processed is None or not hasattr(data_processed, 'object_df'):
  149. return {"PSD": None}
  150. try:
  151. safety = SafetyCalculator(data_processed)
  152. psd_value = safety.get_psd_value()
  153. LogManager().get_logger().info(f"安全指标[PSD]计算结果: {psd_value}")
  154. return {"PSD": psd_value}
  155. except Exception as e:
  156. LogManager().get_logger().error(f"PSD计算异常: {str(e)}", exc_info=True)
  157. return {"PSD": None}
  158. def calculate_collisionrisk(data_processed) -> dict:
  159. """计算碰撞风险"""
  160. if data_processed is None or not hasattr(data_processed, 'object_df'):
  161. return {"collisionRisk": None}
  162. try:
  163. safety = SafetyCalculator(data_processed)
  164. collision_risk_value = safety.get_collision_risk_value()
  165. if safety.collision_risk_data:
  166. safety.generate_metric_chart('collisionRisk')
  167. LogManager().get_logger().info(f"安全指标[collisionRisk]计算结果: {collision_risk_value}")
  168. return {"collisionRisk": collision_risk_value}
  169. except Exception as e:
  170. LogManager().get_logger().error(f"collisionRisk计算异常: {str(e)}", exc_info=True)
  171. return {"collisionRisk": None}
  172. def calculate_lonsd(data_processed) -> dict:
  173. """计算纵向安全距离"""
  174. safety = SafetyCalculator(data_processed)
  175. lonsd_value = safety.get_lonsd_value()
  176. if safety.lonsd_data:
  177. safety.generate_metric_chart('LonSD')
  178. LogManager().get_logger().info(f"安全指标[LonSD]计算结果: {lonsd_value}")
  179. return {"LonSD": lonsd_value}
  180. def calculate_latsd(data_processed) -> dict:
  181. """计算横向安全距离"""
  182. if data_processed is None or not hasattr(data_processed, 'object_df'):
  183. return {"LatSD": None}
  184. try:
  185. safety = SafetyCalculator(data_processed)
  186. latsd_value = safety.get_latsd_value()
  187. if safety.latsd_data:
  188. # 只生成图表,数据导出由chart_generator处理
  189. safety.generate_metric_chart('LatSD')
  190. LogManager().get_logger().info(f"安全指标[LatSD]计算结果: {latsd_value}")
  191. return {"LatSD": latsd_value}
  192. except Exception as e:
  193. LogManager().get_logger().error(f"LatSD计算异常: {str(e)}", exc_info=True)
  194. return {"LatSD": None}
  195. def calculate_btn(data_processed) -> dict:
  196. """计算制动威胁数"""
  197. if data_processed is None or not hasattr(data_processed, 'object_df'):
  198. return {"BTN": None}
  199. try:
  200. safety = SafetyCalculator(data_processed)
  201. btn_value = safety.get_btn_value()
  202. if safety.btn_data:
  203. # 只生成图表,数据导出由chart_generator处理
  204. safety.generate_metric_chart('BTN')
  205. LogManager().get_logger().info(f"安全指标[BTN]计算结果: {btn_value}")
  206. return {"BTN": btn_value}
  207. except Exception as e:
  208. LogManager().get_logger().error(f"BTN计算异常: {str(e)}", exc_info=True)
  209. return {"BTN": None}
  210. def calculate_collisionseverity(data_processed) -> dict:
  211. """计算碰撞严重性"""
  212. if data_processed is None or not hasattr(data_processed, 'object_df'):
  213. return {"collisionSeverity": None}
  214. try:
  215. safety = SafetyCalculator(data_processed)
  216. collision_severity_value = safety.get_collision_severity_value()
  217. if safety.collision_severity_data:
  218. # 只生成图表,数据导出由chart_generator处理
  219. safety.generate_metric_chart('collisionSeverity')
  220. LogManager().get_logger().info(f"安全指标[collisionSeverity]计算结果: {collision_severity_value}")
  221. return {"collisionSeverity": collision_severity_value}
  222. except Exception as e:
  223. LogManager().get_logger().error(f"collisionSeverity计算异常: {str(e)}", exc_info=True)
  224. return {"collisionSeverity": None}
  225. class SafetyRegistry:
  226. """安全指标注册器"""
  227. def __init__(self, data_processed):
  228. self.logger = LogManager().get_logger()
  229. self.data = data_processed
  230. self.safety_config = data_processed.safety_config["safety"]
  231. self.metrics = self._extract_metrics(self.safety_config)
  232. self._registry = self._build_registry()
  233. def _extract_metrics(self, config_node: dict) -> list:
  234. """从配置中提取指标名称"""
  235. metrics = []
  236. def _recurse(node):
  237. if isinstance(node, dict):
  238. if 'name' in node and not any(isinstance(v, dict) for v in node.values()):
  239. metrics.append(node['name'])
  240. for v in node.values():
  241. _recurse(v)
  242. _recurse(config_node)
  243. self.logger.info(f'评比的安全指标列表:{metrics}')
  244. return metrics
  245. def _build_registry(self) -> dict:
  246. """构建指标函数注册表"""
  247. registry = {}
  248. for metric_name in self.metrics:
  249. func_name = f"calculate_{metric_name.lower()}"
  250. if func_name in globals():
  251. registry[metric_name] = globals()[func_name]
  252. else:
  253. self.logger.warning(f"未实现安全指标函数: {func_name}")
  254. return registry
  255. def batch_execute(self) -> dict:
  256. """批量执行指标计算"""
  257. results = {}
  258. for name, func in self._registry.items():
  259. try:
  260. result = func(self.data)
  261. results.update(result)
  262. except Exception as e:
  263. self.logger.error(f"{name} 执行失败: {str(e)}", exc_info=True)
  264. results[name] = None
  265. self.logger.info(f'安全指标计算结果:{results}')
  266. return results
  267. class SafeManager:
  268. """安全指标管理类"""
  269. def __init__(self, data_processed):
  270. self.data = data_processed
  271. self.registry = SafetyRegistry(self.data)
  272. def report_statistic(self):
  273. """计算并报告安全指标结果"""
  274. safety_result = self.registry.batch_execute()
  275. return safety_result
  276. class SafetyCalculator:
  277. """安全指标计算类 - 兼容Safe类风格"""
  278. def __init__(self, data_processed):
  279. self.logger = LogManager().get_logger()
  280. self.data_processed = data_processed
  281. self.df = data_processed.object_df.copy()
  282. self.ego_df = data_processed.ego_data.copy() # 使用copy()避免修改原始数据
  283. self.obj_id_list = data_processed.obj_id_list
  284. self.metric_list = [
  285. 'TTC', 'MTTC', 'THW', 'TLC', 'TTB', 'TM', 'DTC', 'PET', 'PSD', 'LonSD', 'LatSD', 'BTN', 'collisionRisk', 'collisionSeverity'
  286. ]
  287. # 初始化默认值
  288. self.calculated_value = {
  289. "TTC": 10.0,
  290. "MTTC": 10.0,
  291. "THW": 10.0,
  292. "TLC": 10.0,
  293. "TTB": 10.0,
  294. "TM": 10.0,
  295. # "MPrTTC": 10.0,
  296. "PET": 10.0,
  297. "DTC": 10.0,
  298. "PSD": 10.0,
  299. "LatSD": 3.0,
  300. "BTN": 1.0,
  301. "collisionRisk": 0.0,
  302. "collisionSeverity": 0.0,
  303. }
  304. self.time_list = self.ego_df['simTime'].values.tolist()
  305. self.frame_list = self.ego_df['simFrame'].values.tolist()
  306. self.collisionRisk = 0
  307. self.empty_flag = True
  308. # 初始化数据存储列表
  309. self.ttc_data = []
  310. self.mttc_data = []
  311. self.thw_data = []
  312. self.tlc_data = []
  313. self.ttb_data = []
  314. self.tm_data = []
  315. self.lonsd_data = []
  316. self.latsd_data = []
  317. self.btn_data = []
  318. self.collision_risk_data = []
  319. self.collision_severity_data = []
  320. # 初始化安全事件记录表
  321. self.unsafe_events_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
  322. # 设置输出目录
  323. self.output_dir = os.path.join(os.getcwd(), 'data')
  324. os.makedirs(self.output_dir, exist_ok=True)
  325. self.logger.info("SafetyCalculator初始化完成,场景中包含自车的目标物一共为: %d", len(self.obj_id_list))
  326. if len(self.obj_id_list) > 1:
  327. self.unsafe_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
  328. self.unsafe_time_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
  329. self.unsafe_dist_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
  330. self.unsafe_acce_drac_df = pd.DataFrame(
  331. columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
  332. self.unsafe_acce_xtn_df = pd.DataFrame(
  333. columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
  334. self.unsafe_prob_df = pd.DataFrame(columns=['start_time', 'end_time', 'start_frame', 'end_frame', 'type'])
  335. self.most_dangerous = {}
  336. self.pass_percent = {}
  337. self.logger.info("开始执行安全参数计算 _safe_param_cal_new")
  338. self._safe_param_cal_new()
  339. self.logger.info("安全参数计算完成")
  340. def _safe_param_cal_new(self):
  341. self.logger.debug("进入 _safe_param_cal_new 方法")
  342. # 直接复用Safe类的实现
  343. Tc = 0.3 # 安全距离
  344. rho = self.data_processed.vehicle_config["RHO"]
  345. ego_accel_max = self.data_processed.vehicle_config["EGO_ACCEL_MAX"]
  346. obj_decel_max = self.data_processed.vehicle_config["OBJ_DECEL_MAX"]
  347. ego_decel_min = self.data_processed.vehicle_config["EGO_DECEL_MIN"]
  348. ego_decel_lon_max = self.data_processed.vehicle_config["EGO_DECEL_LON_MAX"]
  349. ego_decel_lat_max = self.data_processed.vehicle_config["EGO_DECEL_LAT_MAX"]
  350. ego_decel_max = np.sqrt(ego_decel_lon_max ** 2 + ego_decel_lat_max ** 2)
  351. #TEMP_COMMENT: x_relative_start_dist 注释开始
  352. x_relative_start_dist = self.ego_df["x_relative_dist"]
  353. # 设置安全指标阈值
  354. self.safety_thresholds = {
  355. 'TTC': {'min': 1.5, 'max': None}, # TTC小于1.5秒视为危险
  356. 'MTTC': {'min': 1.5, 'max': None}, # MTTC小于1.5秒视为危险
  357. 'THW': {'min': 1.0, 'max': None}, # THW小于1.0秒视为危险
  358. 'LonSD': {'min': None, 'max': None}, # 根据实际情况设置
  359. 'LatSD': {'min': 0.5, 'max': None}, # LatSD小于0.5米视为危险
  360. 'BTN': {'min': None, 'max': 0.8}, # BTN大于0.8视为危险
  361. 'collisionRisk': {'min': None, 'max': 30}, # 碰撞风险大于30%视为危险
  362. 'collisionSeverity': {'min': None, 'max': 30} # 碰撞严重性大于30%视为危险
  363. }
  364. obj_dict = defaultdict(dict)
  365. obj_data_dict = self.df.to_dict('records')
  366. for item in obj_data_dict:
  367. obj_dict[item['simFrame']][item['playerId']] = item
  368. df_list = []
  369. EGO_PLAYER_ID = 1
  370. ramp_poss = self.ego_df[self.ego_df["link_type"] == 19]["link_coords"].drop_duplicates().tolist() # 寻找匝道的位置坐标
  371. lane_poss = self.ego_df[self.ego_df["lane_type"] == 2]["lane_coords"].drop_duplicates().tolist() # 寻找匝道的位置坐标
  372. for frame_num in self.frame_list:
  373. ego_data = obj_dict[frame_num][EGO_PLAYER_ID]
  374. v1 = ego_data['v']
  375. x1 = ego_data['posX']
  376. y1 = ego_data['posY']
  377. h1 = ego_data['posH']
  378. laneOffset = ego_data["laneOffset"]
  379. v_x1 = ego_data['speedX']
  380. v_y1 = ego_data['speedY']
  381. a_x1 = ego_data['accelX']
  382. a_y1 = ego_data['accelY']
  383. a1 = np.sqrt(a_x1 ** 2 + a_y1 ** 2)
  384. for playerId in self.obj_id_list:
  385. if playerId == EGO_PLAYER_ID:
  386. continue
  387. try:
  388. obj_data = obj_dict[frame_num][playerId]
  389. except KeyError:
  390. continue
  391. x2 = obj_data['posX']
  392. y2 = obj_data['posY']
  393. dist = self.dist(x1, y1, x2, y2)
  394. obj_data['dist'] = dist
  395. v_x2 = obj_data['speedX']
  396. v_y2 = obj_data['speedY']
  397. v2 = obj_data['v']
  398. a_x2 = obj_data['accelX']
  399. a_y2 = obj_data['accelY']
  400. a2 = np.sqrt(a_x2 ** 2 + a_y2 ** 2)
  401. dx, dy = x2 - x1, y2 - y1
  402. # 计算目标车相对于自车的方位角
  403. beta = math.atan2(dy, dx)
  404. # 将全局坐标系下的相对位置向量转换到自车坐标系
  405. # 自车坐标系:车头方向为x轴正方向,车辆左侧为y轴正方向
  406. h1_rad = math.radians(90 - h1) # 转换为与x轴的夹角
  407. # 坐标变换
  408. lon_d = dx * math.cos(h1_rad) + dy * math.sin(h1_rad) # 纵向距离(前为正,后为负)
  409. lat_d = abs(-dx * math.sin(h1_rad) + dy * math.cos(h1_rad)) # 横向距离(取绝对值)
  410. obj_dict[frame_num][playerId]['lon_d'] = lon_d
  411. obj_dict[frame_num][playerId]['lat_d'] = lat_d
  412. if lon_d > 100 or lon_d < -5 or lat_d > 4:
  413. continue
  414. self.empty_flag = False
  415. vx, vy = v_x1 - v_x2, v_y1 - v_y2
  416. ax, ay = a_x2 - a_x1, a_y2 - a_y1
  417. relative_v = np.sqrt(vx ** 2 + vy ** 2)
  418. v_ego_p = self._cal_v_ego_projection(dx, dy, v_x1, v_y1)
  419. v_obj_p = self._cal_v_ego_projection(dx, dy, v_x2, v_y2)
  420. vrel_projection_in_dist = self._cal_v_projection(dx, dy, vx, vy)
  421. arel_projection_in_dist = self._cal_a_projection(dx, dy, vx, vy, ax, ay, x1, y1, x2, y2, v_x1, v_y1,
  422. v_x2, v_y2)
  423. obj_dict[frame_num][playerId]['vrel_projection_in_dist'] = vrel_projection_in_dist
  424. obj_dict[frame_num][playerId]['arel_projection_in_dist'] = arel_projection_in_dist
  425. obj_dict[frame_num][playerId]['v_ego_projection_in_dist'] = v_ego_p
  426. obj_dict[frame_num][playerId]['v_obj_projection_in_dist'] = v_obj_p
  427. obj_type = obj_data['type']
  428. TTC = self._cal_TTC(dist, vrel_projection_in_dist) if abs(vrel_projection_in_dist) > 1e-6 else None
  429. MTTC = self._cal_MTTC(dist, vrel_projection_in_dist, arel_projection_in_dist)
  430. THW = self._cal_THW(dist, v_ego_p) if abs(v_ego_p) > 1e-6 else None
  431. TLC = self._cal_TLC(v1, h1, laneOffset)
  432. TTB = self._cal_TTB(x_relative_start_dist, relative_v, ego_decel_max)
  433. TM = self._cal_TM(x_relative_start_dist, v2, a2, v1, a1)
  434. DTC = self._cal_DTC(vrel_projection_in_dist, arel_projection_in_dist, rho)
  435. # MPrTTC = self._cal_MPrTTC(x_relative_start_dist)
  436. # PET = self._cal_PET(lane_posx1, lane_posy1, lane_posx2, lane_posy2, ramp_posx1, ramp_posy1, ramp_posx2, ramp_posy2, ego_posx, ego_posy, obj_posx, obj_posy, lane_width, delta_t, v1, v2, a1, a2)
  437. PET = None
  438. for lane_pos in lane_poss:
  439. lane_posx1 = ast.literal_eval(lane_pos)[0][0]
  440. lane_posy1 = ast.literal_eval(lane_pos)[0][1]
  441. lane_posx2 = ast.literal_eval(lane_pos)[-1][0]
  442. lane_posy2 = ast.literal_eval(lane_pos)[-1][1]
  443. for ramp_pos in ramp_poss:
  444. ramp_posx1 = ast.literal_eval(ramp_pos)[0][0]
  445. ramp_posy1 = ast.literal_eval(ramp_pos)[0][1]
  446. ramp_posx2 = ast.literal_eval(ramp_pos)[-1][0]
  447. ramp_posy2 = ast.literal_eval(ramp_pos)[-1][1]
  448. ego_posx = x1
  449. ego_posy = y1
  450. obj_posx = x2
  451. obj_posy = y2
  452. delta_t = self._cal_reaction_time_to_avgspeed(self.ego_df)
  453. lane_width = self.ego_df["lane_width"].iloc[0]
  454. PET = self._cal_PET(lane_posx1, lane_posy1, lane_posx2, lane_posy2, ramp_posx1, ramp_posy1, ramp_posx2,
  455. ramp_posy2, ego_posx, ego_posy, obj_posx, obj_posy, lane_width, delta_t, v1, v2, a1, a2)
  456. PSD = self._cal_PSD(x_relative_start_dist, v1, ego_decel_lon_max)
  457. LonSD = self._cal_longitudinal_safe_dist(v_ego_p, v_obj_p, rho, ego_accel_max, ego_decel_min, obj_decel_max)
  458. lat_dist = 0.5
  459. v_right = v1
  460. v_left = v2
  461. a_right_lat_brake_min = 1
  462. a_left_lat_brake_min = 1
  463. a_lat_max = 5
  464. LatSD = self._cal_lateral_safe_dist(lat_dist, v_right, v_left, rho, a_right_lat_brake_min,
  465. a_left_lat_brake_min, a_lat_max)
  466. # 使用自车坐标系下的纵向加速度
  467. lon_a1 = a_x1 * math.cos(h1_rad) + a_y1 * math.sin(h1_rad)
  468. lon_a2 = a_x2 * math.cos(h1_rad) + a_y2 * math.sin(h1_rad)
  469. lon_a = abs(lon_a1 - lon_a2)
  470. lon_d = max(0.1, lon_d) # 确保纵向距离为正
  471. lon_v = v_x1 * math.cos(h1_rad) + v_y1 * math.sin(h1_rad)
  472. BTN = self._cal_BTN_new(lon_a1, lon_a, lon_d, lon_v, ego_decel_lon_max)
  473. # 使用自车坐标系下的横向加速度
  474. lat_a1 = -a_x1 * math.sin(h1_rad) + a_y1 * math.cos(h1_rad)
  475. lat_a2 = -a_x2 * math.sin(h1_rad) + a_y2 * math.cos(h1_rad)
  476. lat_a = abs(lat_a1 - lat_a2)
  477. lat_v = -v_x1 * math.sin(h1_rad) + v_y1 * math.cos(h1_rad)
  478. obj_dict[frame_num][playerId]['lat_v_rel'] = lat_v - (-v_x2 * math.sin(h1_rad) + v_y2 * math.cos(h1_rad))
  479. obj_dict[frame_num][playerId]['lon_v_rel'] = lon_v - (v_x2 * math.cos(h1_rad) + v_y2 * math.sin(h1_rad))
  480. TTC = None if (TTC is None or TTC < 0) else TTC
  481. MTTC = None if (MTTC is None or MTTC < 0) else MTTC
  482. THW = None if (THW is None or THW < 0) else THW
  483. TLC = None if (TLC is None or TLC < 0) else TLC
  484. TTB = None if (TTB is None or TTB < 0) else TTB
  485. TM = None if (TM is None or TM < 0) else TM
  486. DTC = None if (DTC is None or DTC < 0) else DTC
  487. PET = None if (PET is None or PET < 0) else PET
  488. PSD = None if (PSD is None or PSD < 0) else PSD
  489. obj_dict[frame_num][playerId]['TTC'] = TTC
  490. obj_dict[frame_num][playerId]['MTTC'] = MTTC
  491. obj_dict[frame_num][playerId]['THW'] = THW
  492. obj_dict[frame_num][playerId]['TLC'] = TLC
  493. obj_dict[frame_num][playerId]['TTB'] = TTB
  494. obj_dict[frame_num][playerId]['TM'] = TM
  495. obj_dict[frame_num][playerId]['DTC'] = DTC
  496. obj_dict[frame_num][playerId]['PET'] = PET
  497. obj_dict[frame_num][playerId]['PSD'] = PSD
  498. obj_dict[frame_num][playerId]['LonSD'] = LonSD
  499. obj_dict[frame_num][playerId]['LatSD'] = LatSD
  500. obj_dict[frame_num][playerId]['BTN'] = abs(BTN)
  501. # TTC要进行筛选,否则会出现nan或者TTC过大的情况
  502. if not TTC or TTC > 4000: # threshold = 4258.41
  503. collisionSeverity = 0
  504. pr_death = 0
  505. collisionRisk = 0
  506. else:
  507. result, error = spi.quad(self._normal_distribution, 0, TTC - Tc)
  508. collisionSeverity = 1 - result
  509. pr_death = self._death_pr(obj_type, vrel_projection_in_dist)
  510. collisionRisk = 0.4 * pr_death + 0.6 * collisionSeverity
  511. obj_dict[frame_num][playerId]['collisionSeverity'] = collisionSeverity * 100
  512. obj_dict[frame_num][playerId]['pr_death'] = pr_death * 100
  513. obj_dict[frame_num][playerId]['collisionRisk'] = collisionRisk * 100
  514. df_fnum = pd.DataFrame(obj_dict[frame_num].values())
  515. df_list.append(df_fnum)
  516. df_safe = pd.concat(df_list)
  517. col_list = ['simTime', 'simFrame', 'playerId',
  518. 'TTC', 'MTTC', 'THW', 'TLC', 'TTB', 'TM', 'DTC', 'PET', 'PSD', 'LonSD', 'LatSD', 'BTN',
  519. 'collisionSeverity', 'pr_death', 'collisionRisk']
  520. self.df_safe = df_safe[col_list].reset_index(drop=True)
  521. def _cal_v_ego_projection(self, dx, dy, v_x1, v_y1):
  522. # 计算 AB 连线的向量 AB
  523. # dx = x2 - x1
  524. # dy = y2 - y1
  525. # 计算 AB 连线的模长 |AB|
  526. AB_mod = math.sqrt(dx ** 2 + dy ** 2)
  527. # 计算 AB 连线的单位向量 U_AB
  528. U_ABx = dx / AB_mod
  529. U_ABy = dy / AB_mod
  530. # 计算 A 在 AB 连线上的速度 V1_on_AB
  531. V1_on_AB = v_x1 * U_ABx + v_y1 * U_ABy
  532. return V1_on_AB
  533. def _cal_v_projection(self, dx, dy, vx, vy):
  534. # 计算 AB 连线的向量 AB
  535. # dx = x2 - x1
  536. # dy = y2 - y1
  537. # 计算 AB 连线的模长 |AB|
  538. AB_mod = math.sqrt(dx ** 2 + dy ** 2)
  539. # 计算 AB 连线的单位向量 U_AB
  540. U_ABx = dx / AB_mod
  541. U_ABy = dy / AB_mod
  542. # 计算 A 相对于 B 的速度 V_relative
  543. # vx = vx1 - vx2
  544. # vy = vy1 - vy2
  545. # 计算 A 相对于 B 在 AB 连线上的速度 V_on_AB
  546. V_on_AB = vx * U_ABx + vy * U_ABy
  547. return V_on_AB
  548. def _cal_a_projection(self, dx, dy, vx, vy, ax, ay, x1, y1, x2, y2, v_x1, v_y1, v_x2, v_y2):
  549. # 计算 AB 连线的向量 AB
  550. # dx = x2 - x1
  551. # dy = y2 - y1
  552. # 计算 θ
  553. V_mod = math.sqrt(vx ** 2 + vy ** 2)
  554. AB_mod = math.sqrt(dx ** 2 + dy ** 2)
  555. if V_mod == 0 or AB_mod == 0:
  556. return 0
  557. cos_theta = (vx * dx + vy * dy) / (V_mod * AB_mod)
  558. theta = math.acos(cos_theta)
  559. # 计算 AB 连线的模长 |AB|
  560. AB_mod = math.sqrt(dx ** 2 + dy ** 2)
  561. # 计算 AB 连线的单位向量 U_AB
  562. U_ABx = dx / AB_mod
  563. U_ABy = dy / AB_mod
  564. # 计算 A 相对于 B 的加速度 a_relative
  565. # ax = ax1 - ax2
  566. # ay = ay1 - ay2
  567. # 计算 A 相对于 B 在 AB 连线上的加速度 a_on_AB
  568. a_on_AB = ax * U_ABx + ay * U_ABy
  569. VA = np.array([v_x1, v_y1])
  570. VB = np.array([v_x2, v_y2])
  571. D_A = np.array([x1, y1])
  572. D_B = np.array([x2, y2])
  573. V_r = VA - VB
  574. V = np.linalg.norm(V_r)
  575. w = self._cal_relative_angular_v(theta, D_A, D_B, VA, VB)
  576. a_on_AB_back = self._calculate_derivative(a_on_AB, w, V, theta)
  577. return a_on_AB_back
  578. # 计算相对加速度
  579. def _calculate_derivative(self, a, w, V, theta):
  580. # 计算(V×cos(θ))'的值
  581. # derivative = a * math.cos(theta) - w * V * math.sin(theta)theta
  582. derivative = a - w * V * math.sin(theta)
  583. return derivative
  584. def _cal_relative_angular_v(self, theta, A, B, VA, VB):
  585. dx = A[0] - B[0]
  586. dy = A[1] - B[1]
  587. dvx = VA[0] - VB[0]
  588. dvy = VA[1] - VB[1]
  589. # (dx * dvy - dy * dvx)
  590. angular_velocity = math.sqrt(dvx ** 2 + dvy ** 2) * math.sin(theta) / math.sqrt(dx ** 2 + dy ** 2)
  591. return angular_velocity
  592. def _death_pr(self, obj_type, v_relative):
  593. if obj_type == 5:
  594. p_death = 1 / (1 + np.exp(7.723 - 0.15 * v_relative))
  595. else:
  596. p_death = 1 / (1 + np.exp(8.192 - 0.12 * v_relative))
  597. return p_death
  598. def _cal_collisionRisk_level(self, obj_type, v_relative, collisionSeverity):
  599. if obj_type == 5:
  600. p_death = 1 / (1 + np.exp(7.723 - 0.15 * v_relative))
  601. else:
  602. p_death = 1 / (1 + np.exp(8.192 - 0.12 * v_relative))
  603. collisionRisk = 0.4 * p_death + 0.6 * collisionSeverity
  604. return collisionRisk
  605. # 求两车之间当前距离
  606. def dist(self, x1, y1, x2, y2):
  607. dist = np.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
  608. return dist
  609. def generate_metric_chart(self, metric_name: str) -> None:
  610. """生成指标图表
  611. Args:
  612. metric_name: 指标名称
  613. """
  614. try:
  615. # 确定输出目录
  616. if self.output_dir is None:
  617. self.output_dir = os.path.join(os.getcwd(), 'data')
  618. os.makedirs(self.output_dir, exist_ok=True)
  619. # 调用图表生成函数
  620. chart_path = generate_safety_chart_data(self, metric_name, self.output_dir)
  621. if chart_path:
  622. self.logger.info(f"{metric_name}图表已生成: {chart_path}")
  623. else:
  624. self.logger.warning(f"{metric_name}图表生成失败")
  625. except Exception as e:
  626. self.logger.error(f"生成{metric_name}图表失败: {str(e)}", exc_info=True)
  627. # TTC (time to collision)
  628. def _cal_TTC(self, dist, vrel_projection_in_dist):
  629. if vrel_projection_in_dist == 0:
  630. return math.inf
  631. TTC = dist / vrel_projection_in_dist
  632. return TTC
  633. def _cal_MTTC(self, dist, vrel_projection_in_dist, arel_projection_in_dist):
  634. MTTC = math.nan
  635. if arel_projection_in_dist != 0:
  636. tmp = vrel_projection_in_dist ** 2 + 2 * arel_projection_in_dist * dist
  637. if tmp < 0:
  638. return math.nan
  639. t1 = (-1 * vrel_projection_in_dist - math.sqrt(tmp)) / arel_projection_in_dist
  640. t2 = (-1 * vrel_projection_in_dist + math.sqrt(tmp)) / arel_projection_in_dist
  641. if t1 > 0 and t2 > 0:
  642. if t1 >= t2:
  643. MTTC = t2
  644. elif t1 < t2:
  645. MTTC = t1
  646. elif t1 > 0 and t2 <= 0:
  647. MTTC = t1
  648. elif t1 <= 0 and t2 > 0:
  649. MTTC = t2
  650. if arel_projection_in_dist == 0 and vrel_projection_in_dist > 0:
  651. MTTC = dist / vrel_projection_in_dist
  652. return MTTC
  653. # THW (time headway)
  654. def _cal_THW(self, dist, v_ego_projection_in_dist):
  655. if not v_ego_projection_in_dist:
  656. THW = None
  657. else:
  658. THW = dist / v_ego_projection_in_dist
  659. return THW
  660. # TLC (time to line crossing)
  661. def _cal_TLC(self, ego_v, ego_yaw, laneOffset):
  662. TLC = laneOffset/ego_v/np.sin(ego_yaw) if ((ego_v != 0) and (np.sin(ego_yaw) != 0)) else 10.0
  663. if TLC < 0:
  664. TLC = None
  665. return TLC
  666. def _cal_TTB(self, x_relative_start_dist, relative_v, ego_decel_max):
  667. if len(x_relative_start_dist):
  668. return None
  669. if (ego_decel_max == 0) or (relative_v == 0):
  670. return self.calculated_value["TTB"]
  671. else:
  672. x_relative_start_dist0 = x_relative_start_dist.tolist()[0]
  673. TTB = (x_relative_start_dist0 + relative_v * relative_v/2/ego_decel_max)/relative_v
  674. return TTB
  675. def _cal_TM(self, x_relative_start_dist, v2, a2, v1, a1):
  676. if len(x_relative_start_dist):
  677. return None
  678. if (a2 == 0) or (v1 == 0):
  679. return self.calculated_value["TM"]
  680. if a1 == 0:
  681. return None
  682. x_relative_start_dist0 = x_relative_start_dist.tolist()[0]
  683. TM = (x_relative_start_dist0 + v2**2/(2*a2) - v1**2/(2*a1)) / v1
  684. return TM
  685. def velocity(self, v_x, v_y):
  686. v = math.sqrt(v_x ** 2 + v_y ** 2) * 3.6
  687. return v
  688. def _cal_longitudinal_safe_dist(self, v_ego_p, v_obj_p, rho, ego_accel_max, ego_decel_min, ego_decel_max):
  689. lon_dist_min = v_ego_p * rho + ego_accel_max * (rho ** 2) / 2 + (v_ego_p + rho * ego_accel_max) ** 2 / (
  690. 2 * ego_decel_min) - v_obj_p ** 2 / (2 * ego_decel_max)
  691. return lon_dist_min
  692. def _cal_lateral_safe_dist(self, lat_dist, v_right, v_left, rho, a_right_lat_brake_min,
  693. a_left_lat_brake_min, a_lat_max):
  694. # 检查除数是否为零
  695. if a_right_lat_brake_min == 0 or a_left_lat_brake_min == 0:
  696. return self._default_value('LatSD') # 返回默认值
  697. v_right_rho = v_right + rho * a_lat_max
  698. v_left_rho = v_left + rho * a_lat_max
  699. dist_min = lat_dist + (
  700. (v_right + v_right_rho) * rho / 2
  701. + v_right_rho**2 / a_right_lat_brake_min / 2
  702. + ((v_left + v_right_rho) * rho / 2)
  703. + v_left_rho**2 / a_left_lat_brake_min / 2
  704. )
  705. return dist_min
  706. def _cal_DTC(self, v_on_dist, a_on_dist, t):
  707. if a_on_dist == 0:
  708. return None
  709. DTC = v_on_dist * t + v_on_dist ** 2 / a_on_dist
  710. return DTC
  711. def _cal_PET(self, lane_posx1, lane_posy1, lane_posx2, lane_posy2, ramp_posx1, ramp_posy1, ramp_posx2, ramp_posy2, ego_posx, ego_posy, obj_posx, obj_posy, lane_width, delta_t, v1, v2, a1, a2):
  712. dist1 = self.horizontal_distance(lane_posx1, lane_posy1, lane_posx2, lane_posy2, ego_posx, ego_posy)
  713. dist2 = self.horizontal_distance(ramp_posx1, ramp_posy1, ramp_posx2, ramp_posy2, obj_posx, obj_posy)
  714. if ((dist1 <= lane_width/2) and (self._is_alone_the_road(lane_posx1, lane_posy1, lane_posx2, lane_posy2, ego_posx, ego_posy))
  715. and (self._is_in_the_road(ramp_posx1, ramp_posy1, ramp_posx2, ramp_posy2, obj_posx, obj_posy))
  716. and (dist2 <= lane_width/2) and (a1 != 0) and (a2 != 0)):
  717. dist_ego = np.sqrt((ego_posx - lane_posx1)**2 + (ego_posy-lane_posy1)**2)
  718. dist_obj = np.sqrt((obj_posx - ramp_posx2)**2 + (obj_posy-ramp_posy2)**2)
  719. PET = (-2*v2 + np.sqrt((4* v2**2)-8*a2*(v2*delta_t - dist_obj)))/ 2/ a2 - (2*v1 + np.sqrt((4* v1**2)-8*a1*dist_ego))/ 2/ a1 + delta_t
  720. return PET
  721. else:
  722. return None
  723. def _cal_PSD(self, x_relative_start_dist, v1, ego_decel_lon_max):
  724. if v1 == 0:
  725. return None
  726. else:
  727. if len(x_relative_start_dist) > 0:
  728. x_relative_start_dist0 = x_relative_start_dist.tolist()[0]
  729. PSD = x_relative_start_dist0 * 2 * ego_decel_lon_max / v1
  730. return PSD
  731. else:
  732. return None
  733. # DRAC (decelerate required avoid collision)
  734. def _cal_DRAC(self, dist, vrel_projection_in_dist, len1, len2, width1, width2, o_x1, o_x2):
  735. dist_length = dist - (len2 / 2 - o_x2 + len1 / 2 + o_x1) # 4.671
  736. if dist_length < 0:
  737. dist_width = dist - (width2 / 2 + width1 / 2)
  738. if dist_width < 0:
  739. return math.inf
  740. else:
  741. d = dist_width
  742. else:
  743. d = dist_length
  744. DRAC = vrel_projection_in_dist ** 2 / (2 * d)
  745. return DRAC
  746. # BTN (brake threat number)
  747. def _cal_BTN_new(self, lon_a1, lon_a, lon_d, lon_v, ego_decel_lon_max):
  748. BTN = (lon_a1 + lon_a - lon_v ** 2 / (2 * lon_d)) / ego_decel_lon_max # max_ay为此车可实现的最大纵向加速度,目前为本次实例里的最大值
  749. return BTN
  750. # STN (steer threat number)
  751. def _cal_STN_new(self, ttc, lat_a1, lat_a, lat_d, lat_v, ego_decel_lat_max, width1, width2):
  752. STN = (lat_a1 + lat_a + 2 / ttc ** 2 * (lat_d + abs(ego_decel_lat_max * lat_v) * (
  753. width1 + width2) / 2 + abs(lat_v * ttc))) / ego_decel_lat_max
  754. return STN
  755. # BTN (brake threat number)
  756. def cal_BTN(self, a_y1, ay, dy, vy, max_ay):
  757. BTN = (a_y1 + ay - vy ** 2 / (2 * dy)) / max_ay # max_ay为此车可实现的最大纵向加速度,目前为本次实例里的最大值
  758. return BTN
  759. # STN (steer threat number)
  760. def cal_STN(self, ttc, a_x1, ax, dx, vx, max_ax, width1, width2):
  761. STN = (a_x1 + ax + 2 / ttc ** 2 * (dx + np.sign(max_ax * vx) * (width1 + width2) / 2 + vx * ttc)) / max_ax
  762. return STN
  763. # 追尾碰撞风险
  764. def _normal_distribution(self, x):
  765. mean = 1.32
  766. std_dev = 0.26
  767. return (1 / (math.sqrt(std_dev * 2 * math.pi))) * math.exp(-0.5 * (x - mean) ** 2 / std_dev)
  768. def continuous_group(self, df):
  769. time_list = df['simTime'].values.tolist()
  770. frame_list = df['simFrame'].values.tolist()
  771. group_time = []
  772. group_frame = []
  773. sub_group_time = []
  774. sub_group_frame = []
  775. for i in range(len(frame_list)):
  776. if not sub_group_time or frame_list[i] - frame_list[i - 1] <= 1:
  777. sub_group_time.append(time_list[i])
  778. sub_group_frame.append(frame_list[i])
  779. else:
  780. group_time.append(sub_group_time)
  781. group_frame.append(sub_group_frame)
  782. sub_group_time = [time_list[i]]
  783. sub_group_frame = [frame_list[i]]
  784. group_time.append(sub_group_time)
  785. group_frame.append(sub_group_frame)
  786. group_time = [g for g in group_time if len(g) >= 2]
  787. group_frame = [g for g in group_frame if len(g) >= 2]
  788. # 输出图表值
  789. time = [[g[0], g[-1]] for g in group_time]
  790. frame = [[g[0], g[-1]] for g in group_frame]
  791. unfunc_time_df = pd.DataFrame(time, columns=['start_time', 'end_time'])
  792. unfunc_frame_df = pd.DataFrame(frame, columns=['start_frame', 'end_frame'])
  793. unfunc_df = pd.concat([unfunc_time_df, unfunc_frame_df], axis=1)
  794. return unfunc_df
  795. # 统计最危险的指标
  796. def _safe_statistic_most_dangerous(self):
  797. min_list = ['TTC', 'MTTC', 'THW', 'TLC', 'TTB', 'LonSD', 'LatSD', 'TM', 'PET', 'PSD']
  798. max_list = ['DTC', 'BTN', 'collisionRisk', 'collisionSeverity']
  799. result = {}
  800. for metric in min_list:
  801. if metric in self.metric_list:
  802. if metric in self.df.columns:
  803. val = self.df[metric].min()
  804. result[metric] = float(val) if pd.notnull(val) else self._default_value(metric)
  805. else:
  806. result[metric] = self._default_value(metric)
  807. for metric in max_list:
  808. if metric in self.metric_list:
  809. if metric in self.df.columns:
  810. val = self.df[metric].max()
  811. result[metric] = float(val) if pd.notnull(val) else self._default_value(metric)
  812. else:
  813. result[metric] = self._default_value(metric)
  814. return result
  815. def _safe_no_obj_statistic(self):
  816. # 仅有自车时的默认值
  817. result = {metric: self._default_value(metric) for metric in self.metric_list}
  818. return result
  819. def _default_value(self, metric):
  820. # 统一默认值
  821. defaults = {
  822. 'TTC': 10.0,
  823. 'MTTC': 4.2,
  824. 'THW': 2.1,
  825. 'TLC': 10.0,
  826. 'TTB': 10.0,
  827. 'TM': 10.0,
  828. 'DTC': 10.0,
  829. 'PET': 10.0,
  830. 'PSD': 10.0,
  831. 'LonSD': 10.0,
  832. 'LatSD': 2.0,
  833. 'BTN': 1.0,
  834. 'collisionRisk': 0.0,
  835. 'collisionSeverity': 0.0
  836. }
  837. return defaults.get(metric, None)
  838. def report_statistic(self):
  839. if len(self.obj_id_list) == 1:
  840. safety_result = self._safe_no_obj_statistic()
  841. else:
  842. safety_result = self._safe_statistic_most_dangerous()
  843. evaluator = Score(self.data_processed.safety_config)
  844. result = evaluator.evaluate(safety_result)
  845. print("\n[安全性表现及得分情况]")
  846. return result
  847. def get_ttc_value(self) -> float:
  848. if self.empty_flag or self.df_safe is None:
  849. return self._default_value('TTC')
  850. ttc_values = self.df_safe['TTC'].dropna()
  851. ttc_value = float(ttc_values.min()) if not ttc_values.empty else self._default_value('TTC')
  852. # 收集TTC数据
  853. if not ttc_values.empty:
  854. self.ttc_data = []
  855. for time, frame, ttc in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['TTC']):
  856. if pd.notnull(ttc):
  857. self.ttc_data.append({'simTime': time, 'simFrame': frame, 'TTC': ttc})
  858. return ttc_value
  859. def get_mttc_value(self) -> float:
  860. if self.empty_flag or self.df_safe is None:
  861. return self._default_value('MTTC')
  862. mttc_values = self.df_safe['MTTC'].dropna()
  863. mttc_value = float(mttc_values.min()) if not mttc_values.empty else self._default_value('MTTC')
  864. # 收集MTTC数据
  865. if not mttc_values.empty:
  866. self.mttc_data = []
  867. for time, frame, mttc in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['MTTC']):
  868. if pd.notnull(mttc):
  869. self.mttc_data.append({'simTime': time, 'simFrame': frame, 'MTTC': mttc})
  870. return mttc_value
  871. def get_thw_value(self) -> float:
  872. if self.empty_flag or self.df_safe is None:
  873. return self._default_value('THW')
  874. thw_values = self.df_safe['THW'].dropna()
  875. thw_value = float(thw_values.min()) if not thw_values.empty else self._default_value('THW')
  876. # 收集THW数据
  877. if not thw_values.empty:
  878. self.thw_data = []
  879. for time, frame, thw in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['THW']):
  880. if pd.notnull(thw):
  881. self.thw_data.append({'simTime': time, 'simFrame': frame, 'THW': thw})
  882. return thw_value
  883. def get_tlc_value(self) -> float:
  884. if self.empty_flag or self.df_safe is None:
  885. return self._default_value('TLC')
  886. tlc_values = self.df_safe['TLC'].dropna()
  887. tlc_value = float(tlc_values.min()) if not tlc_values.empty else self._default_value('TLC')
  888. # 收集TLC数据
  889. if not tlc_values.empty:
  890. self.tlc_data = []
  891. for time, frame, tlc in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['TLC']):
  892. if pd.notnull(tlc):
  893. self.tlc_data.append({'simTime': time, 'simFrame': frame, 'TLC': tlc})
  894. return tlc_value
  895. def get_ttb_value(self) -> float:
  896. if self.empty_flag or self.df_safe is None:
  897. return self._default_value('TTB')
  898. ttb_values = self.df_safe['TTB'].dropna()
  899. ttb_value = float(ttb_values.min()) if not ttb_values.empty else self._default_value('TTB')
  900. # 收集TTB数据
  901. if not ttb_values.empty:
  902. self.ttb_data = []
  903. for time, frame, ttb in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['TTB']):
  904. if pd.notnull(ttb):
  905. self.ttb_data.append({'simTime': time, 'simFrame': frame, 'TTB': ttb})
  906. return ttb_value
  907. def get_tm_value(self) -> float:
  908. if self.empty_flag or self.df_safe is None:
  909. return self._default_value('TM')
  910. tm_values = self.df_safe['TM'].dropna()
  911. tm_value = float(tm_values.min()) if not tm_values.empty else self._default_value('TM')
  912. # 收集TM数据
  913. if not tm_values.empty:
  914. self.tm_data = []
  915. for time, frame, tm in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['TM']):
  916. if pd.notnull(tm):
  917. self.tm_data.append({'simTime': time, 'simFrame': frame, 'TM': tm})
  918. return tm_value
  919. def get_dtc_value(self) -> float:
  920. if self.empty_flag or self.df_safe is None:
  921. return self._default_value('DTC')
  922. dtc_values = self.df_safe['DTC'].dropna()
  923. return float(dtc_values.min()) if not dtc_values.empty else self._default_value('DTC')
  924. def get_pet_value(self) -> float:
  925. if self.empty_flag or self.df_safe is None:
  926. return self._default_value('PET')
  927. pet_values = self.df_safe['PET'].dropna()
  928. return float(pet_values.min()) if not pet_values.empty else self._default_value('PET')
  929. def get_psd_value(self) -> float:
  930. if self.empty_flag or self.df_safe is None:
  931. return self._default_value('PSD')
  932. psd_values = self.df_safe['PSD'].dropna()
  933. return float(psd_values.min()) if not psd_values.empty else self._default_value('PSD')
  934. def get_lonsd_value(self) -> float:
  935. if self.empty_flag or self.df_safe is None:
  936. return self._default_value('LonSD')
  937. lonsd_values = self.df_safe['LonSD'].dropna()
  938. lonsd_value = float(lonsd_values.mean()) if not lonsd_values.empty else self._default_value('LonSD')
  939. # 收集LonSD数据
  940. if not lonsd_values.empty:
  941. self.lonsd_data = []
  942. for time, frame, lonsd in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['LonSD']):
  943. if pd.notnull(lonsd):
  944. self.lonsd_data.append({'simTime': time, 'simFrame': frame, 'LonSD': lonsd})
  945. return lonsd_value
  946. def get_latsd_value(self) -> float:
  947. if self.empty_flag or self.df_safe is None:
  948. return self._default_value('LatSD')
  949. latsd_values = self.df_safe['LatSD'].dropna()
  950. # 使用最小值而非平均值,与safety1.py保持一致
  951. latsd_value = float(latsd_values.min()) if not latsd_values.empty else self._default_value('LatSD')
  952. # 收集LatSD数据
  953. if not latsd_values.empty:
  954. self.latsd_data = []
  955. for time, frame, latsd in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['LatSD']):
  956. if pd.notnull(latsd):
  957. self.latsd_data.append({'simTime': time, 'simFrame': frame, 'LatSD': latsd})
  958. return latsd_value
  959. def get_btn_value(self) -> float:
  960. if self.empty_flag or self.df_safe is None:
  961. return self._default_value('BTN')
  962. btn_values = self.df_safe['BTN'].dropna()
  963. btn_value = float(btn_values.max()) if not btn_values.empty else self._default_value('BTN')
  964. # 收集BTN数据
  965. if not btn_values.empty:
  966. self.btn_data = []
  967. for time, frame, btn in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['BTN']):
  968. if pd.notnull(btn):
  969. self.btn_data.append({'simTime': time, 'simFrame': frame, 'BTN': btn})
  970. return btn_value
  971. def get_collision_risk_value(self) -> float:
  972. if self.empty_flag or self.df_safe is None:
  973. return self._default_value('collisionRisk')
  974. risk_values = self.df_safe['collisionRisk'].dropna()
  975. risk_value = float(risk_values.max()) if not risk_values.empty else self._default_value('collisionRisk')
  976. # 收集碰撞风险数据
  977. if not risk_values.empty:
  978. self.collision_risk_data = []
  979. for time, frame, risk in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['collisionRisk']):
  980. if pd.notnull(risk):
  981. self.collision_risk_data.append({'simTime': time, 'simFrame': frame, 'collisionRisk': risk})
  982. return risk_value
  983. def get_collision_severity_value(self) -> float:
  984. if self.empty_flag or self.df_safe is None:
  985. return self._default_value('collisionSeverity')
  986. severity_values = self.df_safe['collisionSeverity'].dropna()
  987. severity_value = float(severity_values.max()) if not severity_values.empty else self._default_value('collisionSeverity')
  988. # 收集碰撞严重性数据
  989. if not severity_values.empty:
  990. self.collision_severity_data = []
  991. for time, frame, severity in zip(self.df_safe['simTime'], self.df_safe['simFrame'], self.df_safe['collisionSeverity']):
  992. if pd.notnull(severity):
  993. self.collision_severity_data.append({'simTime': time, 'simFrame': frame, 'collisionSeverity': severity})
  994. return severity_value