Hola querido habrozhitel.
Hoy quiero compartir una pequeña base diseñada para convertir las tarjetas de perforación PCAD-s en código G. Flexible, simple y de código abierto. Es cierto, lo siento, Ospadi, en Qt. Escribir en él, por supuesto, es bueno, pero desplegar y recopilar códigos de otras personas ...
Primera parte La mecanica.
Hace algún tiempo, pospuse mi proyecto en la cabeza por un prius, y para esto:
Mientras esperaba chips, mientras experimentaba con circuitos, me di cuenta claramente de que quería hacer placas de circuito impreso en casa. Sí, sí, hay experiencia en tecnología de planchado por láser e incluso en dibujos con barniz, pero quería algo real. Se decidió utilizar una película fotorresistente y una lámpara UV para uñas. Naturalmente, el problema surgió de la perforación y el enchapado de agujeros. Y a partir de ahí resulta que los agujeros deben ser metalizados antes de que las pistas se graben en el tablero. De lo contrario, aplicar una corriente a cada hoyo es una historia completa.
Resulta que la perforación manual desaparece, porque simplemente no hay nada que navegar (no ofrezcamos papel con cartas ocultas).
Se decidió colgar un huso en una impresora 3D existente en lugar de un cabezal directo, y vivir así. Y aquí hay soluciones que me gustaría compartir hoy.
Cabezal intercambiable para RepRap
Para convertir fácilmente la impresora en un taladro y viceversa, se decidió desmontar el cabezal. El control deslizante fijado a los cinturones está separado, y todo lo demás es extraíble. Dado que estas no son buenas noticias, qué dificultad, no tiene sentido presentar un artículo separado. Aquí hay solo fotos y enlaces a
modelos STL , si alguien quiere hacer exactamente lo mismo. El archivo también contiene fuentes SLDPRT, si hay algo que arreglar. Se balancea lentamente, gracias a ADSL de Beltelecom, pero debería ser mucho tiempo.
El resultado es así:




Cabeza del huso
Aquí todo es simple: después de largos intentos de crear mi propio husillo, decidí comprar uno en AliExpress y simplemente colgarlo en el soporte. No hay foto, mientras que en el proceso.
Generador de código G
Y aquí comienza la diversión.
Con mi globalismo inherente, revisé las soluciones disponibles y me di cuenta de que cada una de ellas no solo puede crear un montón de problemas al implementar tecnología en el hogar, sino también entregarlos de manera regular y metódica, hasta la jubilación. ¿Qué no te gustó? Inflexibilidad Todos ellos son más para máquinas, con características predefinidas de plantillas, etc. Sí, el asunto no es complicado. Pero realmente no quería un día encontrarme con una situación en la que debas modificar ligeramente el algoritmo y no poder hacerlo. Por ejemplo, no he visto una herramienta que pueda rotar agujeros alrededor de un eje. Pero después de la metalización, no puede colocar un tablero 1: 1. Pero estos son pensamientos para el futuro. No lo necesito todavía. Pero ya es posible. En general, quería algo simple, ligero, flexible y ... eficiente. Decidí rociar por mi cuenta.
Las bibliotecas Qt 5.11 se usaron como base. La aplicación está escrita en estilo de consola. La arquitectura de la aplicación está hecha en estilo Linux.
La aplicación recibe un archivo DRL extraído del PCAD al crear el kit Geber. (Puede que tengas que modificar el analizador si quieres alimentarlo con AltiumDesigner. Pero por mí mismo, personalmente decidí alejar a este monstruo Altium del pecado. Por lo que ahora está en sueños terribles y no me deja olvidar mi propio nombre).
Se especifica un archivo XML como parámetro. El formato de este archivo se describirá en la segunda mitad del artículo. Este archivo, de hecho, determina el mecanismo para generar el código G (y, de hecho, cualquier archivo de texto) para transferirlo (código G) a una impresora 3D.
El mecanismo de la aplicación.
- El formato DRL (que es M48 o Excellon ) se lee y se reconoce. El resultado son herramientas que contienen una lista de agujeros que se taladran con estas herramientas.
- Con los datos obtenidos del elemento 1, vamos a XML, buscamos el nodo del script allí y simplemente ejecutamos todo lo que está escrito allí. Hay cinco operadores, pero no necesitamos más.
- Durante la ejecución del paso 2, se produjeron declaraciones de impresión. El resultado se imprime en la secuencia de salida.
Segunda parte Formato de archivo XML
Para que el programa sea lo más flexible posible, se utilizó la biblioteca ScriptEngine. Un poco abrumado por lo que ahora realmente se puede hacer con la configuración. El postulado principal es este: hay muchos parámetros calculados que se manejan de la manera más transparente posible: el texto se pasa al módulo ScriptEngine y se utiliza el resultado. La misma situación ocurre si la combinación $ {blah blah blah} se encuentra en la plantilla G-Code. Además, todo lo que esté dentro de las llaves se pasará al cálculo y el resultado reemplazará toda la plantilla.
Archivo de muestra para mi impresora<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>
Y la versión del mismo archivo después de pruebas prácticas. <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 hecho, no hay nada complicado, si lo lees. Pero analicemos sección por sección:
<variables> <var name=" " value=" "/> </variables>
En la sección de variables, como su nombre lo indica, podemos definir un conjunto arbitrario de variables globales. No afectan la operación del programa de ninguna manera hasta que se encuentran en alguna expresión calculada.
<functions> <plate_increase_dia f="a+0.2"/> </functions>
Las funciones Bueno, más precisamente, una función. Hasta ahora, está predeterminado, uno: el cálculo del diámetro real del taladro para agujeros metalizados. Se sabe que la metalización roba el diámetro, y esto a menudo conduce a incidentes cuando se intenta pegar la pata del componente 0.8, que no entra en el orificio establecido como 0.9. Para no molestarme con esto al diseñar, decidí agregar esta funcionalidad.
El significado de esta sección es determinar las funciones que el convertidor puede usar para ciertos propósitos. Estas funciones no se pueden (¿todavía?) Usar independientemente.
<tools> <tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" /> </tools>
Taladros Aquí debe hacer una referencia al comando de script "alinear herramientas", sobre el cual se detalla a continuación. Cada elemento de esta sección define una celda en la que se recopilarán todas las herramientas reconocidas en el archivo de entrada. La idea es que, a menudo, al diseñar, suceden diámetros de pulgadas y muchas herramientas con sus valores de 0.478 ... 0.492 ... etc. Para no meterse con ellos, establecemos los parámetros
requeridos range_min y range_max. También se requiere
un signo de metalización. Los nodos XML se escanean secuencialmente y, tan pronto como la siguiente herramienta del DRL se ajusta a la definición, el nodo se considera adecuado.
Puede establecer cualquier otro parámetro en el nodo. Su valor puede luego ser usado en plantillas.
Puede establecer la posición en la caja de lápices o las coordenadas donde capturará el taladro, si tiene una máquina con una herramienta de cambio automático. Y puede describir la herramienta con letras para mostrar en la pantalla de la impresora, si usted, como el mío, tiene Marlin y cambio 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>
¡Y ahora, aprecia todo el encanto de una máquina de guiones! Plantillas El convertidor, como ya dije, funciona con plantillas simplemente: busca todas las partes del formulario $ {...} y lo envía a la máquina de script. Y hay un lenguaje similar a JS. Por lo tanto, de hecho, incluso puedes programar un poco. En este ejemplo, puede ver cómo, al mostrar el patrón de inicio, primero definimos un par de variables a las que se les asignaron valores globales. Bueno, solo entonces escribieron una constante, que será el valor de la ejecución de esta pieza.
Cuando esta plantilla se envíe al archivo de salida, veremos:
G90 ;Hello M117 Homing G28 XY M117 Move Z to travel G0 X10 Y10 M76 G92 Z10
Bueno, no puedo presumir. Califique la pieza de la plantilla para perforar cada agujero:
; Holes rest: ${hcnt--} ; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}% M73 P${100-percent}
sí ... cada vez que escribimos un comentario sobre Holes rest, disminuiremos el valor de hcnt. Y, como recordamos, se determinó mientras escribíamos start y, por lo tanto, se encuentra en el contexto anterior. Y luego calcularemos la variable de porcentaje para usarla en otra pieza después, al pasarla al comando M73 (este comando obliga al marlin a mover la barra de progreso). Código G generado por este fragmento:
; Holes rest: 6 ; Percent rest: 13% M73 P87
Por cierto, toolsCount, minX son nombres predefinidos de variables globales.
Observo que los nombres de las plantillas no están predefinidos, es decir puedes usar cualquiera. La plantilla se imprimirá cuando el comando de impresión y su nombre se encuentren en el script.
<script> <command verb="assign tools" />
Y la base es la sección del script
Los nodos con el comando de nombres y el bucle se pueden encontrar dentro de una sección.
Formato de nodo de
comando :
<command verb=" " .... ... />
Una declaración de acción es uno de los pocos operadores. Las opciones para cada uno se describen a continuación. Pueden complementarse con otros que, como ya entendió, pueden usarse en plantillas.
Formato de nodo de bucle:
<loop type=" "> ..... </loop>
un bucle es una sección cuyo contenido se ejecutará para cada elemento determinado por el tipo de bucle. Hay dos de ellos (por ahora):
herramientas : se ejecutará un bucle para cada herramienta y
agujeros de herramientas : se ejecutará un ciclo para cada agujero diseñado para perforar con esta herramienta. Obviamente, el
bucle de agujeros de herramientas solo se puede anidar en
herramientas .
Además, al ejecutar un bucle anidado, todas las variables para la herramienta actual están disponibles. Por qué No lo se Solo lo dije.
Operadores
asignar herramientasOpciones: ninguna.
Asigna cada ejercicio del archivo de herramienta de origen desde XML. Sin ella, la mayoría de las otras acciones no tienen sentido.
unir herramientasOpciones: ninguna.
Más organizacional: combina todas las herramientas que se han asignado del mismo archivo XML. Tiene sentido justo después de las herramientas de asignación, pero decidí dejar que el usuario haga sus operaciones.
herramientas de clasificaciónParámetros: ninguno (todavía).
Clasifica los ejercicios aumentando los diámetros
compensaciónParámetros:
xoffs, yoffs : valores de desplazamiento. La máquina de script se está ejecutando.
Desplaza todos los agujeros a los valores especificados. Sí, a menudo sucede que el tablero no está muy separado en el origen.
imprimirParámetro:
patrón El nombre del patrón.
Imprime una plantilla con el nombre especificado en la secuencia de salida.
contexto de impresiónParámetros:
line_begin, line_end : el principio y el final de cada línea.
Lo de depuración: le permite generar todas las variables disponibles actualmente y sus valores en cualquier parte de la salida. Cada variable se muestra en una línea separada, el principio y el final se especifican en los parámetros
Nombres predefinidos de variables globales.
holesCount, toolsCount - Realmente, realmente, realmente espero que el significado de estas variables no necesite explicación. Si si. Este es el número de herramientas y el número de agujeros.
minX, maxX, minY, maxY , y estos también. No, bueno, por si acaso, estas son las coordenadas del campo de perforación. Todos los agujeros están dentro de este rectángulo. Recalculado después del comando de desplazamiento.
Conclusión
Aquí, de hecho, intenté describir brevemente, pero lo más completamente posible, la tulza creada.
Honestamente, mientras intentaba imaginar escenarios de uso, claramente imaginé cuántas veces aparecería el yugo tártaro-mongol en tierras rusas (se cree que nos trajeron la estera).
De ahí la pregunta: ¿vale la pena confundirse y crear una página web simple donde pueda insertar la entrada y el script, y obtener el código G terminado, sin pasar por las etapas de ensamblaje de la fuente?UPD:
Gracias a quienes votaron.
Lo lavé . Y ... sí: ¿escribí que la página web será simple? Si alguien no es demasiado vago para darle un aspecto más estético, agregue HTML en PM.