オリジナル・レーザープロッター その1-刻印データ作成プログラム
刻印したい画像をパソコンでデータ・ファイルに変換し、SDカードに書き込んで本体のArduinoの入力にします。Arduinoではそれを単純に読み込みながら刻印するだけというのが最も単純でらくちんかと思います。
Arduino側で刻印時に加工できないこともないわけですが、好き勝手な機能、たとえば操作時間短縮のために往復刻印するとか、好きな出力順序にするとかには、事前にPC側で処理しておくのが簡単ですし、PC側なら処理する際の容量に制約が生じません。PC空間なので、入出力が面倒ならファイル全部よんでしまうとかも自由自在ですし。
画像の形式はJPGやBMPですが、ここでご紹介する簡単な言語(フリー)では、画像種類がもっとたくさん扱えるかもしれません。しかし必要性がないので筆者は試したことがありません。こんなところで汎用の画像処理をする必要もないので、JPGだけとかBMPだけとかに決めておけばシンプルですね。「ないよりもあった方が良い」程度の機能は、ない方が良いのが常ですから。
◎Microsoft Small Basicのご紹介
PCなら、PythonでもCでも何の言語でも使えます。しかしWindowsの場合に、ちょっとプログラムを作るのに一番単純で手っ取り早いのはMS Small Basicだと筆者は勝手に思います。作成時間が僅かで済むし、WIndows自体との相性もいい、、、それはあたりまえか。
pythonやgccなど便利な言語はたくさんあり、筆者も普段はそれらを使っていますが、関連するコンポネントと相性が悪いことなどで環境の整合性確保に手間がかかることがあります。VB等は過去のバージョンアップ都度の互換性対応で少し嫌い・・^^;
現在は、仕事や複雑なものを作るとき以外、つまり趣味にはこの単純なSmallBasicを使うことが多く、時間もエネルギーも最小でたいへん楽をしています。
なにせ、型とか変換とかの概念がなく、数値は常に最大桁数精度であるなどの超単細胞(!)が筆者から見ると却って魅力的。それどころか文字と数値との区別すらありません!よって機能はとても限られていますが、使う側の工夫次第。例えば多次元の配列がないため代わりに1行のベクトル内に自分で作るとか。その代わり、わけのわからない環境設定問題に遭遇しないのがたいへん気楽です。つまり全ては自分の手の内というわけで。
言語としては構造化文だけでなく、言語としてはタブーとされがちなGoto(ジャンプ)が許容され、却って書きやすいこともある。逆にSwitch Case (Select When)がないためIf文を並べなければならないことがある。
なにはともあれマイクロソフトからダウンロードしてみてください。ネットでの情報は整っていて、手元にマニュアルが要らない簡単さです。
◎刻印プログラム内での処理
ソースプログラム例を下へ掲載しておきます。短時間で作ったもので、きれいなプログラムとはいえませんが。
処理内容としては
- 画像のある場所へのパスを定義。
- 指定画像を取り込む。
- 画像のサイズ情報を読むなどして、刻印データのヘダーとして出力。
- 画像の各ピクセルの色情報を輝度情報に変換して、一定輝度以下のところを刻印点にする。
- その座標(XY座標系でも他の座標系でも)を刻印データへ出力。
- ファイルの終わり情報(EOF)を書き込む
といったところでしょうか。
なお、Arduino側の処理を楽にするため、数値の桁をそろえてレフトゼロを入れるのですが、この言語だとこれが結構面倒。生活の知恵で必要な桁数の最小値を加算してアウトプットします(4桁なら1000を足せば必ず4桁に揃えられる)。使うArduino側でそれを引きます。
EOFにはタブーの9999をつかってます。そのため、画像の幅最大を8999ピクセルに制限しますが、 そんなに大きい画像は普通はないですね。
myTitle="New Laser Cutter - Step-A V00"
'Text window
TextWindow.Title=myTitle+" (c)2019 Akira Tominaga"
TextWindow.BackgroundColor="Black"
' Get data path
myPath=File.GetSettingsFilePath()
myPath=Text.GetSubText(myPath,1,8)
myPath=myPath+"Dat3\"
' Prepare picture
TextWindow.WriteLine("")
KeyIn:
TextWindow.Write(" Please type-in PIC#. -> ")
keyinN=TextWindow.Read()
myPict=myPath+"PIC"+keyinN+".bmp"
PsizeX=ImageList.GetWidthOfImage(myPict)
If PsizeX>18 Then
Goto ImageOK
Else
TextWindow.WriteLine("*** No such PCL#.bmp ***")
Goto Keyin
EndIf
ImageOK:
PsizeY=ImageList.GetHeightOfImage(myPict)
'Graphics window
GraphicsWindow.BackgroundColor = "Black"
GraphicsWindow.Width=PsizeX*2
GraphicsWindow.Height=PsizeY
GraphicsWindow.Title=myTitle
inPic=ImageList.LoadImage(myPict)
GraphicsWindow.DrawImage(inPic,0,0)
'Setup output file
myFile=myPath+"PICT"+KeyinN+"A.txt"
lnum=1 'PsizeX,PsizeY characters with left zero
szX=PsizeX+1000
szY=PsizeY+1000
X=PsizeX/2
Y=PsizeY/2
CalcPC()
header=szX+","+szY+","+rx10+","+thetax10000
File.WriteLine(myFile,lnum,header)
'Get RGB data and convert them to Brightness data
For Y=0 To PsizeY-1
'TextWindow.Write("Y=")
'TextWindow.WriteLine(Y)
For X=0 To PsizeX-1
color=GraphicsWindow.GetPixel(X,Y)
getkido() ' convert RGB to Brightness
gcolor="#000000"
txdata=""
If kido<128 Then
gcolor="#FFFFFF"
CalcPC() 'Calculate Polar Coordinates
'Set text data
X4=X+1000
Y4=Y+1000
txdata=X4+","+Y4+","+rx10+","+thetax10000
lnum=lnum+1
File.WriteLine(myFile,lnum,txdata)
EndIf
GraphicsWindow.SetPixel(X+PsizeX,Y,gcolor)
EndFor
EndFor
lnum=lnum+1
eof=9999
File.WriteLine(myFile,lnum,eof)
Sound.PlayBellRing()
Sub getkido
HexV=0
Hexc=Text.GetSubText(color,2,1)
Hex2V()
R10=HexV
Hexc=Text.GetSubText(color,2,1)
Hex2V()
R01=HexV
Rv=R10*16+R01
Hexv=Text.GetSubText(color,3,1)
Hex2V()
G10=HexV
Hexc=Text.GetSubText(color,4,1)
Hex2V()
G01=HexV
Gv=G10*16+G01
Hexc=Text.GetSubText(color,5,1)
Hex2V()
B10=HexV
Hexc=Text.GetSubText(color,6,1)
Hex2V()
B01=HexV
Bv=B10*16+B01
Kido=0.299*Rv+0.587*Gv+0.114*Bv
EndSub
Sub Hex2V
If Hexc < "9" Then
Hexv=Hexc
EndIf
If Hexc="A" Then
HexV=10
EndIf
If Hexc="B" Then
HexV=11
EndIf
If Hexc="C" Then
HexV=12
EndIf
If Hexc="D" Then
HexV=13
EndIf
If Hexc="E" Then
HexV=14
EndIf
If Hexc="F" Then
HexV=15
EndIf
EndSub
Sub get4d 'change chrN to chrN4
getdigits()
chrN4=chrNx
if chrN<10 Then
chrN4="000"+chrN4
ElseIf chrN<100 Then
chrN4="00"+chrN4
ElseIf chrN<1000 Then
chrN4="0"+chrN4
EndIf
EndSub
Sub get5d 'change chrN to chrN5
getdigits()
chrN5=chrNx
if chrN<10 Then
chrN5="0000"+chrN5
ElseIf chrN<100 Then
chrN5="000"+chrN5
ElseIf chrN<1000 Then
chrN5="00"+chrN5
ElseIf chrN<10000 Then
chrN5="0"+chrN5
EndIf
EndSub
Sub getdigits
gd=1
gdl=Text.GetLength(chrN)
chrNx=Text.GetSubText(chrN,gd,1)
Loopgd:
If gdl<gd+1 Then
Goto Outgd
EndIf
gd=gd+1
chkd=Text.GetSubText(chrN,gd,1)
If chkd="." Then
Goto Outgd
EndIf
chrNx=Text.GetSubText(chrN,1,gd)
Goto Loopgd
Outgd:
EndSub
Sub CalcPC 'Calculate Polar Coordinates
'from (x,y) to (rx10,thetax10000)
' Get values from origin
xx=X-PsizeX/2 ' set orgin X0 to center
yy=PsizeY+5-Y ' set origin Y0 to bottom+5(avoiding zero division)
' Calculate Radius
r=Math.Power(Math.Power(xx,2)+Math.Power(yy,2),0.5)
chrN=r*10
get5d()
rx10=chrN5+10000
' Calculate Theta
theta=Math.ArcCos(xx/r)
chrN=theta*10000
get5d()
thetax10000=chrN5+10000
EndSub
このプログラムでは任意の画像を刻印データに換えるわけです。
例えば次の左の図を処理すると刻印部として右の図が現れたらできあがり、鐘がチーンとなります。
出力ファイルは次のようになります。1行目はヘダーでXサイズ、Yサイズ、そして別のカッターメカニズムを使う場合のためのヘダーです。2行め以下は刻印データでX、Y、右の二つは別のメカニズム用のデータです。
もちろん白黒からでなくとも刻印データが得られます。次は一例。
別の回に書きますが、Arduino側での読み込みはごく単純で入出力域も1バイトしか要りません。
つまり、与えるデータの順番で刻印するだけですので、往復方向で刻印するには行うこのデータの順番を変えてやればよいだけです。
順番を変えた例は次の様なファイルです。順番変更は別のプログラムで行うのが簡単。処理はあっという間に終わります。なお90ステップほどで作った例は一応下に添付しますが、きれいなプログラムではありません。皆様お好きなようにおつくり下さい。Arduinoでは1バイトずつ読むので、こういう形式でも順次よみこむわけです。
両方向の刻印では次の動画のように高速です。メカに遊びがあるときは、一方向に揃えないとこうはいきませんね。
刻印ファイルを両方向刻印パターンへ変換するプログラムを次に示します。
これは自分用ですが、皆様もっと分かり易いプログラムをお作り下さいね。もちろん言語もどれでもよいわけなので。
myTitle="New Laser Cutter - XY Step B V00"
'Text window
TextWindow.Title=myTitle+" (c)2019 Akira Tominaga"
TextWindow.BackgroundColor="Black"
' Get data path
myPath=File.GetSettingsFilePath()
myPath=Text.GetSubText(myPath,1,8)
myPath=myPath+"Dat3\"
' Prepare input file
TextWindow.WriteLine("")
KeyIn:
TextWindow.Write(" Please type-in PICT file#. -> ")
keyinN=TextWindow.Read()
InFile=myPath+"PICT"+keyinN+"A.txt"
InLnum=1
InText=File.ReadLine(InFile,InLnum)
TextWindow.Write("*******")
TextWindow.Write("InLnum=")
TextWindow.Write(InLnum)
TextWindow.Write(" InText=")
TextWindow.WriteLine(InText)
InChk=Text.GetSubText(InText,1,1)
If InChk>=1 Then
Goto InFileOK
EndIf
Goto KeyIn
InFileOK:
'Prepare output file
OutFile=myPath+"PICT"+KeyinN+"B.txt"
' Transfer header (XYsize+PCcenter)
OutLnum=1
OutText=InText
File.WriteLine(OutFile,OutLnum,OutText)
TextWindow.Write("OutLnum=")
TextWindow.Write(OutLnum)
TextWindow.Write(" OutText=")
TextWindow.WriteLine(OutText)
' Convert fileA to fileB
OutReverse=0
InLnum=InLnum+1
InText=File.ReadLine(InFile,InLnum)
TextWindow.Write("InLnum=")
TextWindow.Write(InLnum)
TextWindow.Write(" InText=")
TextWindow.WriteLine(InText)
OutText=InText
OutRec=1
Ysave=Text.GetSubText(InText,6,4)
MainLoop:
InLnum=InLnum+1
InText=File.ReadLine(InFile,InLnum)
TextWindow.Write("InLnum=")
TextWindow.Write(InLnum)
TextWindow.Write(" InText=")
TextWindow.WriteLine(InText)
InChk=Text.GetSubText(InText,1,4)
If Inchk=9999 Then
OutLnum=OutLnum+1
File.WriteLine(OutFile,OutLnum,OutText)
TextWindow.Write("OutLnum=")
TextWindow.Write(OutLnum)
TextWindow.Write(" OutText=")
TextWindow.WriteLine(OutText)
OutText=InText
OutLnum=OutLnum+1
File.WriteLine(OutFile,OutLnum,OutText)
TextWindow.Write("OutLnum=")
TextWindow.Write(OutLnum)
TextWindow.Write(" OutText=")
TextWindow.WriteLine(OutText)
Goto Eof
EndIf
Ychk=Text.GetSubText(InText,6,4)
If Ychk=Ysave Then
Goto accumText
Else
Ysave=Ychk
Goto accumOut
EndIf
accumText:
If OutReverse=0 Then
OutText=Text.GetSubText(OutText,1,23*OutRec-2)+"##"+InText
Else
OutText=Text.GetSubText(InText,1,21)+"##"+OutText
EndIf
OutRec=OutRec+1
Ysave=Ychk
Goto MainLoop
accumOut:
OutLnum=OutLnum+1
File.WriteLine(OutFile,OutLnum,OutText)
TextWindow.Write("OutLnum=")
TextWindow.Write(OutLnum)
TextWindow.Write(" OutText=")
TextWindow.WriteLine(OutText)
OutText=InText
If OutReverse=0 Then
OutReverse=1
Else
OutReverse=0
EndIf
Goto MainLoop
Eof:
TextWindow.Writeline("Eof")
Sound.PlayBellRing()
なお、作製したプロッターより大きな画像を使うときはArduino側に倍率パラメータを設ければ問題なく使えますが別の回に説明したいと思います。ただしメカの動きが端を超えないようにリミッターをつけるとよいですね。これも別の回に書きます。
では、今回はこのへんで。
記事の最後に注意書きを入れました。必ずご覧くださいね。
回路等は前回の記事にあります:
次に本体のプログラムがあります。
次に基板の作り方などがあります。
以下、重要な注意です。レーザーは失明や傷害、火災等の危険を伴い、周囲にも危険がおよびます。関連法規と取扱い基準(http://kikakurui.com/c6/C6802-2011-01.html)などに従って、正しく管理してください。この記事をみて自作される場合も全て自己責任でお願いします。
©2019 Akira Tominaga, All rights reserved.