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

Энкодеры: считаем обороты и точное расстояние

Робот считает обороты колёс и проезжает отмеренное расстояние, а не «сколько успеет за время».

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

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

Программа считает обороты колёс энкодерами и проезжает отмеренное расстояние. Число импульсов на оборот измерено, точность проверена линейкой. Не нужен для итоговой задачи.

Ты научишься

  • Объяснить, как энкодер превращает оборот колеса в импульсы.
  • Измерить число импульсов на оборот колеса.
  • Пересчитать расстояние в импульсы через длину окружности колеса.
  • Проехать отмеренное расстояние и оценить точность линейкой.

Нужно знать

  • 2.1 Моторы: едем и останавливаемся
  • 3.2 П-регулятор: плавно и быстро

Понадобится

Колесо × 2Мотор-редуктор × 2USB-кабель для Arduino

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

В уроке 2.1 робот ехал «по времени»: моторы включались на две секунды. Но сколько он проедет за две секунды, зависит от заряда батарей, пола и веса. Энкодер считает обороты колеса, поэтому программа может выбирать момент остановки по вращению колёс, а не только по времени. Точность пути всё равно нужно проверить линейкой.

Для урока нужен дополнительный модуль: энкодер на каждое колесо. В основной комплект курса он не входит; без него урок не провести. Факультатив не нужен для итоговой задачи.

Разминка · вспомни
  1. Как в уроке 2.1 робот проезжал заданный участок?
  2. Почему при тех же двух секундах робот с севшими батареями проезжает меньше?
  3. Что такое П-регулятор из урока 3.2 в одной фразе?
1
Занятие 1 · 45 мин
Считаем импульсы
1

Почему время — плохая линейка

8 мин

Что должен делать робот. Роботу нужно проехать заданное расстояние точнее, чем по секундомеру. Для этого понадобятся дополнительные энкодеры: в базовом комплекте их нет. Они измеряют вращение колёс, а не расстояние до препятствия.

Что используем и как это работает. Энкодер выдаёт импульсы при вращении. Счётчик накапливает их; число импульсов за оборот связывает счёт с оборотами колеса. Длина окружности связывает обороты с предполагаемым путём. Прерывание позволяет быстро увеличивать счётчик при импульсе, даже когда основная программа занята другой работой.

Как связать измерение с действием. Измерим импульсы на оборот и используем диаметр своего колеса. На AVR чтение многобайтного счётчика прерывание может разорвать посередине, поэтому копируем его при кратко выключенных прерываниях. Проверим результат линейкой: при проскальзывании колесо вращается, а корпус проходит меньше, поэтому энкодер не даёт абсолютной координаты.

  1. Вспомни программу из урока 2.1, которая едет вперёд по времени. Запусти её дважды: на полной скорости и на скорости вдвое меньше. Время одинаковое.
  2. Отметь, где робот остановился оба раза. Измерь линейкой оба пути и запиши в журнал.
Должно получиться

Ты видишь: при одном и том же времени робот проезжает разное расстояние.

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

Три пути измерены, причины записаны.

  1. Измерь пути «по времени» при скорости 100, 150 и 200. Построй в журнале таблицу «скорость — путь».
  2. Оцени: можно ли по этой таблице пересчитать время для 50 см на скорости 120? Насколько ты доверяешь такому пересчёту завтра, на другом заряде батарей? Объясни.
  3. Запиши, что нужно измерять во время езды, чтобы путь не зависел от этих причин.
Должно получиться

Таблица, оценка пересчёта и ответ — в журнале.

2

Как устроен энкодер

12 мин

Энкодер — это диск с прорезями на оси колеса и датчик-«вилка». Внутри вилки с одной стороны светит светодиод, с другой — приёмник. Когда мимо проходит прорезь, свет попадает на приёмник, и датчик посылает импульс. Один оборот колеса — столько импульсов, сколько прорезей на диске.

  1. Посчитай прорези на диске энкодера. Запиши в журнал.
  2. Найди в таблице проводки своего робота, к каким контактам подключены энкодеры. Запиши номера.
Безопасность

Не подключай энкодеры при включённом питании. Если в таблице проводки контакты энкодеров не указаны — подключение делает преподаватель.

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

В журнале — число прорезей и номера контактов левого и правого энкодеров.

Как это устроено: Что такое прерывание

Импульсы приходят быстро — десятки в секунду. Если программа занята чем-то ещё, она может их пропустить. Поэтому счёт делает прерывание: на каждый импульс Arduino сама откладывает основную программу, прибавляет 1 к счётчику и возвращается обратно. На Arduino Uno так умеют только контакты 2 и 3.

Энкодер — диск с прорезями на оси колеса и оптический датчик щели: прорезь проходит мимо датчика — датчик даёт импульс. Считать импульсы будет прерывание: Arduino сама вызывает короткую функцию на каждый импульс, что бы ни делала основная программа.

  1. Посчитай прорези на диске, запиши номера контактов энкодеров из таблицы проводки.
Предскажи

Колесо диаметром 45 мм, на диске 20 прорезей. Сколько миллиметров проезжает колесо между двумя импульсами? Посчитай до запуска.

  1. Ответь в журнале: почему счётчик импульсов нельзя увеличивать в loop() после delay(200)?
Безопасность

Не подключай энкодеры при включённом питании. Контакты не указаны в таблице — подключает преподаватель.

Подсказка: Сколько проезжает колесо за оборот

За один оборот колесо проходит длину своей окружности: π × диаметр.

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

Число прорезей, контакты, прогноз «мм на импульс» и ответ про delay — в журнале.

  1. Посчитай прорези, запиши контакты энкодеров из таблицы проводки.
  2. Оцени частоту импульсов: колесо 45 мм, 20 прорезей, робот едет 0,3 м/с. Сколько импульсов в секунду даёт каждый энкодер?
  3. Объясни в журнале, почему счёт делают прерыванием и почему на Arduino Uno для этого подходят только контакты 2 и 3.
Безопасность

Не подключай энкодеры при включённом питании. Контакты не указаны в таблице — подключает преподаватель.

Подсказка: Частота импульсов

Сначала — сколько оборотов в секунду делает колесо при 300 мм/с.

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

Оценка частоты и объяснение — в журнале.

3

Импульсов на один оборот

22 мин
  1. Создай скетч LF1_Count. Впиши номера контактов энкодеров.
Счёт импульсовLF1_Count.ino.ino
// Контакты энкодеров: впиши номера из таблицы проводки своего робота.// На Arduino Uno счёт по прерыванию работает только на контактах 2 и 3.const int LEFT_ENCODER_PIN = -1;const int RIGHT_ENCODER_PIN = -1; // Счётчики импульсов. volatile: их меняет прерывание.volatile long leftTicks = 0;volatile long rightTicks = 0; // Arduino вызывает её сама на каждый импульс левого энкодера.void countLeft() {  leftTicks++;} // То же для правого энкодера.void countRight() {  rightTicks++;} void setupEncoders() {  // Без контактов энкодеров программа дальше не пойдёт.  if (LEFT_ENCODER_PIN < 0 || RIGHT_ENCODER_PIN < 0) {    Serial.println("Впиши контакты энкодеров");    while (true) {    }  }  pinMode(LEFT_ENCODER_PIN, INPUT);  pinMode(RIGHT_ENCODER_PIN, INPUT);  attachInterrupt(digitalPinToInterrupt(LEFT_ENCODER_PIN), countLeft, RISING);  attachInterrupt(digitalPinToInterrupt(RIGHT_ENCODER_PIN), countRight, RISING);} void setup() {  Serial.begin(9600);  setupEncoders();} void loop() {  // Читаем оба счётчика, пока прерывания выключены: число не изменится посередине чтения.  noInterrupts();  long left = leftTicks;  long right = rightTicks;  interrupts();   Serial.print("Левое: ");  Serial.print(left);  Serial.print("  правое: ");  Serial.println(right);  delay(200);}
  1. Загрузи и открой монитор порта. Поверни колесо рукой: число должно расти.
  2. Сделай мелом или маркером метку на колесе. Прокрути колесо рукой ровно 10 оборотов по метке. Раздели число импульсов на 10 — это импульсов на оборот.
  3. Повтори для второго колеса. Запиши оба числа в журнал.
Должно получиться

В журнале — импульсов на оборот для каждого колеса. Обычно это число прорезей на диске.

Если не получилось
Что видишьЧто сделать
«Впиши контакты энкодеров»Впиши номера контактов вместо −1
Число не растётПроверь провода и что диск проходит через щель датчика
Число растёт, когда колесо стоитКолесо остановилось на краю прорези: сдвинь его чуть-чуть
  1. Создай скетч LF1_Count по шаблону. Впиши контакты, напиши свою часть.
Шаблон: счёт импульсовLF1_Count_Gaps.ino.ino
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. // Контакты энкодеров: впиши номера из таблицы проводки своего робота.// На Arduino Uno счёт по прерыванию работает только на контактах 2 и 3.const int LEFT_ENCODER_PIN = -1;const int RIGHT_ENCODER_PIN = -1; // Счётчики импульсов. volatile: их меняет прерывание.volatile long leftTicks = 0;volatile long rightTicks = 0; // ===== Твоя часть: напиши сам ===== void countLeft() {  // Каждый импульс левого энкодера увеличивает его счётчик на 1.} void countRight() {  // То же для правого энкодера.} void loop() {  long left = 0;  long right = 0;  // Скопируй оба счётчика в left и right так, чтобы прерывание не изменило их посередине чтения.   Serial.print("Левое: ");  Serial.print(left);  Serial.print("  правое: ");  Serial.println(right);  delay(200);} // ===== Готовое: подключение энкодеров — не меняй ===== void setupEncoders() {  // Без контактов энкодеров программа дальше не пойдёт.  if (LEFT_ENCODER_PIN < 0 || RIGHT_ENCODER_PIN < 0) {    Serial.println("Впиши контакты энкодеров");    while (true) {    }  }  pinMode(LEFT_ENCODER_PIN, INPUT);  pinMode(RIGHT_ENCODER_PIN, INPUT);  attachInterrupt(digitalPinToInterrupt(LEFT_ENCODER_PIN), countLeft, RISING);  attachInterrupt(digitalPinToInterrupt(RIGHT_ENCODER_PIN), countRight, RISING);} void setup() {  Serial.begin(9600);  setupEncoders();}
  1. Прокрути каждое колесо рукой ровно 10 оборотов по метке и посчитай импульсы на оборот.
  2. Сравни с числом прорезей. Совпало? Если нет — найди причину.
Подсказка: Как не испортить число при чтении

Счётчик — long, это 4 байта, а Arduino читает их по одному. Прерывание посередине даст смесь старого и нового числа. Есть пара команд, которая на время выключает прерывания.

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

Счёт работает, импульсы на оборот обоих колёс записаны и сравнены с числом прорезей.

Требование 1. Программа считает импульсы каждого колеса прерыванием и печатает оба счётчика в монитор порта; счётчики читаются так, что прерывание не портит число. Без вписанных контактов программа не идёт дальше.

  1. Напиши программу сам.
Как это устроено: Справка по командам
КомандаЧто делает
attachInterrupt(digitalPinToInterrupt(pin), f, RISING)Вызывать функцию f на каждый фронт импульса на контакте pin
volatile long xПеременная, которую меняет прерывание
noInterrupts() / interrupts()Выключить и снова включить прерывания
  1. Измерь импульсы на оборот: прокрути колесо 10 оборотов по метке, потом 20 оборотов. Насколько различаются два результата? Какой точнее и почему?
  2. Найди положение колеса, при котором счёт растёт без движения. Объясни в журнале, что происходит в датчике.
Подсказка: Идея (1 из 2)

Функции прерывания должны быть короткими: только увеличить счётчик.

Подсказка: Счёт без движения (2 из 2)

На краю прорези свет попадает на приёмник «наполовину», и сигнал дрожит около порога.

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

Программа выполняет требование 1, импульсы на оборот измерены дважды, объяснение дрожания записано.

2
Занятие 2 · 45 мин
Точное расстояние
4

Миллиметры в импульсы

15 мин

За один оборот колесо проезжает длину окружности: π × диаметр. Диаметр колеса — 45 мм. Чтобы проехать 500 мм, нужно 500 / (π × 45) оборотов, а импульсов — в TICKS_PER_REV раз больше.

  1. Посчитай на бумаге: сколько импульсов на 500 мм, если на оборот 20 импульсов?
  2. Создай скетч LF1_Distance, впиши контакты и своё число импульсов на оборот.
Точное расстояниеLF1_Distance.ino.ino
// Контакты энкодеров: впиши номера из таблицы проводки своего робота.// На Arduino Uno счёт по прерыванию работает только на контактах 2 и 3.const int LEFT_ENCODER_PIN = -1;const int RIGHT_ENCODER_PIN = -1; 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 TICKS_PER_REV = 0; // импульсов на оборот колеса: измерь и впишиconst int WHEEL_DIAMETER_MM = 45;const int DISTANCE_MM = 500; // сколько проехатьint speed = 100; volatile long leftTicks = 0;volatile long rightTicks = 0; void countLeft() {  leftTicks++;} void countRight() {  rightTicks++;} // Сколько импульсов нужно, чтобы проехать mm миллиметров.long ticksFor(int mm, int ticksPerRev, int diameterMm) {  float revolutions = mm / (PI * diameterMm);  return (long)(revolutions * ticksPerRev + 0.5);} // Сколько импульсов в среднем насчитали оба колеса.long averageTicks() {  noInterrupts();  long sum = leftTicks + rightTicks;  interrupts();  return sum / 2;} 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 setupEncoders() {  // Без контактов энкодеров программа дальше не пойдёт.  if (LEFT_ENCODER_PIN < 0 || RIGHT_ENCODER_PIN < 0) {    Serial.println("Впиши контакты энкодеров");    while (true) {    }  }  pinMode(LEFT_ENCODER_PIN, INPUT);  pinMode(RIGHT_ENCODER_PIN, INPUT);  attachInterrupt(digitalPinToInterrupt(LEFT_ENCODER_PIN), countLeft, RISING);  attachInterrupt(digitalPinToInterrupt(RIGHT_ENCODER_PIN), countRight, RISING);} void setup() {  Serial.begin(9600);  pinMode(LEFT_DIR_PIN, OUTPUT);  pinMode(LEFT_SPEED_PIN, OUTPUT);  pinMode(RIGHT_DIR_PIN, OUTPUT);  pinMode(RIGHT_SPEED_PIN, OUTPUT);  drive(FORWARD, 0, FORWARD, 0);  setupEncoders();   // Без числа импульсов на оборот робот не поедет.  if (TICKS_PER_REV <= 0) {    Serial.println("Впиши число импульсов на оборот");    while (true) {    }  }  delay(2000); // время убрать руки от робота   long target = ticksFor(DISTANCE_MM, TICKS_PER_REV, WHEEL_DIAMETER_MM);  drive(FORWARD, speed, FORWARD, speed);  while (averageTicks() < target) {  }  drive(FORWARD, 0, FORWARD, 0);  delay(1000); // робот докатывается   Serial.print("Цель, импульсов: ");  Serial.println(target);  Serial.print("Насчитано после остановки: ");  Serial.println(averageTicks());} void loop() {}
  1. Поставь робота на пол у метки старта, включи питание моторов. Робот проедет и остановится. Измерь путь линейкой от метки до той же точки корпуса.
Должно получиться

Ты посчитал: около 71 импульса. Робот проехал примерно 50 см, путь измерен.

  1. Создай скетч LF1_Distance по шаблону, впиши контакты и импульсы на оборот.
  2. Напиши ticksFor и averageTicks по целям.
Шаблон: точное расстояниеLF1_Distance_Gaps.ino.ino
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. // Контакты энкодеров: впиши номера из таблицы проводки своего робота.// На Arduino Uno счёт по прерыванию работает только на контактах 2 и 3.const int LEFT_ENCODER_PIN = -1;const int RIGHT_ENCODER_PIN = -1; 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 TICKS_PER_REV = 0; // импульсов на оборот колеса: измерь и впишиconst int WHEEL_DIAMETER_MM = 45;const int DISTANCE_MM = 500; // сколько проехатьint speed = 100; volatile long leftTicks = 0;volatile long rightTicks = 0; // ===== Твоя часть: напиши сам ===== long ticksFor(int mm, int ticksPerRev, int diameterMm) {  // Сколько оборотов колеса нужно на mm миллиметров: один оборот — длина окружности колеса.  // Сколько это импульсов, округлённо до целого. Число PI в Arduino уже есть.  return 0;} long averageTicks() {  // Среднее двух счётчиков. Читай их так, чтобы прерывание не изменило число посередине.  return 0;} // ===== Готовое: энкодеры, моторы и поездка — не меняй ===== void countLeft() {  leftTicks++;} void countRight() {  rightTicks++;} 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 setupEncoders() {  // Без контактов энкодеров программа дальше не пойдёт.  if (LEFT_ENCODER_PIN < 0 || RIGHT_ENCODER_PIN < 0) {    Serial.println("Впиши контакты энкодеров");    while (true) {    }  }  pinMode(LEFT_ENCODER_PIN, INPUT);  pinMode(RIGHT_ENCODER_PIN, INPUT);  attachInterrupt(digitalPinToInterrupt(LEFT_ENCODER_PIN), countLeft, RISING);  attachInterrupt(digitalPinToInterrupt(RIGHT_ENCODER_PIN), countRight, RISING);} void setup() {  Serial.begin(9600);  pinMode(LEFT_DIR_PIN, OUTPUT);  pinMode(LEFT_SPEED_PIN, OUTPUT);  pinMode(RIGHT_DIR_PIN, OUTPUT);  pinMode(RIGHT_SPEED_PIN, OUTPUT);  drive(FORWARD, 0, FORWARD, 0);  setupEncoders();   // Без числа импульсов на оборот робот не поедет.  if (TICKS_PER_REV <= 0) {    Serial.println("Впиши число импульсов на оборот");    while (true) {    }  }  delay(2000); // время убрать руки от робота   long target = ticksFor(DISTANCE_MM, TICKS_PER_REV, WHEEL_DIAMETER_MM);  drive(FORWARD, speed, FORWARD, speed);  while (averageTicks() < target) {  }  drive(FORWARD, 0, FORWARD, 0);  delay(1000); // робот докатывается   Serial.print("Цель, импульсов: ");  Serial.println(target);  Serial.print("Насчитано после остановки: ");  Serial.println(averageTicks());} void loop() {}
Предскажи

Сколько импульсов вернёт ticksFor(500, 20, 45)? Посчитай до запуска.

  1. Запусти поездку от метки, измерь путь линейкой. Сравни с монитором порта: насколько «насчитано после остановки» больше цели?
Подсказка: Округление до целого

Если прибавить 0.5 и отбросить дробную часть, получится округление до ближайшего целого.

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

Прогноз совпал: 71. Робот проехал около 50 см, перекат после остановки виден в мониторе порта.

Требование 2. Функция long ticksFor(int mm, int ticksPerRev, int diameterMm) переводит миллиметры в импульсы с округлением до ближайшего целого и не переполняется на Arduino Uno.

Приёмка. Напиши функцию в тестовом скетче. Когда все строки PASS, перенеси её в свою программу.

Тест функции ticksForLF1_Ticks_Test.ino.ino
// Проверка функции ticksFor. Напиши функцию здесь, загрузи и открой монитор порта.// Когда все строки PASS, перенеси ticksFor в свою программу. // Требование 2: сколько импульсов нужно, чтобы проехать mm миллиметров (округлённо).long ticksFor(int mm, int ticksPerRev, int diameterMm) {  return 0;} int failed = 0; void expect(int mm, int ticksPerRev, int diameterMm, long expected) {  long actual = ticksFor(mm, ticksPerRev, diameterMm);  Serial.print(actual == expected ? "PASS " : "FAIL ");  Serial.print("ticksFor(");  Serial.print(mm);  Serial.print(", ");  Serial.print(ticksPerRev);  Serial.print(", ");  Serial.print(diameterMm);  Serial.print(") = ");  Serial.print(actual);  Serial.print(", ожидаем ");  Serial.println(expected);  if (actual != expected) {    failed++;  }} void setup() {  Serial.begin(9600);   expect(0, 20, 45, 0);  expect(141, 20, 45, 20);   // почти ровно один оборот  expect(500, 20, 45, 71);   // 70.7 округляется вверх  expect(1000, 20, 65, 98);  // другое колесо  expect(1000, 40, 45, 283);  expect(2000, 40, 45, 566); // 2000 × 40 не помещается в int   if (failed == 0) {    Serial.println("Все проверки пройдены");  } else {    Serial.print("Ошибок: ");    Serial.println(failed);  }} void loop() {}

Требование 3. Программа едет вперёд, пока среднее двух счётчиков не достигнет цели, останавливается и печатает цель и насчитанное после остановки. Без вписанного числа импульсов на оборот робот стоит.

  1. Выполни требования 2 и 3, проедь 500 мм от метки и измерь путь.
Подсказка: Идея (1 из 2)

Импульсы = обороты × импульсы на оборот, обороты = путь / длина окружности.

Подсказка: Тест на 2000 мм не проходит (2 из 2)

2000 * 40 — 80 000, больше, чем вмещает int. Считай в float или long до умножения.

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

Тест пройден, поездка на 500 мм сделана и измерена.

5

Почему робот проезжает дальше

15 мин

После команды «стоп» робот ещё немного катится — как велосипед, если перестать крутить педали.

  1. Сделай три заезда на 500 мм. Запиши пути в журнал.
  2. Уменьши speed до 70 и сделай ещё три заезда. Стало точнее?
speedЗаезд 1Заезд 2Заезд 3
100
70
Должно получиться

Таблица заполнена. Ты видишь, на какой скорости робот точнее.

  1. Сделай три заезда на 500 мм. Для каждого запиши путь линейкой и «перекат» в импульсах из монитора порта.
  2. Добавь в программу поправку: останавливайся на OVERSHOOT импульсов раньше цели, где OVERSHOOT — средний перекат из трёх заездов. Сделай ещё три заезда.
  3. Ответь в журнале: будет ли эта поправка верна на другой скорости? Почему?
Должно получиться

Шесть заездов в журнале, поправка проверена, ответ про скорость записан.

Требование 4. За SLOW_TICKS импульсов до цели робот сбавляет скорость, чтобы меньше катиться после остановки.

  1. Выполни требование 4 и подбери SLOW_TICKS и пониженную скорость.
  2. Сравни три заезда без замедления и три с замедлением: среднюю ошибку и разброс.
  3. Объясни в журнале, чем замедление лучше поправки «остановиться раньше на N импульсов».
Должно получиться

Требование 4 выполнено, сравнение двух способов и вывод — в журнале.

6

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

12 мин
Основное · Ровно 50 см

Проехать 500 мм с ошибкой не больше 2 см в трёх заездах из трёх.

Средний уровень · Прямо на метр

Проехать 1 м, выравнивая колёса по разнице импульсов: если левое насчитало больше — замедлить левое и ускорить правое, как П-регулятор из урока 3.2. Робот уходит от прямой не больше чем на 3 см.

Продвинутый уровень · Ровно 90 градусов

Повернуть на месте на 90°: колёса крутятся в разные стороны, каждое проходит четверть окружности диаметром в колею робота — 90 мм. Посчитать импульсы, проверить угол угольником. Ошибка — не больше 10°.

Проверь себя

Проверь себя

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

● Основное

1.Что считает энкодер?
2.На диске 20 прорезей, на каждую — один импульс. Сколько импульсов за 3 оборота колеса?
3.Какой путь проходит колесо диаметром 45 мм за один оборот?
4.Почему езда «по времени» неточна?
5.Почему после остановки робот проезжает чуть больше цели?

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

1.20 импульсов на оборот, колесо 45 мм. Сколько импульсов нужно на 500 мм?
2.Зачем счётчик импульсов объявлен как volatile long?
3.За одно время левое колесо насчитало 300 импульсов, правое — 270. Куда уводит робота?
4.Как точнее измерить число импульсов на оборот?
5.Почему поправка «остановиться на 5 импульсов раньше» перестаёт работать на другой скорости?

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

1.Робот поворачивает на месте на 90°, колея 90 мм. Какой путь проходит каждое колесо?
2.Функция верна на 500 мм, но для 2000 мм и 40 импульсов на оборот даёт неверное число. В чём ошибка?
long ticksFor(int mm, int ticksPerRev, int diameterMm) {  int ticks = mm * ticksPerRev / (3.14 * diameterMm);  return ticks;}
3.Колесо стоит на краю прорези, а счётчик растёт. Что происходит?
4.Почему цель сравнивают со средним двух счётчиков, а не с одним левым?
5.Счётчик long читают в loop() без noInterrupts(). Что может случиться?

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

Итог

Энкодер превращает оборот колеса в импульсы, а импульсы — в пройденный путь. Робот едет не «сколько успеет», а сколько нужно, при любом заряде батарей.

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

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

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

энкодерДатчик оборотов: диск с прорезями на оси колеса и датчик, который даёт импульс на каждую прорезь.
импульсКороткий сигнал энкодера: прорезь прошла мимо датчика.
прерываниеArduino откладывает основную программу и сразу выполняет короткую функцию, когда на контакт пришёл импульс.
volatileПометка переменной, которую меняет прерывание: программа каждый раз читает её заново.
длина окружностиПуть колеса за один оборот: π × диаметр.