文章目录
垃圾分拣机械臂
总体介绍
本作品将视觉识别技术部署在嵌入式设备,自动控制机械臂进行分拣任务,在我们的设计中,首先使用深度相机将图像信息发送到嵌入式设备,视觉识别算法进行分类检测,将垃圾分为感染性,损失性,病理性,药物性,化学性,并根据相机的深度值计算出目标的三维坐标,由于系统使用眼在手外的结构,根据手眼标定的结果,将坐标转换为机械臂坐标系下的运功坐标,然后机械臂执行分拣任务。
主要功能与特色
本作品结合了图像识别技术,轻量化改进了 YOLOv8 算法,实现了嵌入式设备部署,本作品能够对医疗垃圾自动化分类和分拣,极大提升了处理效率和准确性。系统使用深度相机,实现了对不同高度的垃圾准确检测,能够精准分拣多
种医疗垃圾,包括但不限于针头、玻璃容器、塑料包装、口罩和注射器等,确保了垃圾处理的高准确率和符合规定的分类标准。该系统能够长时间稳定运行,处
理大规模的医疗垃圾流量,高度自动化的系统减少了对人工操作的依赖,降低了
人为错误的可能性,同时减轻了操作人员的劳动强度,提高了整体的工作效率和
作业安全性。
该系统在设计时充分考虑了环保和节能的要求,采用手在眼外的结构设计,
当系统运行时,相机保持不动,减少了遮挡视野的可能性,使得机械臂手可以更
精准地控制操作,减少了能源消耗,符合当前对绿色医疗垃圾处理的倡导。
视频演示
点击查看
文件目录
- 使用YoloV8做的目标检测,机械臂手眼标定使用Aruco的方式,通过深度相机获取三维坐标,与机械臂坐标系之间的转化,得到抓取的坐标
- 深度相机是dabai
- 机械臂自己打印
程序主代码
python
import ctypes
import os
import threading
import time
import cv2
import numpy as np
import serial
from openni import openni2
from ultralytics import YOLO
# 多进程
from multiprocessing import Process, Value, Queue, Array
from orbbec_init import initialize_openni, configure_depth_stream, convert_depth_to_xyz
from port_test import Ser
def orbbec_video(center_p_queue, robot_status):
"""
使用相机进行视频捕捉和物体检测的函数。
"""
model = YOLO('best.pt')
redist_path = "F:\study-python\dabeipro\OpenNI_2.3.0.86_202210111950_4c8f5aa4_beta6_windows\Win64-Release\sdk\libs"
width, height, fps = 640, 400, 30
fx, fy, cx, cy = 524.382751, 524.382751, 324.768829, 212.350189
def mouse_callback(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDBLCLK:
print(x, y, img[y, x])
print("三维坐标:", convert_depth_to_xyz(x, y, img[y, x], fx, fy, cx, cy))
dev = initialize_openni(redist_path)
print(dev.get_device_info())
depth_stream = configure_depth_stream(dev, width, height, fps)
cv2.namedWindow('depth')
cv2.namedWindow('color')
# cv2.setMouseCallback('depth', mouse_callback)
cap = cv2.VideoCapture(0)
fps = 0.0
try:
while True:
t1 = time.time()
# 开始读取深度视频流
frame = depth_stream.read_frame()
# intr = frame.profile.as_video_stream_profile().intrinsics
# print(intr)
frame_data = frame.get_buffer_as_uint16()
# print(frame_data)
img = np.ndarray((frame.height, frame.width), dtype=np.uint16, buffer=frame_data)
dim_gray = cv2.convertScaleAbs(img, alpha=0.17)
kernel_size = 5
dim_gray = cv2.medianBlur(dim_gray, kernel_size)
depth_colormap = cv2.applyColorMap(dim_gray, 2)
# 开始读取彩色视频流
ret, frame = cap.read()
frame = cv2.resize(frame, (640, 400))
frame = cv2.flip(frame, 1)
# 开始推理
results = model.predict(source=frame, **{'save': False, 'conf': 0.62, 'verbose': False}, )
result = results[0].boxes.data.tolist()
result_list = []
max_score_bbox = [0, 0, 0, 0]
category_dict = {
'plastic bottle': 0,
'glass bottle': 0,
'mask': 1,
'gauze': 1,
'injector': 2
}
for idx in range(len(result)):
xmin = int(result[idx][0])
ymin = int(result[idx][1])
xmax = int(result[idx][2])
ymax = int(result[idx][3])
conf = round(float(result[idx][4]), 2)
cls_idx = int(result[idx][5])
cls_name = model.names[cls_idx]
result_list.append([ymin, xmin, ymax, xmax, conf, cls_idx, cls_name])
cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)
box_color = (0, 0, 255)
x = int((xmax - xmin) / 2 + xmin)
y = int((ymax - ymin) / 2 + ymin)
# print(result_list, "中点:", x, y, conf, cls_name)
if y > 400:
print("目标超出深度图像范围")
continue
if conf > max_score_bbox[0]:
max_score_bbox[0] = conf
max_score_bbox[1:] = [x, y, category_dict[cls_name]]
# put text under box
# center_p_queue.put([x_cam, y_cam, z_cam])
# print("center_p_queue执行")
x_cam, y_cam, z_cam = convert_depth_to_xyz(x, y, img[y, x], fx, fy, cx, cy)
cv2.circle(frame, (x,y), 3, (0, 0, 255), -1)
cv2.putText(frame, cls_name, (xmin, ymin + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, box_color, 2)
cv2.putText(frame, "x: {:.3f}".format(x_cam), (xmin, ymin + 40), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
box_color, 2)
cv2.putText(frame, "y: {:.3f}".format(y_cam), (xmin, ymin + 60), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
box_color, 2)
cv2.putText(frame, "z: {:.3f}".format(z_cam), (xmin, ymin + 80), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
box_color, 2)
# print("conf最大", max_score_bbox)
x_cam, y_cam, z_cam = convert_depth_to_xyz(max_score_bbox[1], max_score_bbox[2],
img[max_score_bbox[2], max_score_bbox[1]], fx, fy, cx, cy)
if robot_status.value == 0 and max_score_bbox[0] > 0.25:
center_p_queue.put([x_cam, y_cam, z_cam, max_score_bbox[-1]])
print(x_cam, y_cam, z_cam)
print("center_p_queue执行")
robot_status.value = 1
# # 处理深度图像,生成深度图的彩色版本
# depth_img = np.asanyarray(depth.get_data())
# depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(depth_img, alpha=alpha_val), cv2.COLORMAP_JET)
fps = (fps + (1. / (time.time() - t1))) / 2
depth_colormap = cv2.putText(depth_colormap, "fps= %.2f" % (fps), (0, 40), cv2.FONT_HERSHEY_SIMPLEX, 1,(0, 255, 0), 2)
# 根据机器人的状态,在图像上显示状态信息
if robot_status.value == 0:
status_text = "Status: Searching"
else:
status_text = "Status: Running"
cv2.putText(frame, status_text, (400, 80), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0) if robot_status == 0 else (0, 0, 255), 2)
# 将颜色图像和深度图像叠加显示
# images = np.vstack((color_image, depth_colormap))
cv2.imshow('color', frame)
cv2.imshow('depth', depth_colormap)
key = cv2.waitKey(10)
if int(key) == 113:
break
finally:
# pipeline.stop() # 关闭相机和视频写入器
openni2.unload()
dev.close()
def send_message(contunts):
if ser.isOpen(): # 如果串口已经打开
if contunts:
# com1端口向com2端口写入数据 字符串必须译码
# self.contunts = input("输入内容:")
# 可以自定义输入Gcode命令,规划路径,例如 G1 X10 Y10 Z10 F30,其实就是三维坐标和速度
ser.write((contunts + "\r").encode("utf-8"))
time.sleep(1)
# encode()函数是将字符串转化成相应编码方式的字节形式
# 如str2.encode('utf-8'),表示将unicode编码的字符串str2转换成utf-8编码的字节数据。
# 如果不转换,COM1发送到COM2的信息,COM2(调试助手)中文会识别不出来或者会出现乱码现象
else: # 如果没有读取到com1串口,则执行以下程序
print("open failed")
def robot_grasp(center_p_queue, robot_status):
"""
使用dobot机械臂进行抓取操作。
"""
# 检查图像到机械臂转换参数文件是否存在
if os.path.exists("./save_parms/image_to_arm.npy"):
image_to_arm = np.load("./save_parms/image_to_arm.npy")
else:
print("image_to_arm.npy not exist")
return
#send_message("G28")
ser.write("G28\r".encode("utf-8"))
# send_message("M114")
print("发送成功")
# robot_status.value = 0
while True:
# 循环等待并处理目标中心点信息
if robot_status.value:
# 从队列获取中心点信息
center_p = center_p_queue.get()
center = center_p[0:3]
# 计算机械臂需要移动到的位置
img_pos = np.ones(4)
img_pos[0:3] = center
arm_pos = np.dot(image_to_arm, np.array(img_pos))
print("arm_pos", arm_pos)
# 如果目标位置超出机械臂可达范围,则跳过此次操作
# if np.sqrt(arm_pos[0] * arm_pos[0] + arm_pos[1] * arm_pos[1]) > 320:
# print("Can not reach!!!!!!!!!!!!!!!, distance: {}".format(np.sqrt(arm_pos[0] * arm_pos[0] + arm_pos[1] * arm_pos[1])))
send_message(f"G1 X{0} Y{185} Z{160}") # 回原点
send_message(f"G1 X{arm_pos[0]} Y{arm_pos[1]} Z{arm_pos[2]+80} ") # 移动到目标上方50mm处
send_message(f"G1 X{arm_pos[0]} Y{arm_pos[1]} Z{arm_pos[2]-25} ") # 移动到目标上方处
send_message("M5") # 执行抓取
time.sleep(5)
send_message(f"G1 X{arm_pos[0]} Y{arm_pos[1]} Z{arm_pos[2]+80} ") # 再次移动到目标上方20mm处
if center_p[3] == 0:
send_message(f"G1 X{170} Y{0} Z{160}") # 病理性废物位置
if center_p[3] == 2:
send_message(f"G1 X{-170} Y{30} Z{160}") # 损伤性废物位置
if center_p[3] == 1:
send_message(f"G1 X{170} Y{120} Z{160}") # 感染性废物
send_message("M3") # 打开夹爪
time.sleep(4)
# action.send_message(f"G1 X{0} Y{185} Z{160} ") # 返回原点
print("another one")
# 重置为搜索状态
robot_status.value = 0
else:
# 如果没有检测到目标标记,重置为搜索状态
print("no marker detected")
time.sleep(3)
# robot_status.value = 0
continue
if __name__ == "__main__":
center_arr = Array(ctypes.c_double, [0, 0, 0]) # 存储中心点坐标
robot_status = Value(ctypes.c_int8, 0)
center_p_queue = Queue()
ser = serial.Serial("COM5", baudrate=115200, timeout=2, )
time.sleep(2)
process1 = Process(target=orbbec_video, args=(center_p_queue, robot_status,))
# process2 = Process(target=robot_grasp, args=(center_p_queue, robot_status,))
process1.start()
# process2.start()
robot_grasp(center_p_queue, robot_status)
process1.join()
# process2.join()
完整代码链接
通过网盘分享的文件:robot-arm2
链接: https://pan.baidu.com/s/1GRImjGJQSKJ1_L6rph5LgA?pwd=cje5 提取码: cje5