很多同學對硬體控制 GH 很感興趣,正好今天有同學自己寫程序連外設遇到點小問題,那就正好解答問題順帶做個教程了。這位 Zzzz 同學的帖子如下:
http://bbs.shaper3d.com/thread-24815-1-1.html
Read me
本教學中的例子,用 Grasshopper 的 Firefly 插件做是非常簡單的,但是這篇教學不用Firefly,而是用C#寫腳本來實現。
如果不是對C#程序感興趣,只是想做這種控制效果的話,這個教學粗略瀏覽一下,當作了解製作過程就好,然後直接去下載 Firefly,Firefly 使用簡單而且很強大很穩定。Firefly 的中文教程繁體論壇有。
教學
我們要製作的就是通過旋轉可變電阻的旋鈕來改變Grasshopper在場景中生成物件的形態。效果如下:
一、連線和原理概述
我們要用到的材料是一塊Arduino UNO控制板、一個可變電阻,一塊麵包板和若干根電線。
首先按照下圖所示將材料全部連接起來。
上面這張圖中連線的意思是,可變電阻最外面的兩頭,連接在電源上,即紅色線連的是5V的電源輸出,黃色線連接的是GND地線。中間的藍色線是模擬信號,接到控制板的A0模擬信號輸入接口。模擬信號在這裡其實就是電信號,控制板會按照電流或電壓的不同,將這裡輸入的電流和電壓變化翻譯成0到1024之間的數字。
那麼這個例子的整體思路,就是把電阻輸出的模擬信號,通過控制板翻譯成數字以後發送給電腦,我們在電腦上用Grasshopper中的C#腳本讀取這個數字,把這個數字作為影響形態的參數用在幾何體上就可以實現用電阻控制形態了。
二、上傳代碼
將下面的代碼上傳到Arduino控制板中,如何給Arduino上傳代碼屬於 Arduino 硬體使用的基礎知識,網際網路上有非常多的資料介紹,firefly的幫助文檔中也有很詳細的描述,這裡就不贅述了。
大家可以到firefly的官方網站下載firefly和幫助文檔。
10 val=analogRead(potpin); 上面這段代碼的意思是每隔200毫秒,讀取電阻輸入進來的信號轉成數值,然後把數值送到串口中去。
那麼接下來我們就要學習最重要的一步了,如何把這個數值讀取到 Grasshopper 中。
三、通過C#腳本讀取信號
在寫代碼之前我們先解答一下 Zzzz 同學的問題,這位同學的代碼如下:
1System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort();5int val = sp.ReadByte();他遇到的問題是代碼可以執行,但是只要接上外設,就會非常的卡,拔掉外設就好了,這是什麼原因呢?
我們先逐行來分析一下這段代碼:
System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort();
這句的意思是實例化了一個SerialPort(串口)對象,命名為sp,這個串口對象就可以看成是計算機和外設交互數據串口的代表。
sp.PortName = 「COM3″;
這句把串口的名稱設置COM3,也就是說要用sp這個串口對象代表哪個串口。
通過硬體管理器查看一下,正好arduino在我的計算機上也是COM3
sp.BaudRate = 9600;
設置波特率為9600,接入不同的設備,按設備波特率的不同設置為相應的值。
sp.Open();
這句是打開串口。
int val = sp.ReadByte();
A = val;
這兩句是從串口讀取字節,然後把讀取到的值賦值給C#電池的輸出變量A。
sp.Close();
這句是關閉串口。
了解了每個語句的用途,我們再回過頭來看這個連線圖
從圖中可以看到,為了讓代碼能夠不停的從COM3中讀取數據,連接了一個Timer,每隔20毫秒就會執行一次腳本,這樣做是沒有錯的,但是接了Timer 以後,代碼中的sp.Open()和sp.Close()每隔20毫秒就會執行一次,也就是說每20毫秒就要把串口COM3打開並關閉一次,在如此段的間隔內打開關閉硬體接口是非常消耗資源的,這就是卡的原因所在了。
下面我們就動手來改代碼
為了不用每執行一次就打開關閉一次串口,我在電池外面做了一個開關,需要的時候,從外面打開,代碼裡面主要負責讀取數據,用完了,從外面關掉就好了,不用每執行一次就打開關閉一次。
這個電池裡面的代碼如下:
1System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort("COM3", 9600);5 if (sp.IsOpen == false) sp.Open();6 if (sp.IsOpen) Msg = "埠已開啟";7 int data = sp.ReadByte();13 if (sp.IsOpen) sp.Close();14 if (sp.IsOpen == false) Msg = "埠已關閉";雖然現在已經不卡了,但是用現在這個,有時還會出現錯誤,關閉埠以後馬上打開,會發生打不開的情況,程序會告訴你埠被佔用。這種情況打開關閉五次,就會出現至少兩次打不開,就像這樣:
這是因為每執行一次代碼,就實例化一個串口對象,在打開串口的時候,之前一次的串口對象並沒有完全被系統釋放,依然佔用著串口,再打就打不開了。
那我們就不要不停的去實例話串口對象,實例化一次就好了。要如何做呢?
大家看我這個圖就明白了:
我們把實例話的過程放在左邊的電池裡,也就是說整個運行過程只需要在左邊實例話了一個串口對象,傳遞到右邊的電池中就好了,右邊只是負責打開和關閉埠以及讀取數據。這樣打開關閉點的再快也不會出錯了。
左邊電池的代碼:
1System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort("COM3", 9600);右邊電池的代碼:
1System.IO.Ports.SerialPort sp = ( System.IO.Ports.SerialPort) port;6 if (sp.IsOpen == false) sp.Open();7 if (sp.IsOpen) Msg = "埠已開啟";9 string data = sp.ReadExisting();10 if (data.Contains("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\r"))12 string[] sArray = data.Split('\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\r');21 if (sp.IsOpen) sp.Close();22 if (sp.IsOpen == false) Msg = "埠已關閉";注意,我為了讀到電阻發來的信號,這裡讀取數據用的是sp.ReadExisting()
好了,現在打開串口以後,就可以正確讀出數據了。
四、用串口數據控制形態
現在就可以把我們寫好的腳本放到做好的sketch裡面用電阻去控制形態了,這一步學過Grasshopper的同學都懂得。
例如最前面視頻裡演示的放到一個更具輸入值不同變換高度的。
要下載做好的文件,或對此話題有任何疑問,請點擊閱讀原文前往論壇。
關注公眾帳號Shaper3d獲取更多Rhino相關資訊。