Gripper: pick up and place
The robot drives up to the load, grabs it, takes it away and places it in the right place.
Lesson result
The robot approaches the load via ultrasound, picks it up, turns around, carries it along the line and places it at the other end. Actions are formalized by the functions pickUp() and putDown().
You will learn
- Explain how a servo rotates through a given angle.
- Find your gripper’s open and closed angles experimentally.
- Follow the order: stop - close; open - wait - drive away.
- Collect shipping from the functions of previous lessons.
Need to know
- 5.1 Ultrasound: we see the load
- 4.2 Turns at the intersection and route
Will need
Why this lesson
The robot can reach the cargo. The last skill left for the courier is to take the cargo, deliver it and carefully deliver it. Today the robot will put everything together: line, ultrasound and gripper.
- Which stopping condition in front of the load is correct and why?
- How does a robot turn around on a line?
- What does
followLineFor(1500)?
Keep your fingers out of the gripper while power is on. The servo may move suddenly when a program is uploaded.
How a servo works
8 minWhat should a robot do. The courier must take the cargo, hold it during transportation and release it at the right place. To do this, we will add a mechanical gripper with a servo drive. Ultrasound helps to drive up, but does not capture anything.
What we use and how it works. Inside a servo there is a motor, gearbox, position sensor and control circuitry. Based on control impulses, it receives the given position of the shaft and tries to hold it. The Servo library generates these pulses; gripper.write specifies the position, not the rotation speed. The lever turns the rotation of the shaft into the movement of the gripping jaws.
How to link a dimension to an action. We select the “open” and “closed” angles for our mechanics with small changes; other people's numbers can push the mechanism into a limiter. First we stop the wheels, then close the gripper and give it time. The closing command does not prove that the load is being held: this is verified by observation. We check the connection and power supply with the confirmed diagram of the kit.
A regular motor spins while it is turned on. Servo rotates at a given angle and holds it. Inside it has a motor, gears and a position sensor. We command the angle in degrees: gripper.write(90).
What will the servo do after gripper.write(90), and then gripper.write(40)?
Can you explain how a servo is different from a wheel motor?
A regular motor spins while it is turned on. Servo rotates at a given angle and holds it. We command the angle in degrees: gripper.write(90).
What will the servo do after gripper.write(90), and then gripper.write(40)? And the wheel motor after analogWrite(…, 90)?
- Write in the journal: how a command to the servo drive differs from a command to the wheel motor - what sets the number and when the movement ends.
Comparison of servo and wheel motor recorded.
Servo rotates to a given angle and holds it: inside there is a motor, gears and a position sensor.
- Look at the servo contact on the card on the robot and write it in the angle sample sketch.
- Do the experiment:
gripper.write(40), pause,gripper.write(90), pause. Try pauses of 100, 300 and 600 ms. At what pause does the gripper have time to reach the angle? - Write down approximately how long it takes for the servo to move 50° and why the program should wait after the command.
In the log - an experiment with pauses and the smallest pause at which the gripper reaches the angle.
Find the “open” and “closed” angles
22 minEach gripper has its own angles. Let's find them by experience, like the turning time in lesson 2.2.
- Look at the servo contact on the robot's card and enter it instead
-1.
#include <Servo.h> // Контакт сервопривода захвата: впиши номер из таблицы проводки своего робота.const int SERVO_PIN = -1; int angle = 90; // меняй понемногу, чтобы найти «открыто» и «закрыто» Servo gripper; void setup() { if (SERVO_PIN < 0) { while (true) { } } gripper.attach(SERVO_PIN); gripper.write(angle);} void loop() {}
- Start with
angle = 90. Upload. Change the angle by 10°: find a position where the gripper is open so that the load enters freely. - Find an angle where the gripper holds the load firmly, but does not press with all your might.
- Write both angles in a journal.
Hint: Servo is humming or shaking
The gripper has hit the load or its own stop and cannot reach the corner. Step back 5–10° from this position: the servo drive should not work point-blank.
The log records the “open” and “closed” angles at which the load enters freely and is held securely.
Each gripper has its own angles. Find them through experience - choose the method yourself, but only change angle and write down each attempt.
| Command | What does |
|---|---|
gripper.attach(контакт); | Links a servo to a pin |
gripper.write(угол); | Rotates by an angle of 0...180 and holds it |
- Create a sketch
L52_Servo_Testaccording to the template. Look at the servo contact on the robot's card and enter it instead-1. - Add it
setup()himself: the gripper must turn toangle.
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. #include <Servo.h> // Контакт сервопривода захвата: впиши номер из таблицы проводки своего робота.const int SERVO_PIN = -1;int angle = 90; // меняй понемногу, чтобы найти «открыто» и «закрыто» Servo gripper; // ===== Твоя часть: напиши сам ===== void setup() { if (SERVO_PIN < 0) { while (true) { } } // Захват поворачивается на угол angle и держит его.} void loop() {}
- Find the “open” angle: the load enters the gripper freely.
- Find the "closed" angle: the load is held tightly and the servo does not buzz.
- Check “closed”: lift the robot with the load and tilt it slightly. Doesn't the load fall out?
| Attempt | angle | What is visible | What to change and why |
|---|---|---|---|
| 1 | 90 | ||
| 2 | |||
| 3 | |||
| 4 |
Hint: Servo is humming or shaking
The gripper is stuck and cannot reach the corner. What should you do with the corner?
In the logbook - attempts with explanations, angles “open” and “closed” and tilt testing.
Look at the servo contact on the card on the robot.
Requirement 1. Sketch L52_Servo_Test for selecting angles: the angle is entered as a number into the Serial Monitor, and the gripper is rotated immediately, without loading the program again.
- Only numbers from 0 to 180 are accepted; to another number the program responds that this is not possible, and the gripper does not move.
- After each rotation, the program prints what the angle is now.
- Without a pin number, the program does not touch the servo.
How it works: Command help
| Command | What does |
|---|---|
#include <Servo.h>, Servo gripper; | Connect the library and create a servo drive |
gripper.attach(pin) | Link servo to pin |
gripper.write(angle) | Rotate by angle 0…180 |
Serial.available() | How many characters came from the Serial Monitor |
Serial.parseInt() | Read an integer from incoming characters |
Requirement 2. The “open” and “closed” angles were found with a margin and checked:
- “open” - the load enters the gripper, even if the robot has stopped 1 cm to the left or right of the middle;
- “closed” — find the angle at which the gripper contacts the load, then back off the angle until the servo stops buzzing; record how many degrees you backed off;
- the load does not fall out when the robot with the load is tilted 30° forward and backward.
Requirement 3. Function moveGripper(target) turns the gripper to the angle smoothly - in 2° steps - rather than in one jerk. Compare how the load behaves during sharp and smooth closing.
Hint: Idea (1 of 2)
Remember the current angle in the variable. While it is not equal to the target, move it one step towards the target and take a short pause.
Hint: Code snippet (2 of 2)
currentAngle += (target > currentAngle) ? 2 : -2; - what happens if the target differs by an odd number of degrees?
Hint: Nothing happens in the Serial Monitor
Check that the speed is 9600 and that the monitor sends the line via Enter.
The angle selection sketch fulfills requirement 1. In the log - angles, margin of “closed” in degrees, tilt check and comparison of sharp and smooth closing. moveGripper works.
The order of actions is important
15 minMatch functions pickUp() and putDown() in the final order rule sketch (next lesson):
- Take: first stop, then close the gripper.
- Put: open the grab, wait until it opens, and only then drive away.
At the neighbors putDown() is like this: gripper.write(OPEN_ANGLE); and immediately drive(BACKWARD, …). The cargo falls in and out along with the robot. Why? How to fix it?
Can you explain why there is a pause after opening the gripper.
- Before looking at the code, write down in a log step by step: how to work the robot correctly take cargo and how put. What must happen first: stopping or gripping? Opening the gripper or driving away?
- Compare your order with the rule:
- Take: first stop, then close the gripper.
- Put: open the grab, wait until it opens, and only then drive away.
At the neighbors putDown() is like this: gripper.write(OPEN_ANGLE); and immediately drive(BACKWARD, …). The cargo falls in and out along with the robot. Why? How to fix it?
Your order is written down and checked against the rule, the neighbors' mistake is explained.
At the neighbors putDown() is like this: gripper.write(OPEN_ANGLE); and immediately drive(BACKWARD, …). The cargo falls in and out along with the robot. Why?
- Write down the order rules for “take” and “put.”
- Find with experience the smallest pause after opening, during which the load remains in place: 100, 200, 400, 600 ms, three times each.
- What kind of break will you take into the program and why not the shortest of the successful ones?
The rules are written down, the experience table and the selected pause with justification.
Take and put
20 min- Create a sketch
L52_Pick_Place. Transfer measurementskp,baseSpeed, sensor and servo contacts, their angles.
#include <Servo.h> const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5;const int LEFT_DIR_PIN = 7;const int LEFT_SPEED_PIN = 6;const int RIGHT_DIR_PIN = 4;const int RIGHT_SPEED_PIN = 5;const int FORWARD = HIGH;const int BACKWARD = (FORWARD == HIGH) ? LOW : HIGH; // Измерения и настройки из журнала (уроки 3.1 и 3.2).const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0;float kp = 0.5;int baseSpeed = 100; int leftLevel = 100;int rightLevel = 100; int level(int value, int white, int black) { long result = (long)(value - black) * 100 / (white - black); return constrain(result, 0, 100);} void readSensors() { leftLevel = level(analogRead(LEFT_SENSOR_PIN), LEFT_WHITE, LEFT_BLACK); rightLevel = level(analogRead(RIGHT_SENSOR_PIN), RIGHT_WHITE, RIGHT_BLACK);} void drive(int leftDir, int leftSpeed, int rightDir, int rightSpeed) { digitalWrite(LEFT_DIR_PIN, leftDir); digitalWrite(RIGHT_DIR_PIN, rightDir); analogWrite(LEFT_SPEED_PIN, leftSpeed); analogWrite(RIGHT_SPEED_PIN, rightSpeed);} void stopMotors() { analogWrite(LEFT_SPEED_PIN, 0); analogWrite(RIGHT_SPEED_PIN, 0);} void followLineStep() { int error = leftLevel - rightLevel; int correction = kp * error; drive(FORWARD, constrain(baseSpeed + correction, 0, 255), FORWARD, constrain(baseSpeed - correction, 0, 255));} void setupRobot() { pinMode(LEFT_DIR_PIN, OUTPUT); pinMode(LEFT_SPEED_PIN, OUTPUT); pinMode(RIGHT_DIR_PIN, OUTPUT); pinMode(RIGHT_SPEED_PIN, OUTPUT); stopMotors(); // Без измерений робот не поедет. if (LEFT_WHITE == LEFT_BLACK || RIGHT_WHITE == RIGHT_BLACK) { while (true) { } } delay(2000);} // Контакты ультразвукового датчика: впиши номера из таблицы проводки своего робота.const int TRIG_PIN = -1;const int ECHO_PIN = -1; // Расстояние в сантиметрах или -1, если эхо не вернулось.int readDistanceCm() { digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW); unsigned long echoUs = pulseIn(ECHO_PIN, HIGH, 25000); if (echoUs == 0) { return -1; } return echoUs / 58;} void setupSonar() { // Без контактов датчика программа дальше не пойдёт. if (TRIG_PIN < 0 || ECHO_PIN < 0) { while (true) { } } pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT);} int turnSpeed = 100;int centerMs = 250; // подбираем: проехать от датчиков до центра перекрёсткаint leaveMs = 200; // время, чтобы датчик ушёл со старой линии // Выехать на центр перекрёстка: датчики стоят впереди колёс.void toCenter() { drive(FORWARD, baseSpeed, FORWARD, baseSpeed); delay(centerMs); stopMotors();} // Крутиться на месте, пока датчик со стороны поворота не найдёт новую линию.void turnUntilLine(bool left) { if (left) { drive(BACKWARD, turnSpeed, FORWARD, turnSpeed); } else { drive(FORWARD, turnSpeed, BACKWARD, turnSpeed); } delay(leaveMs); do { readSensors(); } while ((left ? leftLevel : rightLevel) > 30); stopMotors();} void turnLeftAtCrossing() { toCenter(); turnUntilLine(true);} void turnRightAtCrossing() { toCenter(); turnUntilLine(false);} // Развернуться на линии: крутиться, пока линия снова не окажется под датчиком.void turnAroundOnLine() { turnUntilLine(true);} // Ехать по линии заданное время.void followLineFor(unsigned long ms) { unsigned long start = millis(); while (millis() - start < ms) { readSensors(); followLineStep(); } stopMotors();} // Сервопривод захвата: впиши контакт и свои углы из журнала.const int SERVO_PIN = -1;const int OPEN_ANGLE = 90;const int CLOSED_ANGLE = 40;const int STOP_CM = 5;Servo gripper; void setupGripper() { // Без контакта сервопривода программа дальше не пойдёт. if (SERVO_PIN < 0) { while (true) { } } gripper.attach(SERVO_PIN); gripper.write(OPEN_ANGLE); delay(500);} // Ехать по линии и остановиться перед грузом по ультразвуку.void approachCan() { while (true) { int distance = readDistanceCm(); if (distance > 0 && distance <= STOP_CM) { break; } readSensors(); followLineStep(); } stopMotors();} // Сначала остановиться, потом закрыть захват.void pickUp() { stopMotors(); gripper.write(CLOSED_ANGLE); delay(600);} // Открыть захват, подождать и только потом отъехать назад.void putDown() { stopMotors(); gripper.write(OPEN_ANGLE); delay(600); drive(BACKWARD, baseSpeed, BACKWARD, baseSpeed); delay(300); stopMotors();} const unsigned long CARRY_MS = 1500; // сколько ехать по линии с грузом void setup() { setupSonar(); setupGripper(); setupRobot(); approachCan(); pickUp(); turnAroundOnLine(); followLineFor(CARRY_MS); putDown();} void loop() {}
- Place the load at the end of the line, the robot at the other end. Launch it.
Read setup() and tell me step by step what the robot will do.
The robot drives up, takes the load, turns around, carries it along the line and places it.
- Create a sketch
L52_Pick_Placeaccording to the template. Transfer measurementskp,baseSpeed, sensor and servo contacts, their angles. Driving along the line, intersections and access to the cargo are already ready. - Write the gripper setup yourself
setupGripper(),pickUp(),putDown()and delivery tosetup()- according to its order rules from the previous step.
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. #include <Servo.h>const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5;const int LEFT_DIR_PIN = 7;const int LEFT_SPEED_PIN = 6;const int RIGHT_DIR_PIN = 4;const int RIGHT_SPEED_PIN = 5;const int FORWARD = HIGH;const int BACKWARD = (FORWARD == HIGH) ? LOW : HIGH; // Измерения и настройки из журнала (уроки 3.1 и 3.2).const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0;float kp = 0.5;int baseSpeed = 100;int leftLevel = 100;int rightLevel = 100; // Контакты ультразвукового датчика: впиши номера из таблицы проводки своего робота.const int TRIG_PIN = -1;const int ECHO_PIN = -1;int turnSpeed = 100;int centerMs = 250; // подбираем: проехать от датчиков до центра перекрёсткаint leaveMs = 200; // время, чтобы датчик ушёл со старой линии // Сервопривод захвата: впиши контакт и свои углы из журнала.const int SERVO_PIN = -1;const int OPEN_ANGLE = 90;const int CLOSED_ANGLE = 40;const int STOP_CM = 5; Servo gripper;const unsigned long CARRY_MS = 1500; // сколько ехать по линии с грузом // ===== Твоя часть: напиши сам ===== void setupGripper() { // Без контакта сервопривода программа дальше не идёт. // Захват подключён и открыт; дай ему время раскрыться.} void pickUp() { // Взять груз: груз оказывается в захвате и не выталкивается.} void putDown() { // Поставить груз: груз остаётся на месте, когда робот отъезжает назад на 300 мс; в конце колёса стоят.} void setup() { setupSonar(); setupGripper(); setupRobot(); // Доставка: подъехать к грузу, взять, развернуться на линии, проехать с грузом CARRY_MS, поставить.} // ===== Готовое из уроков 3.2–5.1 — не меняй ===== int level(int value, int white, int black) { long result = (long)(value - black) * 100 / (white - black); return constrain(result, 0, 100);} void readSensors() { leftLevel = level(analogRead(LEFT_SENSOR_PIN), LEFT_WHITE, LEFT_BLACK); rightLevel = level(analogRead(RIGHT_SENSOR_PIN), RIGHT_WHITE, RIGHT_BLACK);} void drive(int leftDir, int leftSpeed, int rightDir, int rightSpeed) { digitalWrite(LEFT_DIR_PIN, leftDir); digitalWrite(RIGHT_DIR_PIN, rightDir); analogWrite(LEFT_SPEED_PIN, leftSpeed); analogWrite(RIGHT_SPEED_PIN, rightSpeed);} void stopMotors() { analogWrite(LEFT_SPEED_PIN, 0); analogWrite(RIGHT_SPEED_PIN, 0);} void followLineStep() { int error = leftLevel - rightLevel; int correction = kp * error; drive(FORWARD, constrain(baseSpeed + correction, 0, 255), FORWARD, constrain(baseSpeed - correction, 0, 255));} void setupRobot() { pinMode(LEFT_DIR_PIN, OUTPUT); pinMode(LEFT_SPEED_PIN, OUTPUT); pinMode(RIGHT_DIR_PIN, OUTPUT); pinMode(RIGHT_SPEED_PIN, OUTPUT); stopMotors(); // Без измерений робот не поедет. if (LEFT_WHITE == LEFT_BLACK || RIGHT_WHITE == RIGHT_BLACK) { while (true) { } } delay(2000);} // Расстояние в сантиметрах или -1, если эхо не вернулось.int readDistanceCm() { digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW); unsigned long echoUs = pulseIn(ECHO_PIN, HIGH, 25000); if (echoUs == 0) { return -1; } return echoUs / 58;} void setupSonar() { // Без контактов датчика программа дальше не пойдёт. if (TRIG_PIN < 0 || ECHO_PIN < 0) { while (true) { } } pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT);} // Выехать на центр перекрёстка: датчики стоят впереди колёс.void toCenter() { drive(FORWARD, baseSpeed, FORWARD, baseSpeed); delay(centerMs); stopMotors();} // Крутиться на месте, пока датчик со стороны поворота не найдёт новую линию.void turnUntilLine(bool left) { if (left) { drive(BACKWARD, turnSpeed, FORWARD, turnSpeed); } else { drive(FORWARD, turnSpeed, BACKWARD, turnSpeed); } delay(leaveMs); do { readSensors(); } while ((left ? leftLevel : rightLevel) > 30); stopMotors();} void turnLeftAtCrossing() { toCenter(); turnUntilLine(true);} void turnRightAtCrossing() { toCenter(); turnUntilLine(false);} // Развернуться на линии: крутиться, пока линия снова не окажется под датчиком.void turnAroundOnLine() { turnUntilLine(true);} // Ехать по линии заданное время.void followLineFor(unsigned long ms) { unsigned long start = millis(); while (millis() - start < ms) { readSensors(); followLineStep(); } stopMotors();} // Ехать по линии и остановиться перед грузом по ультразвуку.void approachCan() { while (true) { int distance = readDistanceCm(); if (distance > 0 && distance <= STOP_CM) { break; } readSensors(); followLineStep(); } stopMotors();} void loop() {}
Read yours setup() and tell your partner step by step what the robot will do.
- Place the load at the end of the line, the robot at the other end. Launch it.
Hint: How long to wait
The grab takes time to reach the corner. Try a pause of 300 and 600 ms: does he have time?
Hint: No contact
You have already seen defense without contact in the angle sample sketch.
The robot drives up, takes the load, turns around, carries it along the line and places it.
Requirement 4. Continue with your program L51_Stop_At_Can and save as L52_Pick_Place. The robot delivers along the line:
pickUp()andputDown()follow the order: stop - close; open - wait - drive away;- your function moves the gripper
moveGripper; - without servo contact the program does not start moving;
- in
setup(): drive up to the load, pick it up, turn around on the line, drive with the load, place it.
Acceptance. Cargo at the end of the line, robot at the other end: hands-free delivery.
Hint: Idea (1 of 2)
Each delivery action is a ready-made function from previous lessons or this lesson. All that remains is to connect them in order.
Hint: Plan (2 of 2)
followLineToCan(STOP_CM).pickUp().turnAroundOnLine().- Drive along the line
CARRY_MS. putDown().
The program fulfills requirements 1-4, delivery is hands-free.
Reliability
12 min- Repeat the transportation three times in a row. Where does a robot make mistakes most often?
- Pick up
STOP_CMso that the load is in the middle of the gripper. - Pick up
CARRY_MS, so that the load is placed in the right place.
Three transfers in a row without using your hands. The values are written to the log.
- Repeat the transportation five times in a row. Write down each failure: at what stage and what happened.
| Check-in | Entrance | Capture | Transportation | Production | What happened |
|---|---|---|---|---|---|
| 1 | |||||
| 2 | |||||
| 3 | |||||
| 4 | |||||
| 5 |
- Find the stage where there are the most errors. What one number should I change to fix this? Change and repeat three times.
Hint: What numbers are there
STOP_CM - where the robot stands in front of the load, CARRY_MS - where he puts it, gripper angles - how he holds it.
Five-race table, corrected leg and three straight hands-free hauls.
Measure delivery reliability and improve it according to.
- Make 10 deliveries in a row. For each, record the stage where it failed: approach, gripping, turning around, transport or placement.
- Count how many deliveries out of 10 were completed. Which stage gives you the most breakdowns?
- Fix only this stage and make 10 deliveries again. Has it gotten better? How long?
| Series | Successful out of 10 | Entrance | Capture | Reversal | Transportation | Production |
|---|---|---|---|---|---|---|
| Before | ||||||
| After |
Hint: If there are no breakdowns
Make the conditions more difficult: place the weight 1 cm to the side of the line or lift it baseSpeed. Where will it break first?
Two series of 10 deliveries, the stage with the most failures, what has changed and how much reliability has increased.
Quests by level
13 minTake a weight from the end of the line and place it at the other end of the same line.
The cargo is on one branch, the delivery point is on another. Transport the cargo through the intersection using the functions of lesson 4.2.
Write the function deliver(маршрут до груза, маршрут до места, время до места), which handles all the delivery. Pass routes using decision lines, for example "LS". Then in the final task the delivery will be one line of the program.
Test yourself
Select answers to prepare. They are not stored or evaluated on the site. Take the knowledge test in the course syllabus or discuss the answers with your instructor.
● Basic
★ Intermediate level
★★ Advanced level
Answer questions at your level. Correct answers needed: Beginner - 4 out of 5; Average - 4 out of 5; Advanced - 4 out of 5. Answers will be checked by the teacher or course program.
Summary
The robot has all the skills of the final task: driving, turning, staying in line, finding intersections, seeing cargo and transporting it. Next is the final project.
How to confirm the result
The result of the experiment relates the conditions, program version, observation and conclusion. Comparing attempts with one change helps verify the cause, including failed attempts. The mark refers to practice already completed; repetition for the sake of marking is not necessary. The discrepancy with the forecast sets the next hypothesis to be tested. A model, measurements of a real robot, and an explanation are different types of evidence.
Lesson vocabulary
| servo | A motor that turns at a given angle and holds it. |
| angle | Position of the servo arm in degrees, usually from 0 to 180. |
| library | A ready-made set of commands, which is connected with the #include line. Servo is a library for servos. |