任意位置积木堆叠代码
dofbot_color_stacking功能包包含了训练好的yolov5模型及功能脚本。主要为通过yolov5进行色块的识别与定位,根据位置控制机械臂进行积木堆叠的代码。
“dofbot_color_stacking/scripts/data_collect.py”为摄像头采集积木图像数据和处理的逻辑代码。
def get_pos(self): ''' 获取识别信息 :return: 名称,位置 ''' # 复制原始图像,避免处理过程中干扰 img = self.image.copy() pred, names, drawed_res = infer_image(img, model, labels_dict, cfg) self.frame = drawed_res if len(pred) >= 1: cv.putText(self.frame, "Fix Infer Result, Waiting for Robot Arm Finish..", (30, 50), cv.FONT_HERSHEY_SIMPLEX, 0.8, (0,0,255), 2) data = self.bridge.cv2_to_imgmsg(drawed_res, encoding="bgr8") self.image_pub.publish(data) pred = [pred] msg = {} gn = torch.tensor([640, 480, 640, 480]) if pred: # Process detections cgz_ctr = 0 for i, det in enumerate(pred): # detections per image for *xyxy, conf, cls in reversed(det): prediction_status = True xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh label = '%s %.2f' % (names[int(cls)], conf) # get name name = names[int(cls)] name = name + str(cgz_ctr) if prediction_status: point_x = np.int_(xywh[0] * 640) point_y = np.int_(xywh[1] * 480) cv.circle(self.image, (point_x, point_y), 5, (0, 0, 255), -1) # plot_one_box(xyxy, self.image, label=label, color=colors[int(cls)], line_thickness=2) # 计算方块在图像中的位置 (a, b) = (round(((point_x - 320) / 4000), 5), round(((480 - point_y) / 3000) * 0.8+0.19, 5)) msg[name] = (a, b) cgz_ctr += 1 return msg def get_Sqaure(self, color_hsv): ''' 颜色识别,获得方块的坐标 ''' (lowerb, upperb) = color_hsv # 复制原始图像,避免处理过程中干扰 mask = self.image.copy() # 将图像转换为HSV。 hsv = cv.cvtColor(self.image, cv.COLOR_BGR2HSV) # 筛选出位于两个数组之间的元素。 img = cv.inRange(hsv, lowerb, upperb) # 设置非掩码检测部分全为黑色 mask[img == 0] = [0, 0, 0] # 获取不同形状的结构元素 kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5)) # 形态学闭操作 dst_img = cv.morphologyEx(mask, cv.MORPH_CLOSE, kernel) # 将图像转为灰度图 dst_img = cv.cvtColor(dst_img, cv.COLOR_RGB2GRAY) # 图像二值化操作 ret, binary = cv.threshold(dst_img, 10, 255, cv.THRESH_BINARY) # 获取轮廓点集(坐标) python2和python3在此处略有不同 contours, heriachy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) # 获取轮廓点集(坐标) for i, cnt in enumerate(contours): # boundingRect函数计算边框值,x,y是坐标值,w,h是矩形的宽和高 x, y, w, h = cv.boundingRect(cnt) # 计算轮廓的⾯积 area = cv.contourArea(cnt) # ⾯积限制 if area > 1000: # 中心坐标 point_x = float(x + w / 2) point_y = float(y + h / 2) # 在img图像画出矩形,(x, y), (x + w, y + h)是矩形坐标,(0, 255, 0)设置通道颜色,2是设置线条粗度 cv.rectangle(self.image, (x, y), (x + w, y + h), (0, 255, 0), 2) # 绘制矩形中心 cv.circle(self.image, (int(point_x), int(point_y)), 5, (0, 0, 255), -1) # # 在图片中绘制结果 cv.putText(self.image, self.color_name, (int(x - 15), int(y - 15)), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 255), 2) # 计算方块在图像中的位置 (a, b) = (round(((point_x - 320) / 4000), 5), round(((480 - point_y) / 3000) * 0.8+0.19, 5)) # print("------------------ identify up -------------------") # print(a, b) # print("------------------ identify down -------------------") return (a, b) def server_joint(self, posxy): ''' 发布位置请求,获取关节旋转角度 :param posxy: 位置点x,y坐标 :return: 每个关节旋转角度 ''' # 等待server端启动 self.client.wait_for_service() # 创建消息包 # request = kinemaricsRequest() request = Kinemarics.Request() request.tar_x = posxy[0] request.tar_y = posxy[1] + self.offset request.kin_name = "ik" try: # response = self.client.call(request) self.future = self.client.call_async(request) rclpy.spin_until_future_complete(self.node, self.future) response = self.future.result() if response: # 获得反解响应结果 joints = [0.0, 0.0, 0.0, 0.0, 0.0] joints[0] = response.joint1 joints[1] = response.joint2 joints[2] = response.joint3 joints[3] = response.joint4 joints[4] = response.joint5 # 当逆解越界,出现负值时,适当调节. if joints[2] < 0: joints[1] += joints[2] * 3 / 5 joints[3] += joints[2] * 3 / 5 joints[2] = 0 # print joints return joints except Exception: # rospy.loginfo("arg error") print("arg error")
“dofbot_color_stacking/scripts/dofbot_config.py”为图像检测与机器视觉逻辑代码。
def calibration_map(self, image,xy=None, threshold_num=130): ''' 放置方块区域检测函数 :param image:输入图像 :return:轮廓区域边点,处理后的图像 ''' if xy!=None: self.xy=xy # 机械臂初始位置角度 joints_init = [self.xy[0], self.xy[1], 0, 0, 90, 0] # REVISE # 将机械臂移动到标定方框的状态 self.arm.Arm_serial_servo_write6_array(joints_init, 500) self.image = image self.threshold_num = threshold_num # 创建边点容器 dp = [] h, w = self.image.shape[:2] # print("h",h) # 获取轮廓点集(坐标) contours = self.Morphological_processing() # 遍历点集 for i, c in enumerate(contours): # 计算轮廓区域。 area = cv.contourArea(c) low_bound = None high_bound = None if self.use_other_cam: low_bound = h * w / 2 - 40000 else: low_bound = h * w / 2 high_bound = h * w # 设置轮廓区域范围 if low_bound < area < high_bound: # 计算多边形的矩 mm = cv.moments(c) if mm['m00'] == 0: continue cx = mm['m10'] / mm['m00'] cy = mm['m01'] / mm['m00'] # 绘制轮廓区域 cv.drawContours(self.image, contours, i, (255, 255, 0), 2) # 获取轮廓区域边点 dp = np.squeeze(cv.approxPolyDP(c, 100, True)) # 绘制中心 if not self.data_collect: cv.circle(self.image, (np.int_(cx), np.int_(cy)), 5, (0, 0, 255), -1) return dp, self.image def Morphological_processing(self): ''' 形态学及去噪处理,并获取轮廓点集 ''' # 将图像转为灰度图 gray = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) # 使用高斯滤镜模糊图像。 gray = cv.GaussianBlur(gray, (5, 5), 1) # 图像二值化操作 ref, threshold = cv.threshold(gray, self.threshold_num, 255, cv.THRESH_BINARY) # 获取不同形状的结构元素 kernel = np.ones((3, 3), np.uint8) # 形态学开操作 if self.use_other_cam: blur = cv.morphologyEx(threshold, cv.MORPH_OPEN, kernel, iterations=8) else: blur = cv.morphologyEx(threshold, cv.MORPH_OPEN, kernel, iterations=4) # 提取模式 mode = cv.RETR_EXTERNAL # 提取方法 method = cv.CHAIN_APPROX_NONE # 获取轮廓点集(坐标) python2和python3在此处略有不同 # 层级关系 参数一:输入的二值图,参数二:提取模式,参数三:提取方法。 contours, hierarchy = cv.findContours(blur, mode, method) return contours def Perspective_transform(self, dp, image): ''' 透视变换 :param dp: 方框边点(左上,左下,右下,右上) :param image: 原始图像 :return: 透视变换后图像 ''' if len(dp) != 4: return upper_left = [] lower_left = [] lower_right = [] upper_right = [] for i in range(len(dp)): if dp[i][0]<320 and dp[i][1]<240: upper_left=dp[i] if dp[i][0]<320 and dp[i][1]>240: lower_left=dp[i] if dp[i][0]>320 and dp[i][1]>240: lower_right=dp[i] if dp[i][0]>320 and dp[i][1]<240: upper_right=dp[i] # 原图中的四个顶点 pts1 = np.float32([upper_left, lower_left, lower_right, upper_right]) # 变换后的四个顶点 pts2 = np.float32([[0, 0], [0, 480], [640, 480], [640, 0]]) # 根据四对对应点计算透视变换。 M = cv.getPerspectiveTransform(pts1, pts2) # 将透视变换应用于图像。 Transform_img = cv.warpPerspective(image, M, (640, 480)) return Transform_img
“dofbot_color_stacking/scripts/garbage_grap.py”为机械臂抓取与移动堆叠的逻辑代码。
def move(self, joints, joints_down): ''' 移动过程 :param joints: 移动到物体位置的各关节角度 :param joints_down: 机械臂堆叠各关节角度 ''' # 架起角度 joints_00 = [90, 80, 50, 50, 265, self.grap_joint] # 抬起 joints_up = [135, 80, 50, 50, 265, 30] # 架起 self.arm.Arm_serial_servo_write6_array(joints_00, 1000) sleep(1) # 开合夹爪 # for i in range(5): # self.arm.Arm_serial_servo_write(6, 180, 100) # sleep(0.08) # self.arm.Arm_serial_servo_write(6, 30, 100) # sleep(0.08) # 松开夹爪 self.arm.Arm_serial_servo_write(6, 0, 500) sleep(0.5) # 移动至物体位置 self.arm.Arm_serial_servo_write6_array(joints, 1000) sleep(1) # 夹紧夹爪 self.arm.Arm_serial_servo_write(6, self.grap_joint, 500) sleep(0.5) # 架起 self.arm.Arm_serial_servo_write6_array(joints_00, 1000) sleep(1) # 移动至对应位置上方 self.arm.Arm_serial_servo_write(1, joints_down[0], 1000) sleep(1) # 移动至目标位置 self.arm.Arm_serial_servo_write6_array(joints_down, 1000) sleep(1.5) # 松开夹爪 self.arm.Arm_serial_servo_write(6, 0, 500) sleep(0.5) # 抬起 self.arm.Arm_serial_servo_write6_array(joints_up, 1000) sleep(1) def arm_run(self, move_num, joints): ''' 机械臂移动函数 :param move_num: 抓取次数 :param joints: 反解求得的各关节角度 ''' # a=[132, 50, 20, 60, 265, 100] # b=[132, 55, 38, 38, 265, 100] # c=[132, 60, 45, 30, 265, 100] # d=[132, 65, 55, 20, 265, 100] if move_num == '1' and self.move_status == True: # 此处设置,需执行完本次操作,才能向下运行 self.move_status = False # 将设置机械臂的状态,以保证运行完此过程在执行其它 # print(joints[0], joints[1], joints[2], joints[3], joints[4]) # 获得目标关节角 joints = [joints[0], joints[1], joints[2], joints[3], 265, 30] # 移动到目标前的姿态 joints_down = [135, 50, 20, 60, 265, self.grap_joint] # 调用移动函数 self.move(joints, joints_down) # 执行完此过程 self.move_status = True if move_num == '2' and self.move_status == True: self.move_status = False # print joints[0], joints[1], joints[2], joints[3], joints[4] joints = [joints[0], joints[1], joints[2], joints[3], 265, 30] joints_down = [135, 55, 38, 38, 265, self.grap_joint] self.move(joints, joints_down) self.move_status = True if move_num == '3' and self.move_status == True: self.move_status = False # print joints[0], joints[1], joints[2], joints[3], joints[4] joints = [joints[0], joints[1], joints[2], joints[3], 265, 30] joints_down = [135, 60, 45, 30, 265, self.grap_joint] self.move(joints, joints_down) self.move_status = True if move_num == '4' and self.move_status == True: self.move_status = False # print joints[0], joints[1], joints[2], joints[3], joints[4] joints = [joints[0], joints[1], joints[2], joints[3], 265, 30] joints_down = [135, 65, 55, 20, 265, self.grap_joint] self.move(joints, joints_down) self.move_status = True
父主题: 代码实现