This commit is contained in:
2025-12-28 17:20:02 +08:00
parent b8b9679bc8
commit cddb7531ab
23 changed files with 2138 additions and 198 deletions

120
vision/muju_cls/main.py Normal file
View File

@ -0,0 +1,120 @@
import os
import cv2
from rknnlite.api import RKNNLite
# classify_single_image, StableClassJudge, CLASS_NAMES 已在 muju_cls_rknn 中定义
from muju_cls_rknn import classify_single_image, StableClassJudge, CLASS_NAMES
def run_stable_classification_loop(
model_path,
roi_file,
image_source,
stable_frames=3,
display_scale=0.5, # 显示缩放比例0.5 = 显示为原来 50%
show_window=False # 是否显示窗口
):
"""
image_source: cv2.VideoCapture 对象
"""
judge = StableClassJudge(
stable_frames=stable_frames,
ignore_class=2 # 忽略“有遮挡”类别参与稳定判断
)
cap = image_source
if not hasattr(cap, "read"):
raise TypeError("image_source 必须是 cv2.VideoCapture 实例")
# 可选:创建可缩放窗口
if show_window:
cv2.namedWindow("RTSP Stream - Press 'q' to quit", cv2.WINDOW_NORMAL)
while True:
ret, frame = cap.read()
if not ret:
print("无法读取视频帧(可能是流断开或结束)")
break
# 上下左右翻转
frame = cv2.flip(frame, -1)
# ---------------------------
# 单帧推理
# ---------------------------
result = classify_single_image(frame, model_path, roi_file)
class_id = result["class_id"]
class_name = result["class"]
score = result["score"]
print(f"[FRAME] {class_name} | conf={score:.3f}")
# ---------------------------
# 稳定判断
# ---------------------------
stable_class_id = judge.update(class_id)
if stable_class_id is not None:
print(f"\n稳定输出: {CLASS_NAMES[stable_class_id]}\n")
# ---------------------------
# 显示画面(缩小窗口)
# ---------------------------
if show_window:
h, w = frame.shape[:2]
display_frame = cv2.resize(
frame,
(int(w * display_scale), int(h * display_scale)),
interpolation=cv2.INTER_AREA
)
cv2.imshow("RTSP Stream - Press 'q' to quit", display_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
# ---------------------------
# 配置参数
# ---------------------------
MODEL_PATH = "muju_cls.rknn"
ROI_FILE = "./roi_coordinates/muju_roi.txt"
RTSP_URL = "rtsp://admin:XJ123456@192.168.250.61:554/streaming/channels/101"
STABLE_FRAMES = 3
DISPLAY_SCALE = 0.5 # 显示窗口缩放比例
SHOW_WINDOW = False # 部署时改成 False测试的时候打开
# ---------------------------
# 打开 RTSP 视频流
# ---------------------------
print(f"正在连接 RTSP 流: {RTSP_URL}")
cap = cv2.VideoCapture(RTSP_URL)
# 降低 RTSP 延迟(部分摄像头支持)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
if not cap.isOpened():
print("无法打开 RTSP 流,请检查网络、账号密码或 URL")
exit(1)
print("RTSP 流连接成功,开始推理...")
# ---------------------------
# 启动稳定分类循环三帧稳定判断
# ---------------------------
run_stable_classification_loop(
model_path=MODEL_PATH,
roi_file=ROI_FILE,
image_source=cap,
stable_frames=STABLE_FRAMES,
display_scale=DISPLAY_SCALE,
show_window=SHOW_WINDOW
)

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,282 @@
import os
import cv2
import numpy as np
from rknnlite.api import RKNNLite
from collections import deque
class StableClassJudge:
"""
连续三帧稳定判决器:
- class0 / class1 连续 3 帧 -> 输出
- class2 -> 清空计数,重新统计
"""
def __init__(self, stable_frames=3, ignore_class=2):
self.stable_frames = stable_frames
self.ignore_class = ignore_class
self.buffer = deque(maxlen=stable_frames)
def reset(self):
self.buffer.clear()
def update(self, class_id):
"""
输入单帧分类结果
返回:
- None尚未稳定
- class_id稳定输出结果
"""
# 遇到 class2直接清空重新计数
if class_id == self.ignore_class:
self.reset()
return None
self.buffer.append(class_id)
# 缓冲未满
if len(self.buffer) < self.stable_frames:
return None
# 三帧完全一致
if len(set(self.buffer)) == 1:
stable_class = self.buffer[0]
self.reset() # 输出一次后重新计数(防止重复触发)
return stable_class
return None
# ---------------------------
# 三分类映射模具车1是小的模具车2是大的
# ---------------------------
CLASS_NAMES = {
0: "模具车1",
1: "模具车2",
2: "有遮挡"
}
# ---------------------------
# RKNN 全局实例(只加载一次)
# ---------------------------
_global_rknn = None
def init_rknn_model(model_path):
global _global_rknn
if _global_rknn is not None:
return _global_rknn
rknn = RKNNLite(verbose=False)
ret = rknn.load_rknn(model_path)
if ret != 0:
raise RuntimeError(f"Load RKNN failed: {ret}")
ret = rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_0)
if ret != 0:
raise RuntimeError(f"Init runtime failed: {ret}")
_global_rknn = rknn
print(f"[INFO] RKNN 模型加载成功: {model_path}")
return rknn
# ---------------------------
# 预处理
# ---------------------------
def letterbox(image, new_size=640, color=(114, 114, 114)):
h, w = image.shape[:2]
scale = min(new_size / h, new_size / w)
nh, nw = int(h * scale), int(w * scale)
resized = cv2.resize(image, (nw, nh))
new_img = np.full((new_size, new_size, 3), color, dtype=np.uint8)
top = (new_size - nh) // 2
left = (new_size - nw) // 2
new_img[top:top + nh, left:left + nw] = resized
return new_img
def resize_stretch(image, size=640):
return cv2.resize(image, (size, size))
def preprocess_image_for_rknn(
img,
size=640,
resize_mode="stretch",
to_rgb=True,
normalize=False,
layout="NHWC"
):
if resize_mode == "letterbox":
img_box = letterbox(img, new_size=size)
else:
img_box = resize_stretch(img, size=size)
if to_rgb:
img_box = cv2.cvtColor(img_box, cv2.COLOR_BGR2RGB)
img_f = img_box.astype(np.float32)
if normalize:
img_f /= 255.0
if layout == "NHWC":
out = np.expand_dims(img_f, axis=0)
else:
out = np.expand_dims(np.transpose(img_f, (2, 0, 1)), axis=0)
return np.ascontiguousarray(out)
# ---------------------------
# 单次 RKNN 推理(三分类)
# ---------------------------
def rknn_classify_preprocessed(input_tensor, model_path):
rknn = init_rknn_model(model_path)
input_tensor = np.ascontiguousarray(input_tensor.astype(np.float32))
outs = rknn.inference([input_tensor])
pred = outs[0].reshape(-1).astype(float) # shape = (3,)
class_id = int(np.argmax(pred))
return class_id, pred
# ---------------------------
# ROI
# ---------------------------
def load_single_roi(txt_path):
if not os.path.exists(txt_path):
raise RuntimeError(f"ROI 文件不存在: {txt_path}")
with open(txt_path) as f:
for line in f:
s = line.strip()
if not s:
continue
x, y, w, h = map(int, s.split(','))
return (x, y, w, h)
raise RuntimeError("ROI 文件为空")
def crop_and_return_roi(img, roi):
x, y, w, h = roi
h_img, w_img = img.shape[:2]
if x < 0 or y < 0 or x + w > w_img or y + h > h_img:
raise RuntimeError(f"ROI 超出图像范围: {roi}")
return img[y:y + h, x:x + w]
# ---------------------------
# 单张图片推理(三分类)
# ---------------------------
def classify_single_image(
frame,
model_path,
roi_file,
size=640,
resize_mode="stretch",
to_rgb=True,
normalize=False,
layout="NHWC"
):
if frame is None:
raise FileNotFoundError("输入帧为空")
roi = load_single_roi(roi_file)
roi_img = crop_and_return_roi(frame, roi)
input_tensor = preprocess_image_for_rknn(
roi_img,
size=size,
resize_mode=resize_mode,
to_rgb=to_rgb,
normalize=normalize,
layout=layout
)
class_id, probs = rknn_classify_preprocessed(input_tensor, model_path)
class_name = CLASS_NAMES.get(class_id, f"未知类别({class_id})")
return {
"class_id": class_id,
"class": class_name,
"score": round(float(probs[class_id]), 4),
"raw": probs.tolist()
}
# ---------------------------
# 示例调用
# ---------------------------
if __name__ == "__main__":
model_path = "muju_cls.rknn"
roi_file = "./roi_coordinates/muju_roi.txt"
image_path = "./test_image/test.png"
frame = cv2.imread(image_path)
if frame is None:
raise FileNotFoundError(f"无法读取图片: {image_path}")
result = classify_single_image(frame, model_path, roi_file)
print("[RESULT]", result)
# ---------------------------
# 示例判断逻辑
'''
import cv2
from muju_cls_rknn import classify_single_image,StableClassJudge,CLASS_NAMES
def run_stable_classification_loop(
model_path,
roi_file,
image_source,
stable_frames=3
):
"""
image_source:
- cv2.VideoCapture
"""
judge = StableClassJudge(
stable_frames=stable_frames,
ignore_class=2 # 有遮挡
)
cap = image_source
if not hasattr(cap, "read"):
raise TypeError("image_source 必须是 cv2.VideoCapture")
while True:
ret, frame = cap.read()
# 上下左右翻转
frame = cv2.flip(frame, -1)
if not ret:
print("读取帧失败,退出")
break
result = classify_single_image(frame, model_path, roi_file)
class_id = result["class_id"]
class_name = result["class"]
score = result["score"]
print(f"[FRAME] {class_name} conf={score}")
stable = judge.update(class_id)
if stable is not None:
print(f"\n稳定输出: {CLASS_NAMES[stable]} \n")
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
'''
# ---------------------------

View File

@ -0,0 +1,87 @@
import os
import cv2
from rknnlite.api import RKNNLite
import time
# classify_single_image, StableClassJudge, CLASS_NAMES 已在 muju_cls_rknn 中定义
from .muju_cls_rknn import classify_single_image, StableClassJudge, CLASS_NAMES
# 获取当前文件所在目录的绝对路径
current_dir = os.path.dirname(os.path.abspath(__file__))
def run_stable_classification_loop():
"""
image_source: cv2.VideoCapture 对象
"""
_ret=None
# 使用相对于当前文件的绝对路径
model_path = os.path.join(current_dir, "muju_cls.rknn")
roi_file = os.path.join(current_dir, "roi_coordinates", "muju_roi.txt")
RTSP_URL = "rtsp://admin:XJ123456@192.168.250.61:554/streaming/channels/101"
stable_frames=3
print(f"正在连接 RTSP 流: {RTSP_URL}")
cap =None
try:
cap = cv2.VideoCapture(RTSP_URL)
# 降低 RTSP 延迟(部分摄像头支持)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
if not cap.isOpened():
print("无法打开 RTSP 流,请检查网络、账号密码或 URL")
return None
print("RTSP 流连接成功,开始推理...")
judge = StableClassJudge(
stable_frames=stable_frames,
ignore_class=2 # 忽略“有遮挡”类别参与稳定判断
)
if not hasattr(cap, "read"):
raise TypeError("image_source 必须是 cv2.VideoCapture 实例")
_max_count=10
while True:
_max_count=_max_count-1
ret, frame = cap.read()
if not ret:
print("无法读取视频帧(可能是流断开或结束)")
continue
# 上下左右翻转
frame = cv2.flip(frame, -1)
# ---------------------------
# 单帧推理
# ---------------------------
result = classify_single_image(frame, model_path, roi_file)
class_id = result["class_id"]
class_name = result["class"]
score = result["score"]
print(f"[FRAME] {class_name} | conf={score:.3f}")
# ---------------------------
# 稳定判断
# ---------------------------
stable_class_id = judge.update(class_id)
if stable_class_id is not None:
_ret=CLASS_NAMES[stable_class_id]
if _ret is None:
print("-------当前类别为:为空,继续等待稳定------")
continue
if _ret=="模具车1" or _ret=="模具车2":
break
print(f"当前类别为:{_ret},继续等待稳定...")
else:
print("当前类别为:为空,继续等待稳定...")
time.sleep(0.1)
finally:
if cap is not None:
cap.release()
return _ret

View File

@ -0,0 +1 @@
2,880,385,200

BIN
vision/muju_cls/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

View File

@ -0,0 +1 @@
2,880,385,200

View File

@ -6,7 +6,9 @@ from hardware.transmitter import TransmitterController
import time
import threading
from datetime import datetime
import logging
from hardware.upper_plc import OmronFinsPollingService
from vision.muju_cls.muju_utils import run_stable_classification_loop
class VisualCallback:
# 类变量,用于存储实例引用,实现单例检测
@ -26,19 +28,10 @@ class VisualCallback:
if hasattr(self, '_initialized') and self._initialized:
return
self.angle_mode = "normal"
self.relay_controller = RelayController()
self.transmitter_controller = TransmitterController(self.relay_controller)
self.init_weight = 100
self.mould_finish_weight = 0
self.mould_need_weight = 100
self.finish_count = 0
self.overflow = False
self.is_start_visual=False
# 线程安全的参数传递
self._current_angle = None
self._overflow_detected = None
self._new_data_available = threading.Event()
self._is_processing = threading.Lock()
self._stop_event = threading.Event()
@ -47,43 +40,91 @@ class VisualCallback:
self._door_control_lock = threading.Lock()
# 记录当前控制门的线程名称,用于调试
self._current_controlling_thread = None
# 新增标志位指示safe_control_lower_close是否正在执行
self._is_safe_closing = False
self._is_feed_start=True
#是否启动后的第一个模具
self._is_first_module=True
self.init_val()
# self._setup_logging_2()
#F块完成重量的70%控制夹脚F块多于这个比例就没有记录了注意
self._max_f_angle_ratio=0.7
#完成多少,调整角度比例 多于0.8就没记录了(注意)
self._max_angle_radio=0.8
#完成多少,忽略未浇筑满
self._max_ignore_radio=0.5
# 创建并启动单个持续运行的线程
# self.plc_data=5
self.plc_service = OmronFinsPollingService("192.168.250.233")
self.plc_service.register_data_callback(self.on_plc_update)
# self.plc_service.register_status_callback(self.on_status_change)
self.plc_service.start_polling(interval=2.0)
# 创建并启动单个持续运行的线程
self.callback_thread = threading.Thread(
target=self._run_thread_loop,
daemon=True
)
self.callback_thread.start()
self.feed_thread = threading.Thread(
target=self._run_feed,
daemon=True
)
self.feed_thread.start()
"""启动系统监控"""
self.monitor_thread = threading.Thread(
target=self._monitor_loop,
daemon=True,
name='monitor'
)
self.monitor_thread.start()
def init_val(self):
#初始化值
"""初始化视觉回调处理器"""
self.angle_mode = "normal"
self.overflow = False
self.is_start_visual=False
# 线程安全的参数传递
self._current_angle = None
self._overflow_detected = None
# 新增标志位指示safe_control_lower_close是否正在执行
self._is_safe_closing = False
self._is_feed_start=True
#未浇筑满时间,用于确定是否进入未浇筑满
self._before_finish_time=None
#进入未浇筑满状态标志位
self._is_before_finish=False
#是否浇筑满标志位
self._is_finish=False
# self._is_finish_ratio=0
#是否震荡关闭平面设想90%后关闭几秒,让液面平整)
# self._is_zd_close=False
#浇筑完成比例(重量)
self._is_finish_ratio=0
#下料阶段,汇总时用枚举
self._is_feed_stage=0
#振动相关参数
self._last_arch_one_weight=0
self._last_arch_two_weight=0
self._last_arch_three_weight=0
self._last_arch_four_weight=0
self._last_arch_five_weight=0
self._last_arch_time=0
#是否为F块
self._is_small_f=None
#采集数据用,下料重量=之前下的重量+最后一阶段(下-->模具车)重量差
#记录最后一次下料斗到模具车前的重量
self._finish_weight=0
#记录最后一次下料斗到车初始重量
self._inital_finish_lweight=0
# 初始化控制间隔和堆料状态跟踪属性
self._last_overflow_state = False
self._last_control_time = 0
self._initialized = True
self.plc_data=None
# self.plc_data=5
self.plc_service = OmronFinsPollingService("192.168.250.233")
self.plc_service.register_data_callback(self.on_plc_update)
# self.plc_service.register_status_callback(self.on_status_change)
self.plc_service.start_polling(interval=2.0)
def angle_visual_callback(self, current_angle, overflow_detected):
"""
视觉控制主逻辑,供外部推送数据
@ -111,8 +152,67 @@ class VisualCallback:
# 释放处理锁
self._is_processing.release()
def _monitor_loop(self):
"""监控循环"""
while not self._is_finish:
try:
current_time = time.time()
# 检查下料斗破拱(只有在下料过程中才检查)
if self._is_feed_stage==1: # 下料斗--》模具车
_arch_weight = self.transmitter_controller.read_data(2)
if _arch_weight is not None and _arch_weight>0:
# 检查重量变化是否过慢
if (abs(_arch_weight - self._last_arch_one_weight) < 200) and \
(current_time - self._last_arch_time) >= 2:
print('---------------------第一阶段振动5秒-----------------')
self.relay_controller.control_arch_lower_open_sync(5)
self._last_arch_one_weight = _arch_weight
elif self._is_feed_stage==2: #上料斗到下料斗,料多,谨慎振动
_arch_weight = self.transmitter_controller.read_data(1)
if _arch_weight is not None:
# 检查重量变化是否过慢
# if (abs(_arch_weight - self._last_arch_two_weight) < 50) and \
# (current_time - self._last_arch_time) >= 2:
# self.relay_controller.control_arch_upper_open_sync(3)
self._last_arch_two_weight = _arch_weight
elif self._is_feed_stage==3: #第二次下料斗-》模具车
_arch_weight = self.transmitter_controller.read_data(2)
if _arch_weight is not None:
# 检查重量变化是否过慢
if (abs(_arch_weight - self._last_arch_three_weight) < 100) and \
(current_time - self._last_arch_time) >= 2:
print('---------------------第三阶段振动5秒-----------------')
self.relay_controller.control_arch_lower_open_sync(3)
self._last_arch_three_weight = _arch_weight
elif self._is_feed_stage==4: #上料斗--》下料斗
_arch_weight = self.transmitter_controller.read_data(1)
if _arch_weight is not None:
# 检查重量变化是否过慢
# if (abs(_arch_weight - self._last_arch_four_weight) < 200) and \
# (current_time - self._last_arch_time) > 2:
# self.relay_controller.control_arch_upper_open_sync(5)
self._last_arch_four_weight = _arch_weight
elif self._is_feed_stage==5: #下料斗->模具车
_arch_weight = self.transmitter_controller.read_data(2)
if _arch_weight is not None:
_min_arch_weight=20
if self._is_finish_ratio<self._max_angle_radio:
_min_arch_weight=50
if (abs(_arch_weight - self._last_arch_five_weight) < _min_arch_weight) and \
(current_time - self._last_arch_time) >= 2:
print('---------------------第五阶段振动5秒-----------------')
self.relay_controller.control_arch_lower_open_sync(5)
self._last_arch_five_weight = _arch_weight
# 更新最后读取时间
self._last_arch_time = current_time
time.sleep(2)
except Exception as e:
print(f"监控线程错误: {e}")
def _run_thread_loop(self):
"""
接受视觉回调数据
线程主循环,持续运行
等待新数据,然后调用处理方法
"""
@ -138,14 +238,25 @@ class VisualCallback:
while True:
print("------------已启动----------------")
if self._is_feed_start:
if self.plc_data==5:
self.run_feed()
break
else:
print("-----------上料斗未就位----------------")
print("-----------上料斗未就位----------------")
# if self.plc_data==5:
#_is_finish_ratio完成 比例,根据重量过滤一下
if self._overflow_detected=='未堆料':
if self._is_first_module:
print('------------进入第一块111111-------------')
self._is_first_module=False
self.run_feed_all()
break
elif self._is_finish and self._is_finish_ratio>=0.7:
print('-----------进入连续块111111-----------')
# self.init_val()
# self.run_feed_all()
# else:
# print("-----------上料斗未就位----------------")
# print("---------3--上料斗未就位----------------")
time.sleep(0.5)
time.sleep(2)
def safe_control_lower_close(self,duration=3):
"""线程安全的下料斗关闭方法"""
@ -167,154 +278,294 @@ class VisualCallback:
# 无论成功失败,都要重置标志位
self._is_safe_closing = False
def close_lower_door_visual(self):
"""关闭下料斗门"""
self.is_start_visual=False
time.sleep(0.5)
self.safe_control_lower_close()
def _visual_close(self):
self.is_start_visual=False
self._is_finish=True
self._is_feed_stage=0
print(f'--------进入关闭-----------')
self.safe_control_lower_close(3)
print(f'--------关闭完成-----------')
#记录重量
_current_weight=self.transmitter_controller.read_data(2)
if _current_weight is not None:
self._finish_weight= self._finish_weight+(self._inital_finish_lweight-_current_weight)
with open('weight.txt', 'a') as f:
timestamp = datetime.now().strftime("%H:%M:%S")
f.write(f"{timestamp} - {self._finish_weight}\n")
def run_feed_all(self):
"""
全流程下料:包括判断模具类型
"""
_is_f= run_stable_classification_loop()
print(f'------------已判断出模具类型: {_is_f}-------------')
if _is_f is not None:
if _is_f=='模具车1':
self._is_small_f=True
print('-------------F块模具--------------')
print('-------------F块模具--------------')
print('-------------F块模具--------------')
self.run_feed_f()
elif _is_f=='模具车2':
self._is_small_f=False
self.run_feed()
print('-------------其他模具---------------')
if self._is_small_f is None:
print('-----------未判断出模具类型--------------')
return
def run_feed_f(self):
"""第一阶段下料:下料斗向模具车下料(低速)"""
print("--------------------开始下料F块--------------------")
# loc_relay=self.relay_controller
loc_mitter=self.transmitter_controller
max_weight_none=5
cur_weight_none=0
initial_lower_weight=loc_mitter.read_data(2)
if initial_lower_weight is None:
print("-----f上料斗重量异常-----")
return
first_finish_weight=0
self._finish_weight=first_finish_weight
self._inital_finish_lweight=initial_lower_weight
need_total_weight=0.54*2416
if initial_lower_weight>100:
if not self._is_finish:
self.is_start_visual=True
initial_lower_weight=loc_mitter.read_data(2)
if initial_lower_weight is None:
print("-----f上料斗重量异常2-----")
return
self._is_feed_stage=5
while not self._is_finish:
current_weight = loc_mitter.read_data(2)
if current_weight is None:
cur_weight_none+=1
if cur_weight_none>max_weight_none:
#如果重量连续5次为None认为下料斗未就位跳出循环
print('------------f下到模具车,下料斗重量异常----------------')
print('------------f下到模具车,下料斗重量异常----------------')
self.close_lower_door_visual()
return
#视觉处理关闭,异常的话重量没有生效
continue
cur_weight_none=0
first_finish_weight=initial_lower_weight-current_weight
self._is_finish_ratio=(first_finish_weight)/need_total_weight
print(f'------------已下料比例: {self._is_finish_ratio}-------------')
if self._is_finish_ratio>self._max_f_angle_ratio:
#关5秒
#大于0.7后不再检测了,直接交给视觉控制夹脚
# print(f'------------已下料比例: {self._is_finish_ratio}-------------')
break
# print(f'------------已下料: {first_finish_weight+second_finish_weight}kg-------------')
time.sleep(1)
# initial_lower_weight=_current_lower_weight
print(f'------------已下料F: {first_finish_weight}kg-------------')
print(f'------------已下料F: {first_finish_weight}kg-------------')
print(f'------------已完成-------------')
def run_feed(self):
"""第一阶段下料:下料斗向模具车下料(低速)"""
print("--------------------开始下料--------------------")
print("--------------------开始下料(普通块)--------------------")
loc_relay=self.relay_controller
loc_mitter=self.transmitter_controller
max_weight_none=5
cur_weight_none=0
initial_lower_weight=loc_mitter.read_data(2)
initial_upper_weight=loc_mitter.read_data(1)
# initial_upper_weight=loc_mitter.read_data(1)
if initial_lower_weight is None:
print("---------------下料斗重量异常----------------")
return
first_finish_weight=0
# need_total_weight=1.91*2416
start_time=None
need_total_weight=1.91*2416
# start_time=None
self.is_start_visual=True
if initial_lower_weight>100:
#下料斗的料全部下完
while not self._is_finish:
loc_mitter.is_start_lower=True
self._is_feed_stage=1
while not self._is_finish:
current_weight = loc_mitter.read_data(2)
first_finish_weight=initial_lower_weight-current_weight
if current_weight<500:
# 破拱控制
if start_time is None or time.time()-start_time>5:
start_time=time.time()
loc_relay.control_arch_lower_open()
if current_weight<250:
start_time=None
self.is_start_visual=False
loc_mitter.is_start_lower=False
time.sleep(0.5)
self.safe_control_lower_close()
break
print(f'------------已下料: {first_finish_weight}kg-------------')
if current_weight is None:
cur_weight_none+=1
if cur_weight_none>max_weight_none:
print("-----------下料斗重量异常(第一次下到模具车)--------------")
self.close_lower_door_visual()
return
continue
cur_weight_none=0
if current_weight<250 and current_weight>0:
self.close_lower_door_visual()
break
time.sleep(1)
# _current_lower_weight=loc_mitter.read_data(2)
# first_finish_weight=initial_lower_weight-_current_lower_weight
_current_lower_weight=loc_mitter.read_data(2)
if _current_lower_weight is None:
print("-------下料斗重量异常---------")
return
first_finish_weight=initial_lower_weight-_current_lower_weight
# initial_lower_weight=_current_lower_weight
# print(f'------------已下料: {first_finish_weight}kg-------------')
# print(f'------------已下料: {first_finish_weight}kg-------------')
print(f'------------已下料(第一次): {first_finish_weight}kg-------------')
print(f'------------已下料(第一次): {first_finish_weight}kg-------------')
self._is_feed_stage=0
while self.plc_data!=5:
print('------------上料斗未就位----------------')
print('------------上料斗未就位----------------')
time.sleep(1)
if self.plc_data==5:
print(f'------------上料斗向下料斗转移(留3000KG-------------')
#打开上料斗出砼门开5就开三分之一下
loc_relay.control_upper_open_sync(6)
self._is_feed_stage=2
loc_time_count=1
upper_open_time=time.time()
while not self._is_finish:
loc_mitter.is_start_upper=True
current_upper_weight = loc_mitter.read_data(1)
if current_upper_weight<3000:
if current_upper_weight is None:
cur_weight_none+=1
if cur_weight_none>max_weight_none:
#如果重量连续5次为None认为上料斗未就位跳出循环
print('------------第一次上到下,上料斗重量异常----------------')
print('------------第一次上到下,上料斗重量异常----------------')
loc_relay.control_upper_close_sync(5+loc_time_count)
return
continue
cur_weight_none=0
if current_upper_weight<3000 and current_upper_weight>0:
#关5秒,loc_time_count多关一秒
loc_relay.control_upper_close_sync(4+loc_time_count)
loc_mitter.is_start_upper=False
loc_relay.control_upper_close_sync(5+loc_time_count)
break
else:
if time.time()-upper_open_time>5:
upper_open_time=time.time()
loc_relay.control_upper_open_sync(0.5)
loc_time_count=loc_time_count+0.5
if loc_time_count<6:
upper_open_time=time.time()
loc_relay.control_upper_open_sync(0.8)
loc_time_count=loc_time_count+0.8
else:
time.sleep(0.5)
# time.sleep(0.4)
else:
loc_relay.control_upper_close_sync(6+loc_time_count)
self.is_start_visual=True
loc_mitter.is_start_lower=False
loc_mitter.test_lower_weight=2000
initial_lower_weight=loc_mitter.read_data(2)
if initial_lower_weight is None:
print("-------下料斗重量异常(第二次下料到模具车)---------")
return
self._is_feed_stage=3
while not self._is_finish:
loc_mitter.is_start_lower=True
current_weight = loc_mitter.read_data(2)
second_finish_weight=initial_lower_weight-current_weight
if current_weight<500:
#关5秒
if start_time is None or time.time()-start_time>5:
start_time=time.time()
loc_relay.control_arch_lower_open()
if current_weight<250:
start_time=None
self.is_start_visual=False
loc_mitter.is_start_lower=False
time.sleep(0.5)
self.safe_control_lower_close()
break
print(f'------------已下料: {first_finish_weight+second_finish_weight}kg-------------')
if current_weight is None:
cur_weight_none+=1
if cur_weight_none>max_weight_none:
print("-------下料斗重量异常(第二次下料到模具车)---------")
self.close_lower_door_visual()
return
continue
cur_weight_none=0
# second_finish_weight=initial_lower_weight-current_weight
if current_weight<250:
self.close_lower_door_visual()
break
# print(f'------------已下料: {first_finish_weight+second_finish_weight}kg-------------')
time.sleep(1)
# _current_lower_weight=loc_mitter.read_data(2)
# first_finish_weight=first_finish_weight+initial_lower_weight-_current_lower_weight
# print(f'------------下料: {first_finish_weight}kg-------------')
# print(f'------------已下料: {first_finish_weight}kg-------------')
_current_lower_weight=loc_mitter.read_data(2)
if _current_lower_weight is None:
print("-------下料斗重量异常(第二次下到模)---------")
return
first_finish_weight=first_finish_weight+initial_lower_weight-_current_lower_weight
print(f'------------已下料(第二次): {first_finish_weight}kg-------------')
print(f'------------已下料(第二次): {first_finish_weight}kg-------------')
self._is_feed_stage=0
if self.plc_data==5:
#第二次上料斗向下料斗转移
loc_relay.control_upper_open_sync(6)
loc_relay.control_upper_open_sync(11)
loc_time_count=1
upper_open_time=time.time()
loc_mitter.is_start_upper=False
loc_mitter.test_upper_weight=3000
#第二次到下料斗还需要的量
#loc_left_need_weight=need_total_weight-first_finish_weight
initial_upper_weight=loc_mitter.read_data(1)
start_time=None
# initial_upper_weight=loc_mitter.read_data(1)
# start_time=None
self._is_feed_stage=4
while not self._is_finish:
# print(f'------------上料斗向下料斗转移22222-------------')
loc_mitter.is_start_upper=True
current_upper_weight = loc_mitter.read_data(1)
if current_upper_weight<500:
#关5秒,loc_time_count多关一秒
if start_time is None or time.time()-start_time>4:
start_time=time.time()
loc_relay.control_arch_upper_open()
if current_upper_weight<100:
start_time=None
loc_relay.control_upper_close_sync(4+loc_time_count)
loc_mitter.is_start_upper=False
if current_upper_weight is None:
cur_weight_none+=1
if cur_weight_none>max_weight_none:
#如果重量连续5次为None认为上料斗未就位跳出循环
print('------------第二次上到下,上料斗重量异常----------------')
print('------------第二次上到下,上料斗重量异常----------------')
loc_relay.control_upper_close_sync(15)
break
continue
cur_weight_none=0
if current_upper_weight<400 and current_upper_weight>0:
loc_relay.control_arch_upper_open()
loc_relay.control_upper_open_sync(5)
# start_time=None
#5秒后关闭
loc_relay.control_upper_close_after()#control_upper_close_sync(8+loc_time_count)
break
else:
if time.time()-upper_open_time>3:
if time.time()-upper_open_time>2:
# if loc_time_count<6:
upper_open_time=time.time()
loc_relay.control_upper_open_sync(0.8)
loc_time_count=loc_time_count+0.8
loc_relay.control_upper_open_sync(1)
loc_time_count=loc_time_count+1
else:
time.sleep(0.5)
else:
loc_relay.control_upper_close_sync(15)
# time.sleep(0.4)
#第次下料斗转移到模具车
#第次下料斗转移到模具车
if not self._is_finish:
self.is_start_visual=True
# loc_mitter.is_start_lower=False
# loc_mitter.test_lower_weight=2000
# initial_lower_weight=loc_mitter.read_data(2)
# while not self._is_finish:
# loc_mitter.is_start_lower=True
# current_weight = loc_mitter.read_data(2)
# second_finish_weight=initial_lower_weight-current_weight
# # self._is_finish_ratio=(second_finish_weight+first_finish_weight)/need_total_weight
# # print(f'------------已下料比例: {self._is_finish_ratio}-------------')
# if current_weight<500:
# #关5秒
# if start_time is None or time.time()-start_time>5:
# start_time=time.time()
# loc_relay.control_arch_lower_open()
# print(f'------------已下料: {first_finish_weight+second_finish_weight}kg-------------')
# time.sleep(1)
initial_lower_weight=loc_mitter.read_data(2)
self._finish_weight=first_finish_weight
self._inital_finish_lweight=initial_lower_weight
if initial_lower_weight is None:
print("-------下料斗重量异常(第三次下到模具车)---------")
return
self._is_feed_stage=5
while not self._is_finish:
current_weight = loc_mitter.read_data(2)
if current_weight is None:
cur_weight_none+=1
if cur_weight_none>max_weight_none:
#重量异常退出
print('------------第三次下到模具车,下料斗重量异常----------------')
self.close_lower_door_visual()
return
continue
cur_weight_none=0
second_finish_weight=initial_lower_weight-current_weight
self._is_finish_ratio=(second_finish_weight+first_finish_weight)/need_total_weight
print(f'------------已下料比例: {self._is_finish_ratio}-------------')
if self._is_finish_ratio>self._max_angle_radio:
#关5秒
# print(f'------------已下料比例: {self._is_finish_ratio}-------------')
break
# print(f'------------已下料: {first_finish_weight+second_finish_weight}kg-------------')
time.sleep(1)
# _current_lower_weight=loc_mitter.read_data(2)
# first_finish_weight=first_finish_weight+initial_lower_weight-_current_lower_weight
# print(f'------------已下料: {first_finish_weight}kg-------------')
@ -354,54 +605,66 @@ class VisualCallback:
# time.sleep(3)
else:
if overflow_detected=='浇筑满':
self.is_start_visual=False
self._is_finish=True
self.safe_control_lower_close(3)
self._visual_close()
return
# print(f'--------已关闭已关闭-----------')
elif overflow_detected=="大堆料":
print(f'--------未浇筑满,大堆料,不开关-----------')
else:
self._pulse_control('open',0.5)
print(f'--------未浇筑满,大堆料-----------')
self._pulse_control('open',0.3)
time.sleep(0.3)
self._pulse_control('close',0.6)
time.sleep(2)
self._pulse_control('close',0.4)
time.sleep(1)
self._is_before_finish=True
else:
# self._pulse_control('open',0.5)
# time.sleep(0.3)
# self._pulse_control('close',0.6)
self._pulse_control('open',0.6)
time.sleep(0.3)
self._pulse_control('close',0.7)
time.sleep(1)
self._is_before_finish=True
if self._is_finish_ratio<=self._max_ignore_radio:
#如果重量未达到最大忽略角度,需要跳出
self._is_before_finish=False
return
elif overflow_detected == "浇筑满":
self.is_start_visual=False
self._is_finish=True
self.safe_control_lower_close(3)
self._visual_close()
return
else:
self._before_finish_time=None
# if self._is_finish_ratio>=0.8:
# if overflow_detected == "大堆料":
# TARGET_ANGLE = 10.0 # 大堆料时控制在15度左右
# elif overflow_detected == "小堆料":
# TARGET_ANGLE = 20.0 # 小堆料时控制在35度左右
# else:
# TARGET_ANGLE = 25.0 # 未溢料时开到最大56度
# if self._is_finish_ratio>=0.9 and not self._is_zd_close:
# #关闭10秒直接关闭不等待
# print('-----------------达到90% 关闭--------------------')
# self._is_zd_close=True
# self.safe_control_lower_close()
# time.sleep(10)
# return
# else:
#根据溢料状态动态调整目标角度
if overflow_detected == "大堆料":
TARGET_ANGLE = 15.0 # 大堆料时控制在15度左右
elif overflow_detected == "小堆料":
TARGET_ANGLE = 25.0 # 小堆料时控制在35度左右
if self._is_finish_ratio>=self._max_angle_radio or (self._is_finish_ratio>self._max_f_angle_ratio and self._is_small_f):
if overflow_detected == "大堆料":
TARGET_ANGLE = 5.0 # 大堆料时控制在15度左右
elif overflow_detected == "小堆料":
TARGET_ANGLE = 15.0 # 小堆料时控制在35度左右
else:
TARGET_ANGLE = 25.0 # 未溢料时开到最大56度
else:
TARGET_ANGLE = 45.0 # 未溢料时开到最大56度
if self._is_feed_stage==1 or self._is_feed_stage==3:
#根据溢料状态动态调整目标角度
if overflow_detected == "大堆料":
TARGET_ANGLE = 15.0 # 大堆料时控制在15度左右
elif overflow_detected == "小堆料":
TARGET_ANGLE = 45.0 # 小堆料时控制在35度左右
else:
TARGET_ANGLE = 55.0 # 未溢料时开到最大56度
else:
#根据溢料状态动态调整目标角度
if overflow_detected == "大堆料":
TARGET_ANGLE = 15.0 # 大堆料时控制在15度左右
elif overflow_detected == "小堆料":
TARGET_ANGLE = 25.0 # 小堆料时控制在35度左右
else:
TARGET_ANGLE = 45.0 # 未溢料时开到最大56度
# 确保目标角度在硬件范围内5-56度
TARGET_ANGLE = max(5.0, min(56.0, TARGET_ANGLE))
# PID控制参数
KP = 0.15 # 比例系数
KP = 0.2 # 比例系数
KI = 0 # 积分系数
KD = 0 # 微分系数
# KP = 0.15 # 比例系数
@ -618,6 +881,12 @@ class VisualCallback:
if self.callback_thread.is_alive():
self.callback_thread.join(timeout=1.0)
if self.feed_thread.is_alive():
self.feed_thread.join(timeout=1.0)
if self.monitor_thread.is_alive():
self.monitor_thread.join(timeout=1.0)
def __del__(self):
"""析构函数,确保线程安全关闭"""
self.shutdown()

1018
vision/visual_callback_dq.py Normal file

File diff suppressed because it is too large Load Diff