Энкодеры: считаем обороты и точное расстояние
Робот считает обороты колёс и проезжает отмеренное расстояние, а не «сколько успеет за время».
Результат урока
Программа считает обороты колёс энкодерами и проезжает отмеренное расстояние. Число импульсов на оборот измерено, точность проверена линейкой. Не нужен для итоговой задачи.
Ты научишься
- Объяснить, как энкодер превращает оборот колеса в импульсы.
- Измерить число импульсов на оборот колеса.
- Пересчитать расстояние в импульсы через длину окружности колеса.
- Проехать отмеренное расстояние и оценить точность линейкой.
Нужно знать
- 2.1 Моторы: едем и останавливаемся
- 3.2 П-регулятор: плавно и быстро
Понадобится
Зачем этот урок
В уроке 2.1 робот ехал «по времени»: моторы включались на две секунды. Но сколько он проедет за две секунды, зависит от заряда батарей, пола и веса. Энкодер считает обороты колеса, поэтому программа может выбирать момент остановки по вращению колёс, а не только по времени. Точность пути всё равно нужно проверить линейкой.
Для урока нужен дополнительный модуль: энкодер на каждое колесо. В основной комплект курса он не входит; без него урок не провести. Факультатив не нужен для итоговой задачи.
- Как в уроке 2.1 робот проезжал заданный участок?
- Почему при тех же двух секундах робот с севшими батареями проезжает меньше?
- Что такое П-регулятор из урока 3.2 в одной фразе?
Почему время — плохая линейка
8 минЧто должен делать робот. Роботу нужно проехать заданное расстояние точнее, чем по секундомеру. Для этого понадобятся дополнительные энкодеры: в базовом комплекте их нет. Они измеряют вращение колёс, а не расстояние до препятствия.
Что используем и как это работает. Энкодер выдаёт импульсы при вращении. Счётчик накапливает их; число импульсов за оборот связывает счёт с оборотами колеса. Длина окружности связывает обороты с предполагаемым путём. Прерывание позволяет быстро увеличивать счётчик при импульсе, даже когда основная программа занята другой работой.
Как связать измерение с действием. Измерим импульсы на оборот и используем диаметр своего колеса. На AVR чтение многобайтного счётчика прерывание может разорвать посередине, поэтому копируем его при кратко выключенных прерываниях. Проверим результат линейкой: при проскальзывании колесо вращается, а корпус проходит меньше, поэтому энкодер не даёт абсолютной координаты.
- Вспомни программу из урока 2.1, которая едет вперёд по времени. Запусти её дважды: на полной скорости и на скорости вдвое меньше. Время одинаковое.
- Отметь, где робот остановился оба раза. Измерь линейкой оба пути и запиши в журнал.
Ты видишь: при одном и том же времени робот проезжает разное расстояние.
- Запусти программу «по времени» из урока 2.1 трижды: скорость 100, 150 и 200 при одном времени. Измерь пути линейкой.
- Ответь в журнале: что, кроме скорости в программе, меняет путь за одно и то же время? Назови не меньше трёх причин.
Три пути измерены, причины записаны.
- Измерь пути «по времени» при скорости 100, 150 и 200. Построй в журнале таблицу «скорость — путь».
- Оцени: можно ли по этой таблице пересчитать время для 50 см на скорости 120? Насколько ты доверяешь такому пересчёту завтра, на другом заряде батарей? Объясни.
- Запиши, что нужно измерять во время езды, чтобы путь не зависел от этих причин.
Таблица, оценка пересчёта и ответ — в журнале.
Как устроен энкодер
12 минЭнкодер — это диск с прорезями на оси колеса и датчик-«вилка». Внутри вилки с одной стороны светит светодиод, с другой — приёмник. Когда мимо проходит прорезь, свет попадает на приёмник, и датчик посылает импульс. Один оборот колеса — столько импульсов, сколько прорезей на диске.
- Посчитай прорези на диске энкодера. Запиши в журнал.
- Найди в таблице проводки своего робота, к каким контактам подключены энкодеры. Запиши номера.
Не подключай энкодеры при включённом питании. Если в таблице проводки контакты энкодеров не указаны — подключение делает преподаватель.
В журнале — число прорезей и номера контактов левого и правого энкодеров.
Как это устроено: Что такое прерывание
Импульсы приходят быстро — десятки в секунду. Если программа занята чем-то ещё, она может их пропустить. Поэтому счёт делает прерывание: на каждый импульс Arduino сама откладывает основную программу, прибавляет 1 к счётчику и возвращается обратно. На Arduino Uno так умеют только контакты 2 и 3.
Энкодер — диск с прорезями на оси колеса и оптический датчик щели: прорезь проходит мимо датчика — датчик даёт импульс. Считать импульсы будет прерывание: Arduino сама вызывает короткую функцию на каждый импульс, что бы ни делала основная программа.
- Посчитай прорези на диске, запиши номера контактов энкодеров из таблицы проводки.
Колесо диаметром 45 мм, на диске 20 прорезей. Сколько миллиметров проезжает колесо между двумя импульсами? Посчитай до запуска.
- Ответь в журнале: почему счётчик импульсов нельзя увеличивать в
loop()послеdelay(200)?
Не подключай энкодеры при включённом питании. Контакты не указаны в таблице — подключает преподаватель.
Подсказка: Сколько проезжает колесо за оборот
За один оборот колесо проходит длину своей окружности: π × диаметр.
Число прорезей, контакты, прогноз «мм на импульс» и ответ про delay — в журнале.
- Посчитай прорези, запиши контакты энкодеров из таблицы проводки.
- Оцени частоту импульсов: колесо 45 мм, 20 прорезей, робот едет 0,3 м/с. Сколько импульсов в секунду даёт каждый энкодер?
- Объясни в журнале, почему счёт делают прерыванием и почему на Arduino Uno для этого подходят только контакты 2 и 3.
Не подключай энкодеры при включённом питании. Контакты не указаны в таблице — подключает преподаватель.
Подсказка: Частота импульсов
Сначала — сколько оборотов в секунду делает колесо при 300 мм/с.
Оценка частоты и объяснение — в журнале.
Импульсов на один оборот
22 мин- Создай скетч
LF1_Count. Впиши номера контактов энкодеров.
// Контакты энкодеров: впиши номера из таблицы проводки своего робота.// На 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);}
- Загрузи и открой монитор порта. Поверни колесо рукой: число должно расти.
- Сделай мелом или маркером метку на колесе. Прокрути колесо рукой ровно 10 оборотов по метке. Раздели число импульсов на 10 — это импульсов на оборот.
- Повтори для второго колеса. Запиши оба числа в журнал.
В журнале — импульсов на оборот для каждого колеса. Обычно это число прорезей на диске.
| Что видишь | Что сделать |
|---|---|
| «Впиши контакты энкодеров» | Впиши номера контактов вместо −1 |
| Число не растёт | Проверь провода и что диск проходит через щель датчика |
| Число растёт, когда колесо стоит | Колесо остановилось на краю прорези: сдвинь его чуть-чуть |
- Создай скетч
LF1_Countпо шаблону. Впиши контакты, напиши свою часть.
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. // Контакты энкодеров: впиши номера из таблицы проводки своего робота.// На 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();}
- Прокрути каждое колесо рукой ровно 10 оборотов по метке и посчитай импульсы на оборот.
- Сравни с числом прорезей. Совпало? Если нет — найди причину.
Подсказка: Как не испортить число при чтении
Счётчик — long, это 4 байта, а Arduino читает их по одному. Прерывание посередине даст смесь старого и нового числа. Есть пара команд, которая на время выключает прерывания.
Счёт работает, импульсы на оборот обоих колёс записаны и сравнены с числом прорезей.
Требование 1. Программа считает импульсы каждого колеса прерыванием и печатает оба счётчика в монитор порта; счётчики читаются так, что прерывание не портит число. Без вписанных контактов программа не идёт дальше.
- Напиши программу сам.
Как это устроено: Справка по командам
| Команда | Что делает |
|---|---|
attachInterrupt(digitalPinToInterrupt(pin), f, RISING) | Вызывать функцию f на каждый фронт импульса на контакте pin |
volatile long x | Переменная, которую меняет прерывание |
noInterrupts() / interrupts() | Выключить и снова включить прерывания |
- Измерь импульсы на оборот: прокрути колесо 10 оборотов по метке, потом 20 оборотов. Насколько различаются два результата? Какой точнее и почему?
- Найди положение колеса, при котором счёт растёт без движения. Объясни в журнале, что происходит в датчике.
Подсказка: Идея (1 из 2)
Функции прерывания должны быть короткими: только увеличить счётчик.
Подсказка: Счёт без движения (2 из 2)
На краю прорези свет попадает на приёмник «наполовину», и сигнал дрожит около порога.
Программа выполняет требование 1, импульсы на оборот измерены дважды, объяснение дрожания записано.
Миллиметры в импульсы
15 минЗа один оборот колесо проезжает длину окружности: π × диаметр. Диаметр колеса — 45 мм. Чтобы проехать 500 мм, нужно 500 / (π × 45) оборотов, а импульсов — в TICKS_PER_REV раз больше.
- Посчитай на бумаге: сколько импульсов на 500 мм, если на оборот 20 импульсов?
- Создай скетч
LF1_Distance, впиши контакты и своё число импульсов на оборот.
// Контакты энкодеров: впиши номера из таблицы проводки своего робота.// На 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() {}
- Поставь робота на пол у метки старта, включи питание моторов. Робот проедет и остановится. Измерь путь линейкой от метки до той же точки корпуса.
Ты посчитал: около 71 импульса. Робот проехал примерно 50 см, путь измерен.
- Создай скетч
LF1_Distanceпо шаблону, впиши контакты и импульсы на оборот. - Напиши
ticksForиaverageTicksпо целям.
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. // Контакты энкодеров: впиши номера из таблицы проводки своего робота.// На 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)? Посчитай до запуска.
- Запусти поездку от метки, измерь путь линейкой. Сравни с монитором порта: насколько «насчитано после остановки» больше цели?
Подсказка: Округление до целого
Если прибавить 0.5 и отбросить дробную часть, получится округление до ближайшего целого.
Прогноз совпал: 71. Робот проехал около 50 см, перекат после остановки виден в мониторе порта.
Требование 2. Функция long ticksFor(int mm, int ticksPerRev, int diameterMm) переводит миллиметры в импульсы с округлением до ближайшего целого и не переполняется на Arduino Uno.
Приёмка. Напиши функцию в тестовом скетче. Когда все строки PASS, перенеси её в свою программу.
// Проверка функции 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. Программа едет вперёд, пока среднее двух счётчиков не достигнет цели, останавливается и печатает цель и насчитанное после остановки. Без вписанного числа импульсов на оборот робот стоит.
- Выполни требования 2 и 3, проедь 500 мм от метки и измерь путь.
Подсказка: Идея (1 из 2)
Импульсы = обороты × импульсы на оборот, обороты = путь / длина окружности.
Подсказка: Тест на 2000 мм не проходит (2 из 2)
2000 * 40 — 80 000, больше, чем вмещает int. Считай в float или long до умножения.
Тест пройден, поездка на 500 мм сделана и измерена.
Почему робот проезжает дальше
15 минПосле команды «стоп» робот ещё немного катится — как велосипед, если перестать крутить педали.
- Сделай три заезда на 500 мм. Запиши пути в журнал.
- Уменьши
speedдо 70 и сделай ещё три заезда. Стало точнее?
speed | Заезд 1 | Заезд 2 | Заезд 3 |
|---|---|---|---|
| 100 | |||
| 70 |
Таблица заполнена. Ты видишь, на какой скорости робот точнее.
- Сделай три заезда на 500 мм. Для каждого запиши путь линейкой и «перекат» в импульсах из монитора порта.
- Добавь в программу поправку: останавливайся на
OVERSHOOTимпульсов раньше цели, гдеOVERSHOOT— средний перекат из трёх заездов. Сделай ещё три заезда. - Ответь в журнале: будет ли эта поправка верна на другой скорости? Почему?
Шесть заездов в журнале, поправка проверена, ответ про скорость записан.
Требование 4. За SLOW_TICKS импульсов до цели робот сбавляет скорость, чтобы меньше катиться после остановки.
- Выполни требование 4 и подбери
SLOW_TICKSи пониженную скорость. - Сравни три заезда без замедления и три с замедлением: среднюю ошибку и разброс.
- Объясни в журнале, чем замедление лучше поправки «остановиться раньше на N импульсов».
Требование 4 выполнено, сравнение двух способов и вывод — в журнале.
Задания по уровням
12 минПроехать 500 мм с ошибкой не больше 2 см в трёх заездах из трёх.
Проехать 1 м, выравнивая колёса по разнице импульсов: если левое насчитало больше — замедлить левое и ускорить правое, как П-регулятор из урока 3.2. Робот уходит от прямой не больше чем на 3 см.
Повернуть на месте на 90°: колёса крутятся в разные стороны, каждое проходит четверть окружности диаметром в колею робота — 90 мм. Посчитать импульсы, проверить угол угольником. Ошибка — не больше 10°.
Проверь себя
Выберите ответы для подготовки. Они не сохраняются и не оцениваются на сайте. Пройдите проверку знаний в программе курса или обсудите ответы с преподавателем.
● Основное
★ Средний уровень
★★ Продвинутый уровень
Отвечай на вопросы своего уровня. Нужно верных ответов: Начальный — 4 из 5; Средний — 4 из 5; Продвинутый — 4 из 5. Ответы проверит преподаватель или программа курса.
Итог
Энкодер превращает оборот колеса в импульсы, а импульсы — в пройденный путь. Робот едет не «сколько успеет», а сколько нужно, при любом заряде батарей.
Как подтвердить результат
Результат опыта связывает условия, версию программы, наблюдение и вывод. Сравнение попыток с одним изменением помогает проверить причину, включая неудачные попытки. Отметка относится к уже выполненной практике; повтор ради отметки не нужен. Расхождение с прогнозом задаёт следующую проверяемую гипотезу. Модель, измерения настоящего робота и объяснение — разные виды доказательств.
Словарь урока
| энкодер | Датчик оборотов: диск с прорезями на оси колеса и датчик, который даёт импульс на каждую прорезь. |
| импульс | Короткий сигнал энкодера: прорезь прошла мимо датчика. |
| прерывание | Arduino откладывает основную программу и сразу выполняет короткую функцию, когда на контакт пришёл импульс. |
| volatile | Пометка переменной, которую меняет прерывание: программа каждый раз читает её заново. |
| длина окружности | Путь колеса за один оборот: π × диаметр. |