diff --git a/params/filter_params.json b/params/filter_params.json index 462ce79..cf9652c 100644 --- a/params/filter_params.json +++ b/params/filter_params.json @@ -1,23 +1,29 @@ { - "robot_zero_velocity_trashold": [ - 0.008 + "act_vel_min": [ + 1800.0 ], - "actuator_zero_velocity_trashold": [ - 100.0 + "act_vel_close": [ + 1700.0 ], - "actuator_finishing_velocity": [ - 2000.0 + "act_vel_thresh": [ + 10.0 ], - "actuator_finishing_velocity_trashold": [ + "act_vel_negative": [ 200.0 ], - "actuator_current_trashold": [ - 0.5 + "rob_vel_thresh": [ + 4.0 ], - "ROI_start": [ - 8.0 + "act_force_close": [ + 700.0 ], - "ROI_finish": [ - 43.0 + "act_force_weld": [ + 1300.0 + ], + "force_increase": [ + 100.0 + ], + "force_decrease": [ + 100.0 ] } \ No newline at end of file diff --git a/src/gui/settings_window.py b/src/gui/settings_window.py index 597ded3..e154ec9 100644 --- a/src/gui/settings_window.py +++ b/src/gui/settings_window.py @@ -241,13 +241,15 @@ class OperatorSettings(settingsWindow): class FilterSettings(settingsWindow): def __init__(self, path, name, upd_func): assosiated_names = { - "robot_zero_velocity_trashold" : "Robot zero speed filter +-m/sec", - "actuator_zero_velocity_trashold" : "Actuator zero speed filter +-m/sec", - "actuator_finishing_velocity" : "Actuator finishing speed m/sec", - "actuator_finishing_velocity_trashold" : "Actuator finishing speed filter m/sec", - "actuator_current_trashold": "Actuator force filter N", - "ROI_start" : "Start time, sec", - "ROI_finish" : "End time, sec" + "act_vel_min" : "Minimum for ME speed in Closing mm/sec", + "act_vel_close" : "Maximum for ME speed in Squeeze mm/sec", + "act_vel_thresh" : "ME zero speed threshold mm/sec", + "act_vel_negative" : "ME Relief speed mm/sec", + "rob_vel_thresh": "Robot zero speed threshold mm/sec", + "act_force_close" : "Maximum ME force in Closing, N", + "act_force_weld" : "Minimum ME force in Welding, N", + "force_increase": "ME force rising speed in Squeeze", + "force_decrease":"ME force falling speed in Relief" } super().__init__(path, name, upd_func, assosiated_names) self._num_points.setVisible(False) diff --git a/src/performance/roboter.py b/src/performance/roboter.py index 1df79b9..728b2fa 100644 --- a/src/performance/roboter.py +++ b/src/performance/roboter.py @@ -173,63 +173,104 @@ class TraceStageDetector(BaseTraceStageDetector): def __init__(self, parent:TraceProcessor = None): super().__init__(parent) - def detect_stages(self, df: pd.DataFrame) -> list: - df = df.sort_index() - stage_markers = [] - timestamps = df['time'].to_list() # времена (в секундах) как список - - for i, time in enumerate(timestamps): - mark = self._mark_timestamp(df, i, time) - if mark: stage_markers.append(mark) - - # Объединяем подряд идущие одинаковые метки в единые этапы. - stages = self._merge_marks_to_stages(stage_markers, timestamps) - return stages - def _mark_timestamp(self, df:pd.DataFrame, index:int, time:float) -> Union[str, None]: # Не интересные значения помечаем как "unknown" if (time < self._parent._settings.filter["ROI_start"][0] or time > self._parent._settings.filter["ROI_finish"][0]): return "unknown" - rob_vel = df.loc[index, "CartVel_Act"] - act_vel = df.loc[index, "DriveMotorVel_Act7"] - act_curr = df.loc[index, "DriveMotorCurr_Act7"] + @staticmethod + def is_closing(rob_vel:float, + act_vel:float, + act_force:float, + th:dict) -> bool: + # act_vel > min, rob_vel ~ 0, act_force ниже порога + return act_vel > th['act_vel_min'] and abs(rob_vel) < th['rob_vel_thresh'] and act_force < th['act_force_close'] - if abs(rob_vel) > self._parent._settings.filter["robot_zero_velocity_trashold"][0]: - return "Oncomming" + @staticmethod + def is_squeeze(rob_vel:float, + act_vel:float, + act_force:float, + force_rate:float, + th:dict) -> bool: + # rob_vel ~ 0, act_vel меньше, чем в closing, и резкий рост act_force + return abs(rob_vel) < th['rob_vel_thresh'] and act_vel < th['act_vel_close'] and force_rate > th['force_increase'] - if (abs(act_vel) < self._parent._settings.filter["actuator_zero_velocity_trashold"][0] and - abs(act_curr) > self._parent._settings.filter["actuator_current_trashold"][0]): - return "Squeeze&Welding&Relief" + @staticmethod + def is_welding(rob_vel:float, + act_vel:float, + act_force:float, + th:dict) -> bool: + # обе скорости ≈ 0, act_force выше порога сварки + return abs(rob_vel) < th['rob_vel_thresh'] and abs(act_vel) < th['act_vel_thresh'] and act_force > th['act_force_weld'] - if self._parent._settings.filter["actuator_finishing_velocity_trashold"][0] > abs(abs(act_vel) - self._parent._settings.filter["actuator_finishing_velocity"][0]): - return "Closing" + @staticmethod + def is_relief(rob_vel:float, + act_vel:float, + act_force:float, + force_rate:float, + th:dict) -> bool: + # резкое падение act_force, отрицательный act_vel, малые rob_vel + return force_rate < -th['force_decrease'] and act_vel < -th['act_vel_negative'] and abs(rob_vel) < th['rob_vel_thresh'] + + def detect_stages(self, df:pd.DataFrame): + timestamps = df['time'].to_list() + n = len(df) + th = {key: item[0] for key, item in self._parent._settings.filter.items()} + # Вычисляем разностную производную силы + act_force = df["DriveMotorTorq_Act7"].values + force_diff = np.diff(act_force, prepend=act_force[0]) - return None - - def _merge_marks_to_stages(self, stage_markers:list, timestamps:list) -> list: - stages = [] - current_stage = stage_markers[0] - start_time = timestamps[0] - - for i in range(1, len(stage_markers)): - if stage_markers[i] != current_stage: - end_time = timestamps[i - 1] - stages.append({ - "stage": current_stage, - "start_time": start_time, - "end_time": end_time - }) - current_stage = stage_markers[i] - start_time = timestamps[i] - - stages.append({ - "stage": current_stage, - "start_time": start_time, - "end_time": timestamps[-1] - }) - return stages + states = [] + current_state = "Oncomming" + state_start = timestamps[0] + + # Проходим по всем записям + for i in range(n): + rob_vel = df.loc[i, "CartVel_Act"] + act_vel = df.loc[i, "DriveMotorVel_Act7"] + act_force_val = df.loc[i, "DriveMotorTorq_Act7"] + force_rate = force_diff[i] + + if current_state == "Oncomming": + if self.is_closing(rob_vel, act_vel, act_force_val, th): + state_end = timestamps[i] + states.append(("Oncomming", state_start, state_end)) + current_state = "Closing" + state_start = timestamps[i] + + elif current_state == "Closing": + if self.is_squeeze(rob_vel, act_vel, act_force_val, force_rate, th): + state_end = timestamps[i] + states.append(("Closing", state_start, state_end)) + current_state = "Squeeze" + state_start = timestamps[i] + + elif current_state == "Squeeze": + if self.is_welding(rob_vel, act_vel, act_force_val, th): + state_end = timestamps[i] + states.append(("Squeeze", state_start, state_end)) + current_state = "Welding" + state_start = timestamps[i] + + elif current_state == "Welding": + if self.is_relief(rob_vel, act_vel, act_force_val, force_rate, th): + state_end = timestamps[i] + states.append(("Welding", state_start, state_end)) + current_state = "Relief" + state_start = timestamps[i] + + elif current_state == "Relief": + # Когда признаки relief отпадают, считаем, что цикл завершён и возвращаемся в Oncomming + if not self.is_relief(rob_vel, act_vel, act_force_val, force_rate, th): + state_end = timestamps[i] + states.append(("Relief", state_start, state_end)) + current_state = "Oncomming" + state_start = timestamps[i] + + # Фиксируем последний сегмент + states.append((current_state, state_start, timestamps[-1])) + return states class TextStageDetector(BaseTextStageDetector): @@ -305,8 +346,9 @@ class TraceProcessor(BaseRawTraceProcessor): def _detect_stages(self, trace_df:pd.DataFrame, text_data:list) -> dict[list]: if trace_df is not None and text_data is not None: trace_stages = self._data_detector.detect_stages(trace_df) - welding_stages = self._text_detector.detect_welding(text_data) - events = self._form_events(trace_stages, welding_stages) + #welding_stages = self._text_detector.detect_welding(text_data) + #events = self._form_events(trace_stages, welding_stages) + events = self.__form_events_2(trace_stages) return events return None @@ -354,6 +396,21 @@ class TraceProcessor(BaseRawTraceProcessor): events[trace_stages[i]['stage']][1].append(trace_stages[i]['end_time']) normalized_events = TraceProcessor._normalize_events(events) return normalized_events + + @staticmethod + def __form_events_2(trace_stages:list): + events = { + "Closing":[[],[]], + "Squeeze":[[],[]], + "Welding":[[],[]], + "Relief":[[],[]], + "Oncomming":[[],[]] + } + for stage in trace_stages: + name, start_t, end_t = stage + events[name][0].append(start_t) + events[name][1].append(end_t) + return events @staticmethod def _rename_df_columns(dataframe: pd.DataFrame) -> pd.DataFrame: diff --git a/trace_samples/teslaSP_VelTCP_KRCIpo.dat b/trace_samples/teslaSP_VelTCP_KRCIpo.dat index 47e2f23..cb61e5c 100644 --- a/trace_samples/teslaSP_VelTCP_KRCIpo.dat +++ b/trace_samples/teslaSP_VelTCP_KRCIpo.dat @@ -86,7 +86,7 @@ DIAEXTENDED #BEGINCHANNELHEADER 200,DriveMotorPos_Act7 201,AxisValAtMMInterface -202, +202,� 210,EXPLIZIT 211,teslaSP_VelTCP_KRCIpo.r64 213,BLOCK @@ -101,7 +101,7 @@ DIAEXTENDED #BEGINCHANNELHEADER 200,DriveMotorVel_Act7 201,AxisValAtMMInterface -202,/s +202,�/s 210,EXPLIZIT 211,teslaSP_VelTCP_KRCIpo.r64 213,BLOCK @@ -124,7 +124,7 @@ DIAEXTENDED 220,3806 221,7 240,0 -241,1.000000e+00 +241,1.000000e+03 #ENDCHANNELHEADER @@ -169,7 +169,7 @@ DIAEXTENDED 220,3806 221,10 240,0 -241,1.000000e-03 +241,1.000000e+00 #ENDCHANNELHEADER