И основа — секция script
Внутри секции могут встречаться ноды с именами command и loop.
Формат ноды command:
Оператор действия — один из немногих операторов. Параметры для каждого описаны ниже. Могут быть дополнены любыми другими, которые, как вы уже поняли, можно использовать в шаблонах.
Формат ноды loop:
цикл — это секция, содержимое которой будет исполнено для каждого элемента, определенного типом цикла. Их два (пока):
tools — цикл выполнится для каджого инструмента, иtoolholes — цикл выполнится для каждой дырки, предназначенной для сверления этим инструментом. Очевидно, что цикл toolholes может быть только вложенным в tools.
Генератор g-code
А вот тут начинается самое интересное.
С присущим мне глобализмом я пробежался по имеющимся решениям, и понял, что каждое из них способно не только создать кучу проблем при разворачивании технологии дома, но и доставлять их регулярно и методично, вплоть до пенсии. Что не нравилось? Негибкость.
Все они больше под станки, с предопределенными характеристиками шаблонов, и т.п. Да, дело не сложное. Но очень не хотелось однажды столкнуться с ситуацией, когда нужно чуть видоизменить алгоритм, и не иметь возможности это сделать. К примеру, не встречал тулзу, способную повернуть отверстия вокруг оси.
В качестве базы были использованы библиотеки Qt 5.11. Приложение написано в консольном стиле. Архитектура приложения выполнена в linux-стиле.
На вход приложению подается файл DRL, выдернутый из PCAD при создании Geber-комплекта. (возможно, придется доработать парсер, если захочется скормить ему что-нибудь из AltiumDesigner. Но лично я для себя решил снести этого Альтиум-монстра от греха подальше. За что теперь он является в страшных снах, и не дает забыть собственное имя).
В качестве параметра указывается файл XML. Описанию формата этого файла будет посвящана вторая половина статьи. Этот файл, по сути, определяет механизм формирования G-Code (а на самом деле — любого текстового файла) для передачи его (G-кода) 3D-принтеру.
Головка-шпиндель
Тут все просто — после длительных попыток создать свой шпиндель, решил прикупить оный на AliExpress, и просто повесить на кронштейне. Фото нет, пока в процессе.
Механизм работы приложения
- Читается и распознается формат DRL (который М48 или Excellon). В результате получаются инструменты, содержащие список дырок, которые этими инструментами сверлятся.
- С полученными из п.1 данными мы идем в XML, ищем там ноду script, и попросту исполняем все, что там написано. Есть пяток операторов, а большего нам и не нужно.
- В процессе исполнения п.2 случались операторы print. Результат печатается на выходной поток.
Операторы
assign tools
Параметры: нет.
Проводит присвоение каждому сверлу из исходного файла инструмента из XML. Без него большинство других действий не имеют смысла.
sort toolsПараметры: нет (пока). Сортирует сверла по возрастанию диаметров
offsetПараметры: xoffs,yoffs — значения смещений. Работает скрипт-машина.Смещает все отверстия на указанные значения. Да, часто так бывает, что плата разводится далеко не в начале координат.
printПараметр: pattern Название шаблона.Печатает шаблон с указанным названием на выходной поток.
print contextПараметры:line_begin, line_end — начало и конец каждой строки. Отладочная штука — позволяет в любом месте вывести на выход все доступные в данный момент переменные и их значения. Каждая переменная выводится отдельной строкой, начало и конец указаны в параметрах
Предопределенные имена глобальных переменных.
holesCount, toolsCount
— я очень, очень, очень надеюсь, что смысл этих переменных в пояснении не нуждается. Да-да. Это количество инструментов и количество отверстий.
minX, maxX, minY, maxY
— и этих тоже. Нет, ну на всякий случай — это координаты поля сверления. Все дырочки находятся внутри этого прямоугольника. Пересчитывается после команды offset.
Сверлильный станок. теперь — трёх осевой.
Два года назад, я опубликовал свою первую версию сверлильного станка: https://stanki-doma.ru/blogs/f1fcd5011d/drill-press-in-my-routine1. Так как он до сих пор пользуется вниманием на YouTube, решил модернизировать конструкцию. Встречайте:
Сильно разглагольствовать не буду. Перечислю отличия от предыдущих вариантов:
1. Появилась задняя опора, третья стойка, что делает конструкцию реально прочной.
2. Установлен более лёгкий двигатель RS-550 с цангой вместо патрона. Если честно, он по хуже 775го, но со своей задачей справляется на ура.
Вот ссылка на него:
https://aliexpress.ru/item/32817777939.html?spm=a2g0o.detail.1000060.1.52e2274f1wsq0O&gps-id=pcDetailBottomMoreThisSeller&scm=1007.13339.169870.0&scm_id=1007.13339.169870.0&scm-url=1007.13339.169870.0&pvid=02875c7e-1f63-448f-a0c5-0d86ad23606f&_t=gps-id:pcDetailBottomMoreThisSeller,scm-url:1007.13339.169870.0,pvid:02875c7e-1f63-448f-a0c5-0d86ad23606f,tpp_buckets:668#0#131923#0_668#808#3772#50_668#888#3325#13_668#2846#8108#114_668#2717#7558#112_668#1000022185#1000066059#0_668#3468#15612#339
Изменил подъёмную опору. Теперь зажимной барашек направлен назад, а не в сторону, как в прошлом варианте.
Двигатель фиксируется внутри внутри моторамы верхней и нижней чашкой при помощи резьбы.
Установил регулятор оборотов
, внутрь осевого кронштейна.
Брал на Али вот здесь:
https://aliexpress.ru/item/33017155388.html?spm=a2g0s.9042311.0.0.274233edZlgpfh на 12В, 2А. Оси рычагов, как и прежде, на подшипниках. Добавил поворотный столик для фиксации платы. Столик в двух вариантах с тисочками ( они вращаются вокруг своей оси , двигаются вперёд-назад) и без, просто столик.
Добавил кронштейн для крепления свёрел. Можно держать под рукой свёрла разного диаметра…
Подсветка на трёх ярких светодиодах, соединённых последовательно, ну и про резистор не забываем). Всё спаял на монтажке.
Ну и весь дизайн станка претерпел изменения в лучшую (как мне кажется)) сторону.
Практически каждая деталь состоит из 2-4 слоёв, каждый из которых печатается и обрабатывается отдельно а затем всё склеивается вместе. Как пример- нижнее основание:
и так далее… Я подсчитал , что станок состоит приблизительно из более 70 напечатанных деталей. не считая железа конечно. И правда: Красота-страшная сила))).
Что касается непосредственно работы… Работает тихо. Биений цанги почти нет. Даже на малых оборотах уверенно сверлит текстолит без проблем. Усилие на рычаг — один палец. Легко просверлил насквозь дюралевую 6 мм станину станка когда крепил по центру столик для тисков калёным сверлом 0.8. Сверло целенькое. Не спрашивайте зачем так заморачиваться? Глазу приятно!
Эх жаль видосик вставить напрямую нельзя, а в YouTube пока ролик не смонтировал.
Всем добра!
Сменная головка для reprap
Для того, чтобы можно было легко превращать принтер в сверлилку и обратно, решено было сделать головку разъемной. Бегунок, прикрученный к ремням — отдельно, а все остальное — съемное. Учитывая, что это не бог весть, какая сложность, отдельной статьей выкладывать смысла нет. Тут просто фото и ссылки на
, если кто захочет себе точно такую же. В архиве так же присутствуют SLDPRT-исходники, если что подправить. Качается медленно — спасибо ADSL от белтелекома, но лежать должно долго.
Результат получился вот таким:
Часть вторая. формат xml-файла
Для того, чтобы сделать программу максимально гибкой, была использована библиотека ScriptEngine. Сам чуточку ошалел от того, что теперь реально можно сделать при конфигурации. Основной постулат таков: есть много вычисляемых параметров, работа с которыми ведется максимально прозрачно: текст передается модулю ScriptEngine, и используется результат.
<xml>
<variables>
<var name="ZChangeToolValue" value="30"/>
<var name="ZTravelValue" value="10"/>
<var name="ZDrillValue" value="0"/>
</variables>
<functions>
<!--
predefined function with single parameter:
a - source (requested) diameter
returns - suggested tool diameter for give requested after halvanic
if function nod defined, it assumed return=a
-->
<plate_increase_dia f="a 0.2"/>
</functions>
<tools>
<!--
"tool" node defines a real drill tool for make a hole
Depends on your technical process you can set up different
tools for plated or not holes or join same holes in single tool.
Required parameters for tool are:
1. range_min,range_max - diameters range to assign holes for this tool
You can joun different diameters (f.ex. 0.31-0.4) to single tool
2. plated="yes|no|both" - defines plated property to
Other parameters are optional and can be used later in G-Code patterns.
For example, you can define tool position or toolbox coords for the tool.
-->
<tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" />
<tool description="0,4mm" range_min="0.3" range_max="0.4" plated="both" position="1" />
<tool description="0,5mm" range_min="0.4" range_max="0.5" plated="both" position="2" />
<tool description="0,6mm" range_min="0.5" range_max="0.6" plated="both" position="3" />
<tool description="0,7mm" range_min="0.6" range_max="0.7" plated="both" position="4" />
<tool description="0,8mm" range_min="0.7" range_max="0.8" plated="both" position="5" />
<tool description="0,9mm" range_min="0.8" range_max="0.9" plated="both" position="6" />
<tool description="1,0mm" range_min="0.9" range_max="1.0" plated="both" position="7" />
<tool description="1,1mm" range_min="1.0" range_max="1.1" plated="both" position="8" />
<tool description="1,2mm" range_min="1.1" range_max="5" plated="both" position="9" />
</tools>
<patterns>
<!-- in any pattern you can use any variable from context where it's printing
Example (used inside 'tool' loop type):
Mnnn Please, change tool to ${description} ; message to lcd
Mnnn ; pause
Note : here ${description} is optional tag defined in <tool> node
Use this example outsite the tool loop will cause calculation error.
-->
<pattern name="start">
G90 ;${var hcnt=holesCount;var tcnt=toolsCount;"Hello"}
M117 Homing
G28 X Y
M117 Move Z to travel
G0 X${minX} Y${minY}
M76
G92 Z${ZTravelValue}
</pattern>
<pattern name="finish">
G0 Z${ZChangeToolValue}
M104 S0 ; disable spindle
G0 X0 Y220
M117 Drill finished
M300 S600 P1
; Stats:
; Holes : ${holesCount}
; Tools : ${toolsCount}
</pattern>
<pattern name="set_tool">
; Tools rest: ${tcnt--}
G0 Z${ZChangeToolValue}
G0 X100 Y0
M104 S0 ; disable spindle
M117 Change tool to ${description}
M300 S600 P1
M76 ; pause job
M117 Drilling
M104 S100 ; enable spindle
G28 X
</pattern>
<pattern name="go_drill">
; Holes rest: ${hcnt--}
; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}%
M73 P${100-percent}
G0 Z${ZTravelValue}
G0 X${Math.round(x*100)/100} Y${Math.round(y*100)/100}
G0 Z${ZDrillValue}
G0 Z${ZTravelValue}
</pattern>
</patterns>
<script>
<!--
"assign tools". No parameters
Just assign all tools declared in
DRL-file to tools described in <tools> node.
For each DRL-defined tool will be selected FIRST compatible
tool from <tools> node. I.e. if range 0.3..0.8 will be defined early,
<tool> node for diameter 0.4..0.5 will never be assigned.
Except 'plated' property will be different.
-->
<command verb="assign tools" />
<!--
"assign tools". No parameters
Join all DRL-file tools, assigned to same tool here
to one tool (also holes)
Just avoid multiply changing physical tool to same
-->
<command verb="join tools" />
<!--
"offset".
Offset ALL holes by defined values
xoffs, yoffs - values to offset. Before offset will be calculated
i.e. here you can use global variables.
-->
<command verb="offset" xoffs="-minX 10" yoffs="-minY 10"/>
<!--
loop for each DRL-tool (assigned and joined before).
Context inside will be filled also with tool's properties and
node's parameters
-->
<command verb="print" pattern="start"/>
<loop type="tools">
<command verb="print" pattern="set_tool"/>
<command verb="print context" line_begin=";"/>
<!--
loop for each hole inside the tool.
Context inside will be filled also with hole's properties(x&y) and
node's parameters
-->
<loop type="toolholes">
<command verb="print" pattern="go_drill"/>
<!--
"print context".
Anwhere in script you can use this verb.
It inserts all context variables available.
Usefull for debug but completely useless for
normal work
-->
<command verb="жprint context" line_begin=";"/>
</loop>
</loop>
<command verb="print" pattern="finish"/>
</script>
</xml>
<xml>
<variables>
<var name="ZChangeToolValue" value="10"/>
<var name="ZTravelValue" value="2"/>
<var name="ZDrillValue" value="-3"/>
<var name="FeedHorizontal" value="24000"/>
<var name="FeedDown" value="100"/>
<var name="FeedFree" value="2000"/>
<var name="StartOffsX" value="20"/>
<var name="StartOffsY" value="20"/>
<var name="ZZeroPosition" value="0.1"/>
<var name="first" value="0"/>
</variables>
<functions>
<!--
predefined function with single parameter:
a - source (requested) diameter
returns - suggested tool diameter for give requested after halvanic
if function nod defined, it assumed return=a
-->
<plate_increase_dia f="a 0.3"/>
</functions>
<tools>
<!--
"tool" node defines a real drill tool for make a hole
Depends on your technical process you can set up different
tools for plated or not holes or join same holes in single tool.
Required parameters for tool are:
1. range_min,range_max - diameters range to assign holes for this tool
You can joun different diameters (f.ex. 0.31-0.4) to single tool
2. plated="yes|no|both" - defines plated property to
Other parameters are optional and can be used later in G-Code patterns.
For example, you can define tool position or toolbox coords for the tool.
-->
<tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" />
<tool description="0,4mm" range_min="0.3" range_max="0.4" plated="both" position="1" />
<tool description="0,5mm" range_min="0.4" range_max="0.5" plated="both" position="2" />
<tool description="0,6mm" range_min="0.5" range_max="0.6" plated="both" position="3" />
<tool description="0,7mm" range_min="0.6" range_max="0.7" plated="both" position="4" />
<tool description="0,8mm" range_min="0.7" range_max="0.8" plated="both" position="5" />
<tool description="0,9mm" range_min="0.8" range_max="0.9" plated="both" position="6" />
<tool description="1,0mm" range_min="0.9" range_max="1.0" plated="both" position="7" />
<tool description="1,1mm" range_min="1.0" range_max="1.1" plated="both" position="8" />
<tool description="1,2mm" range_min="1.1" range_max="5" plated="both" position="9" />
</tools>
<patterns>
<!-- in any pattern you can use any variable from context where it's printing
Example (used inside 'tool' loop type):
Mnnn Please, change tool to ${description} ; message to lcd
Mnnn ; pause
Note : here ${description} is optional tag defined in <tool> node
Use this example outsite the tool loop will cause calculation error.
-->
<pattern name="start1">
; Start
</pattern>
<pattern name="set_tool1">
; Set tool ${description}
</pattern>
<pattern name="finish1">
; Finish
</pattern>
<pattern name="go_drill1">
; Drill X${Math.round(x*100)/100} Y${Math.round(y*100)/100}
</pattern>
<pattern name="start">
;${var hcnt=holesCount;var tcnt=toolsCount;"Hello"}
M117 Homing
G28
G0 Z0 F${FeedFree}
G92 Z1.6
</pattern>
<pattern name="finish">
G0 Z${ZChangeToolValue} F${FeedFree}
M400
M5 ; disable spindle
G0 X0 Y220 F${FeedHorizontal}
M117 Drill finished
M300 S600 P100
; Stats:
; Holes : ${holesCount}
; Tools : ${toolsCount}
</pattern>
<pattern name="set_tool">
; Tools rest: ${tcnt--}
G0 Z${ZChangeToolValue} F${FeedFree}
M400
G0 X100 Y0 F${FeedHorizontal}
M117 Stopping spindle
M5 ; disable spindle
M117 Change tool to ${description}
M300 S600 P100
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M25
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
G28 X Y
G0 X${StartOffsX-1} Y${StartOffsX-1} Z${ZTravelValue} F${FeedHorizontal}
G0 Z${ZZeroPosition} F${FeedFree}
M117 Check zero-hole
M300 S600 P100
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M25
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
G92 Z${ZZeroPosition} F${FeedDown}
M117 Starting spindle
M3 ; enable spindle
G0 Z${ZDrillValue} F${FeedDown/3}
G0 Z${ZTravelValue} F${FeedFree}
M117 Drilling
M117 Starting spindle
M3 ; enable spindle
</pattern>
<pattern name="go_drill">
; Holes rest: ${hcnt--}
; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}%
M73 P${100-percent}
M117 Drilling X${Math.round(x*100)/100} Y${Math.round(y*100)/100} Z${ZTravelValue}
G0 Z${ZTravelValue} F${FeedFree}
G0 X${(Math.round(x*100)/100)-2} Y${(Math.round(y*100)/100)-2} F${FeedHorizontal}
G0 X${Math.round(x*100)/100} Y${Math.round(y*100)/100} F${FeedHorizontal}
M400
G0 Z${Math.round((ZZeroPosition 0.2)*100)/100} F${FeedFree}
G0 Z${Math.round((ZZeroPosition-0.3)*100)/100} F${FeedDown/10}
G0 Z${ZDrillValue} F${FeedDown}
M117 Return
G0 Z${ZTravelValue} F${FeedFree}
</pattern>
<pattern name="second_time">
; ${var hcnt=holesCount;var tcnt=toolsCount;"SECOND!!!"}
</pattern>
</patterns>
<script>
<!--
"assign tools". No parameters
Just assign all tools declared in
DRL-file to tools described in <tools> node.
For each DRL-defined tool will be selected FIRST compatible
tool from <tools> node. I.e. if range 0.3..0.8 will be defined early,
<tool> node for diameter 0.4..0.5 will never be assigned.
Except 'plated' property will be different.
-->
<command verb="assign tools" />
<!--
"assign tools". No parameters
Join all DRL-file tools, assigned to same tool here
to one tool (also holes)
Just avoid multiply changing physical tool to same
-->
<command verb="join tools" />
<!--
"offset".
Offset ALL holes by defined values
xoffs, yoffs - values to offset. Before offset will be calculated
i.e. here you can use global variables.
-->
<command verb="offset" xoffs="-minX StartOffsX" yoffs="-minY StartOffsY"/>
<!--
loop for each DRL-tool (assigned and joined before).
Context inside will be filled also with tool's properties and
node's parameters
-->
<command verb="sort tools"/>
<command verb="print" pattern="start"/>
<loop type="tools">
<condition content="first ==0">
<command verb="print" pattern="set_tool"/>
</condition>
<command verb=";print context" line_begin=";"/>
<!--
loop for each hole inside the tool.
Context inside will be filled also with hole's properties(x&y) and
node's parameters
-->
<loop type="toolholes">
<command verb="print" pattern="go_drill"/>
<!--
"print context".
Anwhere in script you can use this verb.
It inserts all context variables available.
Usefull for debug but completely useless for
normal work
-->
<command verb=";print context" line_begin=";"/>
</loop>
</loop>
<condition content="first=0">
<command verb=";dummy"/>
</condition>
<command verb="print" pattern="second_time"/>
<loop type="tools">
<condition content="first >0">
<command verb="print" pattern="set_tool"/>
<command verb=";print context" line_begin=";"/>
<!--
loop for each hole inside the tool.
Context inside will be filled also with hole's properties(x&y) and
node's parameters
-->
<loop type="toolholes">
<command verb="print" pattern="go_drill"/>
<!--
"print context".
Anwhere in script you can use this verb.
It inserts all context variables available.
Usefull for debug but completely useless for
normal work
-->
<command verb=";print context" line_begin=";"/>
</loop>
</condition>
</loop>
<command verb="print" pattern="finish"/>
</script>
</xml>
На самом деле, ничего сложного нет, если вчитаться. Но давайте разберем посекционно:
В секции variables, как следует из названия, мы можем определить произвольный набор глобальных переменных. Они никак не влияют на работу программы, пока не встретятся в каком-нибудь вычисляемом выражении.
Функции. Ну, точнее, функция. Пока она, предопределенная, одна: вычисление реального диаметра сверла для металлизированных отверстий. Известно, что металлизация крадет диаметр, и это частенько приводит к казусам при попытке просунуть ногу компонента 0,8, которая не лезет в отверстие, заложенное, как 0,9. Чтобы не возиться с этим при проектировании я решил добавить этот функционал.
Смысл этой секции — определить функции, которые может использовать конвертер для определенных целей. Эти функции нельзя (пока?) использовать самостоятельно.
Часть первая. механика.
Некоторое время назад я отложил свой проект по голове для приуса, и вот для чего:
Пока ждал микросхемы, пока эксперементировал со схемами, отчетливо понял, что я хочу изготавливать печатные платы дома. Да-да, есть опыт лазерно-утюжной технологии, и даже рисования лаком, но хотелось чего-то настоящего. Решено было использовать пленочный фоторезист и УФ-лампу для ногтей.
Получается, что ручное сверление отпадает, т.к. ориентироваться просто не по чем (бумажки с картами отверстий давайте не будем предлагать).
Решено было навесить на имеющийся 3D-принтер вместо Direct-головы — шпиндель, и так и жить. И вот тут появились решения, которыми я хотел бы сегодня поделиться.
Заключение
Вот, собственно, постарался вкратце, но максимально полно описать сотворенную тулзу.
Честно говоря, пока пытался представить сценарии использования, я отчетливо представлял себе, сколько раз проявится татаро-монгольское иго на землях русских (есть мнение, что именно они принесли нам мат).
Отсюда вопрос: стоит ли заморочится, и сделать простенькую веб-страничку, куда можно вставить вход и скрипт, и получить готовый G-Code, минуя стадии сборки из исходников?UPD:Спасибо проголосовавшим. Запилил. И… да: я же писал, что веб-страничка будет простенькой? Если кому-нибудь будет не лень привести это все в более эстетический вид — бросайте HTML в личку.