✍ DVD Plotter

Uma construção transversal

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: 02-10-2017 [16:20]


GNU/Emacs

1999-2017 (ç) Tiago Charters de Azevedo

São permitidas cópias textuais parciais/integrais em qualquer meio com/sem alterações desde que se mantenha este aviso.

Verbatim copying and redistribution of this entire page are permitted provided this notice is preserved.