Перейти к содержимому
2. Датчик расстояния: пороги и зоны

2. Датчик расстояния: пороги и зоны

Занятие 2. Датчик расстояния: пороги, зоны, Задание 1

Цель занятия: обучающиеся по показанию датчика выводят 0/1 относительно порога и номер зоны 1–6, используя фильтрацию показаний.

Разминка-вспоминание (5 мин, без подглядывания)

  1. Сколько попыток даётся на каждую мини-задачу и какая идёт в зачёт?
  2. Что вернёт датчик расстояния, если перед ним нет объекта?
  3. Почему датчик стоит заподлицо с передним краем рамы?

Затем квиз по регламенту (контрольная точка, цель 1, 7 вопросов): комплектация устройства; размер стенки; значение белой/чёрной карточки; можно ли перезапускать программу в Задании 1; ширина и цвет стартовой линии; сколько мини-задач в итоговом задании; куда разрешён вывод результата.

Разбор (worked example): полное решение Задания 1 регламента — 25 баллов

Условие (Приложение 1). Стенка ставится перед роботом — её положение задаёт порог; порог вывести в см (5 баллов). Судья дважды переставляет стенку (не ближе ±2 см к порогу); после каждой перестановки по нажатию кнопки вывести 0, если стенка ближе порога, 1 — если дальше (по 10 баллов). Программа работает без перезапуска.

Алгоритм

    flowchart TD
    S[Старт программы] --> W1[Судья ставит стенку]
    W1 --> M[Измерить порог: медиана 7 показаний, перевести в см]
    M --> P[Вывести порог на экран/консоль]
    P --> B1[Ждать кнопку 1-й раз]
    B1 --> D1[Измерить медиану → d1]
    D1 --> C1{d1 > порог?}
    C1 -- да --> O1[Вывести 1] 
    C1 -- нет --> Z1[Вывести 0]
    O1 --> B2[Ждать кнопку 2-й раз]
    Z1 --> B2
    B2 --> D2[Измерить медиану → d2]
    D2 --> C2{d2 > порог?}
    C2 -- да --> O2[Вывести 1]
    C2 -- нет --> Z2[Вывести 0]
  

Два решения регламент делает нетривиальным: (а) одиночное показание ультразвука шумит — берём медиану нескольких измерений; (б) «без перезапуска» — значит одна программа с состояниями, порог хранится в переменной всю дорогу.

Эталонный код (C++, ESP32)

#include "stend.h"
#include <algorithm>

int medianSM(int n = 7) {                  // медиана n корректных показаний, в сантиметрах
  int v[15];
  int k = 0;
  while (k < n) {
    int d = distMM();
    if (d > 0) v[k++] = d;                 // -1 = объект не виден, пропускаем
  }
  std::sort(v, v + n);
  return (v[n / 2] + 5) / 10;              // мм -> см
}

void waitPress(Btn b = BTN_R) {      // ждать нажатие И отпускание — иначе одно нажатие сработает дважды
  while (!pressed(b)) delay(10);
  while (pressed(b))  delay(10);
}

void show(int value) {
  Serial.println(value);                   // консоль — основной канал для судьи
  screen(String(value));
}

void setup() {
  Serial.begin(115200);
  stendBegin();

  // --- Этап 1: фиксация порога (5 баллов) ---
  delay(3000);                             // судье время поставить стенку
  int porog = medianSM();
  show(porog);                             // порог в см на экран/консоль

  // --- Этапы 2 и 3: два сравнения (по 10 баллов), без перезапуска ---
  for (int i = 0; i < 2; i++) {
    waitPress();                           // судья переставил стенку, мы жмём кнопку
    int d = medianSM();
    show(d > porog ? 1 : 0);
  }
}

void loop() {}

Построчный разбор ключевых мест: медиана из 7 устойчива к одиночным выбросам и к моменту, когда рука судьи ещё в кадре; waitPress ждёт отпускания — без этого цикл for проглотит оба сравнения за одно длинное нажатие; зазор ±2 см в регламенте гарантирует, что честная медиана не попадёт «на ребро» порога, поэтому строгое > корректно; пауза 3 с в начале — судья должен успеть поставить стенку после запуска (можно заменить ожиданием кнопки, согласовав с судьёй процедуру).

Почему медиана, а не среднее. Ультразвуковой датчик изредка возвращает случайный выброс (переотражение). Среднее семи показаний выброс сдвинет, медиану — нет: она просто возьмёт четвёртое по величине значение. Это стандартный приём робастной обработки сигнала, и он же спасает в задачах на зоны.

Практика

Воспроизвести Задание 1

Партнёр играет судью: ставит стенку, переставляет, командует. Сдать по протоколу: порог → кнопка → 0/1 → кнопка → 0/1, без перезапуска. Сверить с таблицей: 5 + 10 + 10.

Зоны 1–6 (компетенция 1.3.1)

Поле А4 (длина ≈ 30 см) размечено на N равных зон (регламент: от 2 до 6). Откалибровать границы: поставить стенку на каждую линию разметки, записать медианные показания, пороги взять посередине между соседними. Код зоны для N=6:

const int GRAN[] = {5, 10, 15, 20, 25};    // см, откалиброванные границы зон 1..6

int zona(int dSM) {
  int z = 1;
  for (int g : GRAN)
    if (dSM > g) z++;
  return z;                                // 1..6
}

Вывести номер зоны по нажатию кнопки. Проверка: партнёр ставит стенку в случайную зону 5 раз — 5 верных ответов.

Наличие объекта и высота (1.3.1)

«Наличие»: 1 if 0 < d <= 40 см else 0 — граница по дальнему краю поля. «Высота»: призма стоя (21 см) видна датчику на высоте 10 см, лёжа (9 см) — нет; вывод «высокий/низкий» по наличию эха. Реализовать оба, сдать партнёру.

Техника безопасности. Не подносить лицо к ультразвуковому датчику вплотную и не «проверять» его на людях с близкого расстояния; стенку ставить всей ладонью сверху, не двигая устройство.

Типичные ошибки

  1. Одно показание вместо фильтра — первая попытка проходит, вторая ловит выброс. Признак: «дома работало».
  2. Сравнение в миллиметрах с порогом в сантиметрах — всегда 1. Приводите единицы в одном месте (medianSM).
  3. Нет ожидания отпускания кнопки — оба сравнения улетают за одно нажатие, теряете 10 баллов.
  4. Перезапуск между сравнениями — прямое нарушение условия, попытка не засчитывается.
  5. Калибровка зон «по линейке», а не по датчику — датчик меряет до ближайшей отражающей точки стенки, а не до линии на бумаге; границы берите только из реальных показаний.

Мини-проверка выхода

«Зона за 30 секунд»: наставник ставит стенку — команда называет зону с экрана устройства за ≤30 с, три постановки подряд без ошибок. Плюс вопрос: «зачем в Задании 1 запрет ставить стенку ближе ±2 см к порогу?»

Уровни сложности