Занятия 6–7. Датчики: остановки по касанию, расстоянию и линии (задачи 4.5, 4.14–4.20, 4.23)
Занятия 6–7. Остановки по датчикам
Цель занятия 6: обучающиеся останавливают робота по датчику касания (4.14–4.15) и на заданном расстоянии от препятствия (4.5, 4.18), обнаруживают объект сбоку (4.19–4.20). Цель занятия 7: обучающиеся останавливаются на линии в обе стороны (4.16–4.17) и решают задачу «вторая линия на переменном расстоянии» (4.23).
Разминка-вспоминание (занятие 6, 5 мин)
- Формула тиков танкового поворота на θ? Почему k пивота ≠ k танкового?
- Что показал ваш «квадрат» и что вы исправили?
- Почему
distMM()в HAL возвращает медиану из 5? - m вашего робота после калибровки?
Теория: три правила работы с любым датчиком
- Калибруй, не верь даташиту. Порог «линия/фон», зона уверенного обнаружения дальномера, ход кнопки — измеряются на вашем поле при вашем свете. Калибровка — часть программы (режим при старте или отдельный скетч), а не разовое действие.
- Фильтруй. Одиночное показание — шум. Дальномер — медиана из 5; аналоговый датчик линии — среднее из 3–5; кнопка — антидребезг 20–30 мс или подтверждение двумя чтениями.
- Подъезжай медленно. Точность остановки по датчику = скорость × период опроса + тормозной путь. На PWM_MIN оба слагаемых минимальны. Схема всех задач «стоп по датчику»: быстро в зону ожидания → медленно до срабатывания → stopBrake().
Чем отличаются ваши дальномеры (важно при замене датчика)
| Свойство | УЗ (HC-SR04-класс) | Лазерный ToF (VL53L0X-класс) |
|---|---|---|
| Пятно измерения | конус ~15° — «видит» широко | луч <2° — «видит» точку |
| Мёртвая зона | ~2 см | ~3–4 см |
| Слабые места | мягкое, ткань, косые поверхности (эхо уходит), соседний УЗ-робот | чёрные матовые поверхности, яркое солнце |
| 5 В echo → ESP32 | делитель напряжения обязателен | I²C, 3,3 В — напрямую |
| Типовое применение | стоп перед стеной/банкой (4.18) | объект сбоку (4.19–4.20), узкая метка (4.5) |
Благодаря HAL алгоритмы ниже не знают, какой из них стоит: обнаружение объекта сбоку удобнее лазерным (узкий луч не цепляет пол и линию), но работает и с УЗ при аккуратной установке.
Разбор (worked examples)
4.14–4.15: стоп по касанию
void task_touch(int dir) { // dir: +1 вперёд (4.14), -1 назад (4.15)
drive(dir*120, dir*120);
while (!touch()) delay(2);
stopBrake();
// антидребезг живёт в HAL touch(): два чтения с интервалом 20 мс
}Механика важнее кода: бампер должен нажимать кнопку при касании любой точкой передней кромки (планка-качалка на кнопке), для 4.15 — симметричный бампер сзади или разворот робота как «переднего».
4.5 + 4.18: стоп на L см от препятствия
void stopAtMM(int target) {
while (distMM() > target + 150) drive(160,160); // фаза 1: быстро до зоны +15 см
while (distMM() > target) drive(70,70); // фаза 2: ползком до цели
stopBrake();
// 4.5 «определить расстояние до метки»: после стопа вывести distMM()
// на серийник/дисплей/морганием — по формату, который требует судья
}4.19–4.20: объект сбоку
Дальномер смотрит вбок (перпендикулярно курсу). Едем по прямой на энкодерной синхронизации (driveMM-логика без целевых тиков); объект = дистанция упала ниже фона:
// фон калибруем стоя на старте: background = distMM() вбок (там пусто, >1 м или стена)
bool sideObject(int background) { return distMM() < background - 200; } // порог: −20 см
Подтверждение двумя последовательными чтениями обязательно — иначе ловим выброс. Для «назад» (4.20) — тот же код, знак мощности.
4.16–4.17: стоп на линии + калибровка датчика линии
int thrLine; // порог: середина между белым и чёрным
void calibLine() { // режим калибровки при старте:
int w = lineRaw(2); // 1) робот на белом -> запомнили w
waitButton(); // 2) переставили на чёрное -> ч
int b = lineRaw(2);
thrLine = (w + b) / 2; // 3) порог = середина; в журнал: w, b, thr
}
bool onBlack() { return lineRaw(2) > thrLine; } // у большинства модулей чёрное = больше;
// проверить СВОЙ и зафиксировать в HAL
void task_lineStop(int dir) {
drive(dir*100, dir*100);
while (!onBlack()) delay(1);
stopBrake();
}«Стоп на белой линии» из регламента — тот же код с инверсией условия: универсально хранить порог и флаг targetIsBlack.
4.23: вторая линия на переменном расстоянии — конечный автомат
Задача-маркер регламента: расстояние между линиями меняется от попытки к попытке, поэтому никаких «проехать X см» — только события датчика. Ловим не «на линии», а фронты (переходы фон→линия и линия→фон):
stateDiagram-v2
[*] --> ЕДЕМ_ДО_Л1
ЕДЕМ_ДО_Л1 --> НА_Л1 : фон→чёрное
НА_Л1 --> МЕЖДУ : чёрное→фон
МЕЖДУ --> СТОП : фон→чёрное (это Л2!)
СТОП --> [*] : stopBrake()
void task_4_23() {
enum {TO_L1, ON_L1, BETWEEN} st = TO_L1;
bool prev = onBlack();
drive(110, 110);
while (true) {
bool cur = onBlack();
if (st==TO_L1 && cur && !prev) st = ON_L1; // въехали на линию 1
if (st==ON_L1 && !cur && prev) st = BETWEEN; // съехали с линии 1
if (st==BETWEEN && cur && !prev) break; // линия 2!
prev = cur; delay(1);
}
stopBrake();
}Практика
Занятие 6: лотерея условий A
Карточки: {4.14 | 4.15 | 4.18 с L∈{10,15,25} см | 4.19 | 4.20}. Пара тянет две, сдаёт по протоколу (3 попытки). Между попытками судья двигает препятствие/объект.
Занятие 7: калибровка линии + 4.16/4.17
Калибровка на поле, значения w/b/thr — в журнал (интервальный возврат: сравним с калибровкой перед туром — освещение меняет всё).
Занятие 7: 4.23 с тремя расстояниями
Судья ставит вторую линию на 12, 25 и 40 см. Зачёт — все три без правки кода.
Типичные ошибки:
- порог линии «на глаз 2000» вместо калибровки — работает дома, падает на турнире под другими лампами;
- дальномер стоит низко и «видит» пол на дистанции >30 см — приподнять/наклонить, это механика;
- в 4.19 объект обнаружен, а робот проехал мимо: между обнаружением и стопом прошло время → останавливаться надо сразу, а положение объекта при необходимости брать как «тики в момент срабатывания» (пригодится в 3.10);
- УЗ-датчики двух соседних роботов на тренировке глушат друг друга — разводить по времени/полям.
Мини-проверка выхода
Занятие 6 — обе карточки лотереи сданы. Занятие 7 — 4.23 на трёх расстояниях + вопрос: «почему в 4.23 нельзя использовать driveMM?» (расстояние неизвестно и меняется — регламент требует событийного решения).