Introduction:
PLOC2D from SICK is an easy to use flexible part localization sensor system that can be used for Vision Guided Pick and Place Applications. PLOC 2D supports multiple interfaces including TCP/IP, EtherNet/IP and Profinet out of the box. They provide an intuitive browser based interface that allows an user to set up a job and run in matter of few minutes.
In this MecaNetwork article, we will discuss in detail about the integration of a SICK PLOC 2D Vision System with a Meca500-R3 using Python.
System Requirements:
Meca500-R3 system
SICK PLOC 2D Vision System with Power Supply
M12-D coded - RJ45 cable
Windows or Linux PC with Python 3.6 or above and numpy installed
PLOC 2D Robot Guidance System Setup:
A complete setup guide for PLOC 2D is beyond the scope of the this article, however SICK offers a comprehensive integration guide that describes the workflow for setting up a PLOC 2D which has been attached in this article for reference.
The key steps involved are
1. Installation
2. Calibration
In this step, the system will acquire a set of calibration images in order to estimate the lens distortion parameters.
3. Alignment:
In this step, the system will establish the pose of the camera in relation to the robot coordinate system. Once the origin of the reference frame has been detected by the vision system, use 3 point method to calculate the vision reference frame with respect to robot's Base Reference Frame.
4. Configure Job
This step defines the shape that the system will look for
5. Run Job
SICK PLOC-2D VisionController Python Class:
This Python class is used to set up a TCP/IP communication interface with PLOC 2D vision system to send commands and receive responses.
import socket class VisionController: """ Class for the SICK PLOC 2D Vision for Robot Guidance and use with Mecademic Robot Attributes ---------- address : string The IP address associated to the SICK PLOC 2D vision system. socket : socket object Socket connecting to physical SICK PLOC 2D vision system. error : boolean Error Status of the SICK PLOC 2D vision system job. x : float The Cartseian value in mm for X. y : float The Cartseian value in mm for Y. z : float The Cartseian value in mm for z (incl. of offset). rz : float The Cartseian value in mm for Rz. (Uses Staubli's Notation) index : int The index of the current identified part among a set of matches. matches : int Total no of identified parts for the selected job. error_ID : int The error ID returned by SICK PLOC 2D for the selected job. """ def __init__(self, address): """ Constructor for an instance of the class SICK_PLOC2D vision system Parameters ---------- address : string The IP address associated to the SICK PLOC 2D vision system """ self.address = address self.socket = None self.error = False self.errorID = None self.jobID = None self.x = None self.y = None self.z = None self.rz = None self.index = None self.matches = None def connect(self): """ Connects SICK PLOC 2D vision system object communication to the physical SICK PLOC 2D vision system Returns ------- status : boolean Return whether the connection is established """ try: self.socket = socket.socket() self.socket.settimeout(0.1) try: self.socket.connect((self.address,14158)) except socket.timeout: raise TimeoutError # Receive confirmation of connection if self.socket is None: raise RuntimeError return True except TimeoutError: return False except RuntimeError: return False def disconnect(self): """ Disconnects SICK PLOC 2D vision system object communication to the physical SICK PLOC 2D vision system """ if (self.socket is not None): self.socket.close() self.socket = None def _send(self,cmd): """Sends a command to the physical SICK PLOC 2D vision system. Parameters ---------- cmd : string Command to be sent. Returns ------- status : boolean Returns whether the message is sent. """ if self.socket is None: #check that the connection is established return False #if issues detected, no point in trying to send a cmd that won't reach the vision system cmd = cmd status = 0 while status == 0: try: #while the message hasn't been sent status = self.socket.send(cmd.encode('ascii')) #send command in ascii encoding except: break if status != 0: return True #return true when the message has been sent #Message failed to be sent, return false return False def _get_data(self,delay = 1): """Receives message from the SICK PLOC 2D vision system and saves the values in appropriate variables. Parameters ---------- delay: int or float Time to set for timeout of the socket. """ if self.socket is None: return self.socket.settimeout(delay) try: raw_response = self.socket.recv(1024).decode('ascii').split(",") #read message from vision system if raw_response[0].find('Ok') != -1: self.error = False self.errorID = 0 self.jobID = int(raw_response[1]) self.index = int(raw_response[2]) self.matches = int(raw_response[3]) self.x = float(raw_response[4]) self.y = float(raw_response[5]) self.z = float(raw_response[6]) self.rz = float(raw_response[9]) self.score = int(raw_response[-4]) else: self.error = True self.jobID = int(raw_response[1]) self.errorID = int(raw_response[-1]) except TimeoutError: pass def run_job(self,jobId): """Sends and receives with the SICK PLOC 2D vision system. Parameters ---------- jobId : int Job to send to the SICK PLOC 2D vision system. Returns ------- response : boolean Returns whether the job was completed without any error """ cmd = "Run.Locate"+","+str(jobId) status = self._send(cmd) if status: response = self._get_data() if (not self.error): return True else: return False def locate_by_index(self,jobId,index): """Sends and receives with the SICK PLOC 2D vision system. Parameters ---------- jobId : int Job to send to the SICK PLOC 2D vision system. index : int Index of the identified parts to be picked Returns ------- response : list Returns the list of current index and x,y,z and rz values in mm and deg """ cmd = "Run.Locate"+","+str(jobId)+","+str(index) status = self._send(cmd) if status: response = self._get_data() if (not self.error): return tuple((self.index,self.score,self.x,self.y,self.z,self.rz)) def is_in_error(self): return self.error
MecademicRobot Python Class:
Mecademic Robots can be controlled via our Python API available at our Github page. The Python API has classes for both robot control on port 10000 and feedback on port 10001.
Vision Guided Pick Python Class:
The VisionGuidedPick Python class is higher level abstraction class that handles the inetgration of the RobotControl and VisionControl as one and provides methods to perform vision guided pick and place applications. It provides a simple interface for both vision and robot control. It provides various methods that includes getting the total no of identified parts for a given vision job, pick by index, and place etc.,
def get_count(self,jobId): """ Retrieves the total no of identified parts from the vision job Returns ------- counts : int No of identified parts """ self.vision.run_job(jobId) if not self.vision.error: return self.vision.matches
def pick_index(self,jobId,index): """ Starts the pick cycle for a given index of the matched parts Parameters ---------- jobId : int Vision job Id that is used to identify a part index : int Index "n" of the list of identified parts in a vision job """ self.jobId = jobId self.index = index self.count = self.get_count(self.jobId) if self.count != 0: [self.current_index, self.current_index_score, self.pose_x, self.pose_y, self.pose_z, self.pose_rz] = self.vision.locate_by_index(self.jobId,self.index) if self.current_index == self.index: self.robot.MovePose(self.pose_x, self.pose_y, self.pose_z + self.z_offset, self.pose_rx, self.pose_ry, self.pose_rz) self.robot.SetCartLinVel(self.linear_speed/3) self.robot.MoveLin(self.pose_x, self.pose_y, self.pose_z, self.pose_rx, self.pose_ry, self.pose_rz) self.gripper_close() self.delay(0.25) self.robot.SetCartLinVel(self.linear_speed) self.robot.MoveLin(self.pose_x, self.pose_y, self.pose_z + self.z_offset, self.pose_rx, self.pose_ry, self.pose_rz)
def place(self,x,y,z,rx,ry,rz): """ Starts the place sequence with Approach and Retract defined with a user defined Z offset and user defined place pose Parameters ---------- x : float X axis value in mm with respect to robot's BRF y : float Y axis value in mm with respect to robot's BRF z : float Z axis value in mm with respect to robot's BRF rx : float Rotation about X axis in degrees ry : float Rotation about Y axis in degrees rz : float Rotation about Z axis in degrees """ self.robot.MovePose(x, y, z + self.z_offset, rx, ry, rz) self.robot.MoveLin(x, y, z, rx, ry, rz) self.robot.gripper_open() self.robot.delay(0.25) self.robot.MoveLin(x, y, z + self.z_offset, rx, ry, rz)
Calibrating the Vision Reference Frame with respect to Meca500's BRF:
An important step in implementing a vision guided pick and place application is calibration of the Vision Reference Frame with respect to the robot's Base Reference Frame. At Mecademic Robotics, we prefer calculating the WRF using a 3 point method. The VisionGuidedPick class offers a method to calculate the Vision Reference Frame and set it as well. Alternatively, users can also use the standalone executable to calculate the Vision Reference Frame. Reach out to support@mecademic.com for the WRF calculator exe file.
Application Code:
Once we have all the required classes defined, then we can create an vision guided pick and place application by invoking an instance of the VisionGuidedPick class with the IP address of the Meca500 and PLOC 2D and use the methods of this class to get the total count of identified parts, pick by index and place the part.
from VisionGuidedPick import VisionGuidedPick # Create an instance of the vision guided pick class app = VisionGuidedPick('192.168.0.100','192.168.0.1') # Initialize the vision and robot app.init_robot() app.init_vision() # Set the config parameters app.set_vision_ref(100,50,25,0,0,0) app.set_offset(25) app.set_joint_speed(50) app.set_linear_speed(150) app.set_place_pose() # Get the total counts of the matched parts for the selected vision job ID count = app.get_count(1) # Pick the first matched part if count is not None: app.pick_index(1,1) app.place(-120,100,0,180,0,180) # Get the total counts of the matched parts for the selected vision job ID count = app.get_count(1) # Loop through all the identified parts if count is not None: for i in range(1,count+1): app.pick_index(1,i) app.place(-120,100,0,180,0,180)
Summary:
The SICK PLOC 2D is an easy to use vision system that provides an intuitive user interface to setup vision tasks. With a simple TCP/IP communication interface that streams the responses in CSV format we can build an application class and get started. For PLC users, PLOC 2D supports EtherNet/IP which is also supported by Meca500 as well.
Please note that these examples are provided as-is by either Mecademic or it's Partners. These can be used as a starting point for development and testing but Mecademic or it's partners are not under obligation to provide support and are not liable for any errors or unintended behavior caused by the examples. These examples could be updated over time.
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article