Автокормушку для кота я откалибровал в градусах: задвижка открывается на девяносто, корм сыплется ровно две секунды. Через неделю кот орал в шесть утра, а в миске лежало явно больше порции. Лог в Serial показал: каждый цикл вал проходил 450 шагов вместо 512. В скетче красовалась упрощённая формула — угол, умноженный на пять.
Откуда пятёрка? В первые дни я поделил 2048 на 360 в уме и округлил до целого. Только вот хвостик — это 0,689 шага на каждый градус, и на девяноста градусах хвостик превращается в 62 шага, то есть в 11 градусов дуги. Кот получал на двенадцать процентов больше расчётного — не потому что жадный, а потому что я пренебрёг дробью, которая казалась мелочью.
В этом уроке разберём, из чего складывается число 2048, почему честное передаточное число дробное и как записать пересчёт градусов в шаги так, чтобы ни один шаг не потерялся ни в делении, ни в умножении.
Откуда берётся 2048
Складываются два множителя. Первый — ротор: моторная часть до редуктора делает 32 полных шага на оборот, это конструкция статора и зубцов, мы её считали в первом уроке раздела. Второй — пластиковый редуктор: в корпусе 28BYJ-48 четыре пары шестерён, и их произведение даёт около 63,684 оборота ротора на один оборот выходного вала. Никакой эзотерики: 2048 — это просто 32, умноженные на округлённые 64, и оба множителя можно проверить на разобранном моторе за десять минут.
На шильдиках и в описаниях пишут 1:64 — красивое округление, на котором сэкономлено место и силы. Честное число дробное, и разница не теоретическая: 32 × 63,684 = 2037,9 шага на оборот, а не 2048. Если командовать 2048 шагами, вал проедет 361,8 градуса — лишние 1,8° на каждом обороте. За десять оборотов подряд набежит почти 18 градусов: для поворотного столика предметной съёмки это уже заметное уползание, которое нужно компенсировать.
Практический вывод, к которому я пришёл после пары перепроверок: для заслонок, кормушек и шторок смело считайте 2048 — разброс в полпроцента тонет в люфте пластиковых шестерён. Для длинных кинематических цепей с сотнями оборотов ставьте энкодер или концевик-репер и обнуляйте счёт у него: даже идеальная арифметика не отменяет люфт и редкий срыв.
То же число 2048 обслуживает и полушаговый режим, о котором мы говорили в прошлом уроке: такты идут вдвое чаще, позиций на оборот выходит 4096, а шаг ужмается до 0,088°. В формуле пересчёта это меняет одну константу: умножаете угол на 4096 и делите на 360 — то есть на 11,38 шага на градус. Главное — не смешивать константы: половина загадочных «двойных оборотов» в чужих скетчах — это полушаговая партитура, подставленная в формулу для полных шагов.
| Величина | Откуда берётся | Значение |
|---|---|---|
| Шаг ротора, полный режим | Конструкция статора: 32 шага на оборот мотора | 11,25° |
| Передаточное число | Произведение четырёх пар шестерён редуктора | Около 63,684:1, в описаниях округляют до 64 |
| Шаг вала, полный режим | 32 × 63,684 ≈ 2038; на практике считают 2048 | 0,176° (360 / 2048) |
| Шаг вала, полушаг | Такты вдвое чаще | 0,088° (4096 позиций на оборот) |
Теперь рабочая формула: шаги = угол × 2048 / 360. Девяносто градусов — 512, сорок пять — 256, сто восемьдесят — 1024: углы, кратные сорока пяти, делятся без остатка, потому что 360 / 2048 сокращается до 45 / 256. Все прочие градусы дают дробь: тридцать — это 170,67, и step() возьмёт целое. Цена вопроса невелика: половина шага — это 0,088° на валу, про накопление такой ошибки по кругу мы говорили в уроке про позиционирование. Но чтобы ловить дроби осознанно, а не случайно, полезно один раз пройти все ловушки деления и умножения под микроскопом — этим и займёмся.
Первая ловушка — целочисленное деление. Константа 2048 имеет тип int, и запись 2048 / 360 компилятор честно сосчитает на целых: выйдет 5, а 5,689 уйдёт в пол — ровно та кормушка из начала урока. Лечение одной точкой: 360.0 заставляет компилятор считать в дробях. Вторая ловушка — переполнение при умножении: 90 × 2048 = 184 320, а int на плате держит максимум 32 767. Сначала уложите угол в long (разбор целых типов), потом умножайте — и следите, куда пишете результат: переменная под шаги тоже должна быть long.
Собранный порядок вычислений выглядит так: угол перевели в long, умножили на 2048, прибавили 180 для округления, разделили на 360 — и только потом отдали число в step(). Нарушите порядок в любом месте — и арифметика тихо съест точность: деление до умножения вернёт пятёрку, умножение в int — мусор, а без округления вал будет систематически недоезжать почти по целому шагу на каждом ходе. Кот, кстати, до конца проекта питался по правильной формуле — я переписал скетч в тот же вечер, как увидел 450 вместо 512 в логе.
#include <Stepper.h> const int STEPS_PER_REV = 2048; Stepper stepper(STEPS_PER_REV, 8, 10, 9, 11); // угол в градусах -> целое число шагов валаlong stepsForAngle(int angle) { long a = angle; // в long ДО умножения return a * STEPS_PER_REV / 360;} void setup() { stepper.setSpeed(8); Serial.begin(9600); Serial.println("Vvedite ugol v gradusah");} void loop() { if (Serial.available() > 0) { int angle = Serial.parseInt(); long n = stepsForAngle(angle); Serial.print("Ugol: "); Serial.print(angle); Serial.print(" Shagov: "); Serial.println(n); stepper.step(n); Serial.println("Gotovo"); }}Функция stepsForAngle — единственное место, где живёт арифметика, и это не эстетика: если формула размазана по скетчу, рано или поздно кто-то — обычно я сам через полгода — упростит её до angle * 5. Один источник правды, все вызовы через функцию. Чтение угла из Serial — отдельное умение, если забыли синтаксис, вернитесь к уроку про parseInt. Заодно обратите внимание: константа STEPS_PER_REV объявлена int — она влезает, а вот произведения с ней нет; в этом и есть весь смысл перехода на long только в месте умножения.
В симуляторе ниже Serial напечатает steps=682 для 120 градусов. Калькулятор подтверждает: 120 × 2048 / 360 = 682,67, целочисленное деление отсекло две трети шага, и вал недоедет 0,12°. Для кормушки — ничто, для поворотного столика после сотни ходов — уже копилка. Как ужать ошибку — в совете ниже.
Отдельный нюанс — отрицательные углы. Формула с прибавкой 180 для минуса работает навыворот: минус сто двадцать после округления уползёт в другую сторону. Я делаю просто: считаю модуль угла, округляю, а знак прикладываю к готовому числу шагов. В симуляторе ниже, впрочем, честная целочисленная версия без прибавки — чтобы вы видели сырую арифметику, а не уже причёсанную.
Проверяем арифметику в симуляторе
Симулятор крутит циферблат по той же формуле, что и ваш будущий скетч: 120 градусов туда, 120 обратно, Serial печатает пересчитанные шаги. Сверьте каждую цифру с расчётом на бумажке — привычка проверять моторную арифметику отдельно от механики экономит часы: если числа сходятся, а вал едет мимо, виновата уже механика, а не код.
Смотрите и на знак: в Serial видно, что минус сто двадцать градусов превратился в те же 682 шага, только с минусом, и стрелка уехала в обратную сторону. Симметрия тут не случайность: минус в step() — это просто обратный порядок тактов, число шагов не меняется.
Задачи на закрепление — от бумажки к коду. Каждая следующая опирается на предыдущую, а третья готовит вас к чужим скетчам, где формула уже вписана, но вписана неправильно.
- Посчитайте на бумажке шаги для 30°: 30 × 2048 / 360 = 170,67 — то есть 170 с отсечением и 171 с округлением к ближайшему.
- Найдите все углы в пределах оборота, которые переводятся в шаги без дроби: кратные 45 — 0, 45, 90, 135, 180, 225, 270, 315, 360. Проверьте: 45° — это ровно 256 шагов.
- Добавьте к скетчу печать фактического угла: steps × 360 / 2048 — сравните с заданным и увидите цену округления в градусах.
- Заведите угол через Serial.parseInt и крутите вал на произвольные градусы, включая отрицательные: знак минус должен давать вращение назад без отдельных веток кода.
- 2048 = 32 шага ротора × 64; честное передаточное число — около 63,684:1, поэтому точный пересчёт даёт 2038 шагов на оборот
- 2048 шагов против честных 2038 — это 1,8° лишних на каждом обороте; за 10 оборотов набежит 18°
- Шаги из угла: angle × 2048 / 360; углы, кратные 45°, ложатся ровно — 45° это ровно 256 шагов
- Делите с точкой (360.0) и умножайте в long: 90 × 2048 = 184320 — за пределом int в 32767
- Округляйте к ближайшему: (a × 2048 + 180) / 360 — ошибка не больше 0,088°
- Арифметику пересчёта держите в одной функции, а счёт длинных серий оборотов — у репера: концевика, геркона или энкодера
- 1. Сколько полных шагов делает ротор моторной части до редуктора?
- 2. Какое передаточное число у реального 28BYJ-48?
- 3. Что вернёт запись 2048 / 360, если оба числа — целые константы?
- 4. Зачем укладывать угол в long до умножения на 2048?
- 5. Сколько шагов отсчитает step(stepsForAngle(45)) при формуле a * 2048 / 360?
Арифметика редуктора теперь под контролем: формула одна, живёт в одной функции и считает в long с округлением. Если позиция механизма должна переживать выключение питания — сохраняйте счётчик шагов в EEPROM, такой приём со счётчиком мы уже делали. А для механизмов с сотнями оборотов добавьте репер и обнуляйте счёт у него: люфт шестерён и редкий срыв не лечатся формулами, зато лечатся обнулением. Кот, кстати, с тех пор получает порцию до секунды — я проверил на логе: 512 шагов, как вписано.
Частые вопросы по теме урока
- Сколько полных шагов делает ротор моторной части до редуктора?
- Конструкция статора даёт 32 полных шага на оборот ротора, по 11,25° каждый. Всё остальное добавляет редуктор. 32
- Какое передаточное число у реального 28BYJ-48?
- Четыре пары шестерён дают дробное произведение примерно 63,684:1. В описаниях его округляют до 64 — отсюда и привычные 2048 шагов. Около 63,684
- Что вернёт запись 2048 / 360, если оба числа — целые константы?
- Целочисленное деление отсекает дробь: получится 5. Чтобы вышло 5,689, делитель записывают с точкой — 360.0. 5
- Зачем укладывать угол в long до умножения на 2048?
- Умножение в int переполнится и вернёт мусор. long держит до двух миллиардов, поэтому сначала расширяем угол, потом умножаем. 90 × 2048 = 184320 — больше предела int в 32767
- Сколько шагов отсчитает step(stepsForAngle(45)) при формуле a * 2048 / 360?
- 45 × 2048 / 360 = 256 без остатка: углы, кратные 45°, переводятся в шаги ровно, без дроби и ошибки округления. 256