sadr0b0t / stepper_h

Stepper motor control library for ChipKIT/Arduino
GNU Lesser General Public License v3.0
3 stars 1 forks source link

Сделать текущую позицию и размер рабочей области 64 бит #22

Closed sadr0b0t closed 7 years ago

sadr0b0t commented 7 years ago

Сейчас поля stepper current_pos, min_pos и max_pos имеют тип данных long - это знаковое 32битное целое (и на Arduino с AVR и на ChipKIT с PIC32).

Если за базовую единицу измерения брать нанометры (для для целого значение current_pos подругому не получится - например, для шкива GT2 один шаг, поделенный на 32, будет 6.25микрометра=6250нанометров), максимальный полный размер рабочей области будет 4.3метра (если начальная позиция 0, то 2.15м). Для настольного станка хватает, но как-то без запаса.

 *     Единица измерения выбирается в зависимости от задачи и свойств
 *     передаточного механизма.
 * 
 *     Если брать базовую единицу изменения за нанометры (1/1000 микрометра),
 *     то диапазон значений для рабочей области будет от нуля в одну сторону:
 *     2^31=2147483648-1 нанометров /1000/1000/1000=2.15 метров
 *     в обе строны: [-2.15м, 2.15м], т.е. всего 4.3 метра.
 *
 *     Для базовой единицы микрометр (микрон) рабочая область
 *     от -2км до 2км, всего 4км.

Внезапно оказалось, что даже на ардуине (на чипките тем более) есть 64хбитный целочислинный тип данных "long long" (если не ошибаюсь, поддерживается софтово на уровне libc и компилятора).

Можно просто заменить тип данных у current_pos, min_pos и max_pos с long на long long и получить размер рабочей области от нуля в один конец: 2^63=9223372036854776000нанометров /1000/1000/1000=9223372037метров=9223372км (9млн км) полная рабочая область от -9млн до 9млн км = 18млн км (треть пути до Марса).

sprintf работает с long long через формат %lld

sprintf(reply_buffer, "%lld", motor->current_pos);

Serial.println(xxx, DEC) с long long из коробки не работает.

Здесь есть патч Serial.print() of a 64-bit DOUBLE http://forum.arduino.cc/index.php?topic=143584.0

но в главную ветку, похоже, не отправлен (тред 13го года)

итого

Плюсы:

Минусы:

sadr0b0t commented 7 years ago

Предыдущий тикет в тему Разобраться с размерами целочисленных типов на разных архитектурах для step_delay https://github.com/1i7/stepper_h/issues/13

sadr0b0t commented 7 years ago

По производительности, на ChipKIT по сравнению с предыдущими замерами https://github.com/1i7/stepper_h/issues/17 ничего не поменялось:

Если здесь ничего заметно не ухудшилось, на остальные места тем более пох.

sadr0b0t commented 7 years ago

Здесь есть патч Serial.print() of a 64-bit DOUBLE http://forum.arduino.cc/index.php?topic=143584.0 но в главную ветку, похоже, не отправлен (тред 13го года)

тикет висит с 13го года https://github.com/arduino/Arduino/issues/1236

sadr0b0t commented 7 years ago

еще оттуда бонусом оффтопом - быстрый sprintf (itoa_ для всех целых, в том числе 64хбитных) https://github.com/JensGrabner/snc98_Slash-Number-Calculator/tree/master/Software/Arduino/libraries/itoa_ljust

sadr0b0t commented 7 years ago

Есть 64 бит рабочая область https://github.com/1i7/stepper_h/commit/9d516f955be47e5a8a3b07f144fac7dfb4721dc7

Тип данных curren_pos, min_pos и max_pos - long long (int64_t), 64-битное знаковое целое.

Для 64-битного значения current_pos размеры рабочей области с базовой единицей нанометры:

2^63=9223372036854776000 нанометров /1000/1000/1000 = 9223372037 метров /1000 = 9223372км (9 миллионов км).

в обе стороны от -9млн км до 9млн км, всего 18млн км (1/3 пути до Марса)

64-битные типы данных не поддерживаются аппаратно на 32-битных (тем более, на 16-битных) контроллерах, но они реализованы на уровне компилятора и библиотеки libc (как минимум, для платформ ChipKIT и Arduino). Они могу работать чуть медленнее, чем "родные" (на 32-битных контроллерах) 32-битные переменные long, но потеря производительности по факту оказывается не существенной даже в критических частях кода (сравнение с точностью до микросекунд не показало разницы).

При этом использование 64-битных значений фактически позволяет не задумываться о максимальных границах рабочей области.

Для 32хбитного значения current_pos размеры рабочей области были бы:

  • Если брать базовую единицу измерения за нанометры (1/1000 микрометра), то диапазон значений для рабочей области будет от нуля в одну сторону: 2^31=2147483648-1 нанометров/1000/1000/1000=2.15метра в обе строны: [-2.15м, 2.15м], т.е. всего 4.3 метра.

  • Для базовой единицы микрометр (микрон) рабочая область от -2.15км до 2.15км, всего 4.3км.

Для 32-битного случая вариант рабочей области 4.3 метра (2.15, если считать от 0) с нанометрами для многих случаев в принципе приемлем, но почти не оставляет запаса для экспериментов.

Вариант размера рабочей области с базовой идиницей микрометры более, чем достаточен, но размер шага для настольных станков (хотя они на уровне механики могут не поддерживать такую точность) математически часто предполагает доли микрон (6.15мкм, 7.5мкм и т.п.), поэтому в качестве целевой единицы измерения рекомендуется ориентироваться на целочисленные нанометры.

По размерам и выходу за границы теперь можно вообще не париться. Еще для полной красоты, нужно посчитать варианты для переменных step_count в вызовах prepare_steps и внутренних счетчиках stepper_timer (стоит ли их для единообразия тоже привести в long long или и так пойдет с запасом).

По проблеме с Serial.println(long long, DEC): закинул патченую версию Print.h/Print.cpp в репозиторий до лучших времен (появятся в апстриме - удалю) https://github.com/1i7/stepper_h/tree/master/3pty/arduino

For Serial.println(int64_t, DEC) copy patched Print.cpp and Print.h to:

for Arduino platform ~/.arduino15/packages/arduino/hardware/avr/1.6.19/cores/arduino/ for ChipKIT platform ~/.arduino15/packages/chipKIT/hardware/pic32/1.4.3/cores/pic32/

This is required to compile example sketch:

    Serial.print(sm_x.current_pos, DEC);

(sm_x.current_pos has int64_t/"long long" data type)

Or remove/replace this line in example sketch, patched Print is not required to compile stepper_h library core.

patched version of Print by Rob Tillaart https://github.com/RobTillaart

Code to print int64_t and uint64_t for UNO (and maybe DUE) http://forum.arduino.cc/index.php/topic,143584.0.html https://github.com/arduino/Arduino/issues/1236

sadr0b0t commented 7 years ago

По поводу step_count. Посчитаем.

Базовые эксперименты и вычисления задержек между шагами и по частоте таймера были здесь

Добавить вызов max_cycle_time https://github.com/1i7/stepper_h/issues/17

Там выяснили, что на фиолетовом драйвере с делителем шага 32 мотор будет стабильно работать при минимальной задержке между шагами step_delay=30 микросекунд (ок, самый минимальный вариант - 20мкс, но он уже не очень стабильный). На PIC32MX (ChipKIT Uno32) с библиотекой stepper_h с такой задержкой можно крутить одновременно 2 мотора.

для количества шагов step_count знаковое целое (long) можно на один цикл задать максимальное количество шагов: 2^31=2147483648 30мкс*2147483648шагов = 64424509440мкс/1000=64424509секунд/60=1073741824мин/60= 17895697часов/24=745654дней/365=2042лет прошляпил миллисекунды 30мкс2147483648шагов = 64424509440мкс/1000000=64425секунд/60=1074мин/60= 18часов https://duckduckgo.com/?q=2%5E3130%2F1000%2F1000%2F60%2F60&t=canonical&atb=v76-1&ia=calculator

пожалуй, хватит на цикл без int64_t

для step_delay=20 микросекунд (прям совсем максимальная скорость на китайском бросовом железе) 2^3120/1000/1000/60/60=12 часов https://duckduckgo.com/?q=2%5E3120%2F1000%2F1000%2F60%2F60&t=canonical&atb=v76-1&ia=calculator чуть поменьше, но, наверное, тоже достаточно

Если заменить long (32 бит) на int (16 бит на Ардуине): 2^15*20/1000= 655.36 миллисекунд.

вообще ни о чем, long без вариантов

sadr0b0t commented 7 years ago

Сюда же - минимальная задержка между шагами step_delay и расстояние за шаг distance_per_step.

    /**
     * Минимальная задержка между импульсами step, микросекунды
     * (для движения с максимальной скоростью).
     * 
     * для 32-битного знакового целого:
     *   макс задержка=2^31=2147483648 микросекунд=2147483 миллисекунд=2147 секунды=35 минут
     * для 32-битного беззнакового целого:
     *   макс задержка=2^32=4294967296 микросекунд=4294967 миллисекунд=4294 секунды=71 минута=~1 час
     * 
     * итого 32 бит: оба варианта - более, чем достаточно
     * 
     * для 16-битного знакового целого:
     *   макс задержка=2^15=32768 микросекунд=33 миллисекунды - с натягом норм, но на грани (1/30 макс скрости)
     * для 16-битного беззнакового целого:
     *   макс задержка=2^16=65536 микросекунд=65 миллисекунд - не сильно лучше
     * 
     * итого 16 бит: задержки не подходят.
     * 
     * В PIC32/ChipKIT int и long - 32 бит.
     * В AVR/Arduino long - 32 бит, int - 16 бит.
     * 
     * итого: нам нужны 32 бит, для всех платформ (PIC32/ChipKIT, AVR/Arduino) это long.
     */
    unsigned long step_delay;
    /**
     * Расстояние, проходимое координатой за один шаг мотора,
     * базовая единица измерения мотора.
     * 
     * На основе значения distance_per_step счетчик шагов вычисляет
     * текущее положение рабочей координаты current_pos.
     * 
     * Единица измерения выбирается в зависимости от задачи и свойств
     * передаточного механизма (рекомендуется считать за нанометры).
     * 
     * для 32-битного беззнакового целого:
     *   макс расстояние/шаг = 2^32 = 4294967296 нанометров = 4294967 микрометров = 4294 миллиметров = 4 метра
     * 
     * итого 32 бит: вполне достаточно
     * 
     * для 16-битного беззнакового целого:
     *   макс расстояние/шаг = 2^16 = 65536 нанометров = 65 микрометра
     * 
     * итого 16 бит: в ряде ситуаций приемлемо, но уже не достаточно:
     * например, для шкива 3д-принтера на моторе без делителя 1 шаг может
     * быть уже 200 микрометров, т.е. не вмещаться в 16 бит.
     * 
     * В PIC32/ChipKIT int и long - 32 бит.
     * В AVR/Arduino long - 32 бит, int - 16 бит.
     * 
     * итого: нам нужны 32 бит, для всех платформ (PIC32/ChipKIT, AVR/Arduino) это long.
     */
    unsigned long distance_per_step;

Короче, логично ввести такое правило:

Во-первых, для большинства рассмотренных ситуаций 16-битных значений действительно мало. Во-вторых, использование long даёт единообразие на всех платформах.