Робот-курьер Урок 5.2 Все курсыДля учителяПройти в программе Ученик Arduino Uno + шилд моторов L298P
Тема 5 · Урок 5.2

Захват: берём и ставим

Робот подъезжает к грузу, берёт его захватом, увозит и ставит в нужном месте.

2 × 45 мин1–2 ученика на роботаПрактика

Результат урока

Робот подъезжает к грузу по ультразвуку, берёт её, разворачивается, везёт по линии и ставит на другом конце. Действия оформлены функциями pickUp() и putDown().

Ты научишься

  • Объяснить, как сервопривод поворачивается на заданный угол.
  • Найти углы «открыто» и «закрыто» для своего захвата опытом.
  • Соблюдать порядок: остановиться — закрыть; открыть — подождать — отъехать.
  • Собрать перевозку груза из функций прошлых уроков.

Нужно знать

  • 5.1 Ультразвук: видим груз
  • 4.2 Повороты на перекрёстке и маршрут

Понадобится

Захват с сервоприводомУльтразвуковой датчик расстоянияДатчик линии (аналоговый, Trema) × 2

Зачем этот урок

Робот умеет доезжать до груза. Остался последний навык курьера — взять груз, довезти и аккуратно поставить. Сегодня робот соберёт всё вместе: линия, ультразвук и захват.

Разминка · вспомни
  1. Какое условие остановки перед грузом правильное и почему?
  2. Как робот разворачивается на линии?
  3. Что делает followLineFor(1500)?
Безопасность

Не держи пальцы в захвате, когда включено питание. Сервопривод может резко повернуться при загрузке программы.

1
Занятие 1 · 45 мин
Сервопривод и захват
1

Как работает сервопривод

8 мин

Что должен делать робот. Курьер должен взять груз, удержать его при перевозке и отпустить в нужном месте. Для этого добавим механический захват с сервоприводом. Ультразвук помогает подъехать, но сам ничего не захватывает.

Что используем и как это работает. Внутри сервопривода есть мотор, редуктор, датчик положения и управляющая схема. По управляющим импульсам он получает заданное положение вала и старается его удержать. Библиотека Servo формирует эти импульсы; gripper.write задаёт положение, а не скорость вращения. Рычаг превращает поворот вала в движение губок захвата.

Как связать измерение с действием. Углы «открыто» и «закрыто» подбираем для своей механики небольшими изменениями; чужие числа могут упереть механизм в ограничитель. Сначала останавливаем колёса, затем закрываем захват и даём ему время. Команда закрытия не доказывает, что груз удерживается: это проверяем наблюдением. Подключение и питание сверяем с подтверждённой схемой комплекта.

Обычный мотор крутится, пока включён. Сервопривод поворачивается на заданный угол и держит его. Внутри у него мотор, шестерёнки и датчик положения. Командуем углом в градусах: gripper.write(90).

Предскажи

Что сделает сервопривод после gripper.write(90), а потом gripper.write(40)?

Должно получиться

Ты можешь объяснить, чем сервопривод отличается от мотора колеса.

Обычный мотор крутится, пока включён. Сервопривод поворачивается на заданный угол и держит его. Командуем углом в градусах: gripper.write(90).

Предскажи

Что сделает сервопривод после gripper.write(90), а потом gripper.write(40)? А мотор колеса после analogWrite(…, 90)?

  1. Запиши в журнал: чем команда сервоприводу отличается от команды мотору колеса — что задаёт число и когда движение заканчивается.
Должно получиться

Сравнение сервопривода и мотора колеса записано.

Сервопривод поворачивается на заданный угол и держит его: внутри мотор, шестерёнки и датчик положения.

  1. Посмотри контакт сервопривода на карточке у робота и впиши его в скетч пробы угла.
  2. Сделай опыт: gripper.write(40), пауза, gripper.write(90), пауза. Пробуй паузы 100, 300 и 600 мс. При какой паузе захват успевает дойти до угла?
  3. Запиши, сколько примерно времени нужно сервоприводу на 50° и почему программа должна ждать после команды.
Должно получиться

В журнале — опыт с паузами и наименьшая пауза, при которой захват доходит до угла.

2

Найди углы «открыто» и «закрыто»

22 мин

У каждого захвата свои углы. Найдём их опытом, как время поворота в уроке 2.2.

  1. Посмотри контакт сервопривода на карточке у робота и впиши его вместо -1.
Проба углаL52_Servo_Test.ino.ino
#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() {}
  1. Начни с angle = 90. Загрузи. Меняй угол по 10°: найди положение, где захват открыт так, что груз свободно входит.
  2. Найди угол, где захват держит груз крепко, но не давит изо всех сил.
  3. Запиши оба угла в журнал.
Подсказка: Сервопривод гудит или дрожит

Захват упёрся в груз или в собственный упор и не может дойти до угла. Отступи на 5–10° от этого положения: сервопривод не должен работать в упор.

Должно получиться

В журнале записаны углы «открыто» и «закрыто», при которых груз свободно входит и надёжно держится.

У каждого захвата свои углы. Найди их опытом — способ выбери сам, но меняй только angle и записывай каждую попытку.

КомандаЧто делает
gripper.attach(контакт);Связывает сервопривод с контактом
gripper.write(угол);Поворачивает на угол 0…180 и держит его
  1. Создай скетч L52_Servo_Test по шаблону. Посмотри контакт сервопривода на карточке у робота и впиши его вместо -1.
  2. Допиши setup() сам: захват должен повернуться на angle.
Шаблон: проба углаL52_Servo_Test_Gaps.ino.ino
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. #include <Servo.h> // Контакт сервопривода захвата: впиши номер из таблицы проводки своего робота.const int SERVO_PIN = -1;int angle = 90; // меняй понемногу, чтобы найти «открыто» и «закрыто» Servo gripper; // ===== Твоя часть: напиши сам ===== void setup() {  if (SERVO_PIN < 0) {    while (true) {    }  }   // Захват поворачивается на угол angle и держит его.} void loop() {}
  1. Найди угол «открыто»: груз свободно входит в захват.
  2. Найди угол «закрыто»: груз держится крепко, а сервопривод не гудит.
  3. Проверь «закрыто»: подними робота с грузом и слегка наклони. Груз не выпадает?
ПопыткаangleЧто видноЧто поменять и почему
190
2
3
4
Подсказка: Сервопривод гудит или дрожит

Захват упёрся и не может дойти до угла. Что нужно сделать с углом?

Должно получиться

В журнале — попытки с объяснением, углы «открыто» и «закрыто» и проверка наклоном.

Посмотри контакт сервопривода на карточке у робота.

Требование 1. Скетч L52_Servo_Test для подбора углов: угол вводится числом в монитор порта, и захват поворачивается сразу, без новой загрузки программы.

  • Принимаются только числа от 0 до 180; на другое число программа отвечает, что так нельзя, и захват не двигается.
  • После каждого поворота программа печатает, какой угол сейчас.
  • Без номера контакта программа не трогает сервопривод.
Как это устроено: Справка по командам
КомандаЧто делает
#include <Servo.h>, Servo gripper;Подключить библиотеку и создать сервопривод
gripper.attach(pin)Связать сервопривод с контактом
gripper.write(angle)Повернуть на угол 0…180
Serial.available()Сколько символов пришло из монитора порта
Serial.parseInt()Прочитать из пришедших символов целое число

Требование 2. Углы «открыто» и «закрыто» найдены с запасом и проверены:

  • «открыто» — груз входит в захват, даже если робот остановился на 1 см левее или правее середины;
  • «закрыто» — найди угол, где захват упирается в груз, и отступи от него так, чтобы сервопривод не гудел; запиши, на сколько градусов отступил;
  • груз не выпадает, когда робота с грузом наклоняют на 30° вперёд и назад.

Требование 3. Функция moveGripper(target) поворачивает захват к углу плавно — шагами по 2° — а не одним рывком. Сравни, как ведёт себя груз при резком и плавном закрытии.

Подсказка: Идея (1 из 2)

Помни текущий угол в переменной. Пока он не равен цели, сдвигай его на шаг в сторону цели и делай короткую паузу.

Подсказка: Фрагмент кода (2 из 2)

currentAngle += (target > currentAngle) ? 2 : -2; — что будет, если цель отличается на нечётное число градусов?

Подсказка: В мониторе порта ничего не происходит

Проверь скорость 9600 и что монитор отправляет строку по Enter.

Должно получиться

Скетч подбора углов выполняет требование 1. В журнале — углы, запас «закрыто» в градусах, проверка наклоном и сравнение резкого и плавного закрытия. moveGripper работает.

3

Порядок действий важен

15 мин

Сопоставь функции pickUp() и putDown() в итоговом скетче с правилом порядка (следующее занятие):

  • Взять: сначала остановиться, потом закрыть захват.
  • Поставить: открыть захват, подождать, пока он раскроется, и только потом отъехать.
Найди ошибку · Груз уезжает с роботом

У соседей putDown() такая: gripper.write(OPEN_ANGLE); и сразу drive(BACKWARD, …). Груз то падает, то уезжает вместе с роботом. Почему? Как исправить?

Должно получиться

Ты можешь объяснить, зачем пауза после открытия захвата.

  1. До того как смотреть код, запиши в журнал по шагам: как роботу правильно взять груз и как поставить. Что должно случиться раньше: остановка или захват? Открытие или отъезд?
  2. Сравни свой порядок с правилом:
  • Взять: сначала остановиться, потом закрыть захват.
  • Поставить: открыть захват, подождать, пока он раскроется, и только потом отъехать.
Найди ошибку · Груз уезжает с роботом

У соседей putDown() такая: gripper.write(OPEN_ANGLE); и сразу drive(BACKWARD, …). Груз то падает, то уезжает вместе с роботом. Почему? Как исправить?

Должно получиться

Твой порядок записан и сверен с правилом, ошибка соседей объяснена.

Найди ошибку · Груз уезжает с роботом

У соседей putDown() такая: gripper.write(OPEN_ANGLE); и сразу drive(BACKWARD, …). Груз то падает, то уезжает вместе с роботом. Почему?

  1. Запиши правила порядка для «взять» и «поставить».
  2. Найди опытом наименьшую паузу после открытия, при которой груз остаётся на месте: 100, 200, 400, 600 мс, по три раза каждую.
  3. Какую паузу ты возьмёшь в программу и почему не самую короткую из удачных?
Должно получиться

Правила записаны, таблица опыта и выбранная пауза с обоснованием.

2
Занятие 2 · 45 мин
Перевозка груза
4

Взять и поставить

20 мин
  1. Создай скетч L52_Pick_Place. Перенеси измерения, kp, baseSpeed, контакты датчика и сервопривода, свои углы.
Взять и поставитьL52_Pick_Place.ino.ino
#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() {}
  1. Поставь груз в конце линии, робота — на другом конце. Запусти.
Предскажи

Прочитай setup() и расскажи по шагам, что сделает робот.

Должно получиться

Робот подъезжает, берёт груз, разворачивается, везёт его по линии и ставит.

  1. Создай скетч L52_Pick_Place по шаблону. Перенеси измерения, kp, baseSpeed, контакты датчика и сервопривода, свои углы. Езда по линии, перекрёстки и подъезд к грузу уже готовы.
  2. Напиши сам подготовку захвата setupGripper(), pickUp(), putDown() и доставку в setup() — по своим правилам порядка из прошлого шага.
Шаблон: взять и поставитьL52_Pick_Place_Gaps.ino.ino
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. #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() {}
Предскажи

Прочитай свой setup() и расскажи напарнику по шагам, что сделает робот.

  1. Поставь груз в конце линии, робота — на другом конце. Запусти.
Подсказка: Сколько ждать

Захвату нужно время, чтобы дойти до угла. Попробуй паузу 300 и 600 мс: успевает ли он?

Подсказка: Без контакта

Защиту без контакта ты уже видел в скетче пробы угла.

Должно получиться

Робот подъезжает, берёт груз, разворачивается, везёт его по линии и ставит.

Требование 4. Продолжи свою программу L51_Stop_At_Can и сохрани как L52_Pick_Place. Робот выполняет доставку по линии:

  • pickUp() и putDown() соблюдают порядок: остановиться — закрыть; открыть — подождать — отъехать;
  • захват двигается твоей функцией moveGripper;
  • без контакта сервопривода программа не начинает движение;
  • в setup(): подъехать к грузу, взять, развернуться на линии, проехать с грузом, поставить.

Приёмка. Груз в конце линии, робот — на другом конце: доставка без помощи рук.

Подсказка: Идея (1 из 2)

Каждое действие доставки — уже готовая функция из прошлых уроков или этого урока. Осталось соединить их по порядку.

Подсказка: План (2 из 2)
  1. followLineToCan(STOP_CM).
  2. pickUp().
  3. turnAroundOnLine().
  4. Проехать по линии CARRY_MS.
  5. putDown().
Должно получиться

Программа выполняет требования 1–4, доставка проходит без помощи рук.

5

Надёжность

12 мин
  1. Повтори перевозку три раза подряд. Где робот ошибается чаще всего?
  2. Подбери STOP_CM так, чтобы груз оказывался в середине захвата.
  3. Подбери CARRY_MS, чтобы груз ставился в нужном месте.
Должно получиться

Три перевозки подряд без помощи рук. Значения записаны в журнал.

  1. Повтори перевозку пять раз подряд. Каждую неудачу запиши: на каком этапе и что случилось.
ЗаездПодъездЗахватПеревозкаПостановкаЧто случилось
1
2
3
4
5
  1. Найди этап, где ошибок больше всего. Какое одно число поменять, чтобы это исправить? Поменяй и повтори три раза.
Подсказка: Какие числа есть

STOP_CM — где робот встаёт перед грузом, CARRY_MS — где ставит, углы захвата — как держит.

Должно получиться

Таблица из пяти заездов, исправленный этап и три перевозки подряд без помощи рук.

Измерь надёжность доставки и улучши её по данным.

  1. Сделай 10 доставок подряд и отметь для каждой, на каком этапе она сорвалась: подъезд, захват, разворот, перевозка, постановка.
  2. Посчитай, сколько доставок из 10 прошли. Какой этап даёт больше всего срывов?
  3. Исправь только этот этап и снова сделай 10 доставок. Стало лучше? На сколько?
СерияУспешно из 10ПодъездЗахватРазворотПеревозкаПостановка
До
После
Подсказка: Если срывов нет

Усложни условия: поставь груз на 1 см в сторону от линии или подними baseSpeed. Где сорвётся первым?

Должно получиться

Две серии по 10 доставок, этап с больше всего срывов, что изменено и насколько выросла надёжность.

6

Задания по уровням

13 мин
Основное · Перевозка по линии

Взять груз с конца линии и поставить его на другом конце той же линии.

Средний уровень · Через перекрёсток

Груз стоит на одной ветке, место доставки — на другой. Перевези груз через перекрёсток, используя функции урока 4.2.

Продвинутый уровень · Функция deliver

Напиши функцию deliver(маршрут до груза, маршрут до места, время до места), которая выполняет всю доставку. Маршруты передавай строками решений, например "LS". Тогда в итоговой задаче доставка будет одной строкой программы.

Проверь себя

Проверь себя

Выберите ответы для подготовки. Они не сохраняются и не оцениваются на сайте. Пройдите проверку знаний в программе курса или обсудите ответы с преподавателем.

● Основное

1.Чем сервопривод отличается от мотора колеса?
2.В каком порядке правильно взять груз?
3.Зачем в putDown() пауза после открытия захвата?
4.Сервопривод гудит, захват дрожит у груза. Что сделать?
5.Почему углы захвата подбирают опытом?

★ Средний уровень

1.Груз стоит на одной ветке, место доставки — на другой. Что нужно добавить к перевозке по линии?
2.В каком порядке выполняется доставка?
3.Функция pickUp() осталась пустой. Что сделает робот у груза?
4.При проверке наклоном груз выпадает из захвата. Что поменять?
5.Три заезда из пяти сорвались на постановке: груз ставится мимо места. Какое одно число менять первым?

★★ Продвинутый уровень

1.Что в этом вызове означает строка "LS"?
deliver("LS", "R", 1500);
2.Зачем оформлять доставку функцией deliver()?
3.currentAngle = 90, target = 41. Что произойдёт?
void moveGripper(int target) {  while (currentAngle != target) {    currentAngle += (target > currentAngle) ? 2 : -2;    gripper.write(currentAngle);    delay(15);  }}
4.Серия из 10 доставок: срывов на подъезде — 0, на захвате — 1, на развороте — 4, при перевозке — 1, на постановке — 0. Что улучшать первым?
5.После исправления разворота успешных доставок стало 7 из 10 вместо 4. Как убедиться, что помогло именно исправление?

Отвечай на вопросы своего уровня. Нужно верных ответов: Начальный — 4 из 5; Средний — 4 из 5; Продвинутый — 4 из 5. Ответы проверит преподаватель или программа курса.

Итог

У робота есть все умения итоговой задачи: ехать, поворачивать, держаться линии, находить перекрёстки, видеть груз и перевозить его. Дальше — итоговый проект.

Как подтвердить результат

Результат опыта связывает условия, версию программы, наблюдение и вывод. Сравнение попыток с одним изменением помогает проверить причину, включая неудачные попытки. Отметка относится к уже выполненной практике; повтор ради отметки не нужен. Расхождение с прогнозом задаёт следующую проверяемую гипотезу. Модель, измерения настоящего робота и объяснение — разные виды доказательств.

Словарь урока

сервоприводМотор, который поворачивается на заданный угол и держит его.
уголПоложение рычага сервопривода в градусах, обычно от 0 до 180.
библиотекаГотовый набор команд, который подключают строкой #include. Servo — библиотека для сервоприводов.