SCARA Robot Pick and Place
In this tutorial, we’ll be creating a simulation of a SCARA robot performing a pick and place operation between two conveyor belts. After completing this tutorial, you will understand how to model a robot with physics joints and motors, understand how to model conveyors, sensors and suction grippers and be able to use the integrated robot controller component to create a program to control the SCARA robot entirely through the user interface.
Start by downloading the CAD file for the SCARA robot pick and place model.
Solution
The completed model can be downloaded.
Control Method
There are two main ways to control industrial robots in ProtoTwin:
With the robot controller component, you have a few options to choose from when programming the robot:
- The robot can be programmed entirely through the user interface, which is the method explained in the above video.
- The robot can be programmed by writing a custom scripted component using the TypeScript API and adding a call script instruction to the main program.
- The robot can be programmed by asking our AI assistant Torq to create the robot program for you in natural language.
Control Code
The complete source code for the custom scripted component is provided below:
import { type RobotControllerComponent, RobotProgramComponent, DistanceSensorComponent, SuctionGripperComponent } from "prototwin";
import { type Handle, Entity, Frame, Future, Sequence, Wait } from "prototwin";
export class Controller extends RobotProgramComponent {
public sensor: Handle<DistanceSensorComponent> = this.handle(DistanceSensorComponent);
public gripper: Handle<SuctionGripperComponent> = this.handle(SuctionGripperComponent);
// Teachpoints for the robot.
public tpPickApproach: Handle<Entity> = this.handle(Entity);
public tpDropApproach: Handle<Entity> = this.handle(Entity);
public tpPick: Handle<Entity> = this.handle(Entity);
public tpDrop: Handle<Entity> = this.handle(Entity);
// Target frames for the robot controller.
#tpPickApproach: Frame = Frame.identity;
#tpDropApproach: Frame = Frame.identity;
#tpPick: Frame = Frame.identity;
#tpDrop: Frame = Frame.identity;
constructor(entity: Entity) {
super(entity);
}
/**
* Initializes the target frames.
* @param controller The robot controller.
*/
public initializeFrames(controller: RobotControllerComponent): void {
// Get the teachpoint entities.
const tpPickApproach = this.tpPickApproach.value!;
const tpDropApproach = this.tpDropApproach.value!;
const tpPick = this.tpPick.value!;
const tpDrop = this.tpDrop.value!;
// Determine the frame from the entity teachpoints.
// The robot controller expects the target frame to be in the local-space of the robot.
this.#tpPickApproach = controller.worldToLocalFrame(new Frame(tpPickApproach.worldRotation, tpPickApproach.worldPosition));
this.#tpDropApproach = controller.worldToLocalFrame(new Frame(tpDropApproach.worldRotation, tpDropApproach.worldPosition));
this.#tpPick = controller.worldToLocalFrame(new Frame(tpPick.worldRotation, tpPick.worldPosition));
this.#tpDrop = controller.worldToLocalFrame(new Frame(tpDrop.worldRotation, tpDrop.worldPosition));
}
/**
* Executes the program.
* @param controller The robot controller on which the program is to be executed.
* @returns The future that is resolved once the program has been executed.
* @remarks This function is called by the robot controller when it wants to execute the program.
*/
public execute(controller: RobotControllerComponent): Future<void> {
this.initializeFrames(controller);
// The robot arm is controlled using a sequence.
const sequence = new Sequence(false);
sequence.add(() => controller.moveJoint(this.#tpPickApproach));
sequence.add(() => Wait.value(this.sensor.value!.io.state, true));
sequence.add(() => controller.moveJoint(this.#tpPick));
sequence.add(() => this.gripper.value!.state = true);
sequence.add(() => controller.moveJoint(this.#tpPickApproach));
sequence.add(() => controller.moveJoint(this.#tpDropApproach));
sequence.add(() => controller.moveJoint(this.#tpDrop));
sequence.add(() => this.gripper.value!.state = false);
sequence.add(() => controller.moveJoint(this.#tpDropApproach));
return sequence.run();
}
}