Olá, querido habrozhitel.
Hoje, quero compartilhar um pouco de base projetada para converter os cartões de perfuração PCAD-s em código G. Flexível, simples e de código aberto. Verdade, desculpe, ospadi, no Qt. É claro que escrever nele é bom, mas implantar e coletar códigos de outras pessoas ...
Parte I A mecânica
Há algum tempo, adiei meu projeto na cabeça por um prius, e por isso:
Enquanto esperava chips, enquanto experimentava circuitos, percebi claramente que queria fazer placas de circuito impresso em casa. Sim, sim, existe experiência na tecnologia de passar a laser e até mesmo desenhar com verniz, mas eu queria algo real. Decidiu-se usar um filme fotorresistente e uma lâmpada UV para unhas. Naturalmente, surgiu o problema de fazer furos e galvanizar. E a partir daí, os furos precisam ser metalizados antes das faixas serem gravadas no quadro. Caso contrário, aplicar uma corrente a cada furo é uma história completa.
Acontece que a perfuração manual desaparece, porque simplesmente não há nada para navegar (não vamos oferecer papel com cartões de visita).
Decidiu-se pendurar um eixo em uma impressora 3D existente em vez de uma cabeça direta e viver assim. E aqui há soluções que eu gostaria de compartilhar hoje.
Cabeça intercambiável para RepRap
Para transformar facilmente a impressora em uma broca e vice-versa, foi decidido tornar a cabeça destacável. O controle deslizante preso aos cintos é separado e todo o resto é removível. Dado que isso não é uma boa notícia, que dificuldade, não faz sentido elaborar um artigo separado. Aqui estão apenas fotos e links para
modelos STL , se alguém quiser fazer exatamente o mesmo. O arquivo morto também contém fontes SLDPRT, se houver algo para corrigir. Ele oscila lentamente - graças ao ADSL da Beltelecom, mas deve demorar muito.
O resultado é assim:




Cabeça do eixo
Tudo é simples aqui - depois de longas tentativas de criar meu próprio eixo, decidi comprar um no AliExpress e pendurá-lo no suporte. Nenhuma foto durante o processo.
Gerador de código G
E aqui começa a diversão.
Com o meu globalismo inerente, examinei as soluções disponíveis e percebi que cada uma delas não apenas criava um monte de problemas ao implantar a tecnologia em casa, mas também as fornecia regularmente e metodicamente, até a aposentadoria. Do que você não gostou? Inflexibilidade. Todos eles são mais para máquinas, com características predefinidas de modelos, etc. Sim, o assunto não é complicado. Mas eu realmente não queria encontrar um dia uma situação em que você precise modificar levemente o algoritmo e não possa fazê-lo. Por exemplo, eu não vi uma ferramenta que possa girar furos ao redor de um eixo. Mas depois da metalização, você não pode colocar uma placa 1: 1. Mas esses são pensamentos para o futuro. Ainda não preciso disso. Mas já é possível. Em geral, eu queria algo simples, leve, flexível e ... eficiente. Eu decidi polvilhar por conta própria.
As bibliotecas Qt 5.11 foram usadas como base. O aplicativo é escrito no estilo do console. A arquitetura do aplicativo é feita no estilo linux.
O aplicativo recebe um arquivo DRL retirado do PCAD ao criar o kit Geber. (Talvez você precise modificar o analisador se quiser alimentar algo do AltiumDesigner. Mas, pessoalmente, eu decidi tirar esse monstro do Altium do pecado. Pois agora ele está em sonhos terríveis e não me deixa esquecer o meu próprio nome).
Um arquivo XML é especificado como um parâmetro. O formato deste arquivo será descrito na segunda metade do artigo. Este arquivo, de fato, determina o mecanismo para gerar o Código G (e de fato qualquer arquivo de texto) para transferi-lo (Código G) para uma impressora 3D.
O mecanismo da aplicação
- O formato DRL (que é M48 ou Excellon ) é lido e reconhecido. O resultado são ferramentas que contêm uma lista de furos que são perfurados com essas ferramentas.
- Com os dados obtidos no item 1, vamos para XML, procuramos o nó de script lá e simplesmente executamos tudo o que está escrito lá. Existem cinco operadores, mas não precisamos de mais.
- Durante a execução da etapa 2, ocorreram instruções de impressão. O resultado é impresso no fluxo de saída.
Parte Dois Formato de arquivo XML
Para tornar o programa o mais flexível possível, foi utilizada a biblioteca ScriptEngine. Ele mesmo um pouco impressionado com o que agora pode realmente ser feito com a configuração. O postulado principal é o seguinte: existem muitos parâmetros calculados que são tratados da maneira mais transparente possível: o texto é passado para o módulo ScriptEngine e o resultado é usado. A mesma situação ocorre se a combinação $ {blá blá blá} for encontrada no modelo de código G. Além disso, tudo dentro dos colchetes será passado para o cálculo, e todo o modelo será substituído pelo resultado.
Arquivo de exemplo para minha impressora<xml> <variables> <var name="ZChangeToolValue" value="30"/> <var name="ZTravelValue" value="10"/> <var name="ZDrillValue" value="0"/> </variables> <functions> <plate_increase_dia f="a+0.2"/> </functions> <tools> <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> <pattern name="start"> G90 ;${var hcnt=holesCount;var tcnt=toolsCount;"Hello"} M117 Homing G28 XY 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> <command verb="assign tools" /> <command verb="join tools" /> <command verb="offset" xoffs="-minX+10" yoffs="-minY+10"/> <command verb="print" pattern="start"/> <loop type="tools"> <command verb="print" pattern="set_tool"/> <command verb="print context" line_begin=";"/> <loop type="toolholes"> <command verb="print" pattern="go_drill"/> <command verb="print context" line_begin=";"/> </loop> </loop> <command verb="print" pattern="finish"/> </script> </xml>
E a versão do mesmo arquivo após testes práticos <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> <plate_increase_dia f="a+0.3"/> </functions> <tools> <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> <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 XY 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> <command verb="assign tools" /> <command verb="join tools" /> <command verb="offset" xoffs="-minX+StartOffsX" yoffs="-minY+StartOffsY"/> <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 type="toolholes"> <command verb="print" pattern="go_drill"/> <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 type="toolholes"> <command verb="print" pattern="go_drill"/> <command verb=";print context" line_begin=";"/> </loop> </condition> </loop> <command verb="print" pattern="finish"/> </script> </xml>
De fato, não há nada complicado, se você ler. Mas vamos analisar seção por seção:
<variables> <var name=" " value=" "/> </variables>
Na seção de variáveis, como o nome indica, podemos definir um conjunto arbitrário de variáveis globais. Eles não afetam a operação do programa de forma alguma até que se encontrem em alguma expressão calculada.
<functions> <plate_increase_dia f="a+0.2"/> </functions>
Funções Bem, mais precisamente, uma função. Até o momento, é predeterminado um: o cálculo do diâmetro real da broca para furos metalizados. Sabe-se que a metalização rouba o diâmetro, e isso muitas vezes leva a incidentes ao tentar furar a perna do componente 0.8, que não entra no furo definido como 0,9. Para não me incomodar com isso ao projetar, decidi adicionar essa funcionalidade.
O significado desta seção é determinar as funções que o conversor pode usar para determinados fins. Essas funções não podem ser (ainda?) Usadas independentemente.
<tools> <tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" /> </tools>
Exercícios. Aqui você precisa fazer uma referência ao comando de script "align tools", sobre o qual abaixo. Cada elemento desta seção define uma célula na qual todas as ferramentas reconhecidas no arquivo de entrada serão coletadas. A idéia é que, muitas vezes, ao projetar, ocorrem diâmetros em polegadas e muitas ferramentas com valores de 0,478 ... 0,492 ... etc. Para não mexer com eles, definimos os parâmetros
necessários range_min e range_max.
Um sinal de metalização também é necessário. Os nós XML são varridos sequencialmente e, assim que a próxima ferramenta do DRL se encaixa na definição, o nó é reconhecido como adequado.
Você pode definir quaisquer outros parâmetros no nó. Seu valor pode ser usado posteriormente em modelos.
Você pode definir a posição no estojo ou nas coordenadas onde capturar a broca, se tiver uma máquina com uma ferramenta de troca automática. E você pode descrever a ferramenta com letras para exibição na tela da impressora, se você, como o meu, tiver o Marlin e a troca manual de brocas.
<patterns> <pattern name="start"> G90 ;${var hcnt=holesCount;var tcnt=toolsCount;"Hello"} M117 Homing G28 XY M117 Move Z to travel G0 X${minX} Y${minY} M76 G92 Z${ZTravelValue} </pattern>
E agora, aprecie todo o charme de uma máquina de script! Templates. O conversor, como eu já disse, trabalha com modelos simplesmente: ele procura todas as partes do formulário $ {...} e o envia para a máquina de script. E existe uma linguagem semelhante a JS. Portanto, de fato, você pode até programar um pouco. Neste exemplo, você pode ver como, ao exibir o padrão inicial, primeiro definimos um par de variáveis aos quais foram atribuídos valores globais. Bem, só então eles escreveram uma constante, que será o valor da execução desta peça.
Quando este modelo é enviado para o arquivo de saída, veremos:
G90 ;Hello M117 Homing G28 XY M117 Move Z to travel G0 X10 Y10 M76 G92 Z10
Bem, eu não posso me gabar. Classifique a peça do modelo para perfurar cada furo:
; Holes rest: ${hcnt--} ; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}% M73 P${100-percent}
sim ... toda vez que digitarmos um comentário no Holes rest, diminuiremos o valor hcnt. E, como lembramos, foi determinado enquanto digitamos start e, portanto, está localizado no contexto acima. E então calcularemos a variável percentual para usá-la em outra parte posteriormente - ao passar para o comando M73 (este comando força o marlin a mover a barra de progresso). Código G gerado por este fragmento:
; Holes rest: 6 ; Percent rest: 13% M73 P87
a propósito, toolsCount, minX são nomes predefinidos de variáveis globais.
Observo que os nomes dos modelos não são predefinidos, ou seja, você pode usar qualquer. O modelo será impresso quando o comando print e seu nome forem encontrados no script.
<script> <command verb="assign tools" />
E a base é a seção de script
Nós com o comando e loop de nomes podem ser encontrados dentro de uma seção.
Formato do nó de
comando :
<command verb=" " .... ... />
Uma declaração de ação é um dos poucos operadores. As opções para cada um são descritas abaixo. Eles podem ser complementados por outros que, como você já entendeu, podem ser usados em modelos.
Formato do nó de loop:
<loop type=" "> ..... </loop>
um loop é uma seção cujo conteúdo será executado para cada elemento determinado pelo tipo de loop. Existem dois deles (por enquanto):
ferramentas - um loop será executado para cada ferramenta e
furos de ferramentas - um ciclo será executado para cada furo projetado para perfurar com esta ferramenta. Obviamente, o
loop dos
buracos das
ferramentas só pode ser aninhado em
ferramentas .
Ao mesmo tempo, ao executar um loop aninhado, todas as variáveis da ferramenta atual estão disponíveis. Porque Eu não sei Acabei de dizer.
Operadores
atribuir ferramentasOpções: nenhuma.
Atribui cada drill do arquivo da ferramenta de origem do XML. Sem ele, a maioria das outras ações não faz sentido.
juntar ferramentasOpções: nenhuma.
Mais organizacional - combina todas as ferramentas que foram atribuídas da mesma forma no arquivo XML. Faz sentido logo após as ferramentas de atribuição, mas decidi dar ao usuário a oportunidade de realizar suas operações.
ferramentas de classificaçãoParâmetros: nenhum (ainda).
Classifica as brocas aumentando os diâmetros
deslocamentoParâmetros:
xoffs, yoffs - valores de deslocamento. A máquina de scripts está em execução.
Muda todos os furos para os valores especificados. Sim, muitas vezes acontece que o conselho não está muito distante na origem.
impressãoParâmetro:
padrão O nome do padrão.
Imprime um modelo com o nome especificado no fluxo de saída.
contexto de impressãoParâmetros:
line_begin, line_end - o início e o fim de cada linha.
Coisa de depuração - permite gerar todas as variáveis disponíveis no momento e seus valores em qualquer lugar da saída. Cada variável é exibida em uma linha separada, o início e o fim são especificados nos parâmetros
Nomes predefinidos de variáveis globais.
holesCount, toolsCount - Eu realmente espero que o significado dessas variáveis não precise de explicação. Sim sim Este é o número de ferramentas e o número de furos.
minX, maxX, minY, maxY - e estes também. Não, bem, apenas no caso, essas são as coordenadas do campo de perfuração. Todos os furos estão dentro desse retângulo. Recalculado após o comando de deslocamento.
Conclusão
Aqui, de fato, tentei descrever brevemente, mas o mais completamente possível, descrever a tulza criada.
Honestamente, enquanto tentava imaginar cenários de uso, imaginei claramente quantas vezes o jugo tártaro-mongol apareceria em terras russas (acredita-se que eles nos trouxessem o tapete).
Daí a pergunta: vale a pena ficar confuso e criar uma página da Web simples onde você possa inserir a entrada e o script e obter o código G finalizado, ignorando os estágios de montagem da fonte?UPD:
Obrigado a quem votou.
Eu lavei tudo . E ... sim: eu escrevi que a página da web será simples? Se alguém não estiver com preguiça de trazer tudo isso para uma aparência mais estética - coloque HTML no PM.