From d2603cec4d3a576cc29276eb318ae3ccff97a28d Mon Sep 17 00:00:00 2001 From: yanganjie Date: Thu, 6 Nov 2025 10:55:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=87=8D=E9=87=8F=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=98=BE=E7=A4=BA=EF=BC=8Chopper=5Fcontroller=20?= =?UTF-8?q?=E6=8F=90=E4=BE=9B=E4=BA=86=E6=96=99=E6=96=97=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + controller/hopper_controller.py | 153 ++++++++++++++++++ controller/main_controller.py | 73 ++------- core/__pycache__/__init__.cpython-39.pyc | Bin 349 -> 0 bytes core/__pycache__/state.cpython-39.pyc | Bin 846 -> 0 bytes core/__pycache__/system.cpython-39.pyc | Bin 4781 -> 0 bytes view/widgets/conveyor_system_widget.py | 43 ++++- view/widgets/hopper_widget.py | 101 ++++++++---- vision/__pycache__/__init__.cpython-39.pyc | Bin 569 -> 0 bytes .../alignment_detector.cpython-39.pyc | Bin 786 -> 0 bytes .../__pycache__/anger_caculate.cpython-39.pyc | Bin 2191 -> 0 bytes .../__pycache__/angle_detector.cpython-39.pyc | Bin 857 -> 0 bytes vision/__pycache__/camera.cpython-39.pyc | Bin 2226 -> 0 bytes vision/__pycache__/detector.cpython-39.pyc | Bin 2603 -> 0 bytes .../overflow_detector.cpython-39.pyc | Bin 1301 -> 0 bytes .../resize_tuili_image_main.cpython-39.pyc | Bin 3925 -> 0 bytes 16 files changed, 278 insertions(+), 94 deletions(-) create mode 100644 controller/hopper_controller.py delete mode 100644 core/__pycache__/__init__.cpython-39.pyc delete mode 100644 core/__pycache__/state.cpython-39.pyc delete mode 100644 core/__pycache__/system.cpython-39.pyc delete mode 100644 vision/__pycache__/__init__.cpython-39.pyc delete mode 100644 vision/__pycache__/alignment_detector.cpython-39.pyc delete mode 100644 vision/__pycache__/anger_caculate.cpython-39.pyc delete mode 100644 vision/__pycache__/angle_detector.cpython-39.pyc delete mode 100644 vision/__pycache__/camera.cpython-39.pyc delete mode 100644 vision/__pycache__/detector.cpython-39.pyc delete mode 100644 vision/__pycache__/overflow_detector.cpython-39.pyc delete mode 100644 vision/__pycache__/resize_tuili_image_main.cpython-39.pyc diff --git a/.gitignore b/.gitignore index 4570eb6..62b0bd5 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ PyQt6_Fluent_Widgets.egg-info/ PySide2_Fluent_Widgets.egg-info/ /hardware/__pycache__ __pycache__ +/core/__pycache__ +/vision/__pycache__ diff --git a/controller/hopper_controller.py b/controller/hopper_controller.py new file mode 100644 index 0000000..029ed4d --- /dev/null +++ b/controller/hopper_controller.py @@ -0,0 +1,153 @@ +from PySide6.QtCore import QTimer, Signal, QObject, Slot +import threading +from hardware.transmitter import TransmitterController +from hardware.relay import RelayController +from view.widgets.hopper_widget import HopperWidget +from view.widgets.conveyor_system_widget import ConveyorSystemWidget + +# 信号类:后台线程向主线程传递数据 +class HopperSignals(QObject): + upper_weight_updated = Signal(int) # 上料斗重量更新信号 + +class HopperController: + def __init__(self, hopper_view:HopperWidget, conveyor_view:ConveyorSystemWidget): + self.hopper_view = hopper_view + self.conveyor_view = conveyor_view # 控制传送带中的上料斗 + + self.signals = HopperSignals() # 信号 + + # 下料斗夹爪测试用例数据 + # 注意:目前只控制 下料斗的夹爪角度变化 + self.angle = 10 # 夹爪当前角度 + self.max_angle = 60 # 夹爪最大张开角度 + self.min_angle = 10 # 夹爪最小张开角度 + self.is_add = True # 角度增加/减小 控制 + self.timer_angle = QTimer() # 角度更新定时器 + self.timer_angle.setInterval(1000) # 1秒更新一次角度 + + # 重量读取定时器 + self.timer_weight = QTimer() # 重量读取定时器 + self.timer_weight.setInterval(2000) # 每2秒读取一次重量 + + # 绑定信号 + self._connect_signals() + + # 开启定时器 + self.timer_angle.start() + self.timer_weight.start() + + def _connect_signals(self): + # 更新上料斗重量 + self.signals.upper_weight_updated.connect(self.onUpdateUpperHopperWeight) + + # 上料斗重量定时读取 + self.timer_weight.timeout.connect(self.handleReadUpperHopperWeight) + + # 下料斗夹爪定时更新 + self.timer_angle.timeout.connect(self.handleLowerClampAngleUpdate) + + # 上料斗 "开"按钮点击 + self.hopper_view.upper_open_btn.clicked.connect(self.onUpperClampOpenBottonClicked) + + # 上料斗 "破拱"按钮 + self.hopper_view.upper_arch_breaking_signal.connect(self.onUpperArchBreaking) + + # 下料斗 "开"按钮点击 + self.hopper_view.lower_open_btn.clicked.connect(self.onLowerClampOpenBottonClicked) + + # 下料斗 "破拱"按钮 + self.hopper_view.lower_arch_breaking_signal.connect(self.onLowerArchBreaking) + + def handleLowerClampAngleUpdate(self): + """处理下料斗夹爪开合""" + # 角度增减逻辑 + if self.is_add: + self.angle += 1 + else: + self.angle -= 1 + + # 边界控制 + if self.angle > self.max_angle: + self.is_add = False + self.angle = self.max_angle + if self.angle <= self.min_angle: + self.is_add = True + self.angle = self.min_angle + + # 更新下料斗夹爪角度 + self.onUpdateLowerClampAngle(self.angle) + + @Slot(int) + def onUpdateUpperHopperWeight(self, weight:int): + "更新上料斗重量" + self.hopper_view.setUpperHopperWeight(weight) + + # 注意:此时需要同步更新传送带中的上料斗的重量 + self.conveyor_view.setConveyorHopperWeight(weight) + + @Slot(int) + def onUpdateLowerHopperWeight(self, weight:int): + "更新下料斗重量" + self.hopper_view.setLowerHopperWeight(weight) + + @Slot() + def handleReadUpperHopperWeight(self): + # 后台读取上料斗重量 + def upper_weight_task(): + loc_tra = TransmitterController(RelayController()) + # 上料斗重量 (目前只有上料斗安装变送器, 可以读取到重量) + upper_weight = loc_tra.read_data(1) + # 发送信号到主线程更新UI + if upper_weight is not None: + self.signals.upper_weight_updated.emit(upper_weight) + + threading.Thread(target=upper_weight_task, daemon=True).start() + + @Slot(float) + def onUpdateLowerClampAngle(self, angle:float): + """更新下料斗夹爪角度""" + self.hopper_view.setLowerHopperOpeningAngle(angle) + + @Slot(float) + def onUpdateUpperClampAngle(self, angle:float): + """更新上料斗夹爪角度""" + self.hopper_view.setUpperHopperClampAngle(angle) + + @Slot() + def onUpperClampOpenBottonClicked(self): + # 上料斗 夹爪 "开"按钮点击 + print("hopper_controller: onUpperClampOpenBottonClicked") + # 测试上料斗夹爪,6秒打开60度 + self.hopper_view.upper_clamp_widget.testAnimation(target_angle=60, duration=6) + + @Slot(bool) + def onUpperArchBreaking(self, status:bool): + """上料斗破拱: status 为True表示 开启破拱, 为False表示 关闭破拱""" + print("hopper_controller: onUpperArchBreaking ", status) + + @Slot(int) + def onUpperHopperStatusChanged(self, status:int): + """上料斗状态改变: status为 0=绿(正常), 1=黄(警告), 2=红(异常) """ + # 料斗中的状态指示器 + self.hopper_view.setUpperHopperStatus(status) + + @Slot(float) + def onUpdateUpperHopperVolume(self, volume: float): + """更新上料斗显示的方量,如: 2.0""" + self.hopper_view.setUpperHopperVolume(volume) + + @Slot() + def onLowerClampOpenBottonClicked(self): + # 下料斗 夹爪 "开"按钮点击 + print("hopper_controller: onLowerClampOpenBottonClicked") + + @Slot(bool) + def onLowerArchBreaking(self, status:bool): + """下料斗破拱: status 为True表示 开启破拱, 为False表示 关闭破拱""" + print("hopper_controller: onLowerArchBreaking ", status) + + @Slot(int) + def onLowerHopperStatusChanged(self, status:int): + """下料斗状态改变: status为 0=绿(正常), 1=黄(警告), 2=红(异常) """ + # 料斗中的状态指示器 + self.hopper_view.setLowerHopperStatus(status) \ No newline at end of file diff --git a/controller/main_controller.py b/controller/main_controller.py index 586def9..e45aeb9 100644 --- a/controller/main_controller.py +++ b/controller/main_controller.py @@ -5,78 +5,17 @@ from hardware import transmitter from view.main_window import MainWindow from .camera_controller import CameraController from .bottom_control_controller import BottomControlController -from hardware.transmitter import TransmitterController -from hardware.relay import RelayController - -# 定义信号类(用于后台线程向 UI主线程传递数据) -class Signals(QObject): - weight_updated = Signal(int) # 传递上料斗重量 +from .hopper_controller import HopperController class MainController: def __init__(self): # 主界面 self.main_window = MainWindow() - # 1. 用于更新下料斗夹爪角度的QTimer - self.timer_angle = QTimer() - self.timer_angle.setInterval(1000) # 1秒触发一次 - self.timer_angle.timeout.connect(self._onTimer2) # 连接到角度更新函数 - self.timer_angle.start() - - # 下料斗夹具角度相关参数 - self.angle = 10 - self.max_angle = 60 - self.min_angle = 10 - self.is_add = True - - # 2. 用于更新重量的 QTimer - self.timer_weight = QTimer() - self.timer_weight.setInterval(2000) # 2秒触发一次 - self.timer_weight.timeout.connect(self._read_weight_in_background) - self.timer_weight.start() - - # 信号实例(用于后台线程传递数据到主线程) - self.signals = Signals() - self.signals.weight_updated.connect(self._update_upper_weight) # 主线程更新UI - # 初始化子界面和控制器 self._initSubViews() self._initSubControllers() - def _read_weight_in_background(self): - """在后台线程中读取重量""" - def weight_task(): - loc_tra = TransmitterController(RelayController()) - # 上料斗重量 (目前只有上料斗安装变送器, 可以读取到重量) - upper_weight = loc_tra.read_data(1) - # 发送信号到主线程更新UI - if upper_weight is not None: - self.signals.weight_updated.emit(upper_weight) - - # 启动后台线程执行重量读取操作 - threading.Thread(target=weight_task, daemon=True).start() - - def _update_upper_weight(self, weight): - """主线程中 更新界面上料斗重量数据""" - self.main_window.hopper_widget.setUpperHopperWeight(weight) - - def _onTimer2(self): - """QTimer触发的 料斗夹爪角度更新""" - if self.is_add: - self.angle += 1 - else: - self.angle -= 1 - - if self.angle > self.max_angle: - self.is_add = False - self.angle = self.max_angle - if self.angle <= self.min_angle: - self.is_add = True - self.angle = 10 - - # 直接更新UI - self.main_window.hopper_widget.setLowerHopperOpeningAngle(self.angle) - def showMainWindow(self): self.main_window.showFullScreen() self.main_window.dispatch_task_widget.set_task_time("task1","15:44 PM") @@ -85,13 +24,23 @@ class MainController: self.main_window.segment_task_widget.set_task_time("task2","17:24 PM") def _initSubControllers(self): + # 右侧视频显示控制模块 self.camera_controller = CameraController( video_view=self.main_window.vibration_video ) + + # 底部按钮控制模块 self.bottom_control_controller = BottomControlController( bottom_control_widget=self.main_window.bottom_control_widget, main_window=self.main_window ) + # 料斗控制模块(包括 夹爪开合、拱等按钮) + self.hopper_controller = HopperController( + hopper_view = self.main_window.hopper_widget, + conveyor_view = self.main_window.conveyor_system_widget + ) + + def _initSubViews(self): pass \ No newline at end of file diff --git a/core/__pycache__/__init__.cpython-39.pyc b/core/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 893971ba73ea89b906fd4847fab9162fad3a1e08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 349 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 diff --git a/view/widgets/conveyor_system_widget.py b/view/widgets/conveyor_system_widget.py index 9f8b5c2..b1c1942 100644 --- a/view/widgets/conveyor_system_widget.py +++ b/view/widgets/conveyor_system_widget.py @@ -74,12 +74,47 @@ class ConveyorSystemWidget(QWidget): inner_img = ImagePaths.HOPPER2 inner_pixmap = QPixmap(inner_img) if not inner_pixmap.isNull(): - upper_inner_label = QLabel(upper_bg_widget) - upper_inner_label.setPixmap(inner_pixmap) - upper_inner_label.setFixedSize(inner_pixmap.width(), inner_pixmap.height()) - upper_inner_label.move(14, 9) # 保持原位置 + self.upper_inner_label = QLabel(upper_bg_widget) + self.upper_inner_label.setPixmap(inner_pixmap) + self.upper_inner_label.setFixedSize(inner_pixmap.width(), inner_pixmap.height()) + self.upper_inner_label.setScaledContents(False) + self.upper_inner_label.setStyleSheet("background: none;") + self.upper_inner_label.move(14, 9) + self.upper_inner_label.setAlignment(Qt.AlignBottom) return group + + def _update_upper_inner_height(self, total_weight, current_weight: float): + """根据当前重量占比, 更新upper_inner_label的高度, 实现动态进度的效果""" + # 1、处理边界值(超过总重量按100%,低于0按0%) + clamped_weight = max(0.0, min(current_weight, total_weight)) + + # 2、计算占比(0~1之间) + weight_ratio = clamped_weight / (total_weight * 1.0) + + # 3、根据占比计算实际高度 + inner_img_height = 100 # 内部的料斗阴影的高度为100px + target_height = int(weight_ratio * inner_img_height) + + # print("target_height: ", target_height) + + # 4、设置标签高度(动态变化) + self.upper_inner_label.setFixedHeight(target_height) + + # 5、计算标签位置(确保标签底部与父容器底部对齐) + container_bottom = 117 # 容器的高固定为了 117px (背景图片"料斗1"的高) + label_y = container_bottom - target_height - 8 # 标签顶部y坐标 (减去底部8px) + self.upper_inner_label.move(14, label_y) # x固定,y动态计算 + # print("label_y", label_y) + + # 6、强制刷新UI,确保立即显示变化 + self.upper_inner_label.update() + + def setConveyorHopperWeight(self, weight:float): + # 1、更新传送带中的 上料斗内部进度显示 + # 假设上料斗装满之后,总的重量为 5100kg + total_weight = 5100 + self._update_upper_inner_height(total_weight, weight) def create_conveyor(self): """创建传送带组件(包含左右齿轮,group容器背景为传送带图片)""" diff --git a/view/widgets/hopper_widget.py b/view/widgets/hopper_widget.py index 3390208..1dfc5f3 100644 --- a/view/widgets/hopper_widget.py +++ b/view/widgets/hopper_widget.py @@ -89,9 +89,9 @@ class HopperWidget(QWidget): # 背景容器(上位) self.upper_bg_widget = QWidget() self.upper_bg_widget.setFixedSize(outer_width, outer_height) - self.upper_bg_widget.setStyleSheet(f"background-image: url({outer_img}); background-repeat: no-repeat; background-position: center;") + self.upper_bg_widget.setStyleSheet(f"background-image: url({outer_img});background-repeat: no-repeat; background-position: center;") + # self.upper_bg_widget.setStyleSheet(f"background-color:red; background-repeat: no-repeat; background-position: center;") layout.addWidget(self.upper_bg_widget, alignment=Qt.AlignCenter) - # 内框图片(上位) inner_img = ImagePaths.HOPPER2 @@ -99,8 +99,11 @@ class HopperWidget(QWidget): if not inner_pixmap.isNull(): self.upper_inner_label = QLabel(self.upper_bg_widget) self.upper_inner_label.setPixmap(inner_pixmap) - self.upper_inner_label.setFixedSize(inner_pixmap.width(), inner_pixmap.height()) + self.upper_inner_label.setFixedSize(inner_pixmap.width(), inner_pixmap.height()) # 初始宽高 + self.upper_inner_label.setScaledContents(False) # 禁用缩放(避免图片拉伸) + self.upper_inner_label.setStyleSheet("background: none;") self.upper_inner_label.move(14, 9) + self.upper_inner_label.setAlignment(Qt.AlignBottom) # 状态图片(上位,绿色) status_img = ImagePaths.HOPPER_STATUS_GREEN @@ -162,17 +165,6 @@ class HopperWidget(QWidget): self.upper_arch_btn.clicked.connect(self.onUpperArchBreaking) self.lower_arch_btn.clicked.connect(self.onLowerArchBreaking) - self.upper_open_btn.clicked.connect(self.onUpperClampOpen) - self.lower_open_btn.clicked.connect(self.onLowerClampOpen) - - @Slot() - def onUpperClampOpen(self): - self.upper_clamp_widget.testAnimation(target_angle=60, duration=6) # 测试,6秒打开60度 - - @Slot() - def onLowerClampOpen(self): - self.lower_clamp_widget.testAnimation(target_angle=25, duration=6) # 测试,6秒打开30度 - @Slot() def onUpperArchBreaking(self): if self.upper_arch_breaking_status == False: # 不破拱状态 @@ -251,7 +243,10 @@ class HopperWidget(QWidget): self.lower_inner_label = QLabel(self.lower_bg_widget) self.lower_inner_label.setPixmap(inner_pixmap) self.lower_inner_label.setFixedSize(inner_pixmap.width(), inner_pixmap.height()) + self.lower_inner_label.setScaledContents(False) # 禁用图片缩放 + self.lower_inner_label.setStyleSheet("background: none;") self.lower_inner_label.move(14, 9) + self.lower_inner_label.setAlignment(Qt.AlignBottom) # 状态图片(下位) status_img = ImagePaths.HOPPER_STATUS_GREEN @@ -313,10 +308,42 @@ class HopperWidget(QWidget): return group + def _update_upper_inner_height(self, total_weight, current_weight: float): + """根据当前重量占比, 更新upper_inner_label的高度, 实现动态进度的效果""" + # 1、处理边界值(超过总重量按100%,低于0按0%) + clamped_weight = max(0.0, min(current_weight, total_weight)) + + # 2、计算占比(0~1之间) + weight_ratio = clamped_weight / (total_weight * 1.0) + + # 3、根据占比计算实际高度 + inner_img_height = 100 # 内部的料斗阴影的高度为100px + target_height = int(weight_ratio * inner_img_height) + + # print("target_height: ", target_height) + + # 4、设置标签高度(动态变化) + self.upper_inner_label.setFixedHeight(target_height) + + # 5、计算标签位置(确保标签底部与父容器底部对齐) + container_bottom = self.upper_bg_widget.y() + self.upper_bg_widget.height() + label_y = container_bottom - target_height - 8 # 标签顶部y坐标 (减去底部8px) + self.upper_inner_label.move(14, label_y) # x固定,y动态计算 + # print("label_y", label_y) + + # 6、强制刷新UI,确保立即显示变化 + self.upper_inner_label.update() + # 上料斗重量设置 def setUpperHopperWeight(self, weight:float): + # 1、更新上料斗重量标签,显示最新重量 self.upper_weight_label.setText(f"{weight}kg") + # 2、更新上料斗内部进度显示 + # 假设上料斗装满之后,总的重量为 5100kg + total_weight = 5100 + self._update_upper_inner_height(total_weight, weight) + # 上料斗方量设置 def setUpperHopperVolume(self, volume: float): """Args: @@ -334,8 +361,36 @@ class HopperWidget(QWidget): # 下料斗重量设置 def setLowerHopperWeight(self, weight:float): + # 1、更新下料斗显示标签,显示的重量 self.lower_weight_label.setText(f"{weight}kg") + # 2、更新下料斗的进度显示 + # 假设下料斗装满之后 总重量为 5100kg + total_weight = 5100 + self._update_lower_inner_height(total_weight, weight) + + def _update_lower_inner_height(self, total_weight, current_weight: float): + # 1、处理边界值 + clamped_weight = max(0.0, min(current_weight, total_weight)) + + # 2、计算占比 + weight_ratio = clamped_weight / (total_weight * 1.0) + + # 3、根据占比计算当前的实际高度 + inner_img_height = 100 # 内部料斗阴影的高度为100px + target_height = int(weight_ratio * inner_img_height) + + # 4、设置内部阴影标签的高度 + self.lower_inner_label.setFixedHeight(target_height) + + # 5、计算标签位置 + container_bottom = self.lower_bg_widget.y() + self.lower_bg_widget.height() + label_y = container_bottom - target_height - 8 + self.lower_inner_label.move(14, label_y) + + # 6、强制刷新UI,确保立即显示变化 + self.lower_inner_label.update() + # 下料斗开合角度设置 (包括 夹爪和标签) def setLowerHopperOpeningAngle(self, angle: float): """Args: @@ -347,7 +402,7 @@ class HopperWidget(QWidget): # ------------------------------ # 设置上料斗状态(0=绿,1=黄,2=红) # ------------------------------ - def setUpperArchStatus(self, status: int): + def setUpperHopperStatus(self, status: int): """ 设置上料斗状态图片 Args: @@ -369,17 +424,12 @@ class HopperWidget(QWidget): # 加载并缩放图片 status_pixmap = QPixmap(img_path) if not status_pixmap.isNull(): - status_pixmap = status_pixmap.scaled( - 22, 22, - Qt.KeepAspectRatio, - Qt.SmoothTransformation - ) self.upper_status_label.setPixmap(status_pixmap) # ------------------------------ # 设置下料斗状态(0=绿,1=黄,2=红) # ------------------------------ - def setLowerArchStatus(self, status: int): + def setLowerHopperStatus(self, status: int): """ 设置下料斗状态图片 Args: @@ -398,11 +448,6 @@ class HopperWidget(QWidget): status_pixmap = QPixmap(img_path) if not status_pixmap.isNull(): - status_pixmap = status_pixmap.scaled( - 22, 22, - Qt.KeepAspectRatio, - Qt.SmoothTransformation - ) self.lower_status_label.setPixmap(status_pixmap) # 隐藏上料斗 (用于上料斗移动) @@ -428,7 +473,7 @@ if __name__ == "__main__": window.setLowerHopperWeight(2000) window.setUpperHopperVolume(3.0) window.setLowerHopperOpeningAngle(45) - window.setUpperArchStatus(2) - window.setLowerArchStatus(1) + window.setUpperHopperStatus(2) + window.setLowerHopperStatus(1) window.show() sys.exit(app.exec()) \ No newline at end of file diff --git a/vision/__pycache__/__init__.cpython-39.pyc b/vision/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index f4f43ef0f33886b7747eef399e45212dc3c1fcc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 569 zcmYjNJx?1k81`K*m&-Mv!cZapK^A^Mi=YyyQ`H5jgB9u3>1-}n+2_c23CdOpiuh7X zyD%Zt7AYWBVC&!LkQMnFW~MS>J1AhwkKWJcef&mKQz6pl&imW`?;vzzoae{Y&RhNR z_ZkSoh&aS$4tKHZ7`wtM+{2!=JyzvD_N`rIH6Gx=+CHoE5Qh#*5UKrg@Cy>Gp=Q0i ztSQ6MV0$#!Ri|734!6s*pX&H1R0m(w;YIm>5pliifok@G%kdRyh>;RaaT&36+ncL5M>FY zIx|mf6^I^{>r8CGx)dBz9wjC__n7U`b{aDZ5lfSdYm#PFngz*<0nb3yfUn_0voWlJ zo)QCKKeqC0F%HITLfSSwZ8g9oXlU@vM93s=nDOjn^izYGW=2Jf0h}XQ*S>}OH>W64@l8N@gljDdM%=Yw#Qy7h$XO$JCk(V{Rp$Or70UJ)FRk} z1utS%612s7DMCdJr2Y}&Wv;q0xp?vB$+y{-<_+(+@4fxad%w3V85=7guxt4ri{Dj* zex}K=DUjR-4@UtgqWCddKvbDWCDl&fomdWnw9_5%unn-b9%G6l#ESy>8yKVeh^q4_ zh8|FzgsL`F#WC8z4JB5nK8}{~lG0RLsHuVLE!0qj5v$EyE9GdyjMZ2hPh&mSsIjc@ zIM!)yS>rLxFz1gaX-MSjhZr>sjMlEeynJlzpx4J*9)OO9@yJLuWziIxf(SM%MAy@8 zCcygZFMHpfBs)jR#%KThw)c{Qul>&UfIAmmhHF{jl>26@ z`|3mTwAt%)W~1{!=p8*zHg~g@zrM78@3;F_bpcc&*x z1*zA#ZLtkt(wY=cF#r64$Bv9`l9e1(dlm$5|M;B3u}$5zeTU%` zOk%c=_yy4XZ|3fFxmLFq3HRW_60=2lmNDx1RSWon2d)*?L&3cAiW7p2^66O01keLM z2}=~S#9CpCj_rcR6LINZ7hVNGnxdH4#72gyiHqte9#zj_1I`&Z{1UL{O4)OuZV1v3 REX^CRYUHw;Y1hBt=sz%X_{#tQ diff --git a/vision/__pycache__/anger_caculate.cpython-39.pyc b/vision/__pycache__/anger_caculate.cpython-39.pyc deleted file mode 100644 index 6913261bfb28eabca9f84b2f10bd125ffe950d0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2191 zcmaJ?O-vg{6rR~1+Zgi?hNPA1NbaLu50&DzL8`=|?IowaSpy+W+p*@&d$aTY zzIkt~)YJqKjHl_J``!y7^n?rhhYZ3yFs*hVDpIkIVwDxguqO*K5mrH$(sE43NR?Gl zmBw()r{XRY^=*M3>WpG0eSGTVsU)|zJKBKp!+am6^%F*jg8eKewG=tBagbt9ir9$a zQ4Bi)8>_-79>G-bz8t24<1-FZ(UVkL$ct1gV8=(ncNP)dmQ>i8Ml+xVkx5<1vmtvn zWH{wh{c1q1QiBt+_w9#(pZ&d4ELaoSYqD@>1 zbFMnyL~CeMk5GWzKmF5grqybL+Bk+Fi>3(?yjOEyYV!dE98f+5Y&R_(_=K59l>{r{X zw>^pPq)k=RJ0e?OjJcoXH`mt6x5s#Cl;#WW>_vBBwzz(Ad-c3Kd4Fs8!uINg=k?+l z#qR5faD_-|W^`+OV)NmBcYTa&tX=g?7nhbhO+zc){IR&UQNA|6-$p8}^lBXucY56& zoh)5nEU$1)Bb(0TA_^f&?uElU4nd=QVSK+Y&(#0S9>R!OT~>D_q$u}(p)J& zUV1QJiDM6^@#XzEDMqiZ#Z|4hBT`;lC~g$o<+;+G;jM3P6_@T7m%b?#&H;4ArIpf+ zNAA=W_wJ+3d!KV0mggXg?bS=}+(>EmPU*|x(hnEi#Vf_-%N51)^a8lw+_>gW&qg>@ zdk`yzVcLqVsqs`A+`=7f-aF@By0I_L&g^T@uI|R!t-?xaa;msK?SWo8f5Th7gx$#z zz&6_q2TM2er3cqH@2|L%J61X(QK9oiD;}I+#as7TIguI`5Oj+-emllQGm(f&OycM= z8N#&AFezyoJxt;O5U4x!Av`w$Z75~gadRMhkcV-3zC3lVymA!)aUXtPxrWX02T+D! z9cAC-XDUFQUwjcO0COjig+h7ZW(Dde!y`%VZFg{<8|#8;1%UA9>;Q<6uNT(W2;#y+ zgul2RdHU}0j_yn@*{6_nhM4_Y((XQ?X==*ojYCQ{G4;5WvurKhJ(#jmrqK;Gp^fDn@~F*(@I ze5o|i6qU&tk}_;2WpvYKf@!fpS{Z;)i3NMm*US&Rp`eka7XJ5J z<}g_&fuNcI_Tc~y3ITD41F07KKyJi*Pr_jw!nL9gH^Y~JAOUT}FT*d0>*35nF@)b=MVM*z*Nt{vs& M=5-2qhhIqe8}-u;9smFU diff --git a/vision/__pycache__/angle_detector.cpython-39.pyc b/vision/__pycache__/angle_detector.cpython-39.pyc deleted file mode 100644 index 652bdbfbc90c28a0d471a787b0419d3c082ddb43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 857 zcmZWnO=}ZD7@nEk&6lkwEgnR2%_a04L=->pAUW7WF$9L~&X{i5FJ>oVN+N|8Ybdp# zs9>=+_|bat1C>G>lDKgVqn3fu{Ao!TX&US38Ce7$)940bTFba&Fm3AZY!Xev z3|#5;Z?Q@qVt?W3@ayt$>+W!?J@~jc*n2*_zZ0!Jj=nsM+N;s>?qIXK|73f(^&r}P zo!lq|rZC;S>oe|@^8M{jw6PMs-T2vCIl67TW{sEf(RwdhT+xL_a2-}l|l!z-L^cW0)N;at# zWxS>^RhYj)BCgS|3ztgOpkd9L!VQE!$1PdC!lBGwjat4Zh40X?5lZe>Z`mOTR1bM1 z9Q8#pXsC%Am(;2Y!97Wt?~BoYkp=oNwvIA{5S+B%IU|LSLla~0y`YuLjZPi8yMFfh(!5o4bup+H2&nDf-UN5t| zp|&=tQi5np1FhP?AqgZ>Au5NehysN+;jiGbSF}!Y;+_-l?QWb;G1k0!^YPxy{@!oi zxZK&9B2dmG|GrXI2>Ay)`-=pfVW?Ig2u?Vyk`j53aK@!M!lhYOqMVMBth^2Lq*y_@ zzr>+44Ar^-LL;!6lo+R+&5@GCyLg-@=4eUg-8{)tpv8CxPs3cCck&Fh2`+>898(q;A-vUae}PLc!r~L7hPzfokPIaN-gbP74zD0tx2@DRCL3%wr&9JPtC>3HVPG zJ*Jz@bm8J)u5kWBu8=!d&|e3SUaZV(y5T7wyb}qg% zG;TIvQq)XQpVBJ!_$5u_x-qF%LPS)nVcA-3d|J2kx-lNQ%b5*NQB~d0ZB@lcSQ!w6 z#`pc2FO3{RHn(dbF!)7*hLr>wMg{dY+89?CaV9Pk-7aEHT}kx0h&OX(@tTX6OKwc$ zT*O^=5%*ZY8s?k<^HhISwjOS5u0L$une%7A_U}L5{(K&~?b%{BE`Xv$BvBx3MF)yB zicS#Ogy=#&ScGTyxH-wR!Rg zYMv$H_7P=|JV+T&s!R`-(wB9v)kn&v z?FdaEy-SL2ePC*a#=V#+bi?+N?_RBFrVW=XD+e?yrK3Bm8(h0uQt;bVM^rtk9VS&L zP{T3RO1Qyk@Nc~Ug2;^KXqu&H2G|wW3~io1j#+!=2NECh3U5NdD;JdJ+rSP;d=3$P zHl)6Y7m%WHe1)x&aW+F|m`lZ=%f=x2!76EQ)fh3(0$ZfUbek;{n0SyaG++z(et%XOMPv?rx^SE#~Uah5{+OZ91 zLT&`~@)tKS9Xwfc@GgeEXp1Po5D<)+I0?d&fpJ`%0yT=0Ckf5=ViN+>91`zHbfUPZ z!IC_zuugy=jL{TTXq;v6<}(F)8TcI&p92mK#OJ`ngkGcQP|+L2gRT+^C<1mY`0(v7 zZno}iw1L_Bb*}a2?P8V%ALu27R@KVzh+rB&^ce!7ABJinCkT+LdEy9>B6~=r609jg z39@^Z1{o`>;A?>ol&X3uRjt*zQ$;!y~+fUMv)f^dD8&TFw9f diff --git a/vision/__pycache__/detector.cpython-39.pyc b/vision/__pycache__/detector.cpython-39.pyc deleted file mode 100644 index 1a9c6da958bf49137a30c8a90e8dc4a7cef8a004..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2603 zcma)8|8Ltw6u&z=iQ}YQRbY$`Mt_@vP_>_?QP4nQ41}gk0>m_{k`>u*>&UUo*(poZ zqDd_q-BebAGzJ^AgMI0Km?i|MtfS+v;1~Z2&eDGYF^Tuiaq2dmw2S=i?!9;S-sj!B zcd7LCsRY`={9ls;MMD0@NxDfec?&-OeIO<=sY5jEnL}$#V-&}dBh_S07Gv4T)UsMu zj5AKIrf3Q!?-Da>=H`i+n`2tuq#u%!vI+Z60f;Lce#y3Us5+x#b4B>3LkQ zxD$>Ak|EOyEF;jpX^W3L-lzIF_iDObt4yGDUq?D^P1=TI=@rMGaBHxMffmxOi{&tI zEuvAAXw0N0n-G70QO%OyEfd@BfScft<~0OqG?dn>NJclE*01>fW)M;w0Sfj zRl34vNL8xJ*Qq}eWX#U&D*2wxQOFc&mb!z$mt(Dje~P$=WfEi?XDFG4Oluwhoy-hN zISsir%RXbS8uy3DA<{gNvYG>{H(+H>?4C=nc83Ec=FZ_cFH$f@6W}1d3x0 z^ZiJ$jHbd=?7S`Fw2p$z$M_PNQJFHz9yj;zK%Um`c^PC_Dv)Eu0Xfthz)q^HD$OBw zy8V{M*rTftqq$3+RK0R9TDsL*{}C?A7Fe0%#v&==9-NIw9+FP>((d2n~nd;Tz#Sh-wM@F8?7HA~k+RmUq* zhd}CKQP)4ISDaYF`_b$^B>2knmyrmkwpu=n}U8 diff --git a/vision/__pycache__/overflow_detector.cpython-39.pyc b/vision/__pycache__/overflow_detector.cpython-39.pyc deleted file mode 100644 index 1d303ac14fc4ca28ca32f9736ad6782da2b0fb58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1301 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