3D-modeling met OpenSCAD
Ontwerp zelf met OpenSCAD een model voor 3D-printing
Als je kunt programmeren – in welke taal dan ook – ben je gewend te werken met variabelen, loops en functies. Bij OpenSCAD kun je die kennis gebruiken om snel een eigen 3Dmodel in elkaar te zetten. Een lange inwerktijd is daarbij niet aan de orde. We bespreken de eerste stappen met de CAD-programmeertaal aan de hand van een 3D-geprinte versteviging voor Apples Lightning-kabel.
Aangeklikt, formaat getrokken, waarde ingevoerd. En er hangt een kubus ergens aan een kant – en helaas ook verkeerd om. GUI's van CAD-programma's scheppen voor instappers vaak de nodige verwarring. De situatie wordt er niet beter op als je overstapt op het modelling-programma Blender. Zonder sneltoetsen kun je daar immers helemaal niets beginnen. Hoe kom je dan wel snel aan een model dat je thuis op je eigen 3D-printer kunt maken?
Ongeacht voor welke methode je kiest, je zult altijd iets moeten leren. Maar als je al wat wegwijs bent in een programmeertaal, kun je wel tijd besparen. Je programmeert bij OpenSCAD namelijk in een taal die op C lijkt. Ook kun je daar je eigen programmeerexpertise in meenemen. Welke taal je beheerst, is niet zo belangrijk. De syntaxis van OpenSCAD is snel en makkelijk onder de knie te krijgen.
De interface is simpel en overzichtelijk. Links staat een teksteditor voor de broncode, rechts een preview die je met de muis kunt draaien. Rechtsonder maakt een consolevenster melding van syntactische fouten. Dat is alles.
Zo'n spartaanse interface heeft een doorslaggevend voordeel: zelfs als je nooit van te voren met CAD-software hebt gewerkt, kun je extreem snel je weg vinden. Alle ingewikkelde functies zitten in de programmeertaal waarin je de modellen definieert – hiervoor bestaat ook een uitgebreid WikiBook (zie de link aan het eind van het artikel). Als je dus alleen wat boorgaten in enkele vierkanten wilt aanbrengen, kijk je gewoon bij het betreffende gedeelte van de taal en negeer je de rest van wat OpenSCAD verder nog kan. Je bent dus erg snel ingewerkt.
Om nog sneller van start te kunnen moet je gewoon dit artikel lezen. We hebben hier als voorbeeld een oplossing voor een reëel praktijkgeval gevonden. Aan de hand daarvan bespreken we de belangrijkste elementen van de taal.
Knikpunt en kabelbreuk
De originele Lightning-kabel van Apple, waarmee je een iPhone op een usb-lader aansluit, loopt vaak al na korte tijd direct achter het stekkertje kabelbreukjes op. Bij alledaags gebruik wordt de kabel op die plek namelijk vaak een beetje gebogen. Apple heeft tegen deze mechanische belasting nog geen echte maatregelen genomen. Een oplossing is een versteviging met rubberen ribbetjes die vaak bij de uiteinden van stekkers van andere merken te vinden is. Als de kabel dan wordt gebogen, bewegen de ribbetjes zodanig naar elkaar toe dat je de kabel nog hooguit een beetje kunt buigen. De draadjes aan de binnenkant worden daarmee niet uitgerekt en ook de kabel zelf blijft goed.
Je kunt zo'n versteviging ook maken met een consumentenmodel 3D-printer – zover het apparaat tenminste flexibel filament kan verwerken. Met name 3D-printers met zogeheten Direct Drive-extruders zijn bijzonder geschikt voor flexibel filament. De extruder-motor zit daarbij direct boven de printspuit. Daarnaast is het handig als de printer filament met een diameter van 3 mm (in plaats van 1,75 mm) kan verwerken. Het dikkere filament verbuigt dan minder en de wrijving in de extruder blijft daarmee iets kleiner. We hebben in deze test rood 3 mm InnoFlex 45 op een Ultimaker 2 Plus geprint.
Maar je kunt ook gebruikmaken van ander flexibel filament of een andere printer. De keuze is namelijk reuze.
Optellen en aftrekken
OpenSCAD bevat standaard al heel wat basisvormen als vierkanten, cilinders en bollen. Je kunt die op maat aanpassen via parameters als hoogte, breedte, diameter etc. Designs zijn gebaseerd op het feit dat vormen worden samengevoegd (optellen of verenigen) of dat de ene vorm uit de andere wordt gehaald (aftrekken of differentie). Zo vormt een plat vierkant de basis voor een plaat met gaatjes. De gaatjes zijn cilinders die we er van 'aftrekken'.
Voor onze versteviging hebben we twee objecten geprogrammeerd. Het ene is een grof cilindervormige versteviging tegen knikken zonder uitsparing in het midden. Het andere is een deel van de Lightningstekker met kabel, zodat we deze vorm van de versteviging kunnen aftrekken. Je krijgt daarmee uiteindelijk een object met een uitsparing die exact de vorm van de stekker heeft. Om ervoor te zorgen dat we het object ook kunnen printen, moeten we het in tweeën delen en op de printtafel leggen. We hebben het deel dan ook gekloond en telkens de onderste helft verwijderd door daar een groter vierkant af te halen.
Variabelen en vectoren
Omdat OpenSCAD een programmeertaal is, kun je er variabelen mee definiëren. Dat kan erg handig zijn, omdat juist bij 3D-printen niet altijd duidelijk is welke toleranties je moet inbouwen. En die heb je nodig om onderdelen exact passend te maken. Bij een geprinte test werd duidelijk waar het verkeerd ging. Het is alleen niet erg leuk om de code op alle plekken met een verkeerde afmeting te moeten nalopen. Om die reden definiëren CAD-programmeurs voor de belangrijkste waarden zelf nuttige variabelen:
stekkerbreedte = 7.2; stekkerhoogte = 4.6; stekkerlengte = 12;
Uiteraard kun je met zulke variabelen ook rekenen. Met sqrt(2)*dikte/2 bereken je bijvoorbeeld de helft van de dikte vermenigvuldigd met wortel twee. Met zulke formules zorg je ervoor dat het complete object slechts van weinig basiswaarden afhangt. Ook hoef je er geen rekenmachine bij te nemen. Om ruimte te besparen, hebben we in onze broncode echter direct een getal gezet op de plek waar de afmetingen steeds hetzelfde zijn.
OpenSCAD beschouwt drie waarden tussen vierkante haken als een element van een 3D-vector. Zo kun je met vectoren een vierkant definiëren (cube([breedte, diepte, hoogte])) of een verplaatsing (translate([x, y, z])) of een draaiing (rotate([draai_om_x, draai_om_y, draai_om_z]).
Daarnaast worden met de vierkante haken lijsten gedefinieerd die je zo lang kunt maken als je wilt:
hoek_lijst = [-5, 45, 90, 180, 360];
Bij alle waarden zijn de lengtes in millimeters en hoeken in graden.
Schuiven en lussen
Het kunststofdeel van de Lightning-stekker is 7 mm breed, maar slechts 4,5 mm dik. De randen zijn rond. De rondingen komen steeds overeen met een halve cilinder van 4,5 mm doorsnede. Met de volgende regel definieer je een cilinder met constante diameter:
cylinder(d=stekkerhoogte,
h=stekkerlengte, $fn=32);
OpenSCAD kan geen echte rondingen weergeven. Met de parameter $fn=32 maak je echter een ronding van 32 regelmatige hoeken die erg lijkt op de cirkelvormige randen van een cilinder. Je kunt dezelfde parameter gebruiken als je in het printgedeelte een verdieping, voor bijvoorbeeld een moer, wilt aanbrengen: met $fn=6 definieer je dan een cilinder.
Voor de rondingen van de stekker zou je twee van zulke cilinders kunnen definiëren en met translate() gelijkmatig naar links en rechts verplaatsen. Geformuleerd in een loop kun je jezelf op die manier heel wat werk besparen:
for(x=[-1, 1]) { translate([x * (stekkerbreedte / 2 -
stekkerhoogte / 2), 0, 0]) { Als meerdere vormen in elkaar zitten, versmelten ze tot een geheel. cylinder(d=stekkerhoogte,
h=stekkerlengte, $fn=32);
De lus itereert het lijstje. Ofwel: translate() en cylinder() worden twee keer uitgevoerd. Variabele x heeft daarbij eerst -1 als waarde en daarna 1. Door de verplaatsing (stekkerbreedte / 2 – stekkerhoogte / 2) leveren de buitenste randen van de twee cilinders op de breedste plek als resultaat exact de stekkerbreedte op.
Om de vorm te laten uitzien als een Lightning-stekker, vult een vierkant nog de ruimte ertussen:
translate([-(stekkerbreedte / 2–
stekkerhoogte / 2), -stekkerhoogte / 2, 0]) cube([stekkerbreedte - stekkerhoogte,
stekkerhoogte, stekkerlengte]);
translate() is hier noodzakelijk, omdat een vierkant wordt gedefinieerd vanuit een hoek. Cilinders verrijzen juist om hun basis vanuit het middelpunt van de cirkel.
In de complete broncode versmelt de union()-functie de drie objecten tot een.
Convexe conus
De basisvorm van de versteviging is rond en dus gebaseerd op een cilinder. Om een rand aan het eind van de versteviging te voorkomen (alwaar de kabel weer zou kunnen knikken) moet de cilinder bij de kabel worden afgestompt. Dit wordt in de code gedefinieerd met de parameters d1 en d2. De parameters worden in plaats van d doorgegeven aan de cylinder()-functie. d1 legt daarbij de diameter voor de onderkant vast en d2 aan het andere uiteinde van de cilinder. Zodoende ontstaat een stompvorm. Om esthetische redenen wilden we het deel aan de stekker met een constante diameter modelleren.
Aan het uiteinde van de stekker maakten we een afkanting. De basisvorm wordt daarmee gerealiseerd uit drie cylinder()-objecten:
union() { cylinder(d1=diameter - 2, d2=diameter, h=1, $fn=64); translate([0, 0, 1])
cylinder(d=diameter,h=5,$fn=64); translate([0, 0, 6]) cylinder(d1=diameter, d2=4.5,
h=lengte-6, $fn=64); }
Gemoduleerde messen
Een solide kegelstomp zou als kabelversteviging echter veel te stijf zijn. Met uitsparingen in vier richtingen zou die pas de nodige flexibiliteit krijgen. We wilden daarbij alleen niet gewoon een vierkant als uitsparing, maar die ook fraai afkanten in een hoek van 45 graden. Omdat dit 'mes' een grote hoeveelheid uitsparingen moet snijden, is het de moeite daarvoor ook een module te definiëren:
module Uitsnede(breedte, dikte) { translate([-dikte/2, -breedte/2,
sqrt(2)*dikte/2]) cube([dikte, breedte, breedte/2]); translate([0, 0, sqrt(2)*dikte/2]) rotate([0, 45, 0]) cube([sqrt(2)*dikte/2, breedte, sqrt(2)*dikte/2], center=true); }
Modules worden gedefinieerd door parametriseerbare objecten. Met de opgegeven naam kun je ze hergebruiken.
difference() {
// hier wordt de basisvorm gedefinieerd for(i=[0:(lengte-6)/4]) {
for(j=[-1, 1]) { translate([j*0.2, 0, 6+i*4]) rotate([0, j*90, 0])
Uitsnede(diameter+1,1); translate([0, j*0.2, 8+i*4]) rotate([0, j*90, 90])
Uitsnede(diameter+1,1); }}}
De Uitsnede() slijpt nu de basisvorm. Daarbij zijn telkens twee uitsnedes 180 graden ten opzichte van elkaar gedraaid (tweede forlus). Vanaf 6 mm achter de stekker komt nu na elke 4 mm een Uitsnede()-paar (eerste for-lus). De eerste for-lus moet over de gehele lengte uitsnedes stansen. De waarden die lusvariabele i daarbij moet aannemen, moet dus met de lengte toenemen. Naast lijsten die alle waarden apart aangeven, kun je met OpenSCAD ook bereiken definiëren. De syntaxis hiervoor maakt gebruik van een dubbele punt: [startwaarde:doelwaarde]. In het voorbeeld werkt i dus met hele getallen van 0 tot (lengte-6)/4.
Positioneren
Voor de uiteindelijke versteviging maakt de code de Lightning-stekker nog los van de basisvorm. Verder wordt het object gehalveerd, zodat het plat op de printtafel komt te liggen. Omdat we zowel de stekker als de basisvorm rechtop hadden ontworpen, moeten we beide objecten dus eerst draaien en verplaatsen. Laat je de accolades achter een commando als rotate() weg, dan heeft de code alleen betrekking op het volgende commando of object:
module Halfdeel() { difference() { translate([4.9, 0, 0.4]) rotate([0, -90, 0])
Versteviging(47.5, 10); rotate([0, 90, 0]) translate([-0.4, 0, 0])
Stekker(10.5); translate([-45,-6,-6])
cube([52, 12, 6]); }}
Met onderstaande code worden beide helften naast elkaar gezet en in een keer geprint:
for(i=[-1, 1]) { translate([i*4.5, i*18.8, 0]) rotate([0, 0, i*90])
Halfdeel(); }
In de preview lijkt het nu of het model klaar is. Maar om een STL-bestand te exporteren, moet je in OpenSCAD nog met F6 de polygonen laten berekenen. Dat kan enkele seconden duren aangezien die berekening vrij complex is. Als je daarna in het menu op de knop STL drukt, slaat OpenSCAD het bestand op.
Smidje spelen
We hebben beide helften gelast met een heet mes. Schuif hiervoor eerst het lemmet tussen de plakgedeeltes en trek het vervolgens langzaam terug terwijl je beide helften op elkaar drukt. Het mes wordt daarbij besmeurd met het plastic – gebruik dus geen duur keukenmes.
Voortaan beschermt de versteviging je Lightning-kabel effectief tegen kabelbreuken. Bij de link onderaan staat de complete 90-regelige OpenSCAD-code.
We willen met dit voorbeeld duidelijk maken dat je met OpenSCAD snel complexe templates voor printobjecten kunt programmeren. Het bespaart veel tijd dat je je daarbij niet in onoverzichtelijke interfaces hoeft in te werken. De CAD-programmeertaal is echter niet voor elk object even geschikt. Andere programma's bevatten gespecialiseerde functies om bijvoorbeeld modellen voor gegoten vormen te ontwerpen. Bij organische vormen kun je beter een programma als Blender gebruiken. Daarin zit ondersteuning voor subdivision-surfaces. Voor 3D-geprinte houders, behuizingen en zelfs aandrijvingen is OpenSCAD prima. (mvdm)