Czym jest skaner 3D?
Skaner brył jest urządzeniem stosowanym do zapisywania geometrii fizycznych obiektów w pamięci komputera. Znajduje to zastosowanie np. w muzealnictwie – do archwizacji (od malutkich eksponatów do całych budynków); w medycynie, np. do niezwykle precyzyjnego dopasowania protez; w laboratoriach (badania zużycia) i zakładach produkcyjnych (odtwarzanie części do której brak dokumentacji). Ostatnio pojawiają się również zastosowania w marketingu. Skaner zdejmuje wymiary osoby i poleca pasujące rozmiary ubrań czy tworzy wizualizacje – wreszcie koniec z przymierzaniem!
Skanery brył dzielą się ze względu na zasadę działania. Dwie podstawowe to metody:
- optyczna
- stykowa
Różnią się między sobą prędkością procesu skanowania jak i dokładnością. Mniej dokładne, ale szybsze, metody optyczne polegają na oświetleniu obiektu światłem i pomiarze odchyleń od oczekiwanego kształtu wzorcowego. Źródłem światła może być np. laser punktowy, laser liniowy, projektor (tzw. skanowanie światłem strukturalnym). Wolniejsza, ale bardziej precyzyjna jest metoda stykowa. W metodzie stykowej, maszyna współrzędnościowa robi serię pomiarów poprzez dosunięcie ramienia z czujnikiem do obiektu i zarejestrowanie pozycji punktu styku poprzez odczytanie wartości położenia z enkoderów liniowych/obrotowych.
Dobór metody zależy też od materiału, z jakiego wytworzony jest skanowany obiekt. Metody optyczne nie nadają się do materiałów przejrzystych i mocno błyszczących, metodami stykowymi nie powinno się skanować obiektów z materiałów elastycznych.
Uproszczona zasada działania
Opisanie wszystkich metod jest zadaniem na dobrą pracę naukową, dlatego skupię się na zasadzie działania mojego skanera, wykorzystującego laser liniowy.
Obiekt, umieszczony na obrotowej platformie, oświetlony jest laserem liniowym. Pod kątem ok. 30stopni w stosunku do osi optycznej lasera znajduje się kamera (kąt alpha). Kamera obserwuje odkształcenie się linii lasera na obiekcie, przeliczając je na dystans oświetlonego punktu od osi obrotu. Ze względu na to, że jest to dopiero początkowa wersja skanera, pomijam takie rzeczy jak punktowość samego czujnika ccd w kamerze, zniekształcenia na układzie optycznym, błędy wprowadzane przez złej jakości laser.
Kamera wykonuje serię zdjęć wokół obiektu, każde zdjęcie robione jest co stały kąt. W moim wypadku wykonywane jest 120 zdjęć wokół obiektu, co oznacza że na każdym kolejnym zdjęciu obiekt jest obrócony o kolejne 360 stopni / 120 = 3 stopnie. Na rysunku oznaczone jest jako fi. Program następnie, wiersz po wierszu, szuka najjaśniejszego piksela. Następnie, gdy piksel taki zostanie znaleziony mierzony jest dystans b pomiędzy nim środkiem obrazu. Wynik, w pikselach, zmieniany jest na milimetry. Współczynnik ilości pikseli odpowiadającym jednemu milimetrowi dla danej odległości kamery od osi obrotu zmierzyłem sobie wcześniej. Następnie za pomocą prostej trygonometrii zamieniam obliczoną odległość b i stały kąt pomiędzy kamerą a laserem na odległość danego punktu od osi obrotu:
sinus(alpha) = b / ro
ro = b / sinus(alpha)
W ten sposób dostaję współrzędne punktu w tzw. walcowym układzie współrzędnych. W układzie walcowym każdy punkt opisany jest za pomocą trzech parametrów
P = (odległość od osi obrotu, kąt między rzutem punktu a osią x, wysokość)
Czyli to co już zostało oznaczone: P = (ro, fi, z)
W programach CAD czy podobnych graficznych, częściej jednak jest wykorzystywany klasyczny, znany ze szkoły, kartezjański układ współrzędnych, w którym każdy punkt wygląda tak:
P = (odległość od początku układ wzdłuż osi X, wzdłuż osi Y, wzdłuż osi Z)
czyli P = (x, y, z)
Należy wykonać więc konwersję współrzędnych z układu walcowego do kartezjańskiego. Na szczęście to też jest bardzo proste.
x = ro * cos( fi )
y = ro * sin( fi )
z = z
I tak dla każdego zdjęcia.
Jak mówiłem – nie jest to komplet obliczeń, jest to wersja bardzo uproszczona.
Kurczę, rozpisałem się.
Elementy składowe
- Arduino wraz z Arduino IDE
- Processing IDE
- LEGO
- silnik krokowy
- driver silnika krokowego wraz ze źródłem zasilania do niego
- laser liniowy
- kamera internetowa
- Meshlab
Pierwsze co należy zrobić, to przemyśleć ogólną budowę i kształt samej platformy, sposób mocowania kamery i lasera, mocowanie silnika, sprzężenie silnika z przekładniami lego. Mi udało się to zrobić bez niszczenia klocków.
Silnik krokowy
Wykorzystałem silnik krokowy od starej drukarki OKI. Silnik ten jest silnikiem bipolarnym o 4 wyprowadzeniach, posiada 48 kroków na obrót, napięcie pracy 3,7V, prąd pobierany przy moim zastosowaniu to ok 200-250mA podczas ruchu. Silnik ma zintegrowaną przekładnię o przełożeniu 6:1.
Do wyprowadzeń silnika przylutowałem 4-żyłową taśmę, do drugiego końca każdego z przewodów po goldpinie – dzięki temu bez problemu mogę łączyć silnik ze sterownikiem.
Zębatkę przekładni zdjąłem i wywierciłem w niej 6 otworów, które odpowiadają ułożeniu otworów w kole pasowym z lego. Połączyłem to za pomocą 6 osiek o długości „3”.
Zdjęcie pochodzi z internetu, mój silnik jest bardzo podobny, ale nie identyczny.
Driver i zasilacz
Zasilacz to klasyczna aplikacja układu LM317 w postaci zasilacza regulowanego. Schemat załączam, poza tym można znaleźć go w datasheet samego układu. Dodaję też rysunek płytki gotowy do termotransferu. Za pomocą potencjometru należy ustalić napięcie wymagane dla silnika. LM317 w obudowie TO220 może zapewnić maksymalny prąd na poziomie 1,5A, należy mieć to na uwadze.
Do sterowania samym silnikiem wykorzystuję kostkę L293D – są to 2 mostki H zintegrowane w jednej obudowie, o wydajności prądowej maks. 600mA na kanał. Jak pisałem silnik pobiera nie więcej niż 250mA, więc taka konfiguracja jest dla mnie absolutnie wystarczająca.
W podstawowej wersji kostka wykorzystuje 4 porty mikrokontrolera oraz piny +5V i GND. Jednak po dodaniu mniejszej płytki z 2 tranzystorami, można zredukować ilość potrzebnych portów o 2. Szczegóły można znaleźć w tutorialach arduino i na stronie http://www.tigoe.com/pcomp/code/circuits/motors/stepper-motors/
Wzory płytek, wykaz elementów, schematy:
Platforma obrotowa
Silnik zamknięty w obudowie z lego praktycznie nie ma możliwości ruchu. Znów uniknąłem konieczności wiercenia czy klejenia (żaden klocek nie ucierpiał). Za pomocą gumki recepturki, silnik połączony jest mechaniczne z przekładnią. Przełożenie na kołach pasowych wynosi 1:1. Sama przekładnia złożona jest z zębatek o niewielkim luzie o przełożeniu 20:12. Dzięki temu uzyskałem całkowite przełożenie z silnika na paltformę 10:1 (6 1 20/12 : 1). A to oznacza że na jeden pełny obrót platformy potrzeba 10 obrotów silnika krokowego.
Sama platforma natomiast jest zrobiona z pękniętego koła lego (z osadzeniem krzyżowym) i płyty CD, sklejonych (osiowo!) klejem na ciepło. Do samej płyty przyklejony, za pomocą taśmy dwustronnej, został zielony papier. Wygląda to równo i elegancko. Reszta na zdjęciach.
Kamera
Miałem tylko dość słabą kamerę Creative Webcam Vista. Więc wyboru nie miałem żadnego, musiałem ją zastosować. Jest przymocowana do platformy za pomocą lego oczywiście.
Laser liniowy
Najpodlejszej jakości wskaźnik laserowy z bazaru oraz szklany walec w charakterze soczewki walcowej tworzą moduł lasera liniowego. Takie szklane patyczki stosowane są w pracowniach chemicznych/laboratoriach i za cholerę nie pamiętam jak się toto nazywa. Na końcach założone koszulki termokurczliwe dla zabezpieczenia przed ostrymi krawędziami szkła.
Arduino
Leonardo. Kupiony parę miesięcy temu w nettigo.pl.
W sumie arduino nie ma za dużo do roboty, kod też jest wyjątkowo prosty. Za pomocą połączenia Serial procesor odbiera komendy od programu napisanego w processingu i obraca talerz o zadany kąt. No i świeci diodą oczywiście.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <Stepper.h> Stepper oki(48,8,9); const int ledPin = 13; // the pin that the LED is attached to int incomingByte; // a variable to read incoming serial data into void setup() { // initialize serial communication: Serial.begin(9600); // initialize the LED pin as an output: pinMode(ledPin, OUTPUT); oki.setSpeed(60); } void loop() { // see if there's incoming serial data: if (Serial.available() > 0) { // read the oldest byte in the serial buffer: incomingByte = Serial.read(); // if it's a capital H (ASCII 72), turn on the LED: if (incomingByte == 'S') { digitalWrite(ledPin, HIGH); oki.step(4); } // if it's an L (ASCII 76) turn off the LED: if (incomingByte == 'K') { digitalWrite(ledPin, LOW); } } } |
Processing
Processing jest środowiskiem programistycznym, na bazie którego powstało arduino. Bardzo podobna składnia, prosty interfejs i dobra współpraca z ardu to ważne cechy. Sporo bibliotek i dobra ich dokumentacja (w jęz. angielskim) ułatwia zabawę np. z kamerą.
Pierwsze co należy zrobić to doinstalować bibliotekę GSVideo: http://gsvideo.sourceforge.net/ instrukcja jest na stronie.
Zasady działania kodu nie będę opisywał ponownie, powiem tylko że ze względów różnych, w tej wersji, operacja skanowania jest podzielona na dwie pętle: robienie zdjęć oraz obliczenia. Wynik zapisywany jest w pliku *.asc w katalogu projektu procesinga.
Aha. Interfejs na razie jest bardzo ubogi a właściwie go nie ma. Niemniej, skany się zapisują.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
import codeanticode.gsvideo.*; import processing.serial.*; //objects PFont f; GSCapture cam; Serial myPort; PrintWriter output; //colors color black=color(0); color white=color(255); //variables int itr; //iteration float pixBright; float maxBright=0; int maxBrightPos=0; int prevMaxBrightPos; int cntr=1; int row; int col; //scanner parameters float odl = 210; //distance between webcam and turning axle, [milimeter], not used yet float etap = 120; //number of phases profiling per revolution float katLaser = 28*PI/180; //angle between laser and camera [radian] float katOperacji=2*PI/etap; //angle between 2 profiles [radian] //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 pxmmpoz = 5; //pixels per milimeter horizontally 1px=0.2mm float pxmmpion = 5; //pixels per milimeter vertically 1px=0.2mm //================= CONFIG =================== void setup() { size(800, 600); 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, 640, 480,avcams[0]); cam.start(); } //Serial (COM) conf. println(Serial.list()); myPort=new Serial(this, Serial.list()[0], 9600); //output file output=createWriter("skan.asc"); //plik wynikowy *.asc } //============== MAIN PROGRAM ================= void draw() { PImage zdjecie=createImage(cam.width,cam.height,RGB); cam.read(); delay(2000); for (itr=0;itr<etap;itr++) { cam.read(); zdjecie.loadPixels(); cam.loadPixels(); for (int n=0;n<zdjecie.width*zdjecie.height;n++){ zdjecie.pixels[n]=cam.pixels[n]; } zdjecie.updatePixels(); set(20,20,cam); String nazwaPliku="zdjecie-"+nf(itr+1, 3)+".png"; zdjecie.save(nazwaPliku); obroc(); delay(500); } obroc(); licz(); noLoop(); } void licz(){ for (itr=0; itr<etap; itr++){ String nazwaPliku="zdjecie-"+nf(itr+1, 3)+".png"; PImage skan=loadImage(nazwaPliku); String nazwaPliku2="odzw-"+nf(itr+1, 3)+".png"; PImage odwz=createImage(skan.width, skan.height, RGB); skan.loadPixels(); odwz.loadPixels(); int currentPos; fi=itr*katOperacji; println(fi); for(row=0; row<skan.height; row++){ //starting row analysis maxBrightPos=0; maxBright=0; for(col=0; col<skan.width; col++){ currentPos = row * skan.width + col; pixBright=brightness(skan.pixels[currentPos]); if(pixBright>maxBright){ maxBright=pixBright; maxBrightPos=currentPos; } odwz.pixels[currentPos]=black; //setting all pixels black } odwz.pixels[maxBrightPos]=white; //setting brightest pixel white b=((maxBrightPos+1-row*skan.width)-skan.width/2)/pxmmpoz; ro=b/sin(katLaser); //output.println(b + ", " + prevMaxBrightPos + ", " + maxBrightPos); //I used this for debugging x=ro * cos(fi); //changing polar coords to kartesian y=ro * sin(fi); z=row/pxmmpion; if( (ro>=-30) && (ro<=60) ){ //printing coordinates output.println(x + "," + y + "," + z); } }//end of row analysis odwz.updatePixels(); odwz.save(nazwaPliku2); } output.flush(); output.close(); } void obroc() { //sending command to turn myPort.write('S'); delay(50); myPort.write('K'); } |
Skanowanie
Film:
Na razie brak jest podglądu na żywo skanowania, więc do ustawiania kamery używam przykładu GettingStartedWithCaptureWin z katalogu przykładów biblioteki GSVideo.
Następnie zostaje tylko włączyć zasilanie, włączyć laser i nacisnąć Run w IDE processing.
Film jest w słabej jakości. Nic na to nie poradzę, nie mam żadnej innej kamery ponad telefon.
Chmura punktów
Chmura jest wynikiem działania skanera. Są to wczytane punkty zorientowane w przestrzeni. Do działania z chmurami nadaje się program Meshlab, ale u mnie zachowuje się dość niestabilnie. Trochę mi to przeszkadza w przerobieniu chmury na bryłę, ale jestem chyba coraz bliżej oczekiwanego efektu. Gotową bryłę można zapisać jako plik stereolitografii *.stl i wydrukować na drukarce 3D. Otrzymana chmura przedstawia obiekt odbity względem początku układu współrzędnych. Otrzymaną chmurę można zobaczyć poniżej, ale nie jest to dokładnie ta sama co na screenach. Jest to późniejszy skan, wykonany po drobnej przebudowie skanera i w dzień, więc ma trochę szumów (niepotrzebnych wierzchołków).
Do zrobienia
Zdecydowanie wkrótce należy:
- Ulepszenia kodu, m.in. wskazywanie miejsca gdzie Z=0, dzięki temu bryła nie będzie odbita względem punktu P = (o, 0, 0)
- Dopisanie jakiegoś prostego interfejsu
- Wbudowana kalibracja kamery i lasera
- i jeszcze parę…
Pomysły co można ulepszyć:
- odwrócić kamerę o 90 stopni, w efekcie zwiększy się ilość warstw
- lepszy laser liniowy, bez poświaty
- zastosowanie kamery HD, a może jakiejś cyfrowej lustrzanki?
Angielska wersja artykułu umieszczona została na instructables.com, polecam zerknąć: http://www.instructables.com/id/Lets-cook-3D-scanner-based-on-Arduino-and-Proces/
I to chyba było by na tyle.
Super! Nie spodziewałem się, ze to takie proste :)
wszystko się wydaje trudne dopóki się nie nie zobaczy jak jest zrobione, gratulacje dla autora :)
A nie lepiej porobić kilkanaście/dziesiąt zdjęć z różnych perspektyw, wrzucić do odpowiedniego programu i tyle? :)
Nie trzeba nic konstruować i liczyć na dość mizerny efekt… .
Mówię o czymś takim – http://vimeo.com/43442146 od 0:25 zaczyna się prezentacja całej idei :]
popraw jak możesz link do skanu ponieważ jest niedostępny. “404 Not Found”
Linki poprawione :)
Oczywiście że można. Tylko wtedy po co istniały by takie strony jak ta?
ja polecam to rozwiązanie http://www.david-laserscanner.com/ (bardzo podobne do podejścia autora)
No mistrzostwo, aż strach się bać, zaraz sam nauczysz się owce klonować :D
Dzięki za komentarze.
Moja zabawa ze skanerem pomalutku posuwa się naprzód, będę starał się go maksymalnie poprawić, na ile tylko pozwolą moje umiejętności programistyczne (a cholera, nigdy nie byłem w tym mocny).
Kuba: david to chyba najbardziej znane tego typu urządzenie/soft na rynku. Już dobre parę latek mu stuknęło.
LEGO rządzi!!! :)
Ja gdzieś zgubiłem cały karton (spory karton!) swoich klocków LEGO. Pewnie zalega gdzieś w piwnicy… pomiędzy dziesiątkami innych kartonów…
Rurki z laboratorium to “Bagietki”.
O kurde masakra, co zobaczę posta z wykorzystaniem arduino to nakręca mnie do zabawy, a ja ciągle nie mam na to czasu. Brawo dla autora, kupa niezłej roboty.
Fajna sprawa tylko do czego by to można wykorzystać !? :)
Tego typu (nie ten konkretny, ale o tej samej zasadzie działania) skaner wykorzystuje się jako narzędzie inżynierskie – np. do analizy zużycia łopatek lotniczego silnika wirnikowego. Również w reverse engeeneringu, gdzie trzeba odtworzyć dokumentację na podstawie istniejących części. Albo do tworzenia cyfrowych kopii obiektów muzealnych.
Ten konkretny skaner może służyć do przygotowania obiektu do wydrukowania na drukarce 3D. Można zeskanować też np. figurkę i animować ją w blenderze. Zastosowanie ogranicza wyobraźnia ;)
Może warto byłoby zastosować najprostszą poziomicę laserową taką do układania płytek ceramicznych , pewnie poprawiłoby jakość skanu.
W moim przypadku głównym problem jest nadal kamera. Natomiast na dniach powinien do mnie dotrzeć laser liniowy z możliwością ustawienia skupienia. Dodatkowo będzie włączany z poziomu programu a nie ręcznie. Mam też już w domu aparat kręcący filmy 720p więc wkrótce wrzcuę jakąś aktualizację.
Arduino Leonardo ze złączami kosztuje 94 zł
czy można zastąpić go innym modelem Ardurio?
tańszym od Leonardo
Można. Program jest prosty, każde arduino go obsłuży.
Tak mi się przypomniało :)
http://www.indiegogo.com/projects/photon-3d-scanner
Mógłby mi ktoś wytłumaczyć jak zainstalować meshlab? Jak próbuję go zainstalować to wyskakuje mi info, że potrzebne jest microsoft visual ++ 2008 SP1… A jak próbuję to zainstalować, to wyskakują mi opcję: napraw lub usuń.
Czy da się zmienić program tak, żeby zamiast robić zdjęć, nagrywał film? Potem można by np. w ffmpeg rozdzielić go na pojedyncze klatki…
Można. Nawet processing może to zrobić. Tylko przy filmie mogą się pojawić problemy przy zachowaniu stałego kąta podczas rozpędzania i hamowania platformy, jeśli będzie na niej coś ciężkiego. No i przydała by się jakaś dodatkowa synchronizacja, np. enkoderem, którą klatkę wycinać. A zresztą, po co robić nadmiar klatek i kasować 90%, jeśli można zrobić ich tylko tyle, ile jest niezbędne.
Witam,
Jestem pod wrażeniem:) nie wiedziałem że to takie proste. Dowodzi to temu że Polak potrafi.
Chyba niektóre programy potrafią nawet z kinecta zrobić skaner 3D.
http://www.skanery3d.eu/skaner-3d-z-kinect fajna zabawka:)
Pingback: Prosty noktowizor » Majsterkowo.pl
To chyba bagietka(ten szklany pręt)
http://pl.wikipedia.org/wiki/Bagietka_%28przyrz%C4%85d%29
Skaner 3D oparty na Arduino i Processing » Majsterkowo.pl
Bardzo ciekawy artykuł oczywiście aby się dało to praktycznie zastosować trzeba poprawić parę rzeczy a przede wszystkim zastosować jak sam napisałeś lepszej jakości komponenty czyli skaner układ optyczny kamerę o wyższej rozdzielczości i myślę ze także lepszy precyzyjniejszy napęd chyba nie na gumce ale przekładni . czekam z niecierpliwością na dalszy ciąg