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

20150502 進度2
後來把萬向輪上油潤滑之後,車子行進路立刻改善了,直很多了。
裝上遮斷開關,配合車子套件的碼盤。

計劃拿手機或筆電用藍芽和小車連線,指揮小車往前走多少公分、到定點後轉幾度、什麼地方要下筆。
小車接受指令並且自己修正。
20150506
馬爸把前輪換成這個。
原先的輪子在轉向時會因為本身旋轉歸位得時間差,而造成行進路線的誤差。
這個輪子就沒這個問題了。

20150507
用遮蔽膠帶量輪子圓周長:


20150511
1. 改用3D列印的機構來固定馬達和光遮斷開關
2. 換上抓地力比較好的輪子
3. 換回原來的大顆萬象輪
車子的操控一整個改善了!

20150513
基本教練:第一課
其實是想要寫一個"口"字....
[https://www.youtube.com/watch?v=36ZA0ze9PFw](https://www.youtube.com/watch?v=36ZA0ze9PFw)
還沒有做到校正的部份
以目前的機構沒有辦法做到精準,所以寫字應該會像是塗鴉。既然是塗鴉,就真的來塗鴉好了。
在手機上亂畫

因為用車子來寫字,也就是要用輪子走到定點,而車子的控制方式不像是CNC可以直接告訴它走到哪一個座標上面,而要轉方向盤、踩油門。
所以用手機計算:把在螢幕圖形的X,Y座標轉成極座標,然後再轉成轉方向盤、踩油門的指令,來控制車子。

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