Assembly

Within the rapidly transforming world of manufacturing automation, robotic assembly stands out as a crucial technology. It involves the use of 6-axis robots to efficiently position, fit, assemble and construct different components into a wide range of products, with high degree of precision and accuracy. Programming robotic assembly cells remains a challenge when relying on traditional methods, requiring a tedious manual process of tuning motion parameters for each component. This not only makes it time-consuming but also limits the flexibility and adaptability of the assembly process. In this tutorial, we introduce a short program created using the Jacobi platform, designed to be easily configurable for the assembly of various components.

Video

Application Code

  1import asyncio
  2from pathlib import Path
  3
  4from jacobi import Frame, Motion, LinearMotion, LinearSection, Planner, Studio
  5
  6
  7class AssemblyApplication:
  8    project_path = Path.home() / 'Downloads' / 'assembly.jacobi-project'
  9
 10    def __init__(self):
 11        self.planner = Planner.load_from_project_file(self.project_path)
 12        self.environment = self.planner.environment
 13        self.robot = self.environment.get_robot()
 14
 15        self.home = self.environment.get_waypoint('Home')
 16        self.assembly = self.environment.get_waypoint('Assembly')
 17
 18        self.cylinders = [
 19            self.environment.get_obstacle('Cylinder 1'),
 20            self.environment.get_obstacle('Cylinder 2'),
 21            self.environment.get_obstacle('Cylinder 3'),
 22            self.environment.get_obstacle('Cylinder 4'),
 23        ]
 24
 25        # Just for visualization
 26        self.studio = Studio()
 27        self.robot_position = [0, 0, 0, 0, 0, 0]  # Initial joint position [rad]
 28
 29        # TODO: Set up robot driver
 30        # self.driver = FanucDriver(host='192.168.1.135')
 31
 32    async def run_trajectory(self, trajectory, events=Studio.Events()):
 33        # TODO: Use actual robot driver
 34        # self.driver.run(trajectory)
 35
 36        self.studio.run_trajectory(trajectory, events)  # requires the Studio Live feature
 37        self.robot_position = trajectory.positions[-1]
 38
 39    async def grasp(self):
 40        # TODO: Grasp cylinder
 41        await asyncio.sleep(0.5)  # [s]
 42        # TODO: Check input condition
 43
 44    async def release(self):
 45        # TODO: Release cylinder
 46        await asyncio.sleep(0.5)  # [s]
 47        # TODO: Check input condition
 48
 49    async def assemble_cylinder(self, cylinder, retract_from_start=True):
 50        # 1. Move from current position -> Cylinder pick
 51        pick_frame = cylinder.origin
 52        motion = Motion(self.robot_position, pick_frame)
 53        motion.linear_approach = LinearSection(Frame(z=0.1), speed=0.5)  # [m]
 54        if retract_from_start:
 55            motion.linear_retraction = LinearSection(Frame(z=0.05), speed=0.5)  # [m]
 56        trajectory = self.planner.plan(motion)
 57        await self.run_trajectory(trajectory)
 58
 59        # 2. Grasp cylinder
 60        await self.grasp()
 61
 62        cylinder.origin = Frame.Identity()
 63        self.robot.item_obstacle = cylinder
 64
 65        events = Studio.Events()
 66        events[0.0] = Studio.Events.set_item(self.robot.item_obstacle)
 67        events[0.0] = Studio.Events.remove_obstacle(cylinder)
 68
 69        # 3. Move from pick -> Angled assembly approach
 70        assembly_frame = self.assembly.position
 71        assembly_frame_approach = self.assembly.position * Frame(a=0.18)  # [rad]
 72
 73        motion = Motion(self.robot_position, assembly_frame_approach)
 74        motion.linear_retraction = LinearSection(Frame(z=0.1), speed=0.5)  # [m]
 75        motion.linear_approach = LinearSection(Frame(z=0.05), speed=0.1)  # [m]
 76        trajectory = self.planner.plan(motion)
 77        await self.run_trajectory(trajectory, events)
 78
 79        # 4. Orient normal to fixture
 80        motion = LinearMotion(self.robot_position, assembly_frame)
 81        trajectory = self.planner.plan(motion)
 82
 83        events = Studio.Events()
 84        cylinder.origin = self.assembly.position
 85        events[trajectory.duration] = Studio.Events.add_obstacle(cylinder)
 86        events[trajectory.duration] = Studio.Events.set_item(None)
 87        await self.run_trajectory(trajectory, events)
 88
 89        # 5. Release cylinder
 90        await self.release()
 91
 92        self.robot.item_obstacle = None
 93
 94    async def run(self):
 95        # 0. Move to home position
 96        trajectory = self.planner.plan(self.robot_position, self.home)
 97        await self.run_trajectory(trajectory)
 98
 99        for i, cylinder in enumerate(self.cylinders):
100            await self.assemble_cylinder(cylinder, retract_from_start=i > 0)
101
102        # End. Move to home position again
103        motion = Motion(self.robot_position, self.home)
104        motion.linear_retraction = LinearSection(Frame(z=0.1), speed=0.5)  # [m]
105        trajectory = self.planner.plan(motion)
106        await self.run_trajectory(trajectory)
107
108        self.studio.reset()  # Reset the visualization
109
110
111if __name__ == '__main__':
112    application = AssemblyApplication()
113    asyncio.run(application.run())