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

View File

@ -58,6 +58,22 @@ class ArtifactBll:
"OptTime": datetime.now(),
})
def insert_artifact_bycode(self,model: dict) -> bool:
"""根据模具编号插入管片任务"""
if self.dal.exists_by_module_code(model["MouldCode"]):
return False
return self.dal.insert_artifact({
"MouldCode": model["MouldCode"],
"SizeSpecification": model["SizeSpecification"],
"BlockNumber": model["BlockNumber"],
"BetonVolume": model["BetonVolume"],
"PStatus":1,
"Status": 2,
"Source": 2,
"OptTime": datetime.now(),
})
def get_artifacting_task(self) -> ArtifactInfoModel:
"""获取正在进行的管片任务数据"""
loc_item= self.dal.get_top_artifact(1,"ID desc","Status=2")

View File

@ -16,7 +16,7 @@ class BaseDal:
def __init__(self) -> None:
"""初始化数据访问层,创建数据库连接"""
# 假设数据库文件在db目录下
self.db_dao = SQLiteHandler.get_instance("db/three.db", max_readers=50, busy_timeout=4000)
self.db_dao = SQLiteHandler.get_instance("../db/three.db", max_readers=50, busy_timeout=4000)
class ArtifactDal(BaseDal):
def __init__(self):
@ -97,6 +97,21 @@ class ArtifactDal(BaseDal):
except Exception as e:
print(f"根据ID获取构件任务失败: {e}")
return False
def exists_by_module_code(self, module_code: str) -> bool:
"""根据模具编号获取构件任务"""
try:
sql = "SELECT count(1) FROM ArtifactTask WHERE MouldCode = ? and OptTime>?"
results = self.db_dao.execute_read(sql, (module_code,datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)))
rows = list(results)
if rows[0][0] == 1:
return True
return False
except Exception as e:
print(f"根据ID获取构件任务失败: {e}")
return False
def get_by_id(self, artifact_id: int) -> Optional[ArtifactInfoModel]:
"""根据构件ID获取构件任务"""

Binary file not shown.

BIN
db/three.db-shm Normal file

Binary file not shown.

BIN
db/three.db-wal Normal file

Binary file not shown.

Binary file not shown.

View File

@ -384,7 +384,7 @@ class rfid_service:
接收线程的主循环用于接收RFID推送的数据
"""
while self._thread_signal:
self._pause_receive=False
# self._pause_receive=False
if self._pause_receive:
time.sleep(1)
continue
@ -409,11 +409,11 @@ class rfid_service:
received_data += chunk
remaining_bytes -= len(chunk)
print(f"[数据接收] 已接收 {len(received_data)}/{self._buffer_length} 字节")
# print(f"[数据接收] 已接收 {len(received_data)}/{self._buffer_length} 字节")
# 只有接收到完整的数据才算成功
if remaining_bytes == 0:
print(f"[数据接收] 成功接收完整数据包 ({self._buffer_length} 字节)")
# print(f"[数据接收] 成功接收完整数据包 ({self._buffer_length} 字节)")
data = received_data # 保存完整的数据包
self._error_count=0
else:
@ -443,13 +443,13 @@ class rfid_service:
if data:
loc_str = command_hex.parse_user_data_hex(data)
raw_data = binascii.hexlify(data).decode()
print(f"收到RFID推送数据: {raw_data}")
# raw_data = binascii.hexlify(data).decode()
# print(f"收到RFID推送数据: {raw_data}")
# 保存到文件
with open('rfid_data.log', 'a') as f:
timestamp = datetime.now().strftime('%H:%M:%S.%f')[:-3]
f.write(f"[{timestamp}] 解析数据: {loc_str}, 原始数据: {raw_data}\n")
# with open('rfid_data.log', 'a') as f:
# timestamp = datetime.now().strftime('%H:%M:%S.%f')[:-3]
# f.write(f"[{timestamp}] 解析数据: {loc_str}, 原始数据: {raw_data}\n")
if loc_str:
# 将数据添加到缓冲区
@ -514,6 +514,7 @@ class rfid_service:
"""
停止接收RFID推送的数据
"""
print('RFID 线程停止')
self._thread_signal = False
if self._receive_thread:
self._receive_thread.join(timeout=2.0)

View File

@ -73,7 +73,7 @@ class RelayController:
sock.connect((self.host, self.port))
sock.send(byte_data)
response = sock.recv(1024)
print(f"收到继电器响应: {binascii.hexlify(response)}")
# print(f"收到继电器响应: {binascii.hexlify(response)}")
return response
except Exception as e:
print(f"继电器通信错误: {e}")
@ -97,7 +97,7 @@ class RelayController:
def control(self, device, action):
"""控制继电器"""
if device in self.relay_commands and action in self.relay_commands[device]:
print(f"发送控制继电器命令 {device} {action}")
# print(f"发送控制继电器命令 {device} {action}")
self.send_command(self.relay_commands[device][action])
else:
print(f"无效设备或动作: {device}, {action}")
@ -109,6 +109,13 @@ class RelayController:
self.control(self.DOOR_UPPER_CLOSE, 'open')
# 异步5秒后关闭
threading.Thread(target=self._close_upper_s, daemon=True,name="close_upper_s").start()
def control_upper_close_after(self):
"""控制上料斗关在几秒后"""
# 关闭上料斗出砼门
self.control(self.DOOR_UPPER_OPEN, 'close')
# 异步5秒后关闭
threading.Thread(target=self._close_upper_after_s, daemon=True,name="close_upper_after_s").start()
def control_upper_close_sync(self,duration=5):
self.control(self.DOOR_UPPER_OPEN, 'close')
@ -224,6 +231,24 @@ class RelayController:
self.control(self.DOOR_UPPER_CLOSE, 'close')
print("上料斗关闭完成")
def _close_upper_after_s(self):
"""
异步5秒后关闭上料斗20秒
"""
# time.sleep(5)
self.control_arch_upper_open_sync(5)
self.control(self.DOOR_UPPER_CLOSE, 'open')
time.sleep(1)
self.control(self.DOOR_UPPER_CLOSE, 'close')
self.control_arch_upper_open_sync(5)
self.control_arch_upper_open_sync(5)
self.control_arch_upper_open_async(8)
self.control(self.DOOR_UPPER_CLOSE, 'open')
time.sleep(20)
self.control(self.DOOR_UPPER_CLOSE, 'close')
print("上料斗关闭完成")
def _close_lower_5s(self):
time.sleep(6)
self.control(self.DOOR_LOWER_CLOSE, 'close')
@ -238,19 +263,40 @@ class RelayController:
self.control(self.BREAK_ARCH_LOWER, 'open')
# 异步5秒后关闭
threading.Thread(target=self._close_break_arch_lower, daemon=True,name="_close_break_arch_lower").start()
def control_arch_lower_open_sync(self,duration):
"""控制下料斗振动"""
self.control(self.BREAK_ARCH_LOWER, 'open')
# 异步5秒后关闭
time.sleep(duration)
self.control(self.BREAK_ARCH_LOWER, 'close')
def control_arch_upper_open_sync(self,duration):
"""控制下料斗振动"""
self.control(self.BREAK_ARCH_UPPER, 'open')
# 异步5秒后关闭
time.sleep(duration)
self.control(self.BREAK_ARCH_UPPER, 'close')
def _close_break_arch_lower(self):
time.sleep(3)
self.control(self.BREAK_ARCH_LOWER, 'close')
def control_arch_upper_open(self):
"""控制上料斗"""
def control_arch_upper_open_async(self,delay_seconds: float = 15):
"""异步控制上料斗振动
Args:
delay_seconds: 延迟关闭时间默认15秒
"""
# 关闭下料斗出砼门
self.control(self.BREAK_ARCH_UPPER, 'open')
# 异步5秒后关闭
threading.Thread(target=self._close_break_arch_upper, daemon=True,name="_close_break_arch_upper").start()
def _close_break_arch_upper(self):
time.sleep(3)
threading.Thread(target=lambda d: self._close_break_arch_upper(delay_seconds),args=(delay_seconds,), daemon=True, name="_close_break_arch_upper").start()
def _close_break_arch_upper(self, delay_seconds: float = 15):
time.sleep(delay_seconds)
print(f"上料斗振动关闭完成,延迟{delay_seconds}")
self.control(self.BREAK_ARCH_UPPER, 'close')

View File

@ -80,7 +80,7 @@ class TransmitterController:
self.relay_controller.modbus_client.close()
# 直接读取 变送器返回的数据并解析
def read_data_normal(self, transmitter_id):
def read_data_sub(self, transmitter_id):
"""
Args: transmitter_id 为1 表示上料斗, 为2 表示下料斗
@ -90,7 +90,7 @@ class TransmitterController:
BUFFER_SIZE= 1024
IP = None
PORT = None
weight = 0
weight = None
if transmitter_id == 1:
# 上料斗变送器的信息:
IP = ini_manager.upper_transmitter_ip
@ -102,7 +102,7 @@ class TransmitterController:
if not IP or not PORT:
print(f"未配置变送器 {transmitter_id} 的IP或PORT")
return 0
return None
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
s.settimeout(TIMEOUT)
@ -134,7 +134,7 @@ class TransmitterController:
# 成功返回重量int失败返回None
return weight
def read_data(self, transmitter_id):
def read_data_sub_test(self, transmitter_id):
"""
测试用模拟读取变送器数据mock
@ -266,4 +266,16 @@ class TransmitterController:
except (IndexError, ValueError, UnicodeDecodeError) as e:
# print(f"数据解析失败:{e},原始数据包:{packet_data}")
return None
def read_data(self,transmitter_id):
"""获取重量函数根据变送器ID获取当前重量,三次"""
max_try_times=5
try_times=0
while try_times<max_try_times:
weight=self.read_data_sub(transmitter_id)
if weight is not None:
return weight
try_times+=1
print(f'-----获取重量异常-------------- transmitter_id: {transmitter_id}')
print(f'-----获取重量异常-------------- transmitter_id: {transmitter_id}')
return None

31
test_dal_debug.py Normal file
View File

@ -0,0 +1,31 @@
from busisness.dals import ArtifactDal
# 测试 exists_by_module_code 方法
dal = ArtifactDal()
# 测试1: 不存在的模具编号
print("=== 测试1: 不存在的模具编号 ===")
result = dal.exists_by_module_code("不存在的模具编号")
print(f"结果: {result}")
print()
# 测试2: 存在的模具编号(如果有数据的话)
print("=== 测试2: 存在的模具编号 ===")
result = dal.exists_by_module_code("TEST001")
print(f"结果: {result}")
print()
# 测试3: 直接查询数据库验证
print("=== 测试3: 直接SQL查询验证 ===")
db_dao = dal.db_dao
sql = "SELECT count(1) as cnt FROM ArtifactTask WHERE MouldCode = ?"
results = db_dao.execute_read(sql, ("不存在的模具编号",))
print(f"查询结果类型: {type(results)}")
print(f"查询结果: {results}")
rows = list(results)
print(f"rows: {rows}")
if rows:
print(f"rows[0]: {rows[0]}")
print(f"rows[0][0]: {rows[0][0]}")
else:
print("rows为空列表")

View File

@ -1,16 +1,10 @@
# main.py
import time
from datetime import datetime
from config.settings import app_set_config
from core.system import FeedingControlSystem
from hardware import relay
from hardware.relay import RelayController
from hardware.transmitter import TransmitterController
import threading
import time
import cv2
import os
from core.state import FeedStatus
def main():

View File

@ -6,25 +6,55 @@ import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import time
from hardware.RFID.rfid_service import rfid_service
from busisness.blls import ArtifactBll
from busisness.models import ArtifactInfoModel
def test_data_callback(status,raw_data):
rfid=None
rfid_before_mould=''
artifact_bll=ArtifactBll()
def test_data_callback(status,data):
"""
测试用的数据接收回调函数
"""
if status:
print(f"[回调] 收到RFID数据: {raw_data}")
else:
print(f"[回调] 读取RFID数据为空或失败")
global rfid_before_mould
try:
if status==1:
#成功读取RFID标签
#检查标识是否符号要求
if data:
loc_array=data.strip(',').split(',')
if len(loc_array)==4:
if rfid_before_mould!=loc_array[0]:
model={
'MouldCode':loc_array[0],
'BlockNumber':loc_array[1],
'SizeSpecification':loc_array[2],
'BetonVolume':loc_array[3]
}
artifact_bll.insert_artifact_bycode(model)
rfid_before_mould=loc_array[0]
print(f"RFID-生产模具车号:{loc_array[0]}")
else:
print(f"RFID-重复生产模具车号:{loc_array[0]}")
else:
print("RFID标签格式错误")
print(f"成功读取到RFID标签:{data}")
else:
self.rfid_flag_succ=False
print("读取RFID标签失败")
except Exception as e:
print(f"RFID回调处理异常: {e}")
def test_rfid_functions():
"""
测试RFIDHardware的主要功能
"""
global rfid
# 初始化RFID控制器
rfid = rfid_service(host='192.168.250.77', port=6000)
rfid = rfid_service(host='192.168.250.67', port=6000)
# print("=== RFID硬件测试开始 ===")
@ -96,21 +126,38 @@ def test_rfid_functions():
# rfid._data_buffer.append('THR B1-12,B1,6600 * 1500,1.900')
# rfid._data_buffer.append('THR B1-12,B1,6600 * 1500,1.900')
# rfid._process_collected_data()
rfid.start_receiver(callback=test_data_callback)
rfid.start_receiver(callback=test_data_callback)
while True:
rfid._pause_receive = False
time.sleep(10)
# print("接收线程已启动,等待接收数据...")
# 等待5秒模拟接收过程1111111111111
time.sleep(60*60)
finally:
# 确保停止接收线程
# rfid.stop_receiver()
rfid.stop_receiver()
print("\n=== RFID硬件测试结束 ===")
# print("\n=== RFID硬件测试结束 ===")
if __name__ == "__main__":
try:
# model={
# 'ArtifactID':0,
# 'MouldCode':'SHR2B1-4',
# 'BlockNumber':'B2',
# 'SizeSpecification':'6600*1500',
# 'BetonVolume':1.910
# }
# artifact_bll.insert_artifact_bycode(model)
test_rfid_functions()
except KeyboardInterrupt:
if rfid is not None:
rfid.stop_receiver()
print("\n测试被用户中断")
except Exception as e:
if rfid is not None:
rfid.stop_receiver()
print(f"测试过程中发生错误: {e}")

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