---
# System prepended metadata

title: 口丁寫字童子
tags: [hackpad]

---

---
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/)


