Bonjour, cher habrozhitel.
Aujourd'hui, je veux partager un petit travail de base conçu pour convertir les cartes de perçage PCAD en code G. Flexible, simple et open-source. Vrai, désolé, ospadi, sur Qt. Bien sûr, y écrire est bien, mais déployer et collecter les codes des autres ...
Première partie La mécanique.
Il y a quelque temps, j'ai remis mon projet sur la tête pour un prius, et pour cela:
Pendant que j'attendais des puces, en expérimentant des circuits, j'ai clairement réalisé que je voulais faire des circuits imprimés à la maison. Oui, oui, il y a de l'expérience en technologie de repassage laser, et même en dessin avec du vernis, mais je voulais quelque chose de réel. Il a été décidé d'utiliser un film photosensible et une lampe UV pour les ongles. Naturellement, le problème s'est posé de percer et de plaquer des trous. Et il s'avère que les trous doivent être métallisés avant que les pistes ne soient gravées sur la planche. Sinon, appliquer un courant à chaque trou est une histoire entière.
Il s'avère que le forage manuel disparaît, car il n'y a tout simplement rien à naviguer (n'offrons pas de papier avec des cartes fermées).
Il a été décidé de suspendre une broche sur une imprimante 3D existante au lieu d'une tête directe, et de vivre comme ça. Et ici, il y a des solutions que je voudrais partager aujourd'hui.
Tête interchangeable pour RepRap
Afin de transformer facilement l'imprimante en perceuse et vice versa, il a été décidé de rendre la tête détachable. Le curseur fixé aux ceintures est séparé et tout le reste est amovible. Étant donné que ce n'est pas une bonne nouvelle, quelle difficulté, il est inutile de présenter un article séparé. Voici juste des photos et des liens vers des
modèles STL , si quelqu'un veut faire exactement la même chose. L'archive contient également des sources SLDPRT, si quelque chose à corriger. Il oscille lentement - grâce à l'ADSL de Beltelecom, mais cela devrait être long.
Le résultat est comme ceci:




Tête de broche
Tout est simple ici - après de longues tentatives pour créer ma propre broche, j'ai décidé d'en acheter une sur AliExpress et de la suspendre au support. Pas de photo pendant le processus.
Générateur de code G
Et ici, le plaisir commence.
Avec mon globalisme inhérent, je suis passé en revue les solutions disponibles et j'ai réalisé que chacune d'elles pouvait non seulement créer un tas de problèmes lors du déploiement de la technologie à la maison, mais aussi les livrer régulièrement et méthodiquement jusqu'à la retraite. Qu'est-ce que tu n'as pas aimé? Inflexibilité. Tous sont davantage destinés aux machines, avec des caractéristiques prédéfinies de modèles, etc. Oui, l'affaire n'est pas compliquée. Mais je ne voulais vraiment pas rencontrer un jour une situation où vous devez modifier légèrement l'algorithme et ne pas pouvoir le faire. Par exemple, je n'ai pas vu d'outil capable de faire tourner des trous autour d'un axe. Mais après la métallisation, vous ne pouvez pas poser une carte 1: 1. Mais ce sont des pensées pour l'avenir. Je n'en ai pas encore besoin. Mais c'est déjà possible. En général, je voulais quelque chose de simple, léger, flexible et ... efficace. J'ai décidé de saupoudrer tout seul.
Les bibliothèques Qt 5.11 ont été utilisées comme base. L'application est écrite en style console. L'architecture de l'application est réalisée en style linux.
L'application reçoit un fichier DRL retiré du PCAD lors de la création du Geber-kit. (Vous devrez peut-être modifier l'analyseur si vous voulez lui donner quelque chose d'AltiumDesigner. Mais pour moi, j'ai personnellement décidé de retirer ce monstre Altium du péché. Pour ce qu'il est maintenant dans de terribles rêves et ne me laisse pas oublier mon propre nom).
Un fichier XML est spécifié comme paramètre. Le format de ce fichier sera décrit dans la seconde moitié de l'article. Ce fichier, en effet, détermine le mécanisme de génération du G-Code (et en fait, tout fichier texte) pour le transférer (G-code) vers une imprimante 3D.
Le mécanisme de l'application
- Le format DRL (qui est M48 ou Excellon ) est lu et reconnu. Le résultat est des outils contenant une liste de trous percés avec ces outils.
- Avec les données obtenues à partir de l'élément 1, nous allons au XML, recherchons le nœud de script là-bas et exécutons simplement tout ce qui y est écrit. Il y a cinq opérateurs, mais nous n'en avons pas besoin de plus.
- Pendant l'exécution de l'étape 2, des instructions d'impression se sont produites. Le résultat est imprimé sur le flux de sortie.
Deuxième partie Format de fichier XML
Afin de rendre le programme aussi flexible que possible, la bibliothèque ScriptEngine a été utilisée. Lui-même un peu dépassé par ce qui peut maintenant vraiment être fait avec la configuration. Le postulat principal est le suivant: il existe de nombreux paramètres calculés qui sont traités de la manière la plus transparente possible: le texte est transmis au module ScriptEngine et le résultat est utilisé. La même situation se produit si la combinaison $ {blah blah blah} est trouvée dans le modèle G-Code. De plus, tout ce qui se trouve à l'intérieur des accolades sera transmis au calcul et le modèle entier sera remplacé par le résultat.
Exemple de fichier pour mon imprimante<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>
Et la version du même fichier après des tests pratiques <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>
En fait, il n'y a rien de compliqué, si vous le lisez. Mais analysons section par section:
<variables> <var name=" " value=" "/> </variables>
Dans la section des variables, comme son nom l'indique, nous pouvons définir un ensemble arbitraire de variables globales. Ils n'affectent en rien le fonctionnement du programme tant qu'ils ne se rencontrent pas dans une expression calculée.
<functions> <plate_increase_dia f="a+0.2"/> </functions>
Les fonctions Eh bien, plus précisément, une fonction. Jusqu'à présent, il est prédéterminé, un: calcul du diamètre réel du foret pour les trous métallisés. Il est connu que la métallisation vole le diamètre, ce qui conduit souvent à des incidents lorsque l'on tente de coller la jambe du composant 0.8, qui ne pénètre pas dans le trou posé en 0.9. Afin de ne pas déranger cela lors de la conception, j'ai décidé d'ajouter cette fonctionnalité.
Le sens de cette section est de déterminer les fonctions que le convertisseur peut utiliser à certaines fins. Ces fonctions ne peuvent pas (encore?) Être utilisées indépendamment.
<tools> <tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" /> </tools>
Exercices. Ici, vous devez faire référence à la commande de script «aligner les outils», à propos de laquelle ci-dessous. Chaque élément de cette section définit une cellule dans laquelle tous les outils reconnus dans le fichier d'entrée seront collectés. L'idée est que souvent lors de la conception, des diamètres en pouces se produisent, et de nombreux outils avec leurs valeurs de 0,478 ... 0,492 ... etc. Afin de ne pas les déranger, nous définissons les paramètres
requis range_min et range_max.
Un signe de métallisation est également requis. Les nœuds XML sont analysés séquentiellement et dès que l'outil suivant du DRL correspond à la définition, le nœud est reconnu comme approprié.
Vous pouvez définir tout autre paramètre dans le nœud. Leur valeur peut ensuite être utilisée dans des modèles.
Vous pouvez définir la position dans la trousse ou les coordonnées où capturer la perceuse, si vous avez une machine avec un outil de changement automatique. Et vous pouvez décrire l'outil avec des lettres à afficher sur l'écran de l'imprimante, si vous, comme le mien, avez Marlin et changez manuellement les forets.
<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>
Et maintenant, appréciez tout le charme d'une machine à script! Modèles. Le convertisseur, comme je l'ai déjà dit, fonctionne simplement avec les modèles: il recherche toutes les pièces du formulaire $ {...} et l'envoie à la machine de script. Et il existe un langage de type JS. Par conséquent, en fait, vous pouvez même programmer un peu. Dans cet exemple, vous pouvez voir comment, lors de l'affichage du modèle de départ, nous avons d'abord défini une paire de variables auxquelles des valeurs globales ont été affectées. Eh bien, alors seulement, ils ont écrit une constante, qui sera la valeur de l'exécution de cette pièce.
Lorsque ce modèle est sorti dans le fichier de sortie, nous verrons:
G90 ;Hello M117 Homing G28 XY M117 Move Z to travel G0 X10 Y10 M76 G92 Z10
Eh bien, je ne peux pas me vanter. Évaluez la pièce du modèle pour percer chaque trou:
; Holes rest: ${hcnt--} ; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}% M73 P${100-percent}
oui ... chaque fois que nous tapons un commentaire sur le reste des trous, nous décrémentons la valeur hcnt. Et il, comme nous nous en souvenons, a été déterminé pendant que nous tapions start, et, par conséquent, est situé dans le contexte ci-dessus. Et puis nous calculerons la variable en pourcentage afin de l'utiliser dans une autre pièce par la suite - lors du passage à la commande M73 (cette commande force Marlin à déplacer la barre de progression). G-code généré par ce fragment:
; Holes rest: 6 ; Percent rest: 13% M73 P87
par ailleurs, toolsCount, minX sont des noms prédéfinis de variables globales.
Je note que les noms des modèles ne sont pas prédéfinis, c'est-à-dire vous pouvez en utiliser. Le modèle sera imprimé lorsque la commande d'impression et son nom seront rencontrés dans le script.
<script> <command verb="assign tools" />
Et la base est la section script
Les nœuds avec la commande et la boucle names peuvent être trouvés dans une section.
Format du nœud de
commande :
<command verb=" " .... ... />
Un énoncé d'action est l'un des rares opérateurs. Les options pour chacune sont décrites ci-dessous. Ils peuvent être complétés par d'autres qui, comme vous l'avez déjà compris, peuvent être utilisés dans des modèles.
Format de nœud de boucle:
<loop type=" "> ..... </loop>
une boucle est une section dont le contenu sera exécuté pour chaque élément déterminé par le type de boucle. Il y en a deux (pour l'instant):
tools - une boucle sera exécutée pour chaque outil, et
trous d'outils - un cycle sera exécuté pour chaque trou conçu pour le forage avec cet outil. Évidemment, la
boucle de trous d' outils ne peut être imbriquée que dans des
outils .
Dans le même temps, lors de l'exécution d'une boucle imbriquée, toutes les variables de l'outil actuel sont disponibles. Pourquoi? Je ne sais pas. Je viens de le dire.
Les opérateurs
attribuer des outilsOptions: aucune.
Attribue chaque exercice à partir du fichier d'outil source de XML. Sans cela, la plupart des autres actions n'ont aucun sens.
rejoindre les outilsOptions: aucune.
Plus organisationnel - combine tous les outils qui ont été affectés de la même manière à partir du fichier XML. Cela a du sens juste après les outils d'affectation, mais j'ai décidé de donner à l'utilisateur la possibilité de faire ses opérations.
outils de triParamètres: aucun (encore).
Trie les forets en augmentant les diamètres
décalageParamètres:
xoffs, yoffs - valeurs de décalage. La machine de script est en cours d'exécution.
Décale tous les trous aux valeurs spécifiées. Oui, il arrive souvent que la planche ne soit pas très éloignée à l'origine.
imprimerParamètre:
motif Le nom du motif.
Imprime un modèle avec le nom spécifié sur le flux de sortie.
contexte d'impressionParamètres:
line_begin, line_end - le début et la fin de chaque ligne.
Débogage - vous permet de sortir toutes les variables actuellement disponibles et leurs valeurs n'importe où dans la sortie. Chaque variable est affichée sur une ligne distincte, le début et la fin sont spécifiés dans les paramètres
Noms prédéfinis de variables globales.
trousCompte, outilsCompte - J'espère vraiment, vraiment, vraiment que la signification de ces variables n'a besoin d'aucune explication. Oui oui C'est le nombre d'outils et le nombre de trous.
minX, maxX, minY, maxY - et ceux-là aussi. Non, eh bien, juste au cas où, ce sont les coordonnées du champ de forage. Tous les trous sont à l'intérieur de ce rectangle. Recalculé après la commande de décalage.
Conclusion
Ici, en fait, j'ai essayé de décrire brièvement, mais aussi complètement que possible la tulza créée.
Honnêtement, alors que j'essayais d'imaginer des scénarios d'utilisation, j'ai imaginé clairement combien de fois le joug tatar-mongol apparaîtrait sur les terres russes (on pense qu'ils nous ont apporté le tapis).
D'où la question: vaut-il la peine de se confondre et de créer une page Web simple où vous pouvez insérer l'entrée et le script, et obtenir le G-Code fini, en contournant les étapes d'assemblage de la source?UPD:
Merci à ceux qui ont voté.
Je l'ai lavé . Et ... oui: j'ai écrit que la page web sera simple? Si quelqu'un n'est pas trop paresseux pour mettre tout cela dans un look plus esthétique - jetez HTML en PM.