--- title: "口丁寫字童子" tags: hackpad --- # 口丁寫字童子 > [點此觀看原始內容](https://openlabtaipei.hackpad.tw/6xtqJNYnLIV) 口丁寫字童子 ### 介紹 ### 設計理念 1. 移動部份, 採容易取得&程式大家都比較熟的 RC Servo (1.5ms+-0.5ms) 2. 最大挑戰是兩個輪子˙實際輸出, 會隨電壓負載衰退...etc 而偏移,所以才想用 Arduino 既有 PS2 mouse 的程式,搭配滾輪型X Y直接搭在輪子˙上迴饋即時輸出狀況,Arduino 主程式可以藉此修定輸出。 3. 下筆輕重部份,捨去Z軸絕對距離方式,改採翹翹板移動中心方式,以利模仿真人下筆收筆。  4\. 轉彎部份,有一輪不動一輪動的 定軸轉彎,另一種是兩輪反向的定中心轉彎。  5\. 筆跡會沾輪部份,除移動速度放慢,另外底座加個吹風風扇吹乾。 ## 材料 - 減速馬達 *2顆 (  9g 伺服馬達 ) - 9g 伺服馬達 *1 顆 - IRS01A IR Sensor 2 個 - 帶有編碼器的輪子,如[圖](http://letsmakerobots.com/files/userpics/u1433/IMG_4070.jpg)所示。 - 底盤 - 筆 - 風鼓風扇 (把墨吹乾) ### 工具 ### 製作過程 1. 兩邊出力輪軸心  與筆 最好在同一圓週上,  可以簡化程式 center offset 的部份 ## 討論 使用編碼器精準控制移動的方式 使用 IRS01A IR Sensor 去讀取輪子的移動,輪子內會裝上雷切出來的板子,在板子邊緣有多個洞。 轉 G code 的程式碼參考: [https://github.com/dherrendoerfer/uCNC\_controller/blob/master/uCNC\_controller/uCNC_gcode.ino](https://github.com/dherrendoerfer/uCNC_controller/blob/master/uCNC_controller/uCNC_gcode.ino) [G code 參考資料](http://reprap.org/wiki/G-code) [what is g code ?](https://www.youtube.com/watch?v=r5bimWKeMbY) ### 問題 ## 紀錄 20150502 進度 黃偉峻提供小車機構模組,章國俊兄已經把藍芽控制小車的方式完成,目前可以用藍牙來操縱了,不過真的一跑就歪了,因為還沒用到編碼器。XD,紀錄請見下方影片。 [https://www.youtube.com/watch?v=uCvYBIGFheU](https://www.youtube.com/watch?v=uCvYBIGFheU) ![](https://s3-ap-northeast-1.amazonaws.com/hackmd-public/uploads/457532bd-c06a-435d-88b5-3e9ae41b9295.jpg) 20150502 進度2 後來把萬向輪上油潤滑之後,車子行進路立刻改善了,直很多了。 裝上遮斷開關,配合車子套件的碼盤。 ![](https://s3-ap-northeast-1.amazonaws.com/hackmd-public/uploads/93db9149-4e0e-4cb0-9a62-7bf29651765c.jpg) 計劃拿手機或筆電用藍芽和小車連線,指揮小車往前走多少公分、到定點後轉幾度、什麼地方要下筆。 小車接受指令並且自己修正。 20150506 馬爸把前輪換成這個。 原先的輪子在轉向時會因為本身旋轉歸位得時間差,而造成行進路線的誤差。 這個輪子就沒這個問題了。 ![](https://s3-ap-northeast-1.amazonaws.com/hackmd-public/uploads/c1f914dc-c75e-4002-889d-651a9795b1e2.jpg) 20150507 用遮蔽膠帶量輪子圓周長: ![](https://s3-ap-northeast-1.amazonaws.com/hackmd-public/uploads/6d4217ee-2e2d-4882-9d9f-bfc5f31a720a.jpg) ![](https://s3-ap-northeast-1.amazonaws.com/hackmd-public/uploads/097fbc61-51c5-426a-b819-7edf704910f7.jpg) 20150511 1. 改用3D列印的機構來固定馬達和光遮斷開關 2. 換上抓地力比較好的輪子 3. 換回原來的大顆萬象輪 車子的操控一整個改善了! ![](https://s3-ap-northeast-1.amazonaws.com/hackmd-public/uploads/61c7fa90-9222-4e06-9961-f9d06a674bd5.jpg) 20150513 基本教練:第一課 其實是想要寫一個"口"字.... [https://www.youtube.com/watch?v=36ZA0ze9PFw](https://www.youtube.com/watch?v=36ZA0ze9PFw) 還沒有做到校正的部份 以目前的機構沒有辦法做到精準,所以寫字應該會像是塗鴉。既然是塗鴉,就真的來塗鴉好了。 在手機上亂畫 ![](https://s3-ap-northeast-1.amazonaws.com/hackmd-public/uploads/747a422a-2ec9-406b-a2a0-754c95903213.png) 因為用車子來寫字,也就是要用輪子走到定點,而車子的控制方式不像是CNC可以直接告訴它走到哪一個座標上面,而要轉方向盤、踩油門。 所以用手機計算:把在螢幕圖形的X,Y座標轉成極座標,然後再轉成轉方向盤、踩油門的指令,來控制車子。 ![](https://s3-ap-northeast-1.amazonaws.com/hackmd-public/uploads/dd94a4f5-8769-4eb9-afa2-f51d853b2203.png) 用Android手機透過藍芽與Arduino控制的小車連線,讓小車以移動自身位置的方式畫出手機上面的圖形。 圖形可以運用APP以手指頭在螢幕上畫出,或是使用 APP中內建的特殊圖形(在影片中是個玫瑰線)。 目前小車還不能很準確的移動到所望位置,所以影片中的玫瑰線畫完之後首尾沒辦法連起來,差距很大。 手機上面可以同步顯示畫出的進度了 [https://www.youtube.com/watch?v=oj6462PY5eQ](https://www.youtube.com/watch?v=oj6462PY5eQ) 在2015的 maker faire 過後,完程度大約70%,機構還需要再做修改。看看運動可不可以再精確一點。 這樣的寫字方式,把筆放在兩個輪子之間較為適合。 筆可以做成模組,或可換成滴水的方式,我想在地上寫字啊! 滴水的方式可以參考: [https://vimeo.com/29306086](https://vimeo.com/29306086) 馬達考慮換成扭力更大或自帶編碼器的馬達,或是用步進馬達或是修改舵機(360度旋轉)來用。 另一支的影片: [https://youtu.be/gQGjqP5G1oE](https://youtu.be/gQGjqP5G1oE) ## 整理:製造組裝說明手冊 ### 運作方式 ### 機構 - 輪子:圓周 218 mm - 碼盤:共有 20 孔 - 光遮斷開關: 說明:這樣的配合下,輪子轉一圈也就是行進218mm時,光遮斷開關會送出20個pulse;所以前進11mm大約是1個pulse。 - 馬達與減齒輪組: 馬達是玩具用的直流馬達,當兩個馬達都提供相同的電壓時,轉速並不會相等。因此,希望它走直線時並不會如我們所願,就是會歪掉。所以需要配合透過碼盤和光遮斷開關所回饋的pulse數量來修正。這部份需要用到PID,否則會變成蛇行。 ### 連線與遙控 ### 程式碼 penWriting  //servo control  unsigned char servo = 6;  unsigned char count = 0;  unsigned char i = 0;  unsigned char servoTime = 0;  unsigned char servoValue = 0;  unsigned char flag = 0;  unsigned int secondT = 0;  unsigned int k = 0;  //pen  const unsigned int down = 50;  const unsigned int up = 100;  //motor  int L1 = 9;  int L2 = 7;  int R1 = 10;  int R2 = 8;  //speed and time  int dir = 0;  int speeds = 0;  unsigned long time = 0;  long serialValue = 0;  int length = 0;  unsigned char cmd\[400\]; void setup() {   UBRRL=103;   //16M baud 9600,8bit UART   UCSRB=0x18; //enable   pinMode(servo,OUTPUT);   digitalWrite(servo,LOW);   //pin for motor   pinMode(L1,OUTPUT);   pinMode(L2,OUTPUT);   pinMode(R1,OUTPUT);   pinMode(R2,OUTPUT);   timer2();   TIMSK &= ~(2 << 6);   digitalWrite(servo,LOW);   time = 300;   penWrite();   penRise(); } void loop() {    while(!(UCSRA&(1<<RXC)));    if(UDR == 170)    {      while(!(UCSRA&(1<<RXC)));      if(UDR == 85)     {       while(!(UCSRA&(1<<RXC)));       length = UDR;           length = length << 2;       for (k = 0;k<length;++k)       {         while(!(UCSRA&(1<<RXC)));         cmd\[k\] = UDR;         while(!(UCSRA&(1<<RXC)));         cmd\[++k\] = UDR;         while(!(UCSRA&(1<<RXC)));         cmd\[++k\] = UDR;         while(!(UCSRA&(1<<RXC)));         cmd\[++k\] = UDR;        }        while(!(UCSRA&(1<<RXC)));        if(UDR != 0xe0)          return;        while(!(UCSRA&(1<<RXC)));        if(UDR != 0x07)          return;         for (k = 0;k<length;++k)        {          dir = (cmd\[k\] & 0xf0) >> 4;          speeds = (cmd\[k\] & 0xf) * 17;          time = 0;          time = time | cmd\[++k\];          time = time << 8 | cmd\[++k\];          time = time << 8 | cmd\[++k\];          switch (dir)          {          case 8:            stoprun();            break;          case 0:            gforward();            break;          case 1:            gback();            break;          case 2:            turnLeft();            break;          case 3:            turnRight();            break;          case 4:            gLF();            break;          case 5:            gRF();            break;          case 6:            gLB();            break;          case 7:            gRB();            break;          case 9:            penWrite();            break;          case 10:            penRise();            break;          default: stoprun();         }        }    cmd\[0\] = UDR;     }   } } //timer2 for servo control void timer2() {   noInterrupts();   TCCR2 = 0x0f;       //CTC,1024   OCR2 = 155;   TIMSK |= (2 << 6);   interrupts(); } //each interrupt 25hz ISR(TIMER2\_COMP\_vect) { //two intrupt as a circle    if(secondT == 0)    {     digitalWrite(servo,HIGH);     secondT++;     flag = 0;    }    else    {     secondT = 0;    }    //finished in 10ms    for(count = 0;count<127;count++)    {      if(flag == 1)      {       return;      }      //for(i = 0;i != 120;i++);      delayMicroseconds(76);      if(servoTime == servoValue)      {       digitalWrite(servo,LOW);       flag = 1;       servoTime = 0;       //secondT++;       return;      }      else      {        servoTime++;      }    } } //stop void stoprun() {   digitalWrite(L1,LOW);   digitalWrite(L2,LOW);   digitalWrite(R1,LOW);   digitalWrite(R2,LOW);   delay(time); } void gforward() {   //leftmotor   analogWrite(L1,speeds);   digitalWrite(L2,LOW);   //right motor   analogWrite(R1,speeds);   digitalWrite(R2,LOW);   delay(time);   digitalWrite(L1,LOW);   digitalWrite(R1,LOW); } //goback void gback() {   //leftmotor   analogWrite(L1,speeds);   digitalWrite(L2,HIGH);   //right motor   analogWrite(R1,speeds);   digitalWrite(R2,HIGH);   delay(time);   digitalWrite(L1,LOW);   digitalWrite(L2,LOW);   digitalWrite(R1,LOW);   digitalWrite(R2,LOW); } void turnLeft() {   analogWrite(L1,speeds);   digitalWrite(L2,HIGH);   analogWrite(R1,speeds);   digitalWrite(R2,LOW);   delay(time);   digitalWrite(L1,LOW);   digitalWrite(L2,LOW);   digitalWrite(R1,LOW);   digitalWrite(R2,LOW); } void turnRight() {   analogWrite(L1,speeds);   digitalWrite(L2,LOW);   analogWrite(R1,speeds);   digitalWrite(R2,HIGH);   delay(time);   digitalWrite(L1,LOW);   digitalWrite(L2,LOW);   digitalWrite(R1,LOW);   digitalWrite(R2,LOW); } void gLF() {   analogWrite(L1,speeds/2);   digitalWrite(L2,LOW);   analogWrite(R1,speeds);   digitalWrite(R2,LOW);   delay(time);   digitalWrite(L1,LOW);   digitalWrite(L2,LOW);   digitalWrite(R1,LOW);   digitalWrite(R2,LOW); } void gRF() {   analogWrite(L1,speeds);   digitalWrite(L2,LOW);   analogWrite(R1,speeds/2);   digitalWrite(R2,LOW);   delay(time);   digitalWrite(L1,LOW);   digitalWrite(L2,LOW);   digitalWrite(R1,LOW);   digitalWrite(R2,LOW); } void gLB() {   analogWrite(L1,speeds/2);   digitalWrite(L2,HIGH);   analogWrite(R1,speeds);   digitalWrite(R2,HIGH);   delay(time);   digitalWrite(L1,LOW);   digitalWrite(L2,LOW);   digitalWrite(R1,LOW);   digitalWrite(R2,LOW); } void gRB() {   analogWrite(L1,speeds);   digitalWrite(L2,HIGH);   analogWrite(R1,speeds/2);   digitalWrite(R2,HIGH);   delay(time);   digitalWrite(L1,LOW);   digitalWrite(L2,LOW);   digitalWrite(R1,LOW);   digitalWrite(R2,LOW); } void penWrite() { TIMSK |= (2 << 6); servoValue = 14; delay(150); delay(time); } void penRise() {  TIMSK |= (2 << 6);  servoValue = 20;  delay(150);  delay(time); ### } ### 參考資料 [成大學生製作的寫字機器人](https://autowrite.hackpad.com/pgtrerXo4mM) [大人的科學自動手寫機](http://otonanokagaku.net/magazine/vol41/index.html) [doodle bot](http://www.instructables.com/id/Building-a-Doodle-Bot-kit-from-DAGU/#step0) [drawing delta](http://jarkman.co.uk/catalog/robots/drawingdelta.htm) [MURtle - Drawing Robot](http://letsmakerobots.com/node/37495) [piccolo](http://www.piccolo.cc/) [The Bipolar ORD Bot](http://www.buildlog.net/blog/2015/01/the-bipolar-ord-bot/) [http://www.trend.org/tech_info.php?pid=26](http://www.trend.org/tech_info.php?pid=26) [討論連結](https://www.facebook.com/groups/openlab.taipei/permalink/941969892488660/)