data_process.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. ##################################################################
  4. #
  5. # Copyright (c) 2024 CICV, Inc. All Rights Reserved
  6. #
  7. ##################################################################
  8. """
  9. @Authors: zhanghaiwen(zhanghaiwen@china-icv.cn)
  10. @Data: 2024/10/17
  11. @Last Modified: 2024/10/17
  12. @Summary: Evaluation functions
  13. """
  14. import os
  15. import numpy as np
  16. import pandas as pd
  17. import yaml
  18. from modules.lib.log_manager import LogManager
  19. # from lib import log # 确保这个路径是正确的,或者调整它
  20. # logger = None # 初始化为 None
  21. class DataPreprocessing:
  22. def __init__(self, data_path, config_path):
  23. # Initialize paths and data containers
  24. # self.logger = log.get_logger()
  25. self.data_path = data_path
  26. self.case_name = os.path.basename(os.path.normpath(data_path))
  27. self.config_path = config_path
  28. # Initialize DataFrames
  29. self.object_df = pd.DataFrame()
  30. self.driver_ctrl_df = pd.DataFrame()
  31. self.vehicle_sys_df = pd.DataFrame()
  32. self.ego_data_df = pd.DataFrame()
  33. # Environment data
  34. self.lane_info_df = pd.DataFrame()
  35. self.road_mark_df = pd.DataFrame()
  36. self.road_pos_df = pd.DataFrame()
  37. self.traffic_light_df = pd.DataFrame()
  38. self.traffic_signal_df = pd.DataFrame()
  39. self.vehicle_config = {}
  40. self.safety_config = {}
  41. self.comfort_config = {}
  42. self.efficient_config = {}
  43. self.function_config = {}
  44. self.traffic_config = {}
  45. # Initialize data for later processing
  46. self.obj_data = {}
  47. self.ego_data = {}
  48. self.obj_id_list = []
  49. # Data quality level
  50. self.data_quality_level = 15
  51. # Process mode and prepare report information
  52. self._process_mode()
  53. self._get_yaml_config()
  54. self.report_info = self._get_report_info(self.obj_data.get(1, pd.DataFrame()))
  55. def _process_mode(self):
  56. """Handle different processing modes."""
  57. self._real_process_object_df()
  58. def _get_yaml_config(self):
  59. with open(self.config_path, 'r') as f:
  60. full_config = yaml.safe_load(f)
  61. modules = ["vehicle", "T_threshold", "safety", "comfort", "efficient", "function", "traffic"]
  62. # 1. 初始化 vehicle_config(不涉及 T_threshold 合并)
  63. self.vehicle_config = full_config[modules[0]]
  64. # 2. 定义 T_threshold_config(封装为字典)
  65. T_threshold_config = {"T_threshold": full_config[modules[1]]}
  66. # 3. 统一处理需要合并 T_threshold 的模块
  67. # 3.1 safety_config
  68. self.safety_config = {"safety": full_config[modules[2]]}
  69. self.safety_config.update(T_threshold_config)
  70. # 3.2 comfort_config
  71. self.comfort_config = {"comfort": full_config[modules[3]]}
  72. self.comfort_config.update(T_threshold_config)
  73. # 3.3 efficient_config
  74. self.efficient_config = {"efficient": full_config[modules[4]]}
  75. self.efficient_config.update(T_threshold_config)
  76. # 3.4 function_config
  77. self.function_config = {"function": full_config[modules[5]]}
  78. self.function_config.update(T_threshold_config)
  79. # 3.5 traffic_config
  80. self.traffic_config = {"traffic": full_config[modules[6]]}
  81. self.traffic_config.update(T_threshold_config)
  82. @staticmethod
  83. def cal_velocity(lat_v, lon_v):
  84. """Calculate resultant velocity from lateral and longitudinal components."""
  85. return np.sqrt(lat_v ** 2 + lon_v ** 2)
  86. def _real_process_object_df(self):
  87. """Process the object DataFrame."""
  88. try:
  89. # 读取 CSV 文件
  90. merged_csv_path = os.path.join(self.data_path, "merged_ObjState.csv")
  91. # 检查文件是否存在
  92. if not os.path.exists(merged_csv_path):
  93. logger = LogManager().get_logger()
  94. logger.error(f"文件不存在: {merged_csv_path}")
  95. raise FileNotFoundError(f"文件不存在: {merged_csv_path}")
  96. self.object_df = pd.read_csv(
  97. merged_csv_path,
  98. dtype={"simTime": float},
  99. engine="python",
  100. on_bad_lines="skip", # 自动跳过异常行
  101. na_values=["", "NA", "null", "NaN"] # 明确处理缺失值
  102. ).drop_duplicates(subset=["simTime", "simFrame", "playerId"])
  103. self.object_df.columns = [col.replace("+AF8-", "_") for col in self.object_df.columns]
  104. data = self.object_df.copy()
  105. # 使用向量化操作计算速度和加速度,提高性能
  106. data["lat_v"] = data["speedY"] * 1
  107. data["lon_v"] = data["speedX"] * 1
  108. # 使用向量化操作代替 apply
  109. data["v"] = np.sqrt(data["lat_v"] ** 2 + data["lon_v"] ** 2)
  110. # 计算加速度分量
  111. data["lat_acc"] = data["accelY"] * 1
  112. data["lon_acc"] = data["accelX"] * 1
  113. # 使用向量化操作代替 apply
  114. data["accel"] = np.sqrt(data["lat_acc"] ** 2 + data["lon_acc"] ** 2)
  115. # Drop rows with missing 'type' and reset index
  116. data = data.dropna(subset=["type"])
  117. data.reset_index(drop=True, inplace=True)
  118. self.object_df = data.copy()
  119. # Calculate respective parameters for each object
  120. for obj_id, obj_data in data.groupby("playerId"):
  121. self.obj_data[obj_id] = self._calculate_object_parameters(obj_data)
  122. # Get object id list
  123. EGO_PLAYER_ID = 1
  124. self.obj_id_list = list(self.obj_data.keys())
  125. self.ego_data = self.obj_data[EGO_PLAYER_ID]
  126. # 添加这一行:处理自车数据,进行坐标系转换
  127. self.ego_data = self.process_ego_data(self.ego_data)
  128. except Exception as e:
  129. logger = LogManager().get_logger()
  130. logger.error(f"处理对象数据帧时出错: {e}", exc_info=True)
  131. raise
  132. def _calculate_object_parameters(self, obj_data):
  133. """Calculate additional parameters for a single object."""
  134. obj_data = obj_data.copy()
  135. # 确保数据按时间排序
  136. obj_data = obj_data.sort_values(by="simTime").reset_index(drop=True)
  137. obj_data["time_diff"] = obj_data["simTime"].diff()
  138. # 处理可能的零时间差
  139. zero_time_diff = obj_data["time_diff"] == 0
  140. if zero_time_diff.any():
  141. logger = LogManager().get_logger()
  142. logger.warning(f"检测到零时间差: {sum(zero_time_diff)} 行")
  143. # 将零时间差替换为最小非零时间差或一个小的默认值
  144. min_non_zero = obj_data.loc[~zero_time_diff, "time_diff"].min() if (~zero_time_diff).any() else 0.01
  145. obj_data.loc[zero_time_diff, "time_diff"] = min_non_zero
  146. obj_data["lat_acc_diff"] = obj_data["lat_acc"].diff()
  147. obj_data["lon_acc_diff"] = obj_data["lon_acc"].diff()
  148. obj_data["yawrate_diff"] = obj_data["speedH"].diff()
  149. obj_data["lat_acc_roc"] = (
  150. obj_data["lat_acc_diff"] / obj_data["time_diff"]
  151. ).replace([np.inf, -np.inf], [9999, -9999])
  152. obj_data["lon_acc_roc"] = (
  153. obj_data["lon_acc_diff"] / obj_data["time_diff"]
  154. ).replace([np.inf, -np.inf], [9999, -9999])
  155. obj_data["accelH"] = (
  156. obj_data["yawrate_diff"] / obj_data["time_diff"]
  157. ).replace([np.inf, -np.inf], [9999, -9999])
  158. return obj_data
  159. def _get_driver_ctrl_data(self, df):
  160. """
  161. Process and get driver control information.
  162. Args:
  163. df: A DataFrame containing driver control data.
  164. Returns:
  165. A dictionary of driver control info.
  166. """
  167. driver_ctrl_data = {
  168. "time_list": df["simTime"].round(2).tolist(),
  169. "frame_list": df["simFrame"].tolist(),
  170. "brakePedal_list": (
  171. (df["brakePedal"] * 100).tolist()
  172. if df["brakePedal"].max() < 1
  173. else df["brakePedal"].tolist()
  174. ),
  175. "throttlePedal_list": (
  176. (df["throttlePedal"] * 100).tolist()
  177. if df["throttlePedal"].max() < 1
  178. else df["throttlePedal"].tolist()
  179. ),
  180. "steeringWheel_list": df["steeringWheel"].tolist(),
  181. }
  182. return driver_ctrl_data
  183. def _get_report_info(self, df):
  184. """Extract report information from the DataFrame."""
  185. mileage = self._mileage_cal(df)
  186. duration = self._duration_cal(df)
  187. return {"mileage": mileage, "duration": duration}
  188. def _mileage_cal(self, df):
  189. """Calculate mileage based on the driving data."""
  190. if len(df) < 2:
  191. return 0.0 # 数据不足,无法计算里程
  192. if df["travelDist"].nunique() == 1:
  193. # 创建临时DataFrame进行计算,避免修改原始数据
  194. temp_df = df.copy()
  195. temp_df["time_diff"] = temp_df["simTime"].diff().fillna(0)
  196. temp_df["avg_speed"] = (temp_df["v"] + temp_df["v"].shift()).fillna(0) / 2
  197. temp_df["distance_increment"] = temp_df["avg_speed"] * temp_df["time_diff"] / 3.6
  198. temp_df["travelDist"] = temp_df["distance_increment"].cumsum().fillna(0)
  199. mileage = round(temp_df["travelDist"].iloc[-1] - temp_df["travelDist"].iloc[0], 2)
  200. return mileage
  201. else:
  202. # 如果travelDist已经有多个值,直接计算最大值和最小值的差
  203. return round(df["travelDist"].max() - df["travelDist"].min(), 2)
  204. return 0.0 # Return 0 if travelDist is not valid
  205. def _duration_cal(self, df):
  206. """Calculate duration of the driving data."""
  207. return df["simTime"].iloc[-1] - df["simTime"].iloc[0]
  208. def process_ego_data(self, ego_data):
  209. """处理自车数据:将东北天(ENU)坐标系下的速度/加速度转换为车辆坐标系(考虑yaw, pitch, roll)"""
  210. '''
  211. 原字段 新字段名 描述
  212. a_x_body lon_acc_vehicle 车辆坐标系下的纵向加速度
  213. a_y_body lat_acc_vehicle 车辆坐标系下的横向加速度
  214. a_z_body acc_z_vehicle 车辆坐标系下的垂向加速度
  215. v_x_body lon_v_vehicle 车辆坐标系下的纵向速度
  216. v_y_body lat_v_vehicle 车辆坐标系下的横向速度
  217. v_z_body vel_z_vehicle 车辆坐标系下的垂向速度
  218. posH heading_rad 航向角(弧度)
  219. pitch_rad pitch_rad 俯仰角(弧度)
  220. roll_rad roll_rad 横滚角(弧度)
  221. '''
  222. logger = LogManager().get_logger()
  223. if ego_data is None or len(ego_data) == 0:
  224. logger.warning("自车数据为空,无法进行坐标系转换")
  225. return ego_data
  226. ego_data = ego_data.copy()
  227. for col in ['speedZ', 'accelZ']:
  228. if col not in ego_data.columns:
  229. ego_data[col] = 0.0
  230. logger.warning(f"自车数据中缺少列 '{col}',已将其填充为 0.0")
  231. # 角度转为弧度(修正 posH 表示正北为 0° => 车辆朝正东为 0°)
  232. ego_data['yaw_rad'] = np.deg2rad(90 - ego_data['posH'])
  233. ego_data['pitch_rad'] = np.deg2rad(ego_data.get('pitch', 0))
  234. ego_data['roll_rad'] = np.deg2rad(ego_data.get('roll', 0))
  235. # 预计算三角函数(向量化)
  236. cy = np.cos(ego_data['yaw_rad'])
  237. sy = np.sin(ego_data['yaw_rad'])
  238. cp = np.cos(ego_data['pitch_rad'])
  239. sp = np.sin(ego_data['pitch_rad'])
  240. cr = np.cos(ego_data['roll_rad'])
  241. sr = np.sin(ego_data['roll_rad'])
  242. # === 加速度(ENU → 车辆坐标系) ===
  243. ego_data['lon_acc_vehicle'] = (ego_data['accelX'] * (cy * cp) +
  244. ego_data['accelY'] * (cy * sp * sr - sy * cr) +
  245. ego_data['accelZ'] * (cy * sp * cr + sy * sr))
  246. ego_data['lat_acc_vehicle'] = (ego_data['accelX'] * (sy * cp) +
  247. ego_data['accelY'] * (sy * sp * sr + cy * cr) +
  248. ego_data['accelZ'] * (sy * sp * cr - cy * sr))
  249. ego_data['acc_z_vehicle'] = (ego_data['accelX'] * (-sp) +
  250. ego_data['accelY'] * (cp * sr) +
  251. ego_data['accelZ'] * (cp * cr))
  252. # === 速度(ENU → 车辆坐标系) ===
  253. ego_data['lon_v_vehicle'] = (ego_data['speedX'] * (cy * cp) +
  254. ego_data['speedY'] * (cy * sp * sr - sy * cr) +
  255. ego_data['speedZ'] * (cy * sp * cr + sy * sr))
  256. ego_data['lat_v_vehicle'] = (ego_data['speedX'] * (sy * cp) +
  257. ego_data['speedY'] * (sy * sp * sr + cy * cr) +
  258. ego_data['speedZ'] * (sy * sp * cr - cy * sr))
  259. ego_data['vel_z_vehicle'] = (ego_data['speedX'] * (-sp) +
  260. ego_data['speedY'] * (cp * sr) +
  261. ego_data['speedZ'] * (cp * cr))
  262. logger.info("完成车辆坐标系转换(考虑yaw/pitch/roll)")
  263. return ego_data