Яйцебот 2

 

290312 002

3D принтер "Люмен"

 

lumen sm

3D сканер

 

3dscan

ЧПУ Выжигатель

 

woodburner

Контроллер Lumentino

 

lumentino sm

ПО компьютера

Как упоминалось ранее, в статье о прошивке, в Fabscan достаточно проработанное ПО, которое также позволяет выполнять настройку камеры перед сканированием. Как оказалось, это весьма важный процесс, поскольку если отнестись к нему несерьезно, то будут значительные погрешности сканирования.

 Но после нескольких неудачных попыток заставить Win-версию этого ПО нормально общаться со сканером (программа ведет себя странно, посылая вместо одного байта команды целую посылку левых байтов, вводя контроллер в ступор. Можно, конечно, попробовать вычленить коды команд из этих посылок, но тут явно проблемы ПО, а не контроллера), пришел к выводу, что нужно найти какой-то другой временный способ протестировать сканер. Ответ был найден на Instructables

Если для прошивки мы используем Wiring среду Arduino, то для написания ПО компьютера воспользуемся Processing`ом. Поверьте, после Arduino это не будет столь сложным, как кажется. Тем более исходный код у нас есть. Как обычно, корректируем код под нашу прошивку, используем те же команды, что и Fabscan, и у нас получается программка, приведенная ниже. Для ее работы потребуется библиотека работы с камерой: GSVideo.

Суть того, что делает это программа: создает последовательность снимков нашего объекта с камеры, после чего выделяет лазерный луч на этих картинках, делает соответствующие вычисления и записывает полученный результат в файл с расширением *.asc. Этот файл содержит облако точек нашего объекта. Затем этот файл можно открыть для преобразования в программе MeshLab.

Processing - штука развивающаяся и не до конца отработанная, поэтому есть некоторые недоработки. Что первое бросается в глаза - не работает "живое" отображение с камеры во время сканирования.

 

import codeanticode.gsvideo.*;
import processing.serial.*;

//scanner parameters
float odl = 210;  //distance between webcam and turning axle, [milimeter], not used yet
int etap = 200;  //number of phases profiling per revolution
float katLaser1 = 32.5*PI/180;  //angle between laser 1 and camera [radian]
float katLaser2 = 30.0*PI/180;  //angle between laser 2 and camera [radian]
int numLaser = 2;  //1 or 2 lasers
int average = 1;  //0(not used) or 1(used)
float pxmm_x = 4.36; //pixels per milimeter horizontally
float pxmm_y = 4.36; //pixels per milimeter vertically

//objects
PFont f;
GSCapture cam;
Serial myPort;
PrintWriter output1;
PrintWriter output2;

//colors
color black=color(0);
color white=color(255);
int cut=50;

//variables
int itr; //iteration
int laser; //work laser number
float pixBright;
float maxBright=0;
int maxBrightPos=0;
int prevMaxBrightPos;
int cntr=1;
int row;
int col;
int start;

//coordinates
float x, y, z;  //cartesian cords., [milimeter]
float ro;  //first of polar coordinate, [milimeter]
float fi; //second of polar coordinate, [radian]
float b; //distance between brightest pixel and middle of photo [pixel]
float katOperacji=2*PI/etap;  //angle between 2 profiles [radian]

//================= CONFIG ===================

void setup() {
  size(640,480);
  strokeWeight(1);
  smooth();
  background(0);
  //fonts
  f=createFont("Arial",16,true);
  //camera conf.
  String[] avcams=GSCapture.list();
  if (avcams.length==0){
    println("There are no cameras available for capture.");
    textFont(f,12);
    fill(255,0,0);
    text("Camera not ready",680,32);
  }
  else{
    println("Available cameras:");
    for (int i = 0; i < avcams.length; i++) {
      println(avcams[i]);
    }
    textFont(f,12);
    fill(0,255,0);
    text("Camera ready",680,32);
    //cam=new GSCapture(this, 1280, 960,avcams[0]);
    cam=new GSCapture(this, 640, 480,avcams[0]);
    cam.start();
    //delay ============================  
    start = millis();
    while ((millis() - start) < 2000)  {
       };       
  }
 
  //Serial (COM) conf.
  println(Serial.list());
  myPort=new Serial(this, Serial.list()[1], 9600);
  //output file
  output1=createWriter("skan1.asc");  //point cloud laser 1 *.asc
  output2=createWriter("skan2.asc");  //point cloud laser 2 *.asc
}

//============== MAIN PROGRAM =================

void draw() {
 
  myPort.write(205); //Motor On
 
  for (laser=0;laser<numLaser;laser++) {
    if (laser==0) {
       myPort.write(201); //Laser 0 ON
       myPort.write(204); //Clockwise
       }
    else {
      myPort.write(214); //Laser 1 ON
      myPort.write(203); //Counter Clockwise  need 203!!!
      }
    //delay ============================  
    start = millis();
    while ((millis() - start) < 2000)  {
       };   
    for (itr=0;itr<etap;itr++) {
       allwork();  //=================== Main calculations here ===================
       }
    if (laser==0) myPort.write(200); //Laser 0 Off
       else myPort.write(213); //Laser 1 Off
  }
  myPort.write(206); //Motor off  
  output1.flush();
  output1.close();  //save point cloud for laser 1 only
  output2.flush();
  output2.close();  //save point cloud for laser 2 only
  println("Scan finished.");
  noLoop();
}

void allwork(){
  PImage img=createImage(cam.width, cam.height, RGB);
  PImage out=createImage(cam.width, cam.height, RGB);
  cam.read();      
  img.loadPixels();
  cam.loadPixels();
  out.loadPixels();
  String picfile="Laser"+nf(laser+1, 1)+"-"+nf(itr+1, 3)+".png";
  String picfile2="Laser"+nf(laser+1, 1)+"-out-"+nf(itr+1, 3)+".png";
  for (int n=0;n<img.pixels.length;n++){
    img.pixels[n]=cam.pixels[n];
    }
  int currentPos;
  fi=itr*katOperacji+laser*(katLaser1+katLaser2); // Offset 60 grad for second laser
  println(fi);  
  int maxBrights = 1;
  int[] maxBrightPoses = new int[img.width];
  for(row=0; row<img.height; row++){  //starting row analysis
    maxBrightPos=0;
    maxBright=0;
    for(col=0; col<img.width; col++){
      currentPos = row * img.width + col;
      pixBright=brightness(img.pixels[currentPos]);
      if(pixBright>maxBright){
        maxBright=pixBright;
        maxBrightPos=currentPos;
        maxBrights=1;
        maxBrightPoses[maxBrights-1]=currentPos;
      }
      if(pixBright==maxBright){
      maxBrights++;
      maxBrightPoses[maxBrights-1]=currentPos;
      }
      out.pixels[currentPos]=black; //setting all pixels black
    }    
    if ((maxBrights > 1)&&(average==1)) {
     maxBrightPos = 0;
     for (int x=0; x < maxBrights; x++) {
       maxBrightPos += maxBrightPoses[x];
     }
     maxBrightPos = maxBrightPos/maxBrights;
    }
    if (maxBright < cut) continue; // cut lines, where laser a not detected (Bright too small)
    out.pixels[maxBrightPos]=white; //setting brightest pixel white    
    //if (laser==0) b=((maxBrightPos+1-row*img.width)-img.width/2)/pxmm_x;
      //else b=(img.width/2-(maxBrightPos+1-row*img.width))/pxmm_x;
    b=((maxBrightPos+1-row*img.width)-img.width/2)/pxmm_x;  
    if (laser==1) b=-b;
    if (laser==0) ro=b/sin(katLaser1); //Laser 1
       else ro=b/sin(katLaser2); //Laser 2
    //output.println(b + ", " + prevMaxBrightPos + ", " + maxBrightPos); //I used this for debugging   
    if (laser==0) x=ro * cos(fi); else x=ro * cos(-fi); //changing polar coords to kartesian
    if (laser==0) y=ro * sin(fi); else y=ro * sin(-fi);
    //x=ro * cos(fi);
    //y=ro * sin(fi);
    z=row/pxmm_y;   
    if( (ro>=-60) && (ro<=60) ){ //printing coordinates in table borders   
        if (laser==0) output1.println(x + "," + y + "," + z);
          else output2.println(x + "," + y + "," + z);        
    }
  }//end of row analysis  
     
  out.updatePixels();
  out.save(picfile2); // save generated picture
  img.updatePixels();
  img.save(picfile);  // save scanned picture
  image(img,0,0,img.width/2,img.height/2);
  //set(0,0,img);
  stepmotor();      
  println("Etap=",itr);
}  

void stepmotor() {  //sending command to turn
  int steps = 400/etap;
  myPort.write(202); //Turn motor
  myPort.write(steps);   //by N step
  //delay ============================  
  start = millis();
  while ((millis() - start) < 50*steps)  {
     };
}