本文是用python实现上位机开发,使机械臂与视觉结合进行拾取物体。这个过程对我来说是不容易的,因此我是分步实现局部功能,再结合,这样难度会降低。我把整个过程分成了界面,相机,图像处理,标定四个小部分。机械臂用的开塔米罗机械臂,相机是海康相机。
#准备
在编写代码之前,必须要做的就是通读SDK文件,了解它在python中是用何代码进行编写二次开发的。读SDK文件一定要仔细,不然在编写程序时,一个很小的问题能让你搞半天。机械臂连接的话一定要看是否需要通讯协议,我使用的这款不需要。
此外,为更好了解机械臂如何实现,最好先用机械臂自带软件进行操作,熟悉其运动模式和过程。
#界面
我是用tk进行上位机开发。界面设计首先设计好按键位置,例如复位按钮宽度为10,高度为1,执行的命令是home(),只需要在def home()编写对应的代码就能通过复位按钮进行调用。机械臂是有限位的,运动范围是有限的的,使用的机械臂在每次使用前都必须先进行复位操作,且每动作都要“休息”一下,因而需要time.sleep(),定义的步长movelength为5,即每按一次按钮,机械臂移动5个单位距离,都是这个代码只是大概的限位了,各个关节直接的限位我并没有定义。
from tkinter import *
from wlkata_mirobot import WlkataMirobot
import time
arm = WlkataMirobot()
arm.home()
arm.set_speed(2000)
#界面控制类
class Window:
def __init__(self, win, ww, wh):
self.win = win
self.win.geometry("%dx%d+%d+%d" % (ww, wh, 50, 50)) # 界面启动时的初始位置
self.win.title("上位机")
self.btn_home = Button(self.win, text='复位', width=10, height=1, command=self.home)
self.btn_home.place(x=350, y=100)
self.btn_go_to_zero = Button(self.win, text='回零', width=10, height=1, command=self.go_to_zero)
self.btn_go_to_zero.place(x=500, y=100)
self.btn_gripper_stop = Button(self.win, text='打开爪夹', width=10, height=1, command=self.gripper_open)
self.btn_gripper_stop.place(x=350, y=200)
self.btn_clear_close = Button(self.win, text='关闭爪夹', width=10, height=1,command=self.gripper_close)
self.btn_clear_close.place(x=500, y=200)
self.bj1a = Button(self.win, text='x+', width=10, height=1, command=self.load_bj1a)
self.bj1a.place(x=25, y=300)
self.bj1d = Button(self.win, text='x-', width=10, height=1, command=self.load_bj1d)
self.bj1d.place(x=25, y=400)
self.bj2a = Button(self.win, text='y+', width=10, height=1, command=self.load_bj2a)
self.bj2a.place(x=125, y=300)
self.bj2d = Button(self.win, text='y-', width=10, height=1, command=self.load_bj2d)
self.bj2d.place(x=125, y=400)
self.bj3a = Button(self.win, text='z+', width=10, height=1, command=self.load_bj3a)
self.bj3a.place(x=225, y=300)
self.bj3d = Button(self.win, text='z-', width=10, height=1, command=self.load_bj3d)
self.bj3d.place(x=225, y=400)
self.bj4a = Button(self.win, text='a+', width=10, height=1, command=self.load_bj4a)
self.bj4a.place(x=325, y=300)
self.bj4d = Button(self.win, text='a-', width=10, height=1, command=self.load_bj4d)
self.bj4d.place(x=325, y=400)
self.bj5a = Button(self.win, text='b+', width=10, height=1, command=self.load_bj5a)
self.bj5a.place(x=425, y=300)
self.bj5d = Button(self.win, text='b-', width=10, height=1, command=self.load_bj5d)
self.bj5d.place(x=425, y=400)
self.bj6a = Button(self.win, text='c+', width=10, height=1, command=self.load_bj6a)
self.bj6a.place(x=525, y=300)
self.bj6d = Button(self.win, text='c-', width=10, height=1, command=self.load_bj6d)
self.bj6d.place(x=525, y=400)
self.can_pred = Canvas(self.win, width=450, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred.place(x=25, y=460)
self.movelenght = 20.0
def home(self):
arm.home()
print('复位')
def go_to_zero(self):
arm.go_to_zero()
print('回零')
def gripper_open(self):
arm.gripper_open()
print('打开爪夹')
def gripper_close(self):
arm.gripper_close()
print('关闭爪夹')
def load_bj1a(self):
aa = arm.pose.x
time.sleep(1)
bb = aa + self.movelenght
if 125 <= bb<=275:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
time.sleep(1)
arm.set_tool_pose(bb, arm.pose.y, arm.pose.z, arm.pose.roll, arm.pose.pitch, arm.pose.yaw)
time.sleep(1)
def load_bj1d(self):
aa = arm.pose.x
time.sleep(1)
bb = aa - self.movelenght
if 125 <= bb<=275:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
arm.set_tool_pose(bb,arm.pose.y, arm.pose.z, arm.pose.roll, arm.pose.pitch, arm.pose.yaw)
def load_bj2a(self):
aa = arm.pose.y
time.sleep(1)
bb = aa + self.movelenght
if -195 <= bb <= 190:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
arm.set_tool_pose(arm.pose.x, bb, arm.pose.z, arm.pose.roll, arm.pose.pitch, arm.pose.yaw)
def load_bj2d(self):
aa = arm.pose.y
time.sleep(1)
bb = aa - self.movelenght
if -195 <= bb <= 190:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
arm.set_tool_pose(arm.pose.x, bb, arm.pose.z, arm.pose.roll, arm.pose.pitch, arm.pose.yaw)
def load_bj3a(self):
aa = arm.pose.z
time.sleep(1)
bb = aa + self.movelenght
if 55 <= bb <= 315:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
arm.set_tool_pose(arm.pose.x, arm.pose.y, bb, arm.pose.roll, arm.pose.pitch, arm.pose.yaw)
def load_bj3d(self):
aa = arm.pose.z
time.sleep(1)
bb = aa - self.movelenght
if 55 <= bb <= 315:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
arm.set_tool_pose(arm.pose.x, arm.pose.y, bb, arm.pose.roll, arm.pose.pitch, arm.pose.yaw)
def load_bj4a(self):
aa = arm.pose.roll
time.sleep(1)
bb = aa + self.movelenght
print("运动前:%s 运动后:%s" % (aa, bb))
arm.set_tool_pose(arm.pose.x, arm.pose.y, arm.pose.z, bb, arm.pose.pitch, arm.pose.yaw)
def load_bj4d(self):
aa = arm.pose.roll
time.sleep(1)
bb = aa - self.movelenght
print("运动前:%s 运动后:%s" % (aa, bb))
arm.set_tool_pose(arm.pose.x, arm.pose.y, arm.pose.z, bb, arm.pose.pitch, arm.pose.yaw)
def load_bj5a(self):
aa = arm.pose.pitch
time.sleep(1)
bb = aa + self.movelenght
if -4855 <= bb <=4720:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
arm.set_tool_pose(arm.pose.x, arm.pose.y, arm.pose.z, arm.pose.roll, bb, arm.pose.yaw)
def load_bj5d(self):
aa = arm.pose.pitch
time.sleep(1)
bb = aa - self.movelenght
if -4855 <= bb < 4720:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
arm.set_tool_pose(arm.pose.x, arm.pose.y, arm.pose.z, arm.pose.roll, bb, arm.pose.yaw)
def load_bj6a(self):
aa = arm.pose.yaw
bb = aa + self.movelenght
print("运动前:%s 运动后:%s" % (aa, bb))
arm.set_tool_pose(arm.pose.x, arm.pose.y, arm.pose.z, arm.pose.roll, arm.pose.pitch, bb)
def load_bj6d(self):
aa = arm.pose.yaw
bb = aa - self.movelenght
print("运动前:%s 运动后:%s" % (aa, bb))
arm.set_tool_pose(arm.pose.x, arm.pose.y, arm.pose.z, arm.pose.roll, arm.pose.pitch, bb)
#主函数
if __name__ == '__main__':
win = Tk()
ww =700
wh = 510
Window(win, ww, wh)
win.mainloop()
机械臂的这是运行后的界面,x,y,z,a,b,c对应机械臂的坐标控制模式的六个维度
#相机
sys.path.append("./MvImport")添加相机路径,我的在c盘,直接这样添加报错了,所以我在File-settings-project:ttyuyin.py-projectstructure中add路径MvImport,这样就不会报错了(也可以试下添加完整的路径)。在运行这一部分代码之前,先用海康相机自带软件进行连接,改好IP地址。
def Init_Cam():相机初始化,枚举子网内指定的传输协议对应的所有设备,具体代码含义看相机sdk使用手册获取。通过设备型号来查找可用的相机,用于后续的相机连接。
这是图像采集显示的流程图,代码SDK使用手册上有,可以参考。
from tkinter import *
import numpy as np
import cv2
import threading
from PIL import Image, ImageTk
import sys
sys.path.append("./MvImport")
from MvCameraControl_class import *
class Window:
# 初始化界面
def __init__(self, win, ww, wh):
self.win = win
self.ww = ww
self.wh = wh
self.win.geometry("%dx%d+%d+%d" % (ww, wh, 50, 50)) # 界面启动时的初始位置
self.win.title("实时图像采集")
self.vidLabel = Label(win, anchor=NW)
self.vidLabel.pack(expand=YES, fill=BOTH)
self.vidLabel2 = Label(win, anchor=NW)
self.vidLabel2.pack(expand=YES, fill=BOTH)
self.yunxing = Button(self.win, text='运行相机', width=10, height=1, command=self.load_yunxing)
self.yunxing.place(x=565, y=185)
self.close_cam = Button(self.win, text='释放相机', width=10, height=1, command=self.close_cam)
self.close_cam.place(x=765, y=185)
self.close_cam = Button(self.win, text='图像处理', width=10, height=1, command=self.deal_pic_but)
self.close_cam.place(x=565, y=285)
self.can_pred1_x = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred1_x.place(x=535, y=385)
self.can_pred1_y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred1_y.place(x=745, y=385)
self.can_pred2_x = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred2_x.place(x=535, y=485)
self.can_pred2_y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred2_y.place(x=745, y=485)
self.can_pred3_x = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred3_x.place(x=535, y=585)
self.can_pred3_y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred3_y.place(x=745, y=585)
self.BOOL = True
self.cam = MvCamera()
self.nConnectionNum = 0
self.g_bExit = False
self.g_bdeal_pic = False
self.i_iIndex_deal = 0
# 初始化相机
def Init_Cam(self):
deviceList = MV_CC_DEVICE_INFO_LIST()
tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
# 枚举设备
ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
if ret != 0:
print("enum devices fail! ret[0x%x]" % ret)
sys.exit()
#未找到相机设备则退出
if deviceList.nDeviceNum == 0:
print("find no device!")
sys.exit()
print("Find %d devices!" % deviceList.nDeviceNum)
# 找到则循环遍历,获取相机设备信息并打印
for i in range(0, deviceList.nDeviceNum):
mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
print("\ngige device: [%d]" % i)
strModeName = ""
for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
strModeName = strModeName + chr(per)
print("device model name: %s" % strModeName)
nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
print("current ip: %d.%d.%d.%d\n" % (nip1, nip2, nip3, nip4))
elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
print("\nu3v device: [%d]" % i)
strModeName = ""
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName:
if per == 0:
break
strModeName = strModeName + chr(per)
print("device model name: %s" % strModeName)
strSerialNumber = ""
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
if per == 0:
break
strSerialNumber = strSerialNumber + chr(per)
print("user serial number: %s" % strSerialNumber)
# 若相机设备被占用无法连接退出
if int(self.nConnectionNum) >= deviceList.nDeviceNum:
print("intput error!")
sys.exit()
# 选择设备
stDeviceList = cast(deviceList.pDeviceInfo[int(self.nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents
ret = self.cam.MV_CC_CreateHandle(stDeviceList)
if ret != 0:
print("create handle fail! ret[0x%x]" % ret)
sys.exit()
# 打开设备
ret = self.cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
if ret != 0:
print("open device fail! ret[0x%x]" % ret)
sys.exit()
# 探测网络最佳包大小
if stDeviceList.nTLayerType == MV_GIGE_DEVICE:
nPacketSize = self.cam.MV_CC_GetOptimalPacketSize()
if int(nPacketSize) > 0:
ret = self.cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize)
if ret != 0:
print("Warning: Set Packet Size fail! ret[0x%x]" % ret)
else:
print("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)
# 设置触发模式为off
ret = self.cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
if ret != 0:
print("set trigger mode fail! ret[0x%x]" % ret)
sys.exit()
# 获取数据包大小
stParam = MVCC_INTVALUE()
memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))
ret = self.cam.MV_CC_GetIntValue("PayloadSize", stParam)
if ret != 0:
print("get payload size fail! ret[0x%x]" % ret)
sys.exit()
nPayloadSize = stParam.nCurValue
return nPayloadSize
# 相机开始采集图像
def Start(self):
ret = self.cam.MV_CC_StartGrabbing()
if ret != 0:
print("start grabbing fail! ret[0x%x]" % ret)
sys.exit()
# 判读图像格式是彩色还是黑白
def IsImageColor(self, enType):
dates = {
PixelType_Gvsp_RGB8_Packed: 'color',
PixelType_Gvsp_BGR8_Packed: 'color',
PixelType_Gvsp_YUV422_Packed: 'color',
PixelType_Gvsp_YUV422_YUYV_Packed: 'color',
PixelType_Gvsp_BayerGR8: 'color',
PixelType_Gvsp_BayerRG8: 'color',
PixelType_Gvsp_BayerGB8: 'color',
PixelType_Gvsp_BayerBG8: 'color',
PixelType_Gvsp_BayerGB10: 'color',
PixelType_Gvsp_BayerGB10_Packed: 'color',
PixelType_Gvsp_BayerBG10: 'color',
PixelType_Gvsp_BayerBG10_Packed: 'color',
PixelType_Gvsp_BayerRG10: 'color',
PixelType_Gvsp_BayerRG10_Packed: 'color',
PixelType_Gvsp_BayerGR10: 'color',
PixelType_Gvsp_BayerGR10_Packed: 'color',
PixelType_Gvsp_BayerGB12: 'color',
PixelType_Gvsp_BayerGB12_Packed: 'color',
PixelType_Gvsp_BayerBG12: 'color',
PixelType_Gvsp_BayerBG12_Packed: 'color',
PixelType_Gvsp_BayerRG12: 'color',
PixelType_Gvsp_BayerRG12_Packed: 'color',
PixelType_Gvsp_BayerGR12: 'color',
PixelType_Gvsp_BayerGR12_Packed: 'color',
PixelType_Gvsp_Mono8: 'mono',
PixelType_Gvsp_Mono10: 'mono',
PixelType_Gvsp_Mono10_Packed: 'mono',
PixelType_Gvsp_Mono12: 'mono',
PixelType_Gvsp_Mono12_Packed: 'mono'}
return dates.get(enType, '未知')
# 图像显示
def Image_show(self, image):
# 利用CV_BGR2GRAY将原图src转换为灰度图bgr2grayImg
cvimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGBA)
pilImage = Image.fromarray(cvimage)
pilImage = pilImage.resize((400, 400), Image.ANTIALIAS)
# 将图片显示到界面
tkImage = ImageTk.PhotoImage(image=pilImage)
self.vidLabel.configure(image=tkImage)
self.vidLabel.image = tkImage
if self.g_bdeal_pic==True:
self.g_bdeal_pic=False
x,y=self.deal_pic(image)#得到正方体中心坐标
#中心坐标显示到上位机界面
if self.i_iIndex_deal==1:
self.can_pred1_x.create_text(0, 0, text=str(x), anchor='nw', font=('黑体', 20))
self.can_pred1_y.create_text(0, 0, text=str(y), anchor='nw', font=('黑体', 20))
if self.i_iIndex_deal==2:
self.can_pred2_x.create_text(0, 0, text=str(x), anchor='nw', font=('黑体', 20))
self.can_pred2_y.create_text(0, 0, text=str(y), anchor='nw', font=('黑体', 20))
if self.i_iIndex_deal==3:
self.can_pred3_x.create_text(0, 0, text=str(x), anchor='nw', font=('黑体', 20))
self.can_pred3_y.create_text(0, 0, text=str(y), anchor='nw', font=('黑体', 20))
print(x)
print(y)
# 利用CV_BGR2GRAY将原图src转换为灰度图bgr2grayImg
cvimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGBA)
pilImage = Image.fromarray(cvimage)
pilImage = pilImage.resize((400, 400), Image.ANTIALIAS)
# 将图片显示到界面
tkImage = ImageTk.PhotoImage(image=pilImage)
self.vidLabel2.configure(image=tkImage)
self.vidLabel2.image = tkImage
def Work_thread(self, cam=0, pData=0, nDataSize=0):
stFrameInfo = MV_FRAME_OUT_INFO_EX()
memset(byref(stFrameInfo), 0, sizeof(stFrameInfo))
img_buff = None
while self.BOOL:
ret = cam.MV_CC_GetOneFrameTimeout(pData, nDataSize, stFrameInfo, 1000)
if ret == 0:
stConvertParam = MV_CC_PIXEL_CONVERT_PARAM()
memset(byref(stConvertParam), 0, sizeof(stConvertParam))
if self.IsImageColor(stFrameInfo.enPixelType) == 'mono':
stConvertParam.enDstPixelType = PixelType_Gvsp_Mono8
nConvertSize = stFrameInfo.nWidth * stFrameInfo.nHeight
elif self.IsImageColor(stFrameInfo.enPixelType) == 'color':
stConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed
nConvertSize = stFrameInfo.nWidth * stFrameInfo.nHeight * 3
else:
print("not support!!!")
if img_buff is None:
img_buff = (c_ubyte * stFrameInfo.nFrameLen)()
# ---
stConvertParam.nWidth = stFrameInfo.nWidth
stConvertParam.nHeight = stFrameInfo.nHeight
stConvertParam.pSrcData = cast(pData, POINTER(c_ubyte))
stConvertParam.nSrcDataLen = stFrameInfo.nFrameLen
stConvertParam.enSrcPixelType = stFrameInfo.enPixelType
stConvertParam.pDstBuffer = (c_ubyte * nConvertSize)()
stConvertParam.nDstBufferSize = nConvertSize
ret = cam.MV_CC_ConvertPixelType(stConvertParam)
if ret != 0:
print("convert pixel fail! ret[0x%x]" % ret)
del stConvertParam.pSrcData
sys.exit()
else:
# 黑白处理
if self.IsImageColor(stFrameInfo.enPixelType) == 'mono':
img_buff = (c_ubyte * stConvertParam.nDstLen)()
memmove(byref(img_buff), stConvertParam.pDstBuffer, stConvertParam.nDstLen)
img_buff = np.frombuffer(img_buff, count=int(stConvertParam.nDstLen), dtype=np.uint8)
img_buff = img_buff.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth))
self.Image_show(image=img_buff)
# 彩色处理
if self.IsImageColor(stFrameInfo.enPixelType) == 'color':
img_buff = (c_ubyte * stConvertParam.nDstLen)()
memmove(byref(img_buff), stConvertParam.pDstBuffer, stConvertParam.nDstLen)
img_buff = np.frombuffer(img_buff, count=int(stConvertParam.nDstBufferSize), dtype=np.uint8)
img_buff = img_buff.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, 3)
self.Image_show(image=img_buff)
else:
print("no data[0x%x]" % ret)
if self.g_bExit == True:
break
def load_yunxing(self):
# 相机初始化
nPayloadSize = self.Init_Cam()
self.Start()
data_buf = (c_ubyte * nPayloadSize)()
try:
hThreadHandle = threading.Thread(target=self.Work_thread, args=(self.cam, byref(data_buf), nPayloadSize))
hThreadHandle.start()
except:
print("error: unable to start thread")
def close_cam(self):
# 停止取流
ret = self.cam.MV_CC_StopGrabbing()
if ret != 0:
print("stop grabbing fail! ret[0x%x]" % ret)
sys.exit()
# 关闭设备
ret = self.cam.MV_CC_CloseDevice()
if ret != 0:
print("close deivce fail! ret[0x%x]" % ret)
sys.exit()
# 销毁句柄
ret = self.cam.MV_CC_DestroyHandle()
print("close deivce succ" )
self.BOOL=False
# 图像处理
def deal_pic_but(self):
self.i_iIndex_deal=self.i_iIndex_deal+1
self.g_bdeal_pic=True
def deal_pic(self,image):
(R, G, B) = cv2.split(image)
diff_RG = cv2.absdiff(R, G)
diff_GB = cv2.absdiff(G, B)
diff_RB = cv2.absdiff(R, B)
mean_np = np.zeros(3, dtype=np.double)
mean_np[0] = cv2.mean(diff_RG)[0]
mean_np[1] = cv2.mean(diff_GB)[0]
mean_np[2] = cv2.mean(diff_RB)[0]
list_a_max_list = max(mean_np)
max_index = np.argmax(mean_np)
if int(max_index) == 0:
ret, thresh1 = cv2.threshold(diff_RG, list_a_max_list + 20, 255,
cv2.THRESH_BINARY)
elif int(max_index) == 1:
ret, thresh1 = cv2.threshold(diff_GB, list_a_max_list + 20, 255, cv2.THRESH_BINARY)
elif int(max_index) == 2:
ret, thresh1 = cv2.threshold(diff_RB, list_a_max_list + 20, 255, cv2.THRESH_BINARY)
contours, hier = cv2.findContours(thresh1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 筛选的尺寸
min_size = 100
max_size = 1800
lengths = list()
save_list = []
for i in range(len(contours)):
length = cv2.arcLength(contours[i], True)
lengths.append(length)
if (length < max_size and length > min_size):
save_list.append(contours[i])
new_contours = save_list
for c in new_contours:
M = cv2.moments(c)
x = int(M["m10"] / M["m00"])
y = int(M["m01"] / M["m00"])
cv2.circle(image, (x, y), 2, (0, 0, 255), -1)
cv2.drawContours(image, c, -1, (0, 0, 255), 3)
need_x = x
need_y = y
return need_x, need_y
#主函数
if __name__ == '__main__':·
win = Tk()
ww = 1000
wh = 820
Window(win, ww, wh)
win.mainloop()
#图像处理
图像处理是在相机代码的基础上添加的。首先,将图片转为转换为RGB模式,因为cv2是BGR模式;再将array转换成image,并把图片裁剪成400*400的大小,显示到界面上。然后,调用图像处理函数对原图进行处理,计算相机中正方体的中心坐标(正方体最上面一面),并将图像显示到上位机程序中,并显示中心点位置,此位置是在相机坐标中的数值。最后,将处理过后的图片显示处理,包括正方体轮廓及其中心点位置。
from tkinter import *
import numpy as np
import cv2
import threading
from PIL import Image, ImageTk
import sys
sys.path.append("./MvImport")
from MvCameraControl_class import *
class Window:
# 初始化界面
def __init__(self, win, ww, wh):
self.win = win
self.ww = ww
self.wh = wh
self.win.geometry("%dx%d+%d+%d" % (ww, wh, 50, 50)) # 界面启动时的初始位置
self.win.title("实时图像采集")
self.vidLabel = Label(win, anchor=NW)
self.vidLabel.pack(expand=YES, fill=BOTH)
self.vidLabel2 = Label(win, anchor=NW)
self.vidLabel2.pack(expand=YES, fill=BOTH)
self.yunxing = Button(self.win, text='运行相机', width=10, height=1, command=self.load_yunxing)
self.yunxing.place(x=565, y=185)
self.close_cam = Button(self.win, text='释放相机', width=10, height=1, command=self.close_cam)
self.close_cam.place(x=765, y=185)
self.close_cam = Button(self.win, text='图像处理', width=10, height=1, command=self.deal_pic_but)
self.close_cam.place(x=565, y=285)
self.can_pred1_x = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred1_x.place(x=535, y=385)
self.can_pred1_y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred1_y.place(x=745, y=385)
self.can_pred2_x = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred2_x.place(x=535, y=485)
self.can_pred2_y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred2_y.place(x=745, y=485)
self.can_pred3_x = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred3_x.place(x=535, y=585)
self.can_pred3_y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred3_y.place(x=745, y=585)
self.BOOL = True
self.cam = MvCamera()
self.nConnectionNum = 0
self.g_bExit = False
self.g_bdeal_pic = False
self.i_iIndex_deal = 0
# 初始化相机
def Init_Cam(self):
deviceList = MV_CC_DEVICE_INFO_LIST()
tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
# 枚举设备
ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
if ret != 0:
print("enum devices fail! ret[0x%x]" % ret)
sys.exit()
if deviceList.nDeviceNum == 0:
print("find no device!")
sys.exit()
print("Find %d devices!" % deviceList.nDeviceNum)
for i in range(0, deviceList.nDeviceNum):
mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
print("\ngige device: [%d]" % i)
strModeName = ""
for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
strModeName = strModeName + chr(per)
print("device model name: %s" % strModeName)
nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
print("current ip: %d.%d.%d.%d\n" % (nip1, nip2, nip3, nip4))
elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
print("\nu3v device: [%d]" % i)
strModeName = ""
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName:
if per == 0:
break
strModeName = strModeName + chr(per)
print("device model name: %s" % strModeName)
strSerialNumber = ""
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
if per == 0:
break
strSerialNumber = strSerialNumber + chr(per)
print("user serial number: %s" % strSerialNumber)
if int(self.nConnectionNum) >= deviceList.nDeviceNum:
print("intput error!")
sys.exit()
# 选择设备
stDeviceList = cast(deviceList.pDeviceInfo[int(self.nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents
ret = self.cam.MV_CC_CreateHandle(stDeviceList)
if ret != 0:
print("create handle fail! ret[0x%x]" % ret)
sys.exit()
# 打开设备
ret = self.cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
if ret != 0:
print("open device fail! ret[0x%x]" % ret)
sys.exit()
# 探测网络最佳包大小
if stDeviceList.nTLayerType == MV_GIGE_DEVICE:
nPacketSize = self.cam.MV_CC_GetOptimalPacketSize()
if int(nPacketSize) > 0:
ret = self.cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize)
if ret != 0:
print("Warning: Set Packet Size fail! ret[0x%x]" % ret)
else:
print("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)
# 设置触发模式为off
ret = self.cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
if ret != 0:
print("set trigger mode fail! ret[0x%x]" % ret)
sys.exit()
# 获取数据包大小
stParam = MVCC_INTVALUE()
memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))
ret = self.cam.MV_CC_GetIntValue("PayloadSize", stParam)
if ret != 0:
print("get payload size fail! ret[0x%x]" % ret)
sys.exit()
nPayloadSize = stParam.nCurValue
return nPayloadSize
# 相机开始采集图像
def Start(self):
ret = self.cam.MV_CC_StartGrabbing()
if ret != 0:
print("start grabbing fail! ret[0x%x]" % ret)
sys.exit()
# 判读图像格式是彩色还是黑白
def IsImageColor(self, enType):
dates = {
PixelType_Gvsp_RGB8_Packed: 'color',
PixelType_Gvsp_BGR8_Packed: 'color',
PixelType_Gvsp_YUV422_Packed: 'color',
PixelType_Gvsp_YUV422_YUYV_Packed: 'color',
PixelType_Gvsp_BayerGR8: 'color',
PixelType_Gvsp_BayerRG8: 'color',
PixelType_Gvsp_BayerGB8: 'color',
PixelType_Gvsp_BayerBG8: 'color',
PixelType_Gvsp_BayerGB10: 'color',
PixelType_Gvsp_BayerGB10_Packed: 'color',
PixelType_Gvsp_BayerBG10: 'color',
PixelType_Gvsp_BayerBG10_Packed: 'color',
PixelType_Gvsp_BayerRG10: 'color',
PixelType_Gvsp_BayerRG10_Packed: 'color',
PixelType_Gvsp_BayerGR10: 'color',
PixelType_Gvsp_BayerGR10_Packed: 'color',
PixelType_Gvsp_BayerGB12: 'color',
PixelType_Gvsp_BayerGB12_Packed: 'color',
PixelType_Gvsp_BayerBG12: 'color',
PixelType_Gvsp_BayerBG12_Packed: 'color',
PixelType_Gvsp_BayerRG12: 'color',
PixelType_Gvsp_BayerRG12_Packed: 'color',
PixelType_Gvsp_BayerGR12: 'color',
PixelType_Gvsp_BayerGR12_Packed: 'color',
PixelType_Gvsp_Mono8: 'mono',
PixelType_Gvsp_Mono10: 'mono',
PixelType_Gvsp_Mono10_Packed: 'mono',
PixelType_Gvsp_Mono12: 'mono',
PixelType_Gvsp_Mono12_Packed: 'mono'}
return dates.get(enType, '未知')
# 图像显示
def Image_show(self, image):
cvimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGBA) #将BGR转换为RGBA
pilImage = Image.fromarray(cvimage) #array转换成image
pilImage = pilImage.resize((400, 400), Image.ANTIALIAS)#修改图片尺寸为400*400
# 将原图片显示到界面
tkImage = ImageTk.PhotoImage(image=pilImage)
self.vidLabel.configure(image=tkImage) #配置样式
self.vidLabel.image = tkImage
#显示中心点坐标
if self.g_bdeal_pic==True:
self.g_bdeal_pic=False
x,y=self.deal_pic(image)
if self.i_iIndex_deal==1:
self.can_pred1_x.create_text(0, 0, text=str(x), anchor='nw', font=('黑体', 20))
self.can_pred1_y.create_text(0, 0, text=str(y), anchor='nw', font=('黑体', 20))
if self.i_iIndex_deal==2:
self.can_pred2_x.create_text(0, 0, text=str(x), anchor='nw', font=('黑体', 20))
self.can_pred2_y.create_text(0, 0, text=str(y), anchor='nw', font=('黑体', 20))
if self.i_iIndex_deal==3:
self.can_pred3_x.create_text(0, 0, text=str(x), anchor='nw', font=('黑体', 20))
self.can_pred3_y.create_text(0, 0, text=str(y), anchor='nw', font=('黑体', 20))
print(x)
print(y)
cvimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGBA)
pilImage = Image.fromarray(cvimage)
pilImage = pilImage.resize((400, 400), Image.ANTIALIAS)
# 将处理后的图片显示到界面
tkImage = ImageTk.PhotoImage(image=pilImage)
self.vidLabel2.configure(image=tkImage)
self.vidLabel2.image = tkImage
def Work_thread(self, cam=0, pData=0, nDataSize=0):
stFrameInfo = MV_FRAME_OUT_INFO_EX()
memset(byref(stFrameInfo), 0, sizeof(stFrameInfo))
img_buff = None
while self.BOOL:
ret = cam.MV_CC_GetOneFrameTimeout(pData, nDataSize, stFrameInfo, 1000)
if ret == 0:
stConvertParam = MV_CC_PIXEL_CONVERT_PARAM()
memset(byref(stConvertParam), 0, sizeof(stConvertParam))
if self.IsImageColor(stFrameInfo.enPixelType) == 'mono':
stConvertParam.enDstPixelType = PixelType_Gvsp_Mono8
nConvertSize = stFrameInfo.nWidth * stFrameInfo.nHeight
elif self.IsImageColor(stFrameInfo.enPixelType) == 'color':
stConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed
nConvertSize = stFrameInfo.nWidth * stFrameInfo.nHeight * 3
else:
print("not support!!!")
if img_buff is None:
img_buff = (c_ubyte * stFrameInfo.nFrameLen)()
# ---
stConvertParam.nWidth = stFrameInfo.nWidth
stConvertParam.nHeight = stFrameInfo.nHeight
stConvertParam.pSrcData = cast(pData, POINTER(c_ubyte))
stConvertParam.nSrcDataLen = stFrameInfo.nFrameLen
stConvertParam.enSrcPixelType = stFrameInfo.enPixelType
stConvertParam.pDstBuffer = (c_ubyte * nConvertSize)()
stConvertParam.nDstBufferSize = nConvertSize
ret = cam.MV_CC_ConvertPixelType(stConvertParam)
if ret != 0:
print("convert pixel fail! ret[0x%x]" % ret)
del stConvertParam.pSrcData
sys.exit()
else:
# 黑白处理
if self.IsImageColor(stFrameInfo.enPixelType) == 'mono':
img_buff = (c_ubyte * stConvertParam.nDstLen)()
memmove(byref(img_buff), stConvertParam.pDstBuffer, stConvertParam.nDstLen)
img_buff = np.frombuffer(img_buff, count=int(stConvertParam.nDstLen), dtype=np.uint8)
img_buff = img_buff.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth))
self.Image_show(image=img_buff)
# 彩色处理
if self.IsImageColor(stFrameInfo.enPixelType) == 'color':
img_buff = (c_ubyte * stConvertParam.nDstLen)()
memmove(byref(img_buff), stConvertParam.pDstBuffer, stConvertParam.nDstLen)
img_buff = np.frombuffer(img_buff, count=int(stConvertParam.nDstBufferSize), dtype=np.uint8)
img_buff = img_buff.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, 3)
self.Image_show(image=img_buff)
else:
print("no data[0x%x]" % ret)
if self.g_bExit == True:
break
def load_yunxing(self):
# 相机初始化
nPayloadSize = self.Init_Cam()
self.Start()
data_buf = (c_ubyte * nPayloadSize)()
try:
hThreadHandle = threading.Thread(target=self.Work_thread, args=(self.cam, byref(data_buf), nPayloadSize))
hThreadHandle.start()
except:
print("error: unable to start thread")
def close_cam(self):
# 停止取流
ret = self.cam.MV_CC_StopGrabbing()
if ret != 0:
print("stop grabbing fail! ret[0x%x]" % ret)
sys.exit()
# 关闭设备
ret = self.cam.MV_CC_CloseDevice()
if ret != 0:
print("close deivce fail! ret[0x%x]" % ret)
sys.exit()
# 销毁句柄
ret = self.cam.MV_CC_DestroyHandle()
print("close deivce succ" )
self.BOOL=False
# 图像处理
def deal_pic_but(self):
self.i_iIndex_deal=self.i_iIndex_deal+1
self.g_bdeal_pic=True
def deal_pic(self,image):
(R, G, B) = cv2.split(image)
diff_RG = cv2.absdiff(R, G) #计算绝对差值
diff_GB = cv2.absdiff(G, B)
diff_RB = cv2.absdiff(R, B)
mean_np = np.zeros(3, dtype=np.double)
mean_np[0] = cv2.mean(diff_RG)[0] #计算均值
mean_np[1] = cv2.mean(diff_GB)[0]
mean_np[2] = cv2.mean(diff_RB)[0]
list_a_max_list = max(mean_np)
max_index = np.argmax(mean_np) #返回最大值
if int(max_index) == 0:
ret, thresh1 = cv2.threshold(diff_RG, list_a_max_list + 20, 255,
cv2.THRESH_BINARY) #阈值处理灰度图像
elif int(max_index) == 1:
ret, thresh1 = cv2.threshold(diff_GB, list_a_max_list + 20, 255, cv2.THRESH_BINARY)
elif int(max_index) == 2:
ret, thresh1 = cv2.threshold(diff_RB, list_a_max_list + 20, 255, cv2.THRESH_BINARY)
contours, hier = cv2.findContours(thresh1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)#图像轮廓检测
# 筛选的尺寸
min_size = 100
max_size = 1800
lengths = list()
save_list = []
for i in range(len(contours)):
length = cv2.arcLength(contours[i], True)
lengths.append(length)
if (length < max_size and length > min_size):
save_list.append(contours[i])
new_contours = save_list
for c in new_contours:
M = cv2.moments(c) #重心计算
x = int(M["m10"] / M["m00"])
y = int(M["m01"] / M["m00"])
cv2.circle(image, (x, y), 2, (0, 0, 255), -1)
cv2.drawContours(image, c, -1, (0, 0, 255), 3)
need_x = x
need_y = y
return need_x, need_y #得到中心点位置(相机坐标)
#主函数
if __name__ == '__main__':
win = Tk()
ww = 1000
wh = 820
Window(win, ww, wh)
win.mainloop()
运行结果:
#标定
此部分内容最主要的就是仿射矩阵的求取,从相机坐标到世界坐标的转换。先用图像处理得到中心点的坐标,固定好机械臂底座保持不动之后,用机械臂自带程库控制机械臂移动到中心点位置,记录下此时坐标控制模式下的坐标,如此反复三组数据(构成一个三角形),就可求出仿射矩阵。利用仿射矩阵的逆变换就可以在知道正方体位置的情况下,使机械臂移动到正方体中心点,完成智能拾物。z轴的位置大致是不变的,取一个平均值即可,为了使标定更为准确,末轴没有放置爪夹,若想更加完整实现抓取物体,可以设置一个偏置量(添加爪夹后,末端工具中点点不在末端中心),完成打开爪夹-下降-关闭爪夹-上升。
from tkinter import *
import numpy as np
import cv2
import sys
sys.path.append("./MvImport")
class Window:
def __init__(self, win, ww, wh):
#相机坐标
self.point1_x =441
self.point1_y =552
self.point2_x =431
self.point2_y =280
self.point3_x =289
self.point3_y =261
#世界坐标(机械臂坐标)
self.point1_X =14.6
self.point1_Y =-171
self.point2_X =-27.4
self.point2_Y =-171
self.point3_X =-27.4
self.point3_Y =-191
pts1 = np.float32(
[[self.point1_x, self.point1_y], [self.point2_x, self.point2_y], [self.point3_x, self.point3_y]])
pts2 = np.float32(
[[self.point1_X, self.point1_Y], [self.point2_X, self.point2_Y], [self.point3_X, self.point3_Y]])
self.M = cv2.getAffineTransform(pts1, pts2)#仿射变化,仿射矩阵为2*3
print(self.M)
x=np.dot(self.M, [523,681,1]) #仿射逆变换,得到坐标(x,y)
print(x)
if __name__ == '__main__':
win = Tk()
ww =700
wh = 510
Window(win, ww, wh)
win.mainloop()
完整代码
#头文件
from wlkata_mirobot import WlkataMirobot
import time
from tkinter import *
import numpy as np
import cv2
import threading
from PIL import Image, ImageTk
import sys
sys.path.append("./MvImport")
from MvCameraControl_class import *
#界面控制类
class Window:
def __init__(self, win, ww, wh):
self.win = win
self.win.geometry("%dx%d+%d+%d" % (ww, wh, 50, 50)) # 界面启动时的初始位置
self.win.title("上位机")
self.vidLabel = Label(win, anchor=NW)
self.vidLabel.pack(expand=YES, fill=BOTH)
self.vidLabel2 = Label(win, anchor=NW)
self.vidLabel2.pack(expand=YES, fill=BOTH)
self.btn_home = Button(self.win, text='复位', width=10, height=1, command=self.home)
self.btn_home.place(x=800, y=100)
self.btn_go_to_zero = Button(self.win, text='回零', width=10, height=1, command=self.go_to_zero)
self.btn_go_to_zero.place(x=900, y=100)
self.btn_gripper_stop = Button(self.win, text='打开爪夹', width=10, height=1, command=self.gripper_open)
self.btn_gripper_stop.place(x=800, y=200)
self.btn_clear_close = Button(self.win, text='关闭爪夹', width=10, height=1,command=self.gripper_close)
self.btn_clear_close.place(x=900, y=200)
self.bj1a = Button(self.win, text='x+', width=10, height=1, command=self.load_bj1a)
self.bj1a.place(x=800, y=250)
self.bj1d = Button(self.win, text='x-', width=10, height=1, command=self.load_bj1d)
self.bj1d.place(x=900, y=250)
self.bj2a = Button(self.win, text='y+', width=10, height=1, command=self.load_bj2a)
self.bj2a.place(x=800, y=300)
self.bj2d = Button(self.win, text='y-', width=10, height=1, command=self.load_bj2d)
self.bj2d.place(x=900, y=300)
self.bj3a = Button(self.win, text='z+', width=10, height=1, command=self.load_bj3a)
self.bj3a.place(x=800, y=350)
self.bj3d = Button(self.win, text='z-', width=10, height=1, command=self.load_bj3d)
self.bj3d.place(x=900, y=350)
self.bj4a = Button(self.win, text='a+', width=10, height=1, command=self.load_bj4a)
self.bj4a.place(x=800, y=400)
self.bj4d = Button(self.win, text='a-', width=10, height=1, command=self.load_bj4d)
self.bj4d.place(x=900, y=400)
self.bj5a = Button(self.win, text='b+', width=10, height=1, command=self.load_bj5a)
self.bj5a.place(x=800, y=450)
self.bj5d = Button(self.win, text='b-', width=10, height=1, command=self.load_bj5d)
self.bj5d.place(x=900, y=450)
self.bj6a = Button(self.win, text='c+', width=10, height=1, command=self.load_bj6a)
self.bj6a.place(x=800, y=500)
self.bj6d = Button(self.win, text='c-', width=10, height=1, command=self.load_bj6d)
self.bj6d.place(x=900, y=500)
self.yunxing = Button(self.win, text='运行相机', width=10, height=1, command=self.load_yunxing)
self.yunxing.place(x=800, y=0)
self.close_cam = Button(self.win, text='释放相机', width=10, height=1, command=self.close_cam)
self.close_cam.place(x=900, y=0)
self.close_cam = Button(self.win, text='图像处理', width=10, height=1, command=self.deal_pic_but)
self.close_cam.place(x=570, y=5)
self.can_pred1_x = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred1_x.place(x=450, y=40)
self.can_pred1_y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred1_y.place(x=600, y=40)
self.can_pred2_x = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred2_x.place(x=450, y=90)
self.can_pred2_y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred2_y.place(x=600, y=90)
self.can_pred3_x = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred3_x.place(x=450, y=140)
self.can_pred3_y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred3_y.place(x=600, y=140)
self.btn_zhauqugongjian = Button(self.win, text='抓取工件', width=10, height=1, command=self.deal_getworkpiece)
self.btn_zhauqugongjian.place(x=570, y=220)
self.can_pred1_X = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred1_X.place(x=450, y=255)
self.can_pred1_Y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred1_Y.place(x=600, y=255)
self.can_pred2_X = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred2_X.place(x=450, y=305)
self.can_pred2_Y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred2_Y.place(x=600, y=305)
self.can_pred3_X = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred3_X.place(x=450, y=355)
self.can_pred3_Y = Canvas(self.win, width=150, height=40, bg='white', relief='solid', borderwidth=1)
self.can_pred3_Y.place(x=600, y=355)
self.btn_teach.place(x=570, y=420)
self.BOOL = True
self.cam = MvCamera()
self.nConnectionNum = 0
self.g_bExit = False
self.g_bdeal_pic = False
self.g_bdeal_getworkpiece = False
self.i_iIndex_deal = 0
self.movelenght = 20.0
self.i_iIndex_dealrobot = 0
self.point1_x = 633
self.point1_y = 646
self.point2_x = 232
self.point2_y = 612
self.point3_x = 640
self.point3_y = 158
self.point1_X = 47.6
self.point1_Y = -141
self.point2_X = 51.6
self.point2_Y = -195
self.point3_X = -24.4
self.point3_Y = -153
pts1 = np.float32(
[[self.point1_x, self.point1_y], [self.point2_x, self.point2_y], [self.point3_x, self.point3_y]])
pts2 = np.float32(
[[self.point1_X, self.point1_Y], [self.point2_X, self.point2_Y], [self.point3_X, self.point3_Y]])
self.M = cv2.getAffineTransform(pts1, pts2)
self.arm = WlkataMirobot()
#界面
def home(self):
self.arm.home()
print('复位')
def go_to_zero(self):
self.arm.go_to_zero()
print('回零')
def gripper_open(self):
self.arm.gripper_open()
print('打开爪夹')
def gripper_close(self):
self.arm.gripper_close()
print('关闭爪夹')
def load_bj1a(self):
aa = self.arm.pose.x
time.sleep(1)
bb = aa + self.movelenght
if 125 <= bb<=275:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
time.sleep(1)
self.arm.set_tool_pose(bb, self.arm.pose.y, self.arm.pose.z, self.arm.pose.roll, self.arm.pose.pitch, self.arm.pose.yaw)
time.sleep(1)
def load_bj1d(self):
aa = self.arm.pose.x
time.sleep(1)
bb = aa - self.movelenght
if 125 <= bb<=275:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
self.arm.set_tool_pose(bb,self.arm.pose.y, self.arm.pose.z, self.arm.pose.roll, self.arm.pose.pitch, self.arm.pose.yaw)
def load_bj2a(self):
aa = self.arm.pose.y
time.sleep(1)
bb = aa + self.movelenght
if -195 <= bb <= 190:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
self.arm.set_tool_pose(self.arm.pose.x, bb, self.arm.pose.z, self.arm.pose.roll, self.arm.pose.pitch, self.arm.pose.yaw)
def load_bj2d(self):
aa = self.arm.pose.y
time.sleep(1)
bb = aa - self.movelenght
if -195 <= bb <= 190:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
self.arm.set_tool_pose(self.arm.pose.x, bb, self.arm.pose.z, self.arm.pose.roll, self.arm.pose.pitch, self.arm.pose.yaw)
def load_bj3a(self):
aa = self.arm.pose.z
time.sleep(1)
bb = aa + self.movelenght
if 55 <= bb <= 315:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
self.arm.set_tool_pose(self.arm.pose.x, self.arm.pose.y, bb, self.arm.pose.roll, self.arm.pose.pitch, self.arm.pose.yaw)
def load_bj3d(self):
aa = self.arm.pose.z
time.sleep(1)
bb = aa - self.movelenght
if 55 <= bb <= 315:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
self.arm.set_tool_pose(self.arm.pose.x, self.arm.pose.y, bb, self.arm.pose.roll, self.arm.pose.pitch, self.arm.pose.yaw)
def load_bj4a(self):
aa = self.arm.pose.roll
time.sleep(1)
bb = aa + self.movelenght
print("运动前:%s 运动后:%s" % (aa, bb))
self.arm.set_tool_pose(self.arm.pose.x, self.arm.pose.y, self.arm.pose.z, bb, self.arm.pose.pitch, self.arm.pose.yaw)
def load_bj4d(self):
aa = self.arm.pose.roll
time.sleep(1)
bb = aa - self.movelenght
print("运动前:%s 运动后:%s" % (aa, bb))
self.arm.set_tool_pose(self.arm.pose.x, self.arm.pose.y, self.arm.pose.z, bb, self.arm.pose.pitch, self.arm.pose.yaw)
def load_bj5a(self):
aa = self.arm.pose.pitch
time.sleep(1)
bb = aa + self.movelenght
if -4855 <= bb <=4720:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
self.arm.set_tool_pose(self.arm.pose.x, self.arm.pose.y, self.arm.pose.z, self.arm.pose.roll, bb, self.arm.pose.yaw)
def load_bj5d(self):
aa = self.arm.pose.pitch
time.sleep(1)
bb = aa - self.movelenght
if -4855 <= bb < 4720:
print("运动前:%s 运动后:%s" % (aa, bb))
else:
print("超过允许限位")
self.arm.set_tool_pose(self.arm.pose.x, self.arm.pose.y, self.arm.pose.z, self.arm.pose.roll, bb, self.arm.pose.yaw)
def load_bj6a(self):
aa = self.arm.pose.yaw
bb = aa + self.movelenght
print("运动前:%s 运动后:%s" % (aa, bb))
self.arm.set_tool_pose(self.arm.pose.x, self.arm.pose.y, self.arm.pose.z, self.arm.pose.roll, self.arm.pose.pitch, bb)
def load_bj6d(self):
aa = self.arm.pose.yaw
bb = aa - self.movelenght
print("运动前:%s 运动后:%s" % (aa, bb))
self.arm.set_tool_pose(self.arm.pose.x, self.arm.pose.y, self.arm.pose.z, self.arm.pose.roll, self.arm.pose.pitch, bb)
#相机
def Init_Cam(self):
deviceList = MV_CC_DEVICE_INFO_LIST()
tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
# 枚举设备
ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
if ret != 0:
print("enum devices fail! ret[0x%x]" % ret)
sys.exit()
if deviceList.nDeviceNum == 0:
print("find no device!")
sys.exit()
print("Find %d devices!" % deviceList.nDeviceNum)
for i in range(0, deviceList.nDeviceNum):
mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
print("\ngige device: [%d]" % i)
strModeName = ""
for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
strModeName = strModeName + chr(per)
print("device model name: %s" % strModeName)
nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
print("current ip: %d.%d.%d.%d\n" % (nip1, nip2, nip3, nip4))
elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
print("\nu3v device: [%d]" % i)
strModeName = ""
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName:
if per == 0:
break
strModeName = strModeName + chr(per)
print("device model name: %s" % strModeName)
strSerialNumber = ""
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
if per == 0:
break
strSerialNumber = strSerialNumber + chr(per)
print("user serial number: %s" % strSerialNumber)
if int(self.nConnectionNum) >= deviceList.nDeviceNum:
print("intput error!")
sys.exit()
# 选择设备
stDeviceList = cast(deviceList.pDeviceInfo[int(self.nConnectionNum)],
POINTER(MV_CC_DEVICE_INFO)).contents
ret = self.cam.MV_CC_CreateHandle(stDeviceList)
if ret != 0:
print("create handle fail! ret[0x%x]" % ret)
sys.exit()
# 打开设备
ret = self.cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
if ret != 0:
print("open device fail! ret[0x%x]" % ret)
sys.exit()
# 探测网络最佳包大小
if stDeviceList.nTLayerType == MV_GIGE_DEVICE:
nPacketSize = self.cam.MV_CC_GetOptimalPacketSize()
if int(nPacketSize) > 0:
ret = self.cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize)
if ret != 0:
print("Warning: Set Packet Size fail! ret[0x%x]" % ret)
else:
print("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)
# 设置触发模式为off
ret = self.cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
if ret != 0:
print("set trigger mode fail! ret[0x%x]" % ret)
sys.exit()
# 获取数据包大小
stParam = MVCC_INTVALUE()
memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))
ret = self.cam.MV_CC_GetIntValue("PayloadSize", stParam)
if ret != 0:
print("get payload size fail! ret[0x%x]" % ret)
sys.exit()
nPayloadSize = stParam.nCurValue
return nPayloadSize
# 相机开始采集图像
def Start(self):
ret = self.cam.MV_CC_StartGrabbing()
if ret != 0:
print("start grabbing fail! ret[0x%x]" % ret)
sys.exit()
# 判读图像格式是彩色还是黑白
def IsImageColor(self, enType):
dates = {
PixelType_Gvsp_RGB8_Packed: 'color',
PixelType_Gvsp_BGR8_Packed: 'color',
PixelType_Gvsp_YUV422_Packed: 'color',
PixelType_Gvsp_YUV422_YUYV_Packed: 'color',
PixelType_Gvsp_BayerGR8: 'color',
PixelType_Gvsp_BayerRG8: 'color',
PixelType_Gvsp_BayerGB8: 'color',
PixelType_Gvsp_BayerBG8: 'color',
PixelType_Gvsp_BayerGB10: 'color',
PixelType_Gvsp_BayerGB10_Packed: 'color',
PixelType_Gvsp_BayerBG10: 'color',
PixelType_Gvsp_BayerBG10_Packed: 'color',
PixelType_Gvsp_BayerRG10: 'color',
PixelType_Gvsp_BayerRG10_Packed: 'color',
PixelType_Gvsp_BayerGR10: 'color',
PixelType_Gvsp_BayerGR10_Packed: 'color',
PixelType_Gvsp_BayerGB12: 'color',
PixelType_Gvsp_BayerGB12_Packed: 'color',
PixelType_Gvsp_BayerBG12: 'color',
PixelType_Gvsp_BayerBG12_Packed: 'color',
PixelType_Gvsp_BayerRG12: 'color',
PixelType_Gvsp_BayerRG12_Packed: 'color',
PixelType_Gvsp_BayerGR12: 'color',
PixelType_Gvsp_BayerGR12_Packed: 'color',
PixelType_Gvsp_Mono8: 'mono',
PixelType_Gvsp_Mono10: 'mono',
PixelType_Gvsp_Mono10_Packed: 'mono',
PixelType_Gvsp_Mono12: 'mono',
PixelType_Gvsp_Mono12_Packed: 'mono'}
return dates.get(enType, '未知')
#图像显示函数
def Image_show(self, image):
cvimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGBA) # 利用CV_BGR2GRAY将原图src转换为灰度图bgr2grayImg
pilImage = Image.fromarray(cvimage) # 实现array到image的转换
pilImage = pilImage.resize((400, 400), Image.ANTIALIAS) # resize(InputArray src, OutputArray dst, Size dsize,
tkImage = ImageTk.PhotoImage(image=pilImage) # 将图片显示到界面
self.vidLabel.configure(image=tkImage)
self.vidLabel.image = tkImage
if self.g_bdeal_getworkpiece == True:
self.g_bdeal_getworkpiece = False
x, y = self.deal_pic(image)
c = np.array([[x], [y], [1]])
dd = np.dot(self.M, c)
print(dd[0])
print(dd[1])
self.arm.set_tool_pose(int(dd[0]), int(dd[1]), 33, 0, 0, 0)
time.sleep(2)
self.i_iIndex_dealrobot = self.i_iIndex_dealrobot + 1
if self.i_iIndex_dealrobot == 1:
self.can_pred1_X.create_text(0, 0, text=int(dd[0]), anchor='nw', font=('黑体', 20))
self.can_pred1_Y.create_text(0, 0, text=int(dd[1]), anchor='nw', font=('黑体', 20))
self.point1_X = int(dd[0])
self.point1_Y = int(dd[1])
if self.i_iIndex_dealrobot == 2:
self.can_pred2_X.create_text(0, 0, text=int(dd[0]), anchor='nw', font=('黑体', 20))
self.can_pred2_Y.create_text(0, 0, text=int(dd[1]), anchor='nw', font=('黑体', 20))
self.point2_X = int(dd[0])
self.point2_Y = int(dd[1])
if self.i_iIndex_dealrobot == 3:
self.can_pred3_X.create_text(0, 0, text=int(dd[0]), anchor='nw', font=('黑体', 20))
self.can_pred3_Y.create_text(0, 0, text=int(dd[1]), anchor='nw', font=('黑体', 20))
self.point3_X = int(dd[0])
self.point3_Y = int(dd[1])
if self.g_bdeal_pic==True:
self.g_bdeal_pic=False
x,y=self.deal_pic(image)
if self.i_iIndex_deal==1:
self.can_pred1_x.create_text(0, 0, text=str(x), anchor='nw', font=('黑体', 20))
self.can_pred1_y.create_text(0, 0, text=str(y), anchor='nw', font=('黑体', 20))
self.point1_x = x
self.point1_y = y
if self.i_iIndex_deal==2:
self.can_pred2_x.create_text(0, 0, text=str(x), anchor='nw', font=('黑体', 20))
self.can_pred2_y.create_text(0, 0, text=str(y), anchor='nw', font=('黑体', 20))
self.point2_x = x
self.point2_y = y
if self.i_iIndex_deal==3:
self.can_pred3_x.create_text(0, 0, text=str(x), anchor='nw', font=('黑体', 20))
self.can_pred3_y.create_text(0, 0, text=str(y), anchor='nw', font=('黑体', 20))
self.point3_x = x
self.point3_y = y
# 利用CV_BGR2GRAY将原图src转换为灰度图bgr2grayImg
cvimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGBA)
pilImage = Image.fromarray(cvimage)
pilImage = pilImage.resize((400, 400), Image.ANTIALIAS)
# 将图片显示到界面
tkImage = ImageTk.PhotoImage(image=pilImage)
self.vidLabel2.configure(image=tkImage)
self.vidLabel2.image = tkImage
def Work_thread(self, cam=0, pData=0, nDataSize=0):
stFrameInfo = MV_FRAME_OUT_INFO_EX()
memset(byref(stFrameInfo), 0, sizeof(stFrameInfo))
img_buff = None
while self.BOOL:
ret = cam.MV_CC_GetOneFrameTimeout(pData, nDataSize, stFrameInfo, 1000)
if ret == 0:
stConvertParam = MV_CC_PIXEL_CONVERT_PARAM()
memset(byref(stConvertParam), 0, sizeof(stConvertParam))
if self.IsImageColor(stFrameInfo.enPixelType) == 'mono':
stConvertParam.enDstPixelType = PixelType_Gvsp_Mono8
nConvertSize = stFrameInfo.nWidth * stFrameInfo.nHeight
elif self.IsImageColor(stFrameInfo.enPixelType) == 'color':
stConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed
nConvertSize = stFrameInfo.nWidth * stFrameInfo.nHeight * 3
else:
print("not support!!!")
if img_buff is None:
img_buff = (c_ubyte * stFrameInfo.nFrameLen)()
# ---
stConvertParam.nWidth = stFrameInfo.nWidth
stConvertParam.nHeight = stFrameInfo.nHeight
stConvertParam.pSrcData = cast(pData, POINTER(c_ubyte))
stConvertParam.nSrcDataLen = stFrameInfo.nFrameLen
stConvertParam.enSrcPixelType = stFrameInfo.enPixelType
stConvertParam.pDstBuffer = (c_ubyte * nConvertSize)()
stConvertParam.nDstBufferSize = nConvertSize
ret = cam.MV_CC_ConvertPixelType(stConvertParam)
if ret != 0:
print("convert pixel fail! ret[0x%x]" % ret)
del stConvertParam.pSrcData
sys.exit()
else:
# 黑白处理
if self.IsImageColor(stFrameInfo.enPixelType) == 'mono':
img_buff = (c_ubyte * stConvertParam.nDstLen)()
memmove(byref(img_buff), stConvertParam.pDstBuffer, stConvertParam.nDstLen)
img_buff=np.frombuffer(img_buff, count=int(stConvertParam.nDstLen), dtype=np.uint8)
img_buff=img_buff.reshape((stFrameInfo.nHeight,stFrameInfo.nWidth))
self.Image_show(image=img_buff)
# 彩色处理
if self.IsImageColor(stFrameInfo.enPixelType) == 'color':
img_buff = (c_ubyte * stConvertParam.nDstLen)()
memmove(byref(img_buff),stConvertParam.pDstBuffer, stConvertParam.nDstLen)
img_buff=np.frombuffer(img_buff, count=int(stConvertParam.nDstBufferSize), dtype=np.uint8)
img_buff = img_buff.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, 3)
self.Image_show(image=img_buff)
else:
print("no data[0x%x]" % ret)
if self.g_bExit == True:
break
def load_yunxing(self):
nPayloadSize = self.Init_Cam() # 相机初始化
self.Start()
data_buf = (c_ubyte * nPayloadSize)()
try:
hThreadHandle = threading.Thread(target=self.Work_thread, args=(self.cam, byref(data_buf), nPayloadSize))
hThreadHandle.start()
except:
print("error: unable to start thread")
def close_cam(self):
# 停止取流
ret = self.cam.MV_CC_StopGrabbing()
if ret != 0:
print("stop grabbing fail! ret[0x%x]" % ret)
sys.exit()
# 关闭设备
ret = self.cam.MV_CC_CloseDevice()
if ret != 0:
print("close deivce fail! ret[0x%x]" % ret)
sys.exit()
# 销毁句柄
ret = self.cam.MV_CC_DestroyHandle()
print("close deivce succ")
self.BOOL = False
# 图像处理
def deal_pic_but(self):
self.i_iIndex_deal=self.i_iIndex_deal+1
self.g_bdeal_pic=True
def deal_getworkpiece(self):
print(self.M)
self.g_bdeal_getworkpiece = True
def deal_pic(self,image):
(R, G, B) = cv2.split(image)
diff_RG = cv2.absdiff(R, G)
diff_GB = cv2.absdiff(G, B)
diff_RB = cv2.absdiff(R, B)
mean_np = np.zeros(3, dtype=np.double)
mean_np[0] = cv2.mean(diff_RG)[0]
mean_np[1] = cv2.mean(diff_GB)[0]
mean_np[2] = cv2.mean(diff_RB)[0]
list_a_max_list = max(mean_np)
max_index = np.argmax(mean_np)
if int(max_index) == 0:
ret, thresh1 = cv2.threshold(diff_RG, list_a_max_list + 20, 255,
cv2.THRESH_BINARY)
elif int(max_index) == 1:
ret, thresh1 = cv2.threshold(diff_GB, list_a_max_list + 20, 255, cv2.THRESH_BINARY)
elif int(max_index) == 2:
ret, thresh1 = cv2.threshold(diff_RB, list_a_max_list + 20, 255, cv2.THRESH_BINARY)
contours, hier = cv2.findContours(thresh1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 筛选的尺寸
min_size = 100
max_size = 1800
lengths = list()
save_list = []
for i in range(len(contours)):
length = cv2.arcLength(contours[i], True)
lengths.append(length)
if (length < max_size and length > min_size):
save_list.append(contours[i])
new_contours = save_list
for c in new_contours:
# find bounding box coordinates
new_c = c.reshape((c.shape[0], c.shape[2]))
x, y = new_c.sum(axis=0) / new_c.shape[0]
x = int(x)
y = int(y)
cv2.circle(image, (x, y), 2, (0, 0, 255), -1)
cv2.drawContours(image, c, -1, (0, 0, 255), 3)
need_x = x
need_y = y
return need_x, need_y
#主函数
if __name__ == '__main__':
win = Tk()
ww = 1200
wh = 820
Window(win, ww, wh)
win.mainloop()
max_index = np.argmax(mean_np)
if int(max_index) == 0:
ret, thresh1 = cv2.threshold(diff_RG, list_a_max_list + 20, 255,
cv2.THRESH_BINARY)
elif int(max_index) == 1:
ret, thresh1 = cv2.threshold(diff_GB, list_a_max_list + 20, 255, cv2.THRESH_BINARY)
elif int(max_index) == 2:
ret, thresh1 = cv2.threshold(diff_RB, list_a_max_list + 20, 255, cv2.THRESH_BINARY)
contours, hier = cv2.findContours(thresh1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 筛选的尺寸
min_size = 100
max_size = 1800
lengths = list()
save_list = []
for i in range(len(contours)):
length = cv2.arcLength(contours[i], True)
lengths.append(length)
if (length < max_size and length > min_size):
save_list.append(contours[i])
new_contours = save_list
for c in new_contours:
# find bounding box coordinates
new_c = c.reshape((c.shape[0], c.shape[2]))
x, y = new_c.sum(axis=0) / new_c.shape[0]
x = int(x)
y = int(y)
cv2.circle(image, (x, y), 2, (0, 0, 255), -1)
cv2.drawContours(image, c, -1, (0, 0, 255), 3)
need_x = x
need_y = y
return need_x, need_y
#主函数
if name == ‘main’:
win = Tk()
ww = 1200
wh = 820
Window(win, ww, wh)
win.mainloop()
![在这里插入图片描述]()