Находим и считаем перекрёстки
Робот замечает перекрёсток, останавливается на нём и не считает один перекрёсток дважды.
Результат урока
Робот едет по линии с П-регулятором и останавливается на нужном по счёту перекрёстке. Каждый перекрёсток считается один раз.
Ты научишься
- Описать, что показывают датчики на перекрёстке.
- Записать условие перекрёстка и объяснить выбор порога.
- Объяснить, почему один перекрёсток может посчитаться много раз, и исправить это.
- Остановить робота на перекрёстке с заданным номером.
Нужно знать
- 3.2 П-регулятор: плавно и быстро
Понадобится
Зачем этот урок
На полях итоговой задачи к грузам ведут ответвления от перекрёстков. Чтобы свернуть в нужное место, робот должен замечать перекрёстки и знать, какой по счёту перед ним. Сегодня робот научится находить, считать и проезжать перекрёстки.
- Чему равна ошибка П-регулятора, когда линия ровно посередине?
- Что показывает датчик по шкале 0–100 на чёрном?
- Что делает
constrain?
Что видят датчики на перекрёстке
12 минЧто должен делать робот. Курьер должен заметить перекрёсток, доехать до него и при необходимости остановиться. Отдельный датчик перекрёстка не нужен: используем оба датчика линии и их показания вместе.
Что используем и как это работает. На обычной узкой дорожке датчики находятся по её краям. На широкой поперечной полосе оба могут одновременно увидеть чёрное. Программа проверяет это сочетание и решает, продолжать ли движение. Пока датчики остаются над одной полосой, многократные чтения относятся к одному перекрёстку, а не к нескольким.
Как связать измерение с действием. Сначала проверим признаки на неподвижном роботе, затем въезд и выезд с полосы. Для счёта нужно отделить обнаружение перекрёстка от проезда за его границу. Иначе быстрый цикл насчитает одну и ту же полосу много раз. Другая широкая тёмная область тоже может дать такой сигнал: датчики не знают географию поля.
Робот подъезжает к перекрёстку. Что покажут оба датчика по шкале 0–100, когда он окажется на поперечной линии?
- Загрузи скетч
L32_Errorиз урока 3.2. - Медленно провези робота руками по тренировочной дорожке через перекрёсток. Смотри на монитор порта.
- Запиши в журнал показания: на линии посередине, на перекрёстке, после перекрёстка.
На перекрёстке оба датчика показывают почти 0: под ними чёрная поперечная линия.
Робот подъезжает к перекрёстку. Что покажут оба датчика по шкале 0–100, когда он окажется на поперечной линии? А на изгибе линии?
- Загрузи скетч со шкалой из урока 3.2 (
L32_Errorили свой шаблон). - Медленно провези робота руками по дорожке через изгиб и через перекрёсток. Смотри на монитор порта.
- Заполни таблицу и ответь: по какому признаку перекрёсток можно отличить от изгиба?
| Положение | leftLevel | rightLevel |
|---|---|---|
| Линия посередине | ||
| На изгибе | ||
| На перекрёстке | ||
| После перекрёстка |
Таблица заполнена, признак перекрёстка записан словами.
Сколько показаний робот успевает получить, пока едет по поперечной линии?
- Добавь в свою программу из урока 3.2 счётчик повторов
loop()и раз в секунду печатай, сколько повторов прошло. Запиши число повторов в секунду на рабочей скорости. - Возьми свою скорость в мм/с из урока 2.1 или измерь её заново.
- Посчитай: сколько повторов придётся на поперечную линию шириной 19 мм?
- Провези робота руками через перекрёсток и сравни показания со своей таблицей положений.
Подсказка: Как посчитать
Время на линии — ширина линии, делённая на скорость. Повторов — это время, умноженное на число повторов в секунду.
В журнале — повторы в секунду, скорость, расчёт числа повторов на линии и показания на перекрёстке.
Условие перекрёстка
10 минЗапишем правило: перекрёсток — когда оба датчика видят чёрное. «Чёрное» — показание меньше порога 30.
bool onCrossing() {
return leftLevel < 30 && rightLevel < 30;
}
&& читается как «и»: условие верно, только если верны обе части.
Подсказка: Почему 30, а не 0
Даже на чёрном показание редко ровно 0, а при движении робот видит край линии. Порог 30 надёжно отделяет «чёрное» от «края», но его можно подобрать по своим измерениям.
Ты можешь объяснить, почему на изгибе линии условие не срабатывает, а на перекрёстке — срабатывает.
Сформулируй правило сам по своим записям из прошлого шага: какие показания бывают только на перекрёстке и никогда — на прямой или изгибе?
- Запиши правило словами в журнал: что должно быть у левого датчика и что у правого.
- Выбери порог «чёрного» по своим измерениям. Объясни, почему не 0 и почему не 50.
- Запиши условие на языке Arduino. Понадобится знак «и»:
&&.
Кто-то написал условие через || («или»). Где робот тогда остановится по ошибке?
Подсказка: Как записать «и»
a < 30 && b < 30 верно, только если верны обе части.
В журнале — правило словами, порог с объяснением, условие и ответ про ||.
Требование 1. Функция onCrossing() отвечает true на поперечной линии и false во всех остальных положениях робота.
Проверь условие на бумаге, прежде чем писать код. Для каждого положения запиши показания левого и правого датчика по шкале 0–100 и ответ функции:
| Положение | leftLevel | rightLevel | onCrossing() |
|---|---|---|---|
| Линия посередине | |||
| Линия под одним датчиком на крутой дуге 125 мм | |||
| Линия ушла за оба датчика | |||
| Поперечная линия |
- Выбери порог по своим измерениям и обоснуй его: какой запас до самого тёмного показания вне перекрёстка?
- Найди положение, в котором твоё условие может ошибиться. Что тогда сделает робот?
Подсказка: Где искать ошибку
Провези робота руками по дуге 125 мм из урока 3.2: насколько тёмным становится «край» линии под датчиком?
Таблица заполнена по измерениям, порог обоснован запасом, опасное положение и его последствие записаны.
Стоп на перекрёстке
20 мин- Создай скетч
L41_Crossing_Stop. Перенеси измерения,kpиbaseSpeedиз журнала.
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);} // Перекрёсток: оба датчика видят чёрное.bool onCrossing() { return leftLevel < 30 && rightLevel < 30;} // Ехать по линии, пока не найдём перекрёсток.void followLineToCrossing() { while (true) { readSensors(); if (onCrossing()) { return; } followLineStep(); }} void setup() { setupRobot(); followLineToCrossing(); stopMotors();} void loop() {}
- Найди функцию
followLineToCrossing(). Объясни напарнику, как из неё выходит программа. - Поставь робота на линию перед перекрёстком и запусти.
Робот едет по линии и останавливается на первом перекрёстке.
Как это устроено: Как работает followLineToCrossing
while (true) — повторять всегда. Внутри: прочитать датчики; если перекрёсток — return, то есть выйти из функции; иначе сделать шаг П-регулятора. После выхода программа продолжается со строки stopMotors(); в setup().
| Что видишь | Что сделать |
|---|---|
| Робот проезжает перекрёсток | Снизь baseSpeed или подними порог до 40 |
| Робот останавливается на изгибе | Порог слишком высокий, попробуй 20 |
| Робот не едет | Вписаны ли измерения из журнала? |
- Создай скетч
L41_Crossing_Stopпо шаблону. Перенеси измерения,kpиbaseSpeedиз журнала. Функции линии из урока 3.2 уже готовы. - Напиши сам
onCrossing()по своему правилу из прошлого шага,followLineToCrossing()и движение вsetup(). ПокаonCrossing()не написана, она всегда отвечает «перекрёсток», и робот не трогается.
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. 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; // ===== Твоя часть: напиши сам ===== bool onCrossing() { // true — только когда робот на поперечной линии; на прямой и на изгибе — false. return true;} void followLineToCrossing() { // Ехать по линии с П-регулятором, пока не найдёшь перекрёсток; на перекрёстке функция заканчивается.} void setup() { setupRobot(); // Доехать по линии до первого перекрёстка и остановиться.} // ===== Готовое из уроков 3.1–3.2 — не меняй ===== 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);} void loop() {}
Что сделает робот, если после поиска перекрёстка забыть остановить моторы?
- Поставь робота на линию перед перекрёстком и запусти.
Подсказка: Ехать, пока…
Функция повторяет одно и то же — прочитать датчики, сделать шаг по линии — и заканчивается, когда найден перекрёсток. Какой цикл повторяет «всегда», и как выйти из функции посреди него?
Подсказка: Как выйти из функции
return внутри функции сразу заканчивает её. Программа продолжается со следующей строки в setup().
Робот едет по линии и останавливается на первом перекрёстке. Прогноз записан.
| Что видишь | Что сделать |
|---|---|
| Робот проезжает перекрёсток | Снизь baseSpeed или подними порог |
| Робот останавливается на изгибе | Порог слишком высокий |
| Робот не едет | Вписаны ли измерения? Написана ли onCrossing()? |
Требование 2. Продолжи свою программу L32_P_Line из урока 3.2 и сохрани как L41_Crossing_Stop:
- шаг П-регулятора вынесен в функцию
followLineStep(), чтение датчиков — вreadSensors(); П-регулятор при этом работает как раньше; - функция
followLineToCrossing()ведёт робота по линии и заканчивается, как толькоonCrossing()вернулtrue; - в
setup()робот доезжает до первого перекрёстка и останавливается; - защита без измерений и пауза на старте остаются.
Приёмка. Робот останавливается на перекрёстке пять раз из пяти и ни разу на дуге своего уровня.
Подсказка: Идея (1 из 3)
Функция «едет, пока…» — это бесконечный цикл, из которого есть выход по условию.
Подсказка: План (2 из 3)
- Повторять всегда: прочитать датчики.
- Перекрёсток — выйти из функции.
- Иначе — шаг П-регулятора.
Подсказка: Фрагмент кода (3 из 3)
while (true) { readSensors(); if (onCrossing()) { return; } … }
Программа выполняет требования 1–2, пять остановок из пяти записаны в журнал.
Найди ошибку счёта
8 минСоседи хотели считать перекрёстки так:
void loop() {
readSensors();
if (onCrossing()) {
count = count + 1;
}
followLineStep();
}
Робот проехал один перекрёсток, а count стал 40. Почему?
Ты можешь объяснить: пока робот едет по широкой чёрной полосе, условие верно много раз подряд, и каждый раз счётчик растёт.
Соседи заметили двойной счёт и добавили паузу после счёта:
void loop() {
readSensors();
if (onCrossing()) {
count = count + 1;
delay(100);
}
followLineStep();
}
Робот проехал один перекрёсток, а count стал 3. Почему пауза не помогла? Чем она ещё опасна для езды по линии?
- Запиши ответ в журнал.
- Предложи исправление словами: что должна сделать программа после того, как нашла перекрёсток, прежде чем искать следующий?
Ты можешь объяснить, почему пауза не помогает, и предложить исправление.
Соседи решили считать только момент, когда перекрёсток появился:
bool was = false;
void loop() {
readSensors();
bool now = onCrossing();
if (now && !was) {
count = count + 1;
}
followLineStep();
}
Робот проехал один перекрёсток, а count всё равно стал 40. Где ошибка?
- Найди ошибку и запиши исправленный код в журнал.
- Сравни 40 со своим расчётом числа повторов на линии из прошлого шага. Похоже?
- Чем счёт по моменту появления хуже или лучше проезда перекрёстка, который будет в следующем шаге?
Подсказка: Что хранит was
Когда в этой программе меняется значение was?
Ошибка найдена, исправление записано кодом, число 40 объяснено расчётом, сравнение двух способов записано.
Проехать перекрёсток
10 минЧтобы посчитать перекрёсток один раз, его нужно проехать: ехать прямо, пока датчики не съедут с чёрного.
void passCrossing() {
drive(FORWARD, baseSpeed, FORWARD, baseSpeed);
do {
readSensors();
} while (onCrossing());
delay(100);
}
do { … } while (условие) — сделать и повторять, пока условие верно. Короткая пауза в конце уводит датчики подальше от края поперечной линии.
Ты можешь объяснить, когда закончится цикл в passCrossing().
Чтобы посчитать перекрёсток один раз, его нужно проехать: ехать прямо, пока датчики не съедут с чёрного. Напиши функцию passCrossing() в журнале:
- Включи оба мотора вперёд со скоростью
baseSpeed. - Читай датчики, пока робот на перекрёстке.
- После этого подожди 100 мс.
do { … } while (условие); — сделать и повторять, пока условие верно.
Подсказка: Почему do, а не while
Датчики нужно прочитать хотя бы один раз, прежде чем проверять условие.
Подсказка: Зачем пауза в конце
Сразу после чёрного датчики видят край поперечной линии. Что будет, если в этот момент снова искать перекрёсток?
Функция записана в журнал. Ты можешь объяснить, когда закончится цикл и зачем пауза.
Требование 3. Функция passCrossing() проезжает перекрёсток прямо и заканчивается, когда датчики уже не на поперечной линии и не на её краю.
- Напиши функцию в своей программе.
- Исследуй паузу после схода с чёрного: 0, 50, 100, 200 мс. Для каждой запусти робота через два перекрёстка подряд (в
setup()—followLineToCrossing(),passCrossing(),followLineToCrossing(), стоп) и отметь, на каком перекрёстке он остановился. - Запиши, какая пауза нужна и от чего она зависит. Что будет, если поднять
baseSpeed?
| Пауза, мс | Где остановился | Что видно |
|---|---|---|
| 0 | ||
| 50 | ||
| 100 | ||
| 200 |
Подсказка: Идея (1 из 2)
Ехать прямо и повторять чтение датчиков, пока условие перекрёстка верно.
Подсказка: Фрагмент кода (2 из 2)
do { readSensors(); } while (onCrossing()); — почему здесь do, а не while?
passCrossing() работает, таблица паузы заполнена, вывод о связи паузы и скорости записан.
Стоп на нужном перекрёстке
17 мин- Создай скетч
L41_Count. ВTARGETукажи, на каком перекрёстке остановиться.
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);} // Перекрёсток: оба датчика видят чёрное.bool onCrossing() { return leftLevel < 30 && rightLevel < 30;} // Ехать по линии, пока не найдём перекрёсток.void followLineToCrossing() { while (true) { readSensors(); if (onCrossing()) { return; } followLineStep(); }} // Проехать перекрёсток прямо, пока датчики не съедут с чёрного.void passCrossing() { drive(FORWARD, baseSpeed, FORWARD, baseSpeed); do { readSensors(); } while (onCrossing()); delay(100);} const int TARGET = 1; // на каком перекрёстке остановиться void setup() { setupRobot(); for (int i = 1; i < TARGET; i++) { followLineToCrossing(); passCrossing(); } followLineToCrossing(); stopMotors();} void loop() {}
TARGET = 2. Сколько раз выполнится цикл for? Что робот сделает на первом перекрёстке?
- Проверь на дорожке с тремя перекрёстками:
TARGET= 1, 2, 3.
Робот останавливается ровно на перекрёстке с номером TARGET.
- Создай скетч
L41_Countпо шаблону. ВTARGETукажи, на каком перекрёстке остановиться. - Напиши сам
passCrossing()— по своей записи в журнале — и движение вsetup(). Пока твоя часть не написана, робот не трогается.
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. 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 TARGET = 1; // на каком перекрёстке остановиться // ===== Твоя часть: напиши сам ===== void passCrossing() { // Проехать перекрёсток прямо. Функция заканчивается, когда датчики уже не на поперечной линии и не на её краю.} void setup() { setupRobot(); // Перекрёстки до TARGET — найти и проехать прямо; на перекрёстке номер TARGET остановиться.} // ===== Готовое из уроков 3.2 и 4.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);} // Перекрёсток: оба датчика видят чёрное.bool onCrossing() { return leftLevel < 30 && rightLevel < 30;} // Ехать по линии, пока не найдём перекрёсток.void followLineToCrossing() { while (true) { readSensors(); if (onCrossing()) { return; } followLineStep(); }} void loop() {}
TARGET = 3. Сколько раз робот должен проехать перекрёсток прямо?
- Проверь на дорожке с тремя перекрёстками:
TARGET= 1, 2, 3.
Подсказка: Повторить нужное число раз
Цикл for из урока 1.1 повторяет команды заданное число раз. Сколько перекрёстков нужно проехать перед тем, на котором остановиться?
Робот останавливается ровно на перекрёстке с номером TARGET.
Требование 4. Функция goToCrossing(n) ведёт робота по линии, проезжает прямо перекрёстки до n-го и останавливается на n-м. Сохрани программу как L41_Count.
Приёмка. Дорожка с тремя перекрёстками, n = 1, 2, 3, по три запуска на каждое.
n | Запуск 1 | Запуск 2 | Запуск 3 |
|---|---|---|---|
| 1 | |||
| 2 | |||
| 3 |
Что должна сделать функция при n = 0? Реши сам, запиши и проверь, что программа делает именно это.
Подсказка: План (1 из 2)
n - 1 раз: найти перекрёсток и проехать его. Потом найти последний и остановиться.
Подсказка: Фрагмент кода (2 из 2)
for (int i = 1; i < n; i++) { … } — сколько раз выполнится цикл при n = 1?
Девять запусков без ошибки в таблице, поведение при n = 0 выбрано и проверено.
Задания по уровням
10 минДоехать по линии до первого перекрёстка и остановиться на нём.
Остановиться на третьем перекрёстке, проехав два первых прямо. На среднем поле итоговой задачи робот проезжает перекрёстки, не сворачивая.
Сделай так, чтобы робот проезжал все перекрёстки без остановки и не терял линию после них. Подсказка: в loop() — если перекрёсток, passCrossing(), иначе шаг П-регулятора.
Проверь себя
Выберите ответы для подготовки. Они не сохраняются и не оцениваются на сайте. Пройдите проверку знаний в программе курса или обсудите ответы с преподавателем.
● Основное
★ Средний уровень
★★ Продвинутый уровень
Отвечай на вопросы своего уровня. Нужно верных ответов: Начальный — 4 из 5; Средний — 4 из 5; Продвинутый — 4 из 5. Ответы проверит преподаватель или программа курса.
Итог
Робот находит перекрёстки и считает их без повторов. Следующий шаг — сворачивать на перекрёстке в нужную сторону и проходить маршрут.
Как подтвердить результат
Для каждого опыта сохраняй условия, версию программы, наблюдение и вывод. Сравнивая попытки, меняй одну причину за раз; неудачные попытки тоже остаются в журнале. Отметка результата относится к уже выполненным действиям, повторять их ради отметки не нужно. Если результат не получен, запиши расхождение и следующую проверяемую гипотезу. Модель, измерения настоящего робота и объяснение — разные виды доказательств.
Словарь урока
| перекрёсток | Место, где линию пересекает другая линия. Оба датчика одновременно видят чёрное. |
| порог | Число для сравнения: показание меньше 30 по шкале 0–100 считаем чёрным. |
| цикл while | Повторяет команды, пока условие верно. |
| return | Немедленно завершает функцию и возвращает программу туда, откуда функцию вызвали. |