DVD Plotter
A construção de uma plotter reciclando os motores passa-a-passo de umas drives de CDROM não é um projecto original mas coloca alguns desafios de montagem, calibração e programação.
A minha abordagem não difere de muitas outras que se podem encontrar pela net fora, apenas difere no objectivo. Desenhar numa folha de papel, nem que seja de dimensões reduzidas (4cm por 4cm), soluções de equações diferenciais ordinárias.
Assim para além de ter de controlar os motores passo-a-passo é necessário construir a rotina de integração numérica. Implementei um Runge-Kutta de ordem 4 (há quantos exemplos na net) apenas tive de o ajustar às minhas necessidades.
A construção ficou assim:
Está disponível um vídeo.
As funções que implementam a resolução numérica são duas.
// Runge-Kutta float rk(float t, float dt, float x[]){ float xaux[]={0,0,0}; int i=0,j=0; f(t,x,k1); for(i=0;i<m;i++){ xaux[i]=x[i]+dt/2*k1[i];} f(t+dt/2,xaux,k2); for(i=0;i<m;i++){ xaux[i]=x[i]+dt/2*k2[i];} f(t+dt/2,xaux,k3); for(i=0;i<m;i++){ xaux[i]=x[i]+dt/2*k3[i];} f(t+dt,xaux,k4); for(i=0;i<m;i++){ x[i]=x[i]+dt/6.0*(k1[i]+2.0*k2[i]+2*k3[i]+k4[i]);} t=t+dt;} void f(float t, float x[],float aux[]){ aux[0]=c1*(x[1]-x[0]); aux[1]=x[0]*(c2-x[2]); aux[2]=x[0]*x[1]-c3*x[2];}
E o código completo é este
#include <Servo.h> #include "AFMotor.h" #define LINE_BUFFER_LENGTH 512 /* -------------------------------------------------- */ // Plotter parameters // SINGLE, DOUBLE. INTERLEAVE or MICROSTEP. char STEP=MICROSTEP; const int stepsPerRevolution=20; AF_Stepper PStepperY(stepsPerRevolution,1); AF_Stepper PStepperX(stepsPerRevolution,2); float StepsPerMillimeterX=100.0; float StepsPerMillimeterY=100.0; const float pi=3.14159265358979; const int penZUp=80; const int penZDown=66; const int penServoPin =10; Servo penServo; /* -------------------------------------------------- */ /* -------------------------------------------------- */ // Drawing robot limits, in mm float Xmin=0; float Xmax=40; float Ymin=0; float Ymax=40; float Zmin=0; float Zmax=1; float Xpos=Xmin; float Ypos=Ymin; float Zpos=Zmax; float StepInc=1; int StepDelay=0; int LineDelay=0; int penDelay=5; /* -------------------------------------------------- */ /* -------------------------------------------------- */ // begin ode parameters const float c1=10.0; const float c2=28.0; const float c3=8.0/3.0; const int m=3; // ode order const float dt=0.01; //integration step float x[m]={1, 1, 1}; // init conditions float xaux[m]={0, 0, 0}; // aux float k1[m], k2[m], k3[m], k4[m]; // arrays for the Runge-Kutta intermediate points /* -------------------------------------------------- */ void setup() { // Serial.begin(9600); PStepperX.setSpeed(5); PStepperY.setSpeed(5); penServo.attach(penServoPin); penServo.write(penZUp); delay(100);} void loop(){ int i=0; int n=10000; float t=0; penUp(); delay(1000); /* -------------------------------------------------- */ // Do some jogging... /* -------------------------------------------------- */ move(Xmax,0); move(Xmax,Ymax); move(0,Ymax); move(0,0); /* -------------------------------------------------- */ /* -------------------------------------------------- */ // Draw the square limits /* -------------------------------------------------- */ drawLine(Xmax,0); drawLine(Xmax,Ymax); drawLine(0,Ymax); drawLine(0,0); /* -------------------------------------------------- */ penUp(); delay(2000); /* -------------------------------------------------- */ // Move to initial position /* -------------------------------------------------- */ move(x[1],x[2]); // use random initials conditions // or close to the one given {1,1,1} for(i=0;i<=n;i++){ rk(t,dt, x); drawLine(Xmax/2+x[1]*2.0/3.0,x[2]*4.0/5.0); } penUp(); move(Xmax,Ymax); delay(100000);} void penUp(){ penServo.write(penZUp); delay(penDelay);} void penDown(){ penServo.write(penZDown); delay(penDelay);} void drawLine(float x1, float y1){ if (x1 >= Xmax){x1=Xmax;} if (x1 <= Xmin){x1=Xmin;} if (y1 >= Ymax){y1=Ymax;} if (y1 <= Ymin){y1=Ymin;} x1=(int)(x1*StepsPerMillimeterX); // Convert coordinates to steps y1=(int)(y1*StepsPerMillimeterY); float x0=Xpos; float y0=Ypos; long dx=abs(x1-x0); // Let's find out the change for the coordinates long dy=abs(y1-y0); int sx=x0<x1 ? StepInc : -StepInc; int sy=y0<y1 ? StepInc : -StepInc; long i; long over=0; penDown(); if (dx>dy){ for (i=0; i<dx; ++i) { PStepperX.onestep(sx,STEP); over+=dy; if (over>=dx) { over-=dx; PStepperY.onestep(sy,STEP);} delay(StepDelay);}} else{ for (i=0; i<dy; ++i){ PStepperY.onestep(sy,STEP); over+=dx; if (over>=dy){ over-=dy; PStepperX.onestep(sx,STEP);} delay(StepDelay);}} Xpos=x1; Ypos=y1;} void move(float x1, float y1){ penUp(); if (x1 >= Xmax){ x1=Xmax;} if (x1 <= Xmin){ x1=Xmin;} if (y1 >= Ymax){ y1=Ymax;} if (y1 <= Ymin){ y1=Ymin;} x1=(int)(x1*StepsPerMillimeterX); // Convert coordinates to steps y1=(int)(y1*StepsPerMillimeterY); float x0=Xpos; float y0=Ypos; long dx=abs(x1-x0); // Let's find out the change for the coordinates long dy=abs(y1-y0); int sx=x0<x1 ? StepInc : -StepInc; int sy=y0<y1 ? StepInc : -StepInc; long i; long over=0; if (dx>dy){ for (i=0; i<dx; ++i) { PStepperX.onestep(sx,STEP); over+=dy; if (over>=dx) { over-=dx; PStepperY.onestep(sy,STEP);} delay(StepDelay);}} else{ for (i=0; i<dy; ++i){ PStepperY.onestep(sy,STEP); over+=dx; if (over>=dy){ over-=dy; PStepperX.onestep(sx,STEP);} delay(StepDelay);}} Xpos=x1; Ypos=y1;} // Runge-Kutta float rk(float t, float dt, float x[]){ float xaux[]={0,0,0}; int i=0,j=0; f(t,x,k1); for(i=0;i<m;i++){ xaux[i]=x[i]+dt/2*k1[i];} f(t+dt/2,xaux,k2); for(i=0;i<m;i++){ xaux[i]=x[i]+dt/2*k2[i];} f(t+dt/2,xaux,k3); for(i=0;i<m;i++){ xaux[i]=x[i]+dt/2*k3[i];} f(t+dt,xaux,k4); for(i=0;i<m;i++){ x[i]=x[i]+dt/6.0*(k1[i]+2.0*k2[i]+2*k3[i]+k4[i]);} t=t+dt; } void f(float t, float x[],float aux[]){ aux[0]=c1*(x[1]-x[0]); aux[1]=x[0]*(c2-x[2]); aux[2]=x[0]*x[1]-c3*x[2];}
Parte das peças usadas foram impressas e modeladas usando o OpenScad:
include <../utils/polyholes.scad> $fn=64; h=15; module pin(r=2.8){ difference(){ union(){ cylinder(h=h,r=5,center=true); translate([0,0,1]){ cylinder(h,r=r,center=true);}} poly_cylinder(h=2*h,r=2,center=true);}} module print(){ n=3; translate([0,0,h/2]){ for(i=[0:n]){ translate([0,12*i,0]){ pin(2.8);} translate([12,12*i,0]){ pin(7.1/2);}}} translate([20,0,0]){ rotate([0,0,90]) fixing();} translate([20,11*2.5,0]){ rotate([0,0,90]) fixing();}} module fixing(l=25,rc=.1*22){ difference(){ minkowski(){ cube([l-rc,l-rc,l-rc],center=true); sphere(rc);} translate([0,0,-l/2+.01]){ cube([2*l,2*l,l],center=true);} translate([0,l/2,l]){ cube([2*l,l,2*l],center=true);} translate([0,-l/1.4,l/1.4]){ rotate([45,0,0]) cube([1.5*l,l,l],center=true);} translate([0,-l/4,l-4.5]){ cylinder(l,r=4,center=true,$fn=6); poly_cylinder(2*l,r=2,center=true);} translate([l/3,0,l/4]){ rotate([90,0,0]){ translate([0,0,l-4.5]){ cylinder(l,r=4,center=true,$fn=6); poly_cylinder(2*l,r=2,center=true);}}} translate([-l/3,0,l/4]){ rotate([90,0,0]){ translate([0,0,l-4.5]){ cylinder(l,r=4,center=true,$fn=6); poly_cylinder(2*l,r=2,center=true);}}}}} module otherfixing(l=25,rc=.1*22){ difference(){ minkowski(){ cube([l-rc,l-rc,l-rc],center=true); sphere(rc);} translate([0,0,-l/2+.01]){ cube([2*l,2*l,l],center=true);} translate([0,l/2,l]){ cube([2*l,l,2*l],center=true);} translate([0,-l/1.4,l/1.4]){ rotate([45,0,0]) cube([1.5*l,l,l],center=true);} translate([0,-l/4,l-4.5]){ cylinder(l,r=4.5,center=true,$fn=64); poly_cylinder(2*l,r=2,center=true);} translate([l/3,0,l/4]){ rotate([90,0,0]){ translate([0,0,l-4.5]){ cylinder(l,r=4.5,center=true,$fn=66); poly_cylinder(2*l,r=2,center=true);}}} translate([-l/3,0,l/4]){ rotate([90,0,0]){ translate([0,0,l-4.5]){ cylinder(l,r=4.5,center=true,$fn=64); poly_cylinder(2*l,r=2,center=true);}}}}} a=25.4*4/3; b=25.4*2; module penbase(){ difference(){ cube([a,b,2.5],center=true); translate([0,12,10/2]){ rotate([0,0,90]){ poly_cylinder(h=b,r=2.1,center=true);}}} //Servo holder difference(){ translate([a/2-5/2,-b/2+2.5,15/2-2.5/2]){ cube([5,5,15],center=true);} translate([a/2-1.25/2,-b/2+5-2,2+5]){ rotate([0,90,0]){ cylinder(10,r=.8,center=true);}}} //Servo holder difference(){ translate([a/2-5/2,-b/2+5+2.5+23.5,15/2-2.5/2]){ cube([5,5,15],center=true);} translate([a/2-1.25/2,-b/2+21+7+2.5,2+5]){ rotate([0,90,0]){ cylinder(h=10,r=.8,center=true);}}} difference(){ union(){ translate([-a/2+6,b/2-2.5,10/2-2.5/2]){ cube([12,5,10],center=true);} translate([-a/2+6,-b/2+2.5,10/2-2.5/2]){ cube([12,5,10],center=true);}} translate([-a/2+6-3,2.5,10/2]){ rotate([0,90,90]){ poly_cylinder(h=b,r=3.1/2,center=true);}} translate([-a/2+6+3,2.5,10/2]){ rotate([0,90,90]){ poly_cylinder(h=b,r=3.1/2,center=true);}} }} module penholder(){ difference(){ translate([-a/2+6,0,22/2+2.5/2+1]){ cube([13,12,22],center=true);} translate([-a/2+6-3,2.5,10/2]){ rotate([0,90,90]){ poly_cylinder(h=b,r=3.3/2,center=true);}} translate([-a/2+6+3,2.5,10/2]){ rotate([0,90,90]){ poly_cylinder(h=b,r=3.3/2,center=true);}} translate([-a/2+6,2.5,13]){ rotate([0,90,90]){ poly_cylinder(h=b,r=4.25,center=true);}} translate([-a/2+6,2.5,25]){ rotate([0,0,90]){ cube([20,2,20],center=true);}} translate([-a/2+6,0,21]){ rotate([0,90,0]){ poly_cylinder(h=b,r=2,center=true);}} }} module otherpin(h=7,r=2){ difference(){ cylinder(h=h,r=5,center=true); poly_cylinder(h=2*h,r=2,center=true);}} module foot(h=12,r=2){ difference(){ cylinder(h=h,r1=5,r2=5,center=true); translate([0,0,-h+5]){ poly_cylinder(h,r=6.25/2,center=true,$fn=6);} poly_cylinder(h=1.1*h,r=2.1,center=true);}} module penpin(h=7,r=2){ difference(){ hull(){ cylinder(h=h,r=5,center=true); translate([0,5,0]) cylinder(h=h,r=5,center=true);} poly_cylinder(h=2*h,r=r,center=true);}} penbase();Palavras chave/keywords: dvd, plotter, maker, diy, arduino
Criado/Created: 25-07-2017 [18:30]
Última actualização/Last updated: 10-10-2022 [14:47]
(c) Tiago Charters de Azevedo