Станок с ЧПУ «Кулибин» или моя жизнь среди нанотехнологий

angry emoticon

Начался этот кошмар около месяца назад. Отец узнал где-то про вот этот девайс, конструктор станков с ЧПУ «Кулибин», и поручил мне его добыть, с целью дальнейшего применения на разнообразные рукодельные нужды. Поскольку о грядущей промышленной революции в связи с применением автоматики для личного производства я рассказывал в лекциях еще черти когда, я с энтузиазмом взялся за это дело, а небезызвестный мой тезка Вольф взялся мне помогать.

Я сразу скажу вам, что так не задалбывался, пожалуй, в течение лет пяти, не менее… Но давайте все по порядку, честно и без утаек, потому что рассказ будет длинный как жираф.

Очень быстро, удалось найти дилера этих устройств в Москве, находящегося в маленьком офисе в подвальном помещении. Несмотря на ужасненький сайт, нас встретил приветливый дядька, который немедленно вынес нам из подсобки небольшую коробку, плотно набитую пятнадцатью килограммами высоких технологий, каковую мы тут же приобрели за 15000 рублей.

Открыли мы ее естественно тут же, но до сборки дело дошло только через несколько дней. Внутри коробки было найдено шесть шаговых двигателей SM-200 производства фирмы «Элион», двенадцать тяжелых металлических реек в дырках, контроллер, завернутый в пластик от папки для бумаг вместо корпуса, блок питания на 12В 1А, некоторое количество крепежа, некоторое количество тщательно замотанного скотчем фторопласта, два приличных гаечных ключа на десять, плохая отвертка и диск с документацией.

С первой проблемой мы столкнулись, пытаясь прочитать документацию, которая была предоставлена в виде вордового файла, набитого фотографиями, отформатированного без применения стилей (в результате чего все поехало) и зачем-то пересыпанного выдержками из учения ТРИЗ.1 Чего они делали в инструкции мы так и не поняли, но они занимали не менее трети ее текста, не способствуя при этом основному назначению оной — помочь нам понять, как же собрать из этого конструктора хотя бы самый простой вариант фрезерного станка с ЧПУ, что мы собственно и собирались сделать. На диске также было найдено: Драйвер для микросхем RS232-USB фирмы FTDI (почему-то для всех видов винды до XP, но не для более поздних), штатный софт, и несколько десятков ценных книг в djvu, снабженных гордыми подписями сканировавших их добровольцев, одна из которых была столь же сакраментальна сколь истинна — «Scan Pirat». Могли бы немножко постесняться, ну да ладно, я против чтоли…

Решение, лежащее в основе конструктора — простое, гениальное и неправильное… впрочем, все решения, которые были таки обнаружены в поставленной конструкции, этим отличаются. Оси станка построены на каретках, передвигаемых шаговым двигателем посредством вала из шпильки с нарезанной резьбой М6, проходящего через гайку, закрученную внутрь полой квадратной каретки, что в конструировании систем позиционирования в общем нормально, хотя обычно применяются совсем другие резьбы… ну да ладно. Каретки, вместо каких-нибудь подшипников или еще чего, скользят по рейкам на фторопластовой прокладке. Поскольку все эти детали изготовлены с дичайшими допусками, прокладка прижимается специальными винтиками. Моторы, согласно инструкции, прижимаются к рейкам при помощи обычных хомутиков, с добавлением прокладок для компенсации вышеуказанных допусков. Два мотора с двумя каретками должны перемещать одну логическую ось, для чего должны быть установленны параллельно.

С самой первой проблемой мы столкнулись, попытавшись собрать конструкцию, изображенную на фотографии в инструкции. В ней присутствовало на одну рейку больше, чем набор вообще содержал, но посмотрев в паспорт2 мы пришли к выводу, что никакого некомплекта не имеется. Просто на фотографии изображена лишняя рейка. Причем там, где она не добавляла конструкции никакой особенной жесткости.

Собрав, в меру сил, изображенную конструкцию, мы начали гонять двигатели туда и сюда при помощи штатного софта на Visual Basic. Я не знаю кто писал этот софт, но подозреваю, что этому человеку лучше не оказываться от меня в радиусе десяти метров — так недалеко до очень некрасивых, гхм, несчастных случаев.3 Софт владеет способностью интерпретировать G-код, стандартный язык программирования ЧПУ, а также генерить его на основе двумерных изображений, понимая их как heightmap, и моделей в формате STL. К сожалению, больше хороших слов я сказать о нем не могу, потому что:

  • Модели в формате STL понимаются путем рендера их в heightmap, не обращая внимания на реальные свойства детали, не думая о размере заготовки, вообще ни о чем, и в один проход. Повернуть их можно только абстрактно, программа лучше вас знает какой стороной надо повернуть деталь.
  • Посмотреть трехмерно ничего нельзя, софт дает вам только двумерное представление кода который пойдет на вывод. Соответственно, вы не знаете на сколько инструмент подымется над заготовкой при переходе от точки к точке, и знать не можете.
  • Алгоритм преобразования heightmap в G-код дубовый, и периодически гоняет головку туда-сюда вхолостую в задумчивости где бы еще кусок отпилить.
  • По рабочей области бегает спрайт с изображением головки станка. Когда спрайт находится за пределами рабочей области и пропадает с экрана, скорость перемещения головки возрастает вдвое.
  • Задав программе перегнать головку из одного угла рабочей области в другой, остановить перемещение можно только прибив программу насмерть.
  • Паспортная скорость перемещения головки 4мм/с. Программа гоняет ее со скоростью 1мм/с не взирая на то, есть там чего пилить или она просто перемещает головку к новому месту работы.

В общем, софт дубовый донельзя. Тем не менее, его хватило нам для того, чтобы убедиться в главном: Конструкция работает, для достаточно малых значений слова «работает».

Ее регулярно насмерть заедает из-за перекоса осей, поскольку двигатели нещадно теряют шаги, дергаются в разные стороны и дребезжат так, что трясется пол.

В общем, очень быстро мы пришли к выводу, что штатный софт должен отправиться на помойку, и я начал размышлять, как бы прикрутить это чудо-юдо к EMC. EMC2 — наследник стандартного софта, написанного много лет назад для индустриальных целей, и на нем можно завести более или менее что угодно, хотя бы теоретически. Осталось только написать драйвер, чем я и занялся.

Штатный контроллер. Зачем именно им микроконтроллер, когда программа в нем тоже дубовая…

Штатный контроллер. Зачем именно им микроконтроллер, когда программа в нем тоже дубовая...

Довольно быстро я выяснил, что микросхема FTDI в контроллере «Кулибина» используется в bitbang mode, т.е. обойтись просто записью в последовательный порт мне не удастся. Для того, чтобы понять, что штатный софт с ней делает, пришлось закопаться довольно глубоко, и внутри были обнаружены выдающиеся примеры быдлокода, вроде реализации задержки при помощи счета до 200000.4 Выяснилось, что контроллер тоже тупой, и использует 6 бит из 8 для того, чтобы закодировать перемещение одного двигателя в одну сторону — и еще ему зачем-то нужен тактовый бит, видимо для того чтобы если какой-то байтик по дороге потеряется, пропустить не один байтик а до кучи сразу два.

В общем получилось вот это:

#!/usr/bin/python

import ftdi

class Kulibin(object):

    # That's axes X, Y and Z respectively.
    _COMMAND = (0b10000,0b00100,0b10100)
    _STEPSIZE = 0.0025

    def __init__(self):

        self._pos = [0,0,0]
        self.counter = 0

        self.port = ftdi.ftdi_context()
    
        if ftdi.ftdi_init(self.port) < 0:
            raise self.FTDIError('FTDI Device Not found')
        if ftdi.ftdi_usb_open(self.port,0x403,0x6001) < 0:
            raise self.FTDIError('Unable to open FTDI device.')
        if ftdi.ftdi_usb_reset(self.port) < 0:
            raise self.FTDIError('Could not reset the FTDI device.')
        if ftdi.ftdi_set_bitmode(self.port,0xff,ftdi.BITMODE_BITBANG) < 0:
            raise self.FTDIError('Failed to set bitbang mode.')
        if ftdi.ftdi_set_latency_timer(self.port,1) < 0:
            raise self.FTDIError('Failed to set latency timer.')

    def getpos(self):
        return tuple([x*self._STEPSIZE for x in self._pos])
    pos = property(getpos)
        
    class FTDIError(Exception):
        def __init__(self,value):
            self.value = value
        def __str__(self):
            return repr(self.value)

    def __step(self,motor,direction): 
        byte = self._COMMAND[motor] | self.counter
        self.counter = self.counter ^ 0b10
        if direction < 0:
            byte = byte | 1
        if ftdi.ftdi_write_data(self.port,chr(byte),1) < 0:
            raise self.FTDIError('Error writing data to the FTDI device.')

    def move(self,motor,distance): # Takes a float, assumes millimeters.
        steps = int(round(distance/self._STEPSIZE))
        direction = cmp(steps,0)
        for i in range(0,abs(steps)):
            self.__step(motor,direction)
            self._pos[motor] = self._pos[motor]+direction

if __name__ == "__main__":                    

    cnc = Kulibin()

    cnc.move(2,20)
    print cnc.pos

Тупо, но работает. На вопрос на форуме как прикрутить это к EMC, народ отреагировал нервно поднятыми бровями, обозвав саму конструкцию нехорошими словами за никакую скорость и уродский интерфейс контроллера. Тем не менее, как следует помедитировав, я домедитировался до следующей цепочки: Мой драйвер трансформирует все вышеприведенное в т.н. «ноги»,5 по две штуки на ось — пуск мотора и направление движения. Именно с такими ногами, хотя и на параллельном порту, привык работать программный генератор сигналов шаговых двигателей EMC.

Я это написал, и даже завел. Оно даже работало. Тем не менее, полный код я приводить не буду, ибо очень скоро выяснил, что от этого придется отказаться: Что бы я ни делал со штатным контроллером, он не может переместить более одного двигателя в одну сторону за раз, тогда как EMC подразумевает что работает с полноценной системой с независимыми осями, и хочет двигать все оси одновременно. Несмотря на то, что теоретически, EMC можно заставить работать с таким устройством, нагородив многометровый конфиг, и можно даже написать ему драйвер более или менее реального времени, который упростит конфиг, значительно проще и правильнее спаять контроллер на параллельный порт самому.

Схема контроллера который получился у меня.

Что и было сделано. После некоторой медитации на даташиты, чтения манов и вопросов на форуме, я породил схему, примитивную как доска, несмотря на большое количество используемых в ней проводков — и Вольф сел паять ее на макетке, выслушивая по ходу дела мои дополнения. На сами микросхемы-драйверы шаговых двигателей, замену износившихся со времен моих последних занятий электроникой инструментов, и прочие материалы, ушло изрядное количество денег и матюков, потому что все время выяснялось что мы что-то забыли, и в поисках забытого был многожды перерыт весь мой морг.6

Если вдруг кто-то тоже владеет этим, гхм, конструктором, и хочет пойти моим путем, к схеме будут следующие пояснения:

  • Шесть коннекторов для моторов подразумевают что их ноги расположены в том же порядке, что и на штатных разъемах «Кулибина». Вам принципиально только чтобы первую обмотку со второй не перепутать.
  • Конденсаторы вокруг 7805 опциональны, если вы питаете конструкцию от хорошего блока питания.
  • Если у вас есть чем сделать печатную плату, лучше ее сделать, ибо с проводками мы задолбались более всего. Если бы не они, схема тривиальна.
  • В каком порядке разводить входные ноги микросхем на параллельный порт — исключительно дело вкуса, EMC жрет всё, и меняет направление вращения моторов по мановению волшебной палочки. Главное, не перепутать их с пятью входными ногами параллельного порта, которые вам еще пригодятся.
  • Не верьте мне на слово, я не претендую на то, что хорошо понимаю что делаю.

Конфиг EMC для такого устройства выглядит примерно так:

loadrt hal_parport cfg="0x378 out  "
loadrt stepgen step_type=10,10,10

addf parport.0.read base-thread
addf stepgen.make-pulses base-thread
addf parport.0.write base-thread

# Эти строчки присутствуют в конфигах, которые генерит Stepconf Wizard.
# Они тут не нужны, поскольку несовместимы с управлением моторами
# непосредственно по четырем фазам.
#addf parport.0.reset base-thread
#setp parport.0.reset-time 5000

# .....

# Порядок соединения ног зависит от того, куда вы вывели
# входы каждой из L293D.
net xa => parport.0.pin-01-out
net xb => parport.0.pin-02-out
net xc => parport.0.pin-03-out
net xd => parport.0.pin-04-out

# .....

setp stepgen.0.position-scale [AXIS_0]SCALE
setp stepgen.0.steplen 200
setp stepgen.0.maxaccel [AXIS_0]STEPGEN_MAXACCEL
setp stepgen.0.maxvel [AXIS_0]STEPGEN_MAXVEL
setp stepgen.0.dirdelay 10000
net xpos-cmd axis.0.motor-pos-cmd => stepgen.0.position-cmd
net xpos-fb stepgen.0.position-fb => axis.0.motor-pos-fb
net xa <= stepgen.0.phase-A
net xb <= stepgen.0.phase-B
net xc <= stepgen.0.phase-C
net xd <= stepgen.0.phase-D
net xenable axis.0.amp-enable-out => stepgen.0.enable

Таким манером мне тут же удалось разогнать моторы до паспортных 4мм/с и даже быстрее. На радостях от того, что все работает,7 мы даже оборудовали оси концевиками, чтобы проделывать автоматическое выставление машины в ноль по датчикам.

Концевики сделаны из микросвитчей с лапкой. Сначала мы сделали гораздо более геморройную конструкцию…

Концевики сделаны из микросвитчей с лапкой. Сначала мы сделали гораздо более геморройную конструкцию...

После чего мы сели пересобирать конструкцию сначала, с намерением вывести из системы все перекосы… Наивные. В этот раз мы уже были вооружены штангенциркулем, угольником, и прочими измерительными инструментами. По окончании сборки мы дружно помянули Ктулху и компанию, потому что выяснилось, что изображенные на фотографиях конструкции нарушают теорему Пифагора, и построить из них станок, в котором все оси будут строго перпендикулярны без применения дополнительных компонентов невозможно.

Потому что рейки кривые!

И ладно бы только это… Шаговые двигатели не могут быть прикручены к рейкам при помощи штатных хомутов, потому что хомуты пережимают им катушки, и от этого двигатели начинает заедать на высоких оборотах. Назрела потребность в очередном походе на строительный рынок, который мы уже однажды посетили в связи с потребностью замены погнутых шпилек, и изготовлении новой системы крепления.

Крепление двигателя было изготовлено из алюминиевой рейки.

Крепление двигателя было изготовлено из алюминиевой рейки.

После досыпания в конструкцию еще денег, получилась система наподобие биплана на растяжках, которую по крайней мере можно было вывести в строгие перпендикуляры при помощи гаечного ключа, и которая не прогибалась под весом привинченного на головку дремеля. На ней наконец были выполнены первые гравировальные работы.

Биплан на растяжках… Вся конструкция в сборе. Иначе выровнять ее было нереально.

Биплан на растяжках... Вся конструкция в сборе. Иначе выровнять ее было нереально.

По результатам… Буду ли я рекомендовать купить продукцию МНТЦ? Вот вам мой ответ:

Да, вот именно то что там написано.

Да, вот именно то что там написано.

Кратко: Если бы мы сконструировали станок с нуля, это обошлось бы нам дешевле, и отняло бы меньше сил, чем доведение этого набора до ума.

P.S. Для тех кому интересно, полный комплект конфигов EMC для работы с вышеприведенной схемой прилагается.


  1. Разговор о смысле, применимости и научности ТРИЗ как такового я оставлю за кадром, ибо мое мнение об этом явлении весьма неоднозначно. ↩︎

  2. Да, это устройство, помимо инструкции на диске, имеет напечатанный на двух листках бумаги совкового типа паспорт. ↩︎

  3. Побью олуха томом «Искусства компьютерного программирования» Кнута, проще говоря, и возможно насмерть. ↩︎

  4. Для непосвященных объясню, что приличные люди так не делают, поскольку задержка будет считаться с разной скоростью на разных машинах… ↩︎

  5. В EMC связь драйверов железа реализуется на манер микросхем — у драйверов есть «ноги», и вы соединяете их «проводками» — богато и мощно, хотя и злопутанно… ↩︎

  6. Кто бы мог подумать что у меня нету запаса L7805, они же КР142ЕН5, однако ж не было! ↩︎

  7. Схемотехник из меня вообще никакой, не могу же я уметь хорошо вообще все… ↩︎