Inspection

Robotic quality inspection systems utilize robots to examine and assess products on a production line according to predefined criteria, such as size, shape, color, and surface defects. These systems have gained popularity in manufacturing, warehousing, and distribution facilities due to their ability to provide faster and more consistent inspections, thereby improving overall product quality and reducing human error. However, the increasing demand for 6 or 7-axis robots, while offering enhanced flexibility in handling complex inspections, also introduces greater complexity in programming and optimization, leading to higher costs for engineering, onsite support, and reprogramming.

In this tutorial, we introduce a short program created using the Jacobi platform, designed to be easily configurable for robotic quality inspection.

Video

Application Code

  1from pathlib import Path
  2import time
  3
  4import numpy as np
  5
  6from jacobi import Frame, Planner, JacobiError, PathFollowingMotion, LinearPath, CircularPath, BlendedPath, Studio
  7
  8
  9class InspectionProject:
 10    def __init__(self, planner, studio):
 11        self.planner = planner
 12        self.studio = studio
 13        self.object_list = [planner.environment.get_obstacle(o) for o in ['laptop', 'console', 'phone']]
 14
 15    def go_home(self):
 16        """Move the robot to the home position."""
 17
 18        home = [1.347, -1.208, 1.759, 4.16, -1.571, -0.224]
 19        traj = self.planner.plan(self.studio.get_joint_position(), home)
 20        self.studio.run_trajectory(traj)
 21        time.sleep(0.1)
 22
 23    def visualize_path(self, path_type):
 24        """Visualize the calculated path of the robot tool with the attached camera in Studio."""
 25
 26        path = path_type.calculate_path(0.3, 0.01)
 27        p = [path[i].translation for i in range(len(path))]
 28        self.studio.add_robot_path(p)
 29
 30    def inspect_laptop(self):
 31        """Inspect the laptop object using a blended path."""
 32
 33        # Move from the home position to the inspection start position
 34        start = Frame(x=-0.16, y=0.44, z=0.89, a=-2.3)
 35        traj = self.planner.plan(self.studio.get_joint_position(), start)
 36        self.studio.run_trajectory(traj)
 37
 38        # Define the waypoints and robot tool velocity for the inspection path
 39        velocity = 0.3  # [m/s]
 40        waypoints = [
 41            start,
 42            Frame(x=0.17, y=0.44, z=0.89, a=-2.3),
 43            Frame(x=0.17, y=0.48, z=0.89, a=-2.5),
 44            Frame(x=-0.16, y=0.48, z=0.89, a=-2.5),
 45            Frame(x=-0.16, y=0.52, z=0.89, a=-2.7),
 46            Frame(x=0.17, y=0.52, z=0.89, a=-2.7),
 47            Frame(x=0.17, y=0.56, z=0.89, a=-2.9),
 48            Frame(x=-0.16, y=0.56, z=0.89, a=-2.9),
 49            Frame(x=-0.16, y=0.60, z=0.89, a=-3.10),
 50            Frame(x=0.17, y=0.60, z=0.89, a=-3.10),
 51            Frame(x=0.17, y=0.64, z=0.89, a=-3.3),
 52            Frame(x=-0.16, y=0.64, z=0.89, a=-3.3),
 53            Frame(x=-0.16, y=0.68, z=0.89, a=-3.5),
 54            Frame(x=0.17, y=0.68, z=0.89, a=-3.5),
 55            Frame(x=0.17, y=0.72, z=0.89, a=-3.7),
 56            Frame(x=-0.16, y=0.72, z=0.89, a=-3.7),
 57            Frame(x=-0.16, y=0.76, z=0.89, a=-3.9),
 58            Frame(x=0.17, y=0.76, z=0.89, a=-3.9),
 59        ]
 60
 61        # Create the blended path and visualize it in Studio
 62        path = BlendedPath(waypoints, 0.01)
 63        self.visualize_path(path)
 64
 65        # Create the motion object and set the reference configuration
 66        motion = PathFollowingMotion(path, velocity)
 67        motion.reference_config = self.studio.get_joint_position()
 68
 69        # Plan the trajectory and run it in Studio
 70        traj = self.planner.plan(motion)
 71        self.studio.run_trajectory(traj)
 72        self.studio.reset()
 73        time.sleep(0.4)
 74
 75    def inspect_console(self):
 76        """Inspect the console object using a circular path."""
 77
 78        # Move from the home position to the inspection start position
 79        start = Frame(y=0.45, z=1.0, a=-2.6)
 80        traj = self.planner.plan(self.studio.get_joint_position(), start)
 81        self.studio.run_trajectory(traj)
 82
 83        # Define the first circular path parameters and robot tool velocity
 84        velocity = 0.12  # [m/s]
 85        theta = 2 * np.pi
 86        center = [0.0, 0.61, 1.0]
 87        normal = [0.0, 0.0, 1.0]
 88
 89        # Create the circular path and visualize it in Studio
 90        path = CircularPath(start, theta, center, normal, True)
 91        self.visualize_path(path)
 92
 93        # Create the motion object, set the reference configuration and plan the first motion
 94        motion = PathFollowingMotion(path, velocity)
 95        motion.reference_config = self.studio.get_joint_position()
 96        traj = self.planner.plan(motion)
 97
 98        # Create the linear path to move the robot tool to the next inspection position and visualize it
 99        pos = self.planner.environment.get_robot().calculate_tcp(traj.positions[-1])
100        path = LinearPath(pos, Frame(y=0.45, z=0.85, a=-2.4))
101        self.visualize_path(path)
102
103        # Create the motion object, set the reference configuration and plan the second motion
104        move_to_next = PathFollowingMotion(path, velocity)
105        move_to_next.reference_config = traj.positions[-1]
106        traj += self.planner.plan(move_to_next)
107
108        # Define the second circular path parameters
109        start = Frame(y=0.45, z=0.85, a=-2.4)
110        center = [0.0, 0.61, 0.85]
111        normal = [0.0, 0.0, -1.0]
112
113        # Create the circular path and visualize it in Studio
114        path = CircularPath(start, theta, center, normal, True)
115        self.visualize_path(path)
116
117        # Create the motion object, set the reference configuration and plan the third motion
118        motion = PathFollowingMotion(path, velocity)
119        motion.reference_config = traj.positions[-1]
120        traj += self.planner.plan(motion)
121
122        # Run the full trajectory in Studio
123        self.studio.run_trajectory(traj)
124        self.studio.reset()
125        time.sleep(0.4)
126
127    def inspect_phone(self):
128        """Inspect the phone case object using a blended path."""
129
130        # Move from the home position to the inspection start position
131        start = Frame(x=-0.05, y=0.55, z=0.85, a=3.14)
132        traj = self.planner.plan(self.studio.get_joint_position(), start)
133        self.studio.run_trajectory(traj)
134
135        # Define the waypoints and robot tool velocity for the inspection path
136        velocity = 0.05  # [m/s]
137        waypoints = [
138            start,
139            Frame(x=0.05, y=0.55, z=0.85, a=3.14),
140            Frame(x=0.05, y=0.6, z=0.85, a=3.14),
141            Frame(x=-0.05, y=0.6, z=0.85, a=3.14),
142            Frame(x=-0.05, y=0.65, z=0.85, a=3.14),
143            Frame(x=0.05, y=0.65, z=0.85, a=3.14),
144        ]
145
146        # Create the blended path and visualize it in Studio
147        path = BlendedPath(waypoints, 0.01)
148        self.visualize_path(path)
149
150        # Create the motion object and set the reference configuration
151        motion = PathFollowingMotion(path, velocity)
152        motion.reference_config = self.studio.get_joint_position()
153
154        # Plan the trajectory and run it in Studio
155        traj = self.planner.plan(motion)
156        self.studio.run_trajectory(traj)
157        self.studio.reset()
158        time.sleep(0.4)
159
160    def switch_obstacle(self, name_visible):
161        """Switch the view and collision checking to the specified obstacle."""
162
163        for o in self.object_list:
164            if o.name == name_visible:
165                o.for_visual = True
166                self.planner.environment.add_obstacle(o)
167            else:
168                try:  # noqa: SIM105
169                    self.planner.environment.remove_obstacle(self.planner.environment.get_obstacle(o.name))
170                except JacobiError:
171                    pass
172                o.for_visual = False
173            self.studio.update_obstacle(o)
174        time.sleep(0.5)
175
176    def run(self):
177        """Run the inspection project."""
178
179        self.go_home()
180
181        self.switch_obstacle('laptop')
182        self.inspect_laptop()
183        self.go_home()
184
185        self.switch_obstacle('console')
186        self.inspect_console()
187        self.go_home()
188
189        self.switch_obstacle('phone')
190        self.inspect_phone()
191        self.go_home()
192
193
194if __name__ == '__main__':
195    # Load project from Studio Download
196    project_path = Path.home() / 'Downloads' / 'Inspection-UR5e.jacobi-project'
197
198    # Run the inspection project
199    inspection = InspectionProject(
200        planner=Planner.load_from_project_file(project_path),
201        studio=Studio(),
202    )
203    inspection.run()