Датчики линии и первое следование
Робот «смотрит» на пол, отличает чёрное от белого и сам держится линии.
Результат урока
Робот проезжает по чёрной линии с плавным изгибом, поворачивая по сигналам двух датчиков. В бортовом журнале записаны показания датчиков на белом и чёрном.
Ты научишься
- Прочитать показания датчика командой analogRead и вывести их в монитор порта.
- Различать строки Serial Monitor и каналы телеметрии с временем и единицами; сравнивать сохранённые показания модели и настоящего робота.
- Измерить показания на белом и чёрном и объяснить, почему у каждого датчика они свои.
- Записать правило «показание ближе к чёрному — значит, под датчиком линия».
- Составить два правила поворота и проверить их сначала на подставке, затем на дорожке.
Нужно знать
- 1.1 Собираем робота и оживляем плату
- 2.1 Моторы: едем и останавливаемся
Понадобится
Зачем этот урок
До сих пор робот делал только то, что ты заранее записал: ехать столько-то, повернуть так-то. Сегодня он начнёт сам принимать решения. Два датчика будут смотреть на пол, и робот будет поворачивать, когда линия уходит в сторону.
- Какая команда задаёт скорость мотора и какие числа она принимает?
- Что делает
delay(500)? - Какое колесо надо остановить, чтобы робот повернул налево?
Как робот может «увидеть» чёрную линию, если у него нет камеры? Обсудите с напарником и запишите догадку в бортовой журнал. В конце урока проверите.
Пока меняешь провода, USB-кабель отключён, питание моторов выключено. Опыты с моторами — сначала на подставке: колёса не касаются стола.
Подготовь дорожку и робота
5 минЧто должен делать робот. Чтобы ехать по дорожке, роботу нужно замечать, когда линия оказалась под левым или правым датчиком. Для этого используем два направленных вниз датчика отражения; они смотрят на пол, а не ищут предметы впереди.
Что используем и как это работает. В датчике есть излучатель и приёмник отражённого света. Поверхности отражают свет по-разному, а приёмник превращает отражённый свет в электрический сигнал. analogRead считывает его как число. Это не готовый ответ «чёрное»: сначала измеряем белое и чёрное для каждого датчика при рабочей высоте.
Как связать измерение с действием. Программа сравнивает чтение с измеренным порогом и выбирает действие колёс. Над линией слева и справа должны получиться разные команды коррекции. Проверим четыре сочетания карточек на подставке, затем движение по линии. Одинаковые показания обоих датчиков ещё не доказывают, что робот стоит правильно: оба могут оказаться и на белом, и на чёрном.
Датчику нужна понятная картинка: светлый фон и тёмная линия.
- Возьми светлое матовое поле с чёрной линией (или наклей чёрную матовую изоленту на белый лист). Для начала хватит прямой с одним плавным изгибом.
- Подготовь две карточки: белую и чёрную.
- Найди на роботе левый и правый датчики. Они смотрят вниз и стоят по обе стороны от линии, когда робот стоит на ней.
Дорожка и карточки готовы. Ты можешь показать левый и правый датчик.
Датчику нужна понятная картинка: светлый фон и тёмная линия. Для заданий среднего уровня на дорожке понадобится и перекрёсток.
- Возьми светлое матовое поле с чёрной линией или наклей чёрную матовую изоленту на белый лист: прямая, плавный изгиб и один перекрёсток.
- Подготовь две карточки: белую и чёрную.
- Найди на роботе левый и правый датчики. Поставь робота на линию и проверь: линия проходит между датчиками и не касается ни одного из них?
Дорожка с перекрёстком и карточки готовы. Линия помещается между датчиками.
Подготовь дорожку для своего уровня и проверь, подходит ли она твоему роботу.
- Сделай дорожку: прямая, плавный изгиб, крутая дуга и перекрёсток. Подготовь белую и чёрную карточки.
- Измерь линейкой ширину линии и расстояние между датчиками (между серединами окошек).
- Поставь робота на линию. Сколько миллиметров остаётся от края линии до каждого датчика? Запиши в журнал: что будет, если линия шире расстояния между датчиками?
Дорожка готова, в журнале — ширина линии, расстояние между датчиками, запас с каждой стороны и ответ на вопрос.
Посмотри, что видит датчик
12 минДатчик светит на пол и меряет, сколько света вернулось. Плата превращает это в число.
Чтобы увидеть число, плата будет отправлять его на компьютер, а мы откроем монитор порта.
- Создай скетч
L31_Read_Sensorsи набери программу. Левый датчик подключён кA4, правый — кA5.
const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5; void setup() { Serial.begin(9600);} void loop() { int leftValue = analogRead(LEFT_SENSOR_PIN); int rightValue = analogRead(RIGHT_SENSOR_PIN); Serial.print("L="); Serial.print(leftValue); Serial.print(" R="); Serial.println(rightValue); delay(200);}
- Загрузи программу. Открой Инструменты → Монитор порта и выбери скорость 9600.
- Поднеси под левый датчик белую карточку, затем чёрную. Держи карточку на той же высоте, на которой датчик стоит над полом.
В мониторе бегут строки вида L=… R=…. Когда меняешь карточку под датчиком, его число заметно меняется.
Как это устроено: Что значит каждая строка
analogRead(LEFT_SENSOR_PIN)— прочитать показание датчика: число от 0 до 1023.int leftValue = …— сохранить число в переменнуюleftValue.int— целое число.Serial.begin(9600)— начать связь с компьютером. 9600 — скорость, такая же должна быть выбрана в мониторе порта.Serial.printпечатает в той же строке,Serial.println— печатает и переходит на новую.- Текст в кавычках печатается как есть, а имя переменной без кавычек заменяется её числом.
| Что видишь | Что сделать |
|---|---|
| Вместо чисел непонятные символы | В мониторе порта выбери 9600. |
| Число не меняется | Проверь провод сигнала S и номер входа в программе. |
| Меняется не тот датчик | Провода левого и правого датчиков перепутаны. Поменяй их местами или номера в программе. |
Датчик светит на пол и меряет, сколько света вернулось. Плата превращает это в число, а чтобы увидеть его, программа отправляет число на компьютер.
| Команда | Что делает |
|---|---|
analogRead(контакт) | Показание датчика: число от 0 до 1023 |
Serial.begin(9600); | Включает связь с компьютером, один раз |
Serial.print(x); | Печатает в той же строке |
Serial.println(x); | Печатает и переходит на новую строку |
- Создай скетч
L31_Read_Sensorsпо шаблону и напиши обе части сам. Левый датчик подключён кA4, правый — кA5.
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5; // ===== Твоя часть: напиши сам ===== void setup() { // Включи связь с компьютером на той же скорости, что в мониторе порта: 9600.} void loop() { // Пять раз в секунду печатай одну строку: L=число левого датчика R=число правого.}
- Загрузи программу и открой монитор порта со скоростью 9600.
- Поднеси под левый датчик белую карточку, затем чёрную. Держи карточку на той же высоте, на которой датчик стоит над полом.
Что напечатает монитор, если имя переменной в Serial.print взять в кавычки?
Подсказка: Текст и число в одной строке
Текст в кавычках печатается как есть, а имя переменной без кавычек заменяется её числом.
В мониторе бегут строки вида L=… R=…. Когда меняешь карточку под датчиком, его число заметно меняется.
| Что видишь | Что сделать |
|---|---|
| Вместо чисел непонятные символы | Скорость в мониторе порта и в программе должна совпадать: 9600. |
| Все числа в одной длинной строке | Нет перехода на новую строку. |
| Меняется не тот датчик | Провода левого и правого датчиков перепутаны. |
Датчик светит на пол и меряет, сколько света вернулось. Левый датчик подключён к A4, правый — к A5.
Как это устроено: Справка по командам
| Команда | Что делает |
|---|---|
analogRead(pin) | Показание датчика: число от 0 до 1023 |
Serial.begin(9600) | Включает связь с компьютером, один раз в setup() |
Serial.print(x) / Serial.println(x) | Печатает в монитор порта; println — с переходом строки |
millis() | Сколько миллисекунд прошло с включения платы |
Требование. Скетч L31_Read_Sensors пять раз в секунду печатает строку t=… L=… R=…, где t — время в миллисекундах с включения.
Приёмка.
- По числам
tв мониторе проверь, что строки идут примерно каждые 200 мс. На сколько миллисекунд отличается промежуток от 200 и почему? - Поднеси под левый датчик белую, затем чёрную карточку на рабочей высоте. Запиши, на сколько меняется число.
Подсказка: Почему промежуток не ровно 200
Сколько времени занимают сами чтение и печать, кроме delay(200)?
Программа выполняет требование. В журнале — реальный промежуток между строками с объяснением и изменение числа на белом и чёрном.
Проверь оба датчика
8 минБудут ли числа левого и правого датчиков одинаковыми над одним и тем же белым полем?
- Поставь робота на белое поле. Запомни числа L и R.
- Подведи чёрную карточку только под левый датчик, потом только под правый.
- Сдвинь робота так, чтобы линия была точно между датчиками. Какие числа сейчас?
Ты знаешь, какое число у каждого датчика на белом и как оно меняется на чёрном. Когда линия между датчиками, оба показывают «белое».
Будут ли числа левого и правого датчиков одинаковыми над одним и тем же белым полем?
- Заполни таблицу для четырёх положений робота:
| Положение | L | R |
|---|---|---|
| Оба над белым | ||
| Чёрное только под левым | ||
| Чёрное только под правым | ||
| Линия точно между датчиками |
- Почему в последней строке оба числа «белые», хотя робот стоит на линии? Запиши.
Таблица заполнена, прогноз проверен, ответ про положение «между датчиками» записан.
Сними профиль датчиков: как меняются числа, пока линия проходит под роботом поперёк.
- Положи робота поперёк линии так, чтобы линия была далеко справа от правого датчика.
- Сдвигай робота влево шагами по 5 мм, пока линия не уйдёт за левый датчик. На каждом шаге записывай L и R.
| Сдвиг, мм | L | R |
|---|---|---|
| 0 | ||
| 5 | ||
| … |
- Нарисуй в журнале два графика: L и R от сдвига. Найди по графику, где линия ровно между датчиками и на сколько миллиметров можно уйти в сторону, пока датчик её не заметит.
Таблица и два графика. В журнале — ширина «слепой зоны» между датчиками в миллиметрах.
Измерь белое и чёрное
12 минДатчики не одинаковые: даже над одним полем их числа разные. Поэтому каждому датчику нужны свои измерения. Такая настройка по своим измерениям называется калибровкой.
- Поставь робота на белое поле. Посмотри на несколько строк и запиши типичное число левого датчика в бортовой журнал: «Левый, белое».
- Подведи под левый датчик чёрную линию. Запиши «Левый, чёрное».
- Повтори для правого датчика.
- Проверь себя: повтори каждое измерение ещё раз. Числа должны быть близки к записанным.
| Датчик | Белое | Чёрное |
|---|---|---|
| Левый | ||
| Правый |
В журнале четыре числа. Белое и чёрное у каждого датчика заметно отличаются.
Подсказка: Белое и чёрное почти одинаковые
Датчик может стоять слишком высоко или линия блестит. Опусти датчики ближе к полу и проверь линию в другом месте поля, где нет бликов.
Как это устроено: Почему у одних датчиков чёрное — большое число, а у других — маленькое
Это зависит от устройства датчика. Одни выдают тем большее число, чем больше света вернулось, другие — наоборот. Поэтому мы не запоминаем «чёрное — это много», а измеряем свои числа.
Датчики не одинаковые: даже над одним полем их числа разные. Поэтому каждому датчику нужны свои измерения — калибровка.
- Для каждого датчика запиши по три показания на белом и три на чёрном, в разных местах дорожки.
- Выбери типичное число для каждой клетки и запиши, почему именно его.
| Датчик | Белое: 3 числа | Белое | Чёрное: 3 числа | Чёрное |
|---|---|---|---|---|
| Левый | ||||
| Правый |
Подсказка: Числа в разных местах разные
Освещение и поле неодинаковые. Берут число, близкое к остальным, а не самое удачное.
В журнале таблица с повторами и четыре выбранных числа. Белое и чёрное заметно отличаются.
Откалибруй датчики и докажи, что калибровке можно доверять.
- Для каждого датчика запиши по 10 показаний на белом и 10 на чёрном. Найди наименьшее и наибольшее в каждой серии — это разброс.
- Сравни разброс с разницей белого и чёрного. Во сколько раз разница больше разброса?
- Повтори белое у окна и в тёмном углу класса. Меняются ли числа сильнее разброса?
- Запиши вывод: где можно доверять калибровке и когда её нужно повторить.
| Датчик | Белое: мин–макс | Чёрное: мин–макс | Разница типичных |
|---|---|---|---|
| Левый | |||
| Правый |
Таблица разбросов, сравнение с разницей и вывод, когда калибровку повторять.
Научи программу замечать линию
10 минКак программе решить, что под датчиком линия? Сравним новое показание с нашими двумя измерениями: если число ближе к чёрному, чем к белому, — под датчиком линия.
Пример: белое — 100, чёрное — 800. Пришло число 650. До чёрного 150, до белого 550. Ближе к чёрному — значит, линия.
- Создай скетч
L31_Line_Detect. Впиши свои четыре числа вместо нулей.
const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5; // Впиши свои измерения из бортового журнала.const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0; void setup() { Serial.begin(9600);} void loop() { int leftValue = analogRead(LEFT_SENSOR_PIN); int rightValue = analogRead(RIGHT_SENSOR_PIN); bool leftOnLine = abs(leftValue - LEFT_BLACK) < abs(leftValue - LEFT_WHITE); bool rightOnLine = abs(rightValue - RIGHT_BLACK) < abs(rightValue - RIGHT_WHITE); Serial.print("L="); Serial.print(leftValue); Serial.print(" R="); Serial.print(rightValue); if (leftOnLine) { Serial.print(" слева линия"); } if (rightOnLine) { Serial.print(" справа линия"); } Serial.println(); delay(200);}
- Загрузи программу, открой монитор порта и подводи чёрную карточку под датчики по очереди.
Когда под левым датчиком чёрное, в строке появляется «слева линия», под правым — «справа линия».
Команда соседей написала так:
bool leftOnLine = abs(leftValue - LEFT_BLACK) > abs(leftValue - LEFT_WHITE);
Под левым датчиком чёрная карточка, а «слева линия» не печатается. Найди ошибку и объясни, что программа проверяет сейчас.
Как это устроено: Что такое abs и bool
abs(число)убирает знак:abs(-150)равно 150. Так мы получаем расстояние между двумя числами, не думая, какое из них больше.bool— переменная, в которой хранится не число, а ответ «да» или «нет» (trueилиfalse).if (leftOnLine) { … }— если ответ «да», выполнить команды в скобках.
Программе нужно правило: когда считать, что под датчиком линия? Сравни новое показание с двумя измерениями: если оно ближе к чёрному, чем к белому, — под датчиком линия.
Белое — 100, чёрное — 800, пришло число 650. Линия под датчиком или нет? Посчитай до запуска: сколько от 650 до чёрного и сколько до белого?
- Создай скетч
L31_Line_Detectпо шаблону и впиши свои четыре числа вместо нулей. - Напиши
loop()сам: решение «линия или нет» для каждого датчика и печать.
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5; // Впиши свои измерения из бортового журнала.const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0; // ===== Твоя часть: напиши сам ===== void loop() { int leftValue = analogRead(LEFT_SENSOR_PIN); int rightValue = analogRead(RIGHT_SENSOR_PIN); // Для каждого датчика реши, линия ли под ним (правило «ближе к чёрному»): leftOnLine и rightOnLine. // Печатай строку L=… R=…, а если под датчиком линия — допиши «слева линия» или «справа линия». // Пять строк в секунду.} // ===== Готовое из прошлого шага — не меняй ===== void setup() { Serial.begin(9600);}
- Загрузи программу, открой монитор порта и подводи чёрную карточку под датчики по очереди.
- Ответь в журнале: что начнёт печатать программа, если в ней перепутать белое и чёрное местами? Почему?
Подсказка: Как измерить расстояние между числами
От 650 до 800 — это 800 - 650. Но программа заранее не знает, какое число больше. Команда abs(…) убирает знак: abs(650 - 800) равно 150.
Подсказка: Да или нет
Ответ «да или нет» хранит тип bool. Команды в if (…) { … } выполняются, только если ответ «да».
«слева линия» и «справа линия» появляются, только когда под датчиком чёрное. Прогноз про 650 сошёлся, ответ про перепутанные числа записан.
Команда соседей написала так:
bool leftOnLine = abs(leftValue - LEFT_BLACK) < abs(leftValue - RIGHT_WHITE);
Иногда «слева линия» печатается, когда под левым датчиком белое поле. Найди ошибку и объясни, почему она проявляется не всегда.
Требование 1. Функция onLine(value, white, black) отвечает true, когда под датчиком линия:
- показание ближе к чёрному измерению, чем к белому;
- работает и когда у датчика чёрное — большое число, и когда маленькое;
- внутри нет готовых чисел-порогов: только
value,whiteиblack.
Почему нельзя просто написать value > 500? Назови два случая, когда такое правило ошибётся.
Приёмка. Напиши onLine в тестовом скетче, загрузи его и открой монитор порта (9600). Тест проверяет восемь случаев для двух видов датчиков, в том числе показания за пределами твоих измерений.
// Проверка функции onLine. Напиши функцию здесь, загрузи и открой монитор порта.// Когда все строки PASS, перенеси onLine в свою программу. // Требование 1: true, если показание ближе к чёрному, чем к белому.bool onLine(int value, int white, int black) { return false;} int failed = 0; void expect(int value, int white, int black, bool expected) { bool actual = onLine(value, white, black); Serial.print(actual == expected ? "PASS " : "FAIL "); Serial.print("onLine("); Serial.print(value); Serial.print(", "); Serial.print(white); Serial.print(", "); Serial.print(black); Serial.print(") = "); Serial.print(actual ? "true" : "false"); Serial.print(", ожидаем "); Serial.println(expected ? "true" : "false"); if (actual != expected) { failed++; }} void setup() { Serial.begin(9600); // Датчик, у которого чёрное — большое число, а белое — маленькое. expect(780, 120, 780, true); expect(120, 120, 780, false); expect(600, 120, 780, true); expect(300, 120, 780, false); // За пределами измерений. expect(900, 120, 780, true); expect(50, 120, 780, false); // Датчик, у которого наоборот: белое — большое число, чёрное — маленькое. expect(300, 900, 120, true); expect(700, 900, 120, false); if (failed == 0) { Serial.println("Все проверки пройдены"); } else { Serial.print("Ошибок: "); Serial.println(failed); }} void loop() {}
Когда все строки PASS, добавь onLine в свою программу из шага «Посмотри, что видит датчик» и сохрани её как L31_Line_Detect. Пусть она печатает «слева линия» и «справа линия». Впиши свои измерения и проверь карточкой.
Подсказка: Идея (1 из 3)
Нужны два расстояния: от показания до чёрного и от показания до белого. Какое меньше?
Подсказка: План (2 из 3)
- Расстояние до чёрного — разница без знака.
- Расстояние до белого — так же.
- Вернуть результат сравнения двух расстояний.
Подсказка: Фрагмент кода (3 из 3)
abs(value - black) — расстояние до чёрного. Зачем здесь abs? Что будет без него у датчика, где чёрное — маленькое число?
Тест показывает «Все проверки пройдены». Твоя программа печатает «слева линия» и «справа линия», только когда под датчиком чёрное.
Придумай, как поворачивать
7 минРобот едет вперёд. Линия должна оставаться между датчиками. Что делать, если линия оказалась под левым датчиком? Значит, робот уехал вправо, и ему надо повернуть налево.
Самый простой способ повернуть налево — остановить левое колесо, а правое пусть едет.
- Поставь выключенного робота на линию. Сдвинь его так, чтобы линия оказалась под левым датчиком. Покажи, какое колесо нужно остановить.
- То же — для правого датчика.
- Заполни таблицу в журнале:
| Что видят датчики | Левое колесо | Правое колесо | Куда едет робот |
|---|---|---|---|
| Оба белое | вперёд | вперёд | прямо |
| Левый — линия | ? | ? | ? |
| Правый — линия | ? | ? | ? |
| Оба — линия | ? | ? | ? |
Таблица заполнена. Ты можешь объяснить, почему остановка одного колеса поворачивает робота.
Робот едет вперёд, и линия должна оставаться между датчиками. Если линия оказалась под одним датчиком, робот уехал в сторону. Колесо пока умеет только ехать или стоять. Как вернуть робота к линии?
- Поставь выключенного робота на линию. Сдвинь его так, чтобы линия оказалась под левым датчиком. Куда робот уехал? Куда ему повернуть? Какое колесо для этого остановить?
- То же — для правого датчика.
- Заполни таблицу в журнале:
| Что видят датчики | Левое колесо | Правое колесо | Куда едет робот |
|---|---|---|---|
| Оба белое | вперёд | вперёд | прямо |
| Левый — линия | ? | ? | ? |
| Правый — линия | ? | ? | ? |
| Оба — линия | ? | ? | ? |
- Объясни в журнале одной фразой, почему остановка одного колеса поворачивает робота.
Подсказка: Какое колесо останавливать
Вспомни урок 2.2: левое колесо стоит, правое едет. Вокруг какого колеса повернётся робот и в какую сторону?
Таблица заполнена, объяснение записано. Ты показал на выключенном роботе оба поворота.
Робот едет вперёд, и линия должна оставаться между датчиками. Составь правила сам: что делают колёса в каждом из четырёх случаев и почему.
| Что видят датчики | Левое колесо | Правое колесо | Куда едет робот | Почему |
|---|---|---|---|---|
| Оба белое | ||||
| Левый — линия | ||||
| Правый — линия | ||||
| Оба — линия |
- Проверь каждую строку на выключенном роботе: сдвинь его рукой и покажи, куда он должен поехать.
- «Оба белое» бывает в двух разных положениях робота. В каких? Может ли программа их различить? Запиши, что тогда случится с роботом.
- На прямой дорожке «оба — линия» почти не бывает. Где на итоговом поле это встретится и что тогда должен делать робот?
Подсказка: Два случая «оба белое»
Подумай, где может быть линия, когда ни один датчик её не видит.
Таблица из четырёх строк с объяснением. В журнале записано, в каком положении программа не знает, где линия.
Запиши правила в программу
10 минСоберём всё вместе: датчики, наше правило «ближе к чёрному» и моторы из урока 2.1.
- Создай скетч
L31_First_Line. Перенеси свои четыре измерения.
const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5; // Впиши свои измерения из бортового журнала.const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0; 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; int speed = 100; // начальная скорость, подбираем на подставке void setup() { pinMode(LEFT_DIR_PIN, OUTPUT); pinMode(LEFT_SPEED_PIN, OUTPUT); pinMode(RIGHT_DIR_PIN, OUTPUT); pinMode(RIGHT_SPEED_PIN, OUTPUT); analogWrite(LEFT_SPEED_PIN, 0); analogWrite(RIGHT_SPEED_PIN, 0); digitalWrite(LEFT_DIR_PIN, FORWARD); digitalWrite(RIGHT_DIR_PIN, FORWARD); // Без измерений робот не поедет: программа остановится здесь. if (LEFT_WHITE == LEFT_BLACK || RIGHT_WHITE == RIGHT_BLACK) { while (true) { } } delay(2000); // время убрать руки от робота} void loop() { int leftValue = analogRead(LEFT_SENSOR_PIN); int rightValue = analogRead(RIGHT_SENSOR_PIN); bool leftOnLine = abs(leftValue - LEFT_BLACK) < abs(leftValue - LEFT_WHITE); bool rightOnLine = abs(rightValue - RIGHT_BLACK) < abs(rightValue - RIGHT_WHITE); int leftSpeed = speed; int rightSpeed = speed; if (leftOnLine) { leftSpeed = 0; } if (rightOnLine) { rightSpeed = 0; } analogWrite(LEFT_SPEED_PIN, leftSpeed); analogWrite(RIGHT_SPEED_PIN, rightSpeed);}
- Найди в
loop()два правила из твоей таблицы. Объясни напарнику, что делает каждое. - Нажми Проверить.
Как это устроено: Почему скорость задаётся заново в каждом повторе
В начале loop() обе скорости снова становятся равны speed. Если бы мы этого не делали, колесо, остановленное один раз, так бы и стояло, даже когда датчик снова увидит белое.
Как это устроено: Зачем строка while (true)
Если ты забыл вписать измерения, белое и чёрное равны нулю, и программа не может отличить линию. Тогда она «застревает» в пустом бесконечном цикле, и робот не поедет. Это защита от случайного старта.
Соберём всё вместе: датчики, правило «ближе к чёрному» и моторы из урока 2.1. Колёса будут слушаться правил из твоей таблицы.
- Создай скетч
L31_First_Lineпо шаблону. Впиши свои четыре измерения. Настройка моторов и защита уже готовы. - Напиши
loop()сам: перенеси свои условия изL31_Line_Detect, задай колёсам скорости по своей таблице правил и отправь их на моторы.
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5; // Впиши свои измерения из бортового журнала.const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0;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;int speed = 100; // начальная скорость, подбираем на подставке // ===== Твоя часть: напиши сам ===== void loop() { int leftValue = analogRead(LEFT_SENSOR_PIN); int rightValue = analogRead(RIGHT_SENSOR_PIN); // Реши, линия ли под каждым датчиком, — перенеси свои условия из L31_Line_Detect. // Колёса выполняют твою таблицу правил: линия под датчиком — его колесо стоит. // Колесо, которое правило остановило, снова едет, когда его датчик видит белое.} // ===== Готовое: настройка и защита — не меняй ===== void setup() { pinMode(LEFT_DIR_PIN, OUTPUT); pinMode(LEFT_SPEED_PIN, OUTPUT); pinMode(RIGHT_DIR_PIN, OUTPUT); pinMode(RIGHT_SPEED_PIN, OUTPUT); analogWrite(LEFT_SPEED_PIN, 0); analogWrite(RIGHT_SPEED_PIN, 0); digitalWrite(LEFT_DIR_PIN, FORWARD); digitalWrite(RIGHT_DIR_PIN, FORWARD); // Без измерений робот не поедет: программа остановится здесь. if (LEFT_WHITE == LEFT_BLACK || RIGHT_WHITE == RIGHT_BLACK) { while (true) { } } delay(2000); // время убрать руки от робота}
Что случится, если задать скорость колёс один раз в setup(), а не в начале каждого повтора loop()?
- Нажми Проверить.
Подсказка: Колесо остановилось и больше не едет
Когда правило снова разрешает колесу ехать? Откуда в каждом повторе берётся его скорость?
Подсказка: Правило колеса
if (проверка) { … } выполняет команды в скобках, только если проверка верна. Сверься со своей таблицей: какую переменную проверить и какую скорость поменять?
loop() написан, Проверить проходит без ошибок, ответ на прогноз записан в журнал.
Как это устроено: Зачем строка while (true)
Если ты забыл вписать измерения, белое и чёрное равны нулю, и программа не может отличить линию. Тогда она «застревает» в пустом бесконечном цикле, и робот не поедет. Это защита от случайного старта.
Возьми каркас: в нём контакты, моторы и функция drive из урока 2.1. Перенеси в него свои измерения и функцию onLine и сохрани как L31_First_Line. Эта программа понадобится тебе в уроке 3.2.
// Каркас движения по линии. Требования к программе — в тексте урока.const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5; // Впиши свои измерения из бортового журнала.const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0; 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; 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);} // Требование 1: перенеси функцию onLine из тестового скетча.bool onLine(int value, int white, int black) { return false;} void setup() { 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); // Требование 3: защита от запуска без измерений и время убрать руки.} void loop() { // Требование 2: прочитать датчики, решить по своей таблице правил, задать скорости. drive(FORWARD, 0, FORWARD, 0);}
Требование 2. Колёса выполняют твою таблицу правил:
- в каждом повторе программа заново читает оба датчика и решает заново: колесо, остановленное один раз, снова едет, когда его датчик видит белое;
- решение принимает твоя функция
onLine; - скорость — одна именованная переменная
speedв начале скетча.
Требование 3. Робот безопасен:
- без измерений белого и чёрного колёса стоят;
- после включения есть 2 секунды, чтобы убрать руки.
Приёмка — на подставке в следующем шаге: робот выполняет каждую строку твоей таблицы правил, а без измерений колёса не крутятся.
Что сделает робот, если скорости обоих колёс задать один раз в setup()? Запиши ответ до проверки.
Подсказка: Идея (1 из 3)
Каждый повтор: прочитать датчики → решить → отправить скорости на моторы. Решение начинается с «оба едут», а правила его меняют.
Подсказка: План (2 из 3)
- Прочитать оба датчика.
- Спросить
onLineдля каждого. - Обе скорости —
speed. - Правила из таблицы меняют скорости.
- Отправить скорости в
drive.
Подсказка: Фрагмент кода (3 из 3)
if (onLine(leftValue, LEFT_WHITE, LEFT_BLACK)) { leftSpeed = 0; } — а для правого датчика? А строка «оба — линия» из твоей таблицы?
Программа выполняет требования 2 и 3, Проверить проходит без ошибок.
Проверь на подставке
6 минЗагружай программу при выключенном питании моторов. Колёса не касаются стола.
- Поставь робота на подставку. Загрузи программу.
- Включи питание моторов. Оба колеса должны крутиться вперёд.
- Подведи чёрную карточку под левый датчик: левое колесо остановится. Убери карточку: колесо снова поедет. Проверь правый датчик.
- Выключи питание моторов.
Каждое колесо останавливается, когда под его датчиком чёрное, и снова едет на белом.
| Что видишь | Что сделать |
|---|---|
| Колёса не крутятся | Проверь, что вписаны измерения и включено питание моторов. |
| Колесо крутится назад | Проверь FORWARD и подключение мотора. |
| Останавливается не то колесо | Перепутаны датчики или моторы. Проверь по монитору порта и по меткам Л/П. |
- Поставь робота на подставку, загрузи программу, включи питание моторов.
- Проверь все четыре строки своей таблицы правил: подставляй карточки так, чтобы датчики видели нужное. Записывай, что делают колёса.
| Что видят датчики | Ждал | Увидел |
|---|---|---|
| Оба белое | ||
| Левый — линия | ||
| Правый — линия | ||
| Оба — линия |
- Проверь свой прогноз про скорость, заданную в
setup(). Выключи питание моторов.
Таблица заполнена, все строки совпали с правилами. Прогноз проверен.
| Что видишь | Что сделать |
|---|---|
| Колёса не крутятся | Написан ли loop()? Вписаны ли измерения? Включено ли питание моторов? |
| Останавливается не то колесо | Перепутаны датчики или моторы. Проверь по монитору порта и по меткам Л/П. |
Приёмка требований 2–3 на подставке.
- Загрузи программу с нулями вместо измерений. Колёса не должны крутиться. Впиши измерения и загрузи снова.
- Проверь каждую строку своей таблицы правил карточками.
- Засеки, через сколько секунд после включения трогаются колёса.
| Проверка | Ждал | Увидел |
|---|---|---|
| Без измерений | ||
| Оба белое | ||
| Левый — линия | ||
| Правый — линия | ||
| Оба — линия | ||
| Пауза на старте |
Все проверки совпали с требованиями. Если нет — исправлено и проверено снова.
Первый заезд по линии
8 мин- Выключи питание моторов, отключи USB-кабель.
- Поставь робота на прямой участок: линия между датчиками.
- Включи питание и наблюдай. Будь готов выключить питание, если робот уходит с дорожки.
- Попробуй проехать изгиб. Запиши в журнал, что получилось.
Робот едет вдоль линии и проходит плавный изгиб. Он двигается рывками — это нормально для такого способа.
- Выключи питание моторов, отключи USB-кабель.
- Поставь робота на прямой участок: линия между датчиками. Включи питание. Будь готов выключить его, если робот уходит с дорожки.
- Пройди дорожку три раза при
speed = 100, потом три раза при 80 и при 130. Записывай, сколько раз робот прошёл изгиб.
speed | Прошёл из 3 | Что видно |
|---|---|---|
| 80 | ||
| 100 | ||
| 130 |
- Почему робот едет рывками? Запиши одной фразой.
Таблица заездов при трёх скоростях и объяснение рывков.
- Выключи питание моторов, отключи USB-кабель. Будь готов выключить питание, если робот уходит с дорожки.
- Найди наибольшую
speed, при которой робот проходит плавный изгиб три раза из трёх. Начни с 80 и поднимай шагами по 20. - На наибольшей скорости, которую робот не выдержал, посмотри, где он теряет линию. Объясни в журнале причину через твою таблицу правил.
- Запиши, что нужно изменить в правилах, чтобы робот держал линию быстрее. Проверишь это в уроке 3.2.
Наибольшая надёжная скорость, место и причина схода, идея улучшения правил.
Робот проходит прямую и плавный изгиб по двум правилам и не теряет линию.
Сейчас, если оба датчика видят чёрное, робот останавливается. На среднем поле через перекрёстки нужно проезжать прямо. Измени программу так, чтобы при чёрном под обоими датчиками оба колеса ехали вперёд. Проверь на дорожке с одним перекрёстком.
Вместо остановки колеса замедляй его: например, до трети от speed. Сравни на одном изгибе два варианта — «стоп» и «замедление». Какой плавнее? При какой скорости каждый вариант ещё держит линию? Запиши результаты в журнал: это первый шаг к П-регулятору, который понадобится на крутых дугах продвинутого поля.
Проверь себя
Выберите ответы для подготовки. Они не сохраняются и не оцениваются на сайте. Пройдите проверку знаний в программе курса или обсудите ответы с преподавателем.
● Основное
★ Средний уровень
★★ Продвинутый уровень
Отвечай на вопросы своего уровня. Нужно верных ответов: Начальный — 4 из 5; Средний — 4 из 5; Продвинутый — 4 из 5. Ответы проверит преподаватель или программа курса.
Итог
Робот научился сам выбирать, какое колесо остановить, по показаниям двух датчиков. Мы не угадывали числа, а измерили их на своём поле. Рывки при движении — цена простого правила «едет или стоит». В уроке 3.2 робот научится поворачивать плавно: тем сильнее, чем дальше линия ушла в сторону.
Вернись к своей догадке из начала урока. Подтвердилась ли она?
Как подтвердить результат
Для каждого опыта сохраняй условия, версию программы, наблюдение и вывод. Сравнивая попытки, меняй одну причину за раз; неудачные попытки тоже остаются в журнале. Отметка результата относится к уже выполненным действиям, повторять их ради отметки не нужно. Если результат не получен, запиши расхождение и следующую проверяемую гипотезу. Модель, измерения настоящего робота и объяснение — разные виды доказательств.
Словарь урока
| датчик линии | Датчик, который светит на пол и измеряет, сколько света отразилось. Белое отражает много, чёрное — мало. |
| показание | Число, которое программа получает от датчика. У нашей платы это число от 0 до 1023. |
| монитор порта | Панель Serial Monitor в MyRobotica, где видны сообщения, которые плата отправляет на компьютер. |
| калибровка | Настройка программы по измерениям своего робота и своего поля. |
| переменная | Именованное место в памяти, где программа хранит число. |
| условие | Проверка if: команды в фигурных скобках выполняются, только если проверка верна. |
| телеметрия | Передача именованных измерений с временем и единицами, чтобы строить графики и сохранять наблюдения. |
| АЦП | Аналого-цифровое преобразование: напряжение на входе платы превращается в число от 0 до 1023. |