Skip to content
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]
  

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

Эталонный код (SPIKE 3, Python)

from hub import port, button, light_matrix
import distance_sensor, runloop

DIST = port.C

def median_sm(n=7):
    """Медиана n корректных показаний, в сантиметрах."""
    vals = []
    while len(vals) < n:
        d = distance_sensor.distance(DIST)
        if d > 0:                      # -1 = объект не виден, пропускаем
            vals.append(d)
    vals.sort()
    return round(vals[n // 2] / 10)    # мм -> см

async def wait_press(b=button.RIGHT):
    """Ждать нажатие И отпускание — иначе одно нажатие сработает дважды."""
    while not button.pressed(b):
        await runloop.sleep_ms(10)
    while button.pressed(b):
        await runloop.sleep_ms(10)

async def show(value):
    print(value)                        # консоль — основной канал для судьи
    await light_matrix.write(str(value))

async def main():
    # --- Этап 1: фиксация порога (5 баллов) ---
    await runloop.sleep_ms(3000)        # судье время поставить стенку
    porog = median_sm()
    await show(porog)                   # порог в см на экран/консоль

    # --- Этапы 2 и 3: два сравнения (по 10 баллов), без перезапуска ---
    for _ in range(2):
        await wait_press()              # судья переставил стенку, мы жмём кнопку
        d = median_sm()
        await show(1 if d > porog else 0)

runloop.run(main())

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

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

Практика

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

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

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

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

GRAN = [5, 10, 15, 20, 25]        # см, откалиброванные границы зон 1..6
def zona(d_sm):
    z = 1
    for g in GRAN:
        if d_sm > g:
            z += 1
    return z                      # 1..6

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

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

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

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

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

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

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

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

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