Таймлапс заката я снимал трижды, и все три раза горизонт в ролике дёргался, будто камеру толкали в плечо: между кадрами я сдвигал платформу руками, на глаз. Двадцать сантиметров хода за два часа съёмки — рукам это надоедает к двадцатому кадру, и каждый следующий сдвиг выходит чуть короче предыдущего. Лечение давно придумано: слайдер, который между кадрами отъезжает сам и на одинаковые миллиметры. Сервопривод не годится — у него ход 180 градусов, моторчику с крыльчаткой плевать на миллиметры, и только шаговик с ходовым винтом умеет считать шаги и переводить их в миллиметры. Круговой собрат этого проекта — столик для предметной съёмки — уже был в итоговом уроке про поворотный стол, здесь камера едет по прямой.
Механика у слайдера прозаичная: винт TR8×8, латунная гайка, платформа на двух направляющих. Мотор 28BYJ-48 через плату ULN2003 крутит винт, гайка ползёт по резьбе и тащит платформу. Вся работа урока — в арифметике: перевести сантиметры в минуту, которые хочет камера, в шаги в секунду, которые понимает мотор. У меня эта пара чисел сходится в одну константу: 256 шагов на миллиметр.
Соберём расчёт скорости, разберём, почему setSpeed при таймлапс-темпе бессилен и что с этим делать, добавим тумблер старт-пауза и кнопку возврата каретки, а в конце прогоним слайдер в симуляторе — там каретка ползёт со скоростью один миллиметр в минуту, и это лучшая демонстрация того, насколько медленный у таймлапса пульс.
Винт TR8×8: восемь миллиметров за оборот
TR в названии винта — трапецеидальная резьба, 8 — диаметр в миллиметрах, второе число — свинец: столько гайка проходит за один полный оборот. У TR8×8 это восемь миллиметров. Тонкость, на которой спотыкаются: свинец — не шаг резьбы. TR8×8 — четырёхзаходный винт: профиль одного витка виден как 2 мм, но заходов четыре, и за оборот гайка съезжает по всем четырём — 2 × 4 = 8 мм. Мерить резьбу штангенциркулем и верить показаниям — уехать в расчёте в четыре раза.
Арифметика дальше короткая. Вал делает 2048 шага на оборот — откуда это число, мы считали в уроке про передаточное число и 2048 шага, — делим на восемь миллиметров свинца и получаем 256 шагов на миллиметр. Один шаг двигает каретку на 3,9 микрометра: в семнадцать раз тоньше человеческого волоса. Для кадра таймлапса это избыточная точность, но запас не мешает: часть микрон съест упругость пластиковой гайки и люфт направляющих.
У свинца есть вторая цена, о которой в магазинах не пишут: крутой винт проворачивается. Гайку TR8×8 я свободно вращаю пальцами — платформа без питания скользит от нажатия ладонью. Для горизонтального слайдера это пустяк, для вертикальной оси — авария: выключили питание, платформа поехала вниз сама. На вертикаль берут мелкий свинец вроде TR8×2, который держит вес сам, — об этом разговор во втором уроке.
| Величина | Значение | Откуда берётся |
|---|---|---|
| Шагов на миллиметр | 256 | 2048 шага на оборот / 8 мм свинца |
| Один шаг | 3,9 мкм | 8 мм / 2048 |
| Скорость 1 мм/мин | 256 шагов в минуту, 4,3 шага в секунду | 256 шагов/мм × 1 мм/мин |
| Ход 100 мм | 25 600 шагов, 100 минут при 1 мм/мин | 256 шагов/мм × 100 |
Почему setSpeed не умеет 1 мм/мин
Желаемая скорость переводится в обороты делением: 1 мм/мин на винте со свинцом 8 мм — это 0,125 оборота в минуту. И вот здесь библиотека Stepper выходит из игры: параметр setSpeed целочисленный, дробь 0,125 он съест до нуля, а внутри библиотеки из этого числа делением считается задержка между шагами — с нулём в делителе результат непредсказуем. Скорости ниже одного оборота в минуту через setSpeed не задают, точка.
Рецепт, которым я пользуюсь: setSpeed держит потолок (10 RPM — 341 шаг в секунду), а реальный темп задаёт millis-таймер. Считаем интервал: 60 000 мс / (256 шагов/мм × 1 мм/мин) = 234 мс на шаг. В loop() каждые 234 мс просим у мотора один шаг — и loop() остаётся живым всё остальное время: тумблер, кнопка и светодиод опрашиваются между шагами. Техника та же, что в качелях из урока про порции шагов и в планировщике на millis, только доведена до предела: одна порция равна одному шагу.
Четыре шага в секунду — нагрузка смешная: срыв шагов начинается ближе к пятистам шагам в секунду, тут запас в сто раз. Платить за низкий темп нечем, кроме терпения: ход в сто миллиметров на скорости 1 мм/мин занимает сто минут, и это нормально — закат снимается два часа.
#include <Stepper.h> const int STEPS_PER_REV = 2048;const float STEPS_PER_MM = 256.0; // 2048 шага / 8 мм свинца TR8x8const float MM_PER_MIN = 1.0; // скорость таймлапсаconst long LIMIT_STEPS = 25600; // ход 100 мм: 256 x 100 Stepper stepper(STEPS_PER_REV, 8, 10, 9, 11); long pos = 0; // позиция каретки в шагах от нуляlong pechat = 0; // где последний раз печаталиunsigned long tShag = 0;long interval; // мс на один шаг void setup() { stepper.setSpeed(10); // потолок подачи; темп задаёт таймер interval = 60000.0 / (STEPS_PER_MM * MM_PER_MIN); // 234 мс pinMode(2, INPUT_PULLUP); // кнопка: возврат каретки pinMode(3, INPUT); // тумблер: старт/пауза pinMode(12, OUTPUT); // светодиод: движение идёт Serial.begin(9600); Serial.println("Slider: 1 mm/min, hod 100 mm");} void loop() { if (digitalRead(2) == LOW && pos != 0) { stepper.step(-pos); // быстрый возврат к нулю Serial.print("vozvrat, bylo mm="); Serial.println(pos / STEPS_PER_MM, 1); pos = 0; } int edem = digitalRead(3); if (edem == HIGH && pos < LIMIT_STEPS && millis() - tShag >= interval) { stepper.step(1); pos++; tShag = millis(); digitalWrite(12, HIGH); if (pos - pechat >= 128) { // каждые полмиллиметра pechat = pos; Serial.print("mm="); Serial.println(pos / STEPS_PER_MM, 1); } } if (edem == LOW || pos >= LIMIT_STEPS) { digitalWrite(12, LOW); }}Три места в скетче, на которые смотреть. Первое — interval: константа из арифметики свинца, и когда поменяете винт, менять нужно именно её. Второе — stepper.step(1): один шаг за вызов, loop() между ними свободен. Третье — печать через отметку pechat: каждые 128 шагов, то есть полмиллиметра, Serial выдаёт строку — монитор не заливает, а отчёт приходит раз в полминуты.
Кнопка возвращает каретку к нулю одним блокирующим step(-pos): на 10 RPM это до семидесяти пяти секунд на полном ходу, и всё это время плата глуха — здесь блокировка нарочно, возврат не обязан быть вежливым. На настоящем слайдере к нулю лучше вести по концевику: счётчик шагов после срыва или ручного сдвига врёт, а концевик — железный репер, который не врёт. Как искать ноль и зачем лимит хода, разобрано в уроке про homing в нулевую точку и уроке про лимиты хода; здесь же ход ограничен константой LIMIT_STEPS — мотор просто останавливается на краю.
Слайдер в симуляторе
В симуляторе та же константа 234 мс на шаг, и циферблат мотора ползёт со скоростью, которую на живом железе пришлось бы наблюдать час: тумблер D3 включает ход, кнопка D2 возвращает каретку к нулю, светодиод D12 горит, пока движение идёт. Serial печатает пройденные миллиметры каждые полмиллиметра пути.
- TR8×8 проносит гайку на 8 мм за оборот: 2048 шага / 8 мм = 256 шагов на миллиметр, шаг 3,9 мкм
- 1 мм/мин = 256 шагов в минуту = 4,3 шага в секунду — интервал подачи 234 мс
- setSpeed в Stepper.h целочисленный: темпы ниже 1 RPM задаёт millis-таймер по одному шагу, setSpeed остаётся потолком
- Ход 100 мм — 25 600 шагов и 100 минут при 1 мм/мин; промах в один оборот — восемь миллиметров расхождения кадров
- Крутой свинец проворачивается под нагрузкой: для вертикальной оси берите TR8×2 или механический упор
- 1. Сколько шагов нужно каретке на 10 мм хода при винте TR8×8?
- 2. Почему темп 1 мм/мин задаёт millis-таймер, а не setSpeed(0.125)?
- 3. На сколько двигается каретка за один шаг на TR8×8?
- 4. Каретка на 1 мм/мин должна проехать 100 мм. Сколько займёт ход?
- 5. После ручного сдвига каретки счётчик позиции начинает врать. Что возвращает ему честность?
Слайдер едет, но каждая смена винта или микрошага означает пересчёт констант руками. Следующий урок забирает эту работу у калькулятора: подадим свинец, микрошаг и желаемую скорость в Serial и получим готовые цифры — шагов на миллиметр, обороты в минуту и вердикт, по силам ли мотору такая скорость.
Частые вопросы по теме урока
- Сколько шагов нужно каретке на 10 мм хода при винте TR8×8?
- 256 шагов на миллиметр, значит 10 мм — 2560 шагов. 25 600 — это полный ход в сто миллиметров. 2560
- Почему темп 1 мм/мин задаёт millis-таймер, а не setSpeed(0.125)?
- Скорости ниже одного RPM подают шагами из таймера, а setSpeed держат потолком вроде 10 RPM. Параметр setSpeed целочисленный: дробь 0,125 он съест до нуля
- На сколько двигается каретка за один шаг на TR8×8?
- 8 мм / 2048 = 0,0039 мм. А 0,176 градуса — это угол вала на один шаг, не путь гайки. 3,9 мкм
- Каретка на 1 мм/мин должна проехать 100 мм. Сколько займёт ход?
- Миллиметр в минуту — сто миллиметров за сто минут. Для таймлапса это норма, а не поломка. 100 минут
- После ручного сдвига каретки счётчик позиции начинает врать. Что возвращает ему честность?
- Концевик — физический якорь: каретка возвращается к нему и счётчик обнуляется. Homing разбирается отдельным уроком. Репер-концевик в нулевой точке