Open Source
    Microcontroller
    Automatic Control
    Coding Notes

2014年5月29日 星期四

利用Processing 顯示 Arduino 加速規模組姿態

清晨7:56 Posted by Unknown , , 1 comment



加速規,用來量測加速度的裝置,但也因為重力恆向下,也可以拿來測量重力於其他方向的分量,進而推算得到物體目前的姿態。

本篇文章主要討論如何從三軸加速規擷取訊號到Arduino,並透過序列傳輸將三軸的分量傳至電腦由Processing接收,並將物體姿態繪出顯示於螢幕。




Arduino ADXL345 三軸重力加速模組 




本篇範例所使用的 Arduino ADXL345 三軸重力加速模組 (數位輸出) Datasheet 。


我們有兩種方法能讓Arduino與加速度模組溝通,分別是SPI 跟 I2C ,文章使用 I2C 通訊作為範例,主要因為Arduino 提供非常方便的 library 供我們使用。


我們可以在Arduino 的 Wire Library 說明頁中看到 Uno 的腳位是  A4 (SDA),  A5 (SCL),因此加速度模組與Arduino 的連接也非常的簡單。


ADXL345 的VCC 接到 Arduino 的 3.3V ,GND 接到 Arduino 的 GND,SDA 接到 Arduino 的 A4,SCL 接到 Arduino 的 A5,這樣接線就完成了。



Arduino 與加速度感測器的連接


硬體準備就緒後,就進入軟體部分。


首先下載 Arduino 的 Wire Library,解壓縮後放到 arduino 的Library資料夾中。


接著下載 ADXL345 的 Arduino Library 。若不想用別人的程式碼可以可以照著 ADXL345 的 Datasheet 自己試著寫,但在此我們先用別人寫好的來修改,畢竟我們的目的是在Processing 上顯示出姿態。


在 ADXL345 的 Datasheet 第10頁中解釋了如何透過I2C跟 ADXL345 溝通,也因此可以看到 Library 的前段是在配置記憶體位置,為了避免記憶體衝突。


為了將資料傳至Processing(注意:鮑率要對應),我們必須修改一小段程式碼,請將 readAccel() 的副程式改寫成如下:

void readAccel() {

  uint8_t howManyBytesToRead = 6;

  readFrom( DATAX0, howManyBytesToRead, _buff); //read the acceleration data from the ADXL345



  // each axis reading comes in 10 bit resolution, ie 2 bytes.  Least Significat Byte first!!

  // thus we are converting both bytes in to one int

  int x = (((int)_buff[1]) << 8) | _buff[0];  

  int y = (((int)_buff[3]) << 8) | _buff[2];

  int z = (((int)_buff[5]) << 8) | _buff[4];




    Serial.write('X');

    Serial.write(_buff[1]);

    Serial.write(_buff[0]);

    Serial.write(_buff[3]);

    Serial.write(_buff[2]);

    Serial.write(_buff[5]);

    Serial.write(_buff[4]);



}

因為 Serial 是一個 byte 一個 byte 傳送,因此我們在Arduino 讀取到的高位元與低位元的值就直接傳至 Processing 再處理,不用再多做一個步驟,並加上一個 'X' 當做起始字元。

接下來就是Processing 的程式碼(再次提醒:鮑率要對應!):

import processing.serial.*;



Serial serial;

int[] invalue=new int[6];

int[] Val=new int[3];



float G=250;

float xacc;

float yacc;

float zacc;



void setup(){

  size(600,300,P3D);

  fill(0, 102, 153);

  println(Serial.list());

  serial=new Serial(this,Serial.list()[3],9600);

  println("Start!");



}



void draw(){

  if(serial.available()>20){

      if(serial.read()=='X'){

        int sign = 1;

        invalue[1]=serial.read();

        invalue[0]=serial.read();

        invalue[3]=serial.read();

        invalue[2]=serial.read();

        invalue[5]=serial.read();

        invalue[4]=serial.read();

          if( invalue[1]>>7 == 1 ) {

             invalue[1]= 255 - invalue[1];

             invalue[0] = 256 - invalue[0];

             sign = -1;

          } else {

            sign = 1;

          }Val[0] = sign * ( invalue[1] << 8 | invalue[0] );

          if( invalue[3]>>7 == 1 ) {

             invalue[3]= 255 - invalue[3];

             invalue[2] = 256 - invalue[2];

             sign = -1;

          } else {

            sign = 1;

          }Val[1] = sign * ( invalue[3] << 8 | invalue[2] );

          if( invalue[5]>>7 == 1 ) {

             invalue[5]= 255 - invalue[5];

             invalue[4] = 256 - invalue[4];

             sign = -1;

          } else {

            sign = 1;

          }Val[2] = sign * ( invalue[5] << 8 | invalue[4] );

          println(Val);

          background(0);

          textSize(20);

          translate(width/2, height/2);

          text("z-acc:"+Val[2],-250,-100);

          text("x-acc:"+Val[0],-250,-80);

          text("y-acc="+Val[1],-250,-60);



          rotateX(asin(Val[1]/G));  //xacc

          rotateZ(asin(-Val[0]/G));   //Yacc

 

          box(100,20,100);

        }

     }

    }

注意到程式碼紅、藍、綠的地方,這裡我們要對接收到的高低位元資料做處理,ADXL345 傳過來的資料要合併這裡整理出一個規則:
如果值為正,則不改變,但如果值為負,則全部的0變為1;全部的1變為0。

也就是說若值為 256,則高低位元分別是 00000001 00000000;

而值為 -256,則高低位元是 11111110 11111111;
也因此有了上面的處理步驟。


得到資料後,便透過重力在個座標的分量來計算出現在加速規的姿態,這步驟需要一點空間概念,不過並不難,思考一下應該可以得到跟筆者一樣的答案。

以上就是如何利用Processing 顯示 Arduino 加速規模組姿態。




2014年5月13日 星期二

利用 Arduino + Processing 做簡易示波器(Oscilloscope)

中午12:28 Posted by Unknown , , 1 comment


Arduino 內建 10 位元的ADC,可以整合各式感測器,像是磁力感測器、紅外線感測器、旋轉編碼器或是加速規等等,利用 Arduino 量測各種類比訊號,通常只能透過串列埠(Serial port)的監控視窗來檢查讀值的狀況,串列埠畫面的快速刷新有時候不好判斷訊號的前後關係,這時候利用 Processing 豐富的視覺化輸出,便可以做一個簡易的示波器介面。

Processing 的 serial library 可以讓 Processing 使用串列埠的通訊功能,當 Arduino 將感測器的讀值丟到串列埠時,Processing 便可以從那將他們抓進來做繪圖處理。 串列阜通訊是由 UART 傳輸器控制,一般以 10 位元為一個傳輸組,包含 1 位元的起始位元, 1 位元的停止位元,以及夾在兩者之間的 8 位元資料,這 8 位元才是我們真正要傳輸的感測值。 8 位元亦即 0 ~ 255,因此當感測器的值大於 255 (或是資料宣告的型態大於 8 位元,如:float ),在串列埠傳輸時便需要拆成數個傳輸組,待接收之後才能將他們一一合併為原始讀值。

串列埠通訊以鮑率 (Baud rate) 為資料傳輸速度的單位,例如鮑率 9600 Bd,表示每秒可傳輸 9600 個位元,按前述可知,此時每秒傳輸 960 個傳輸組,也就是 960 的位元組,所以是 960 Bps,其實很慢,如果傳輸的值較大,或占用較多的位元組,速度將會更慢。因此,使用這種方法做的示波器,其頻寬最高也只在數百 ~ 數 kHz 之間,變化太快的感測值,示波器是量不到的,若進一步考量 Arduino 與 Processing 處理接收資料的速度,頻寬更是會低到不行喔

以下簡介簡易示波器的實現流程:



$ Arduino端 $


為求 demo 方便,利用 Arduino 來模擬感測器讀值的產生,並將其不斷的丟到串列埠上。



1. 在 setup() 的設定中, 以 Serial.begin(115200) 初始化串列通訊,115200為傳輸鮑率,依需求可變更,前提是接收端所設鮑率必須跟此值一樣。



2. 在 loop() 中,產生虛擬的時變感測值,例如低頻的弦波訊號,值域為 0 ~ 1023 的整數intValue,有接上感測器的話,這裡便是存入感測器的原始讀值。




3. 由於 Arduino 的 int 變數會佔用 2 個位元組 (16個位元),為確保傳輸資料的正確性,在串列傳輸前必須先拆解為2 個獨立的位元組,可分別設為高位元 ( byteHigh ) 與低位元組 (byteLow),以 uint8_t ( unsigned int 8 bit ) 宣告這兩個變數。

   a. intValue >> 8 : 將 intValue 的二進位碼全部向右移 8 個位元,用這個方法取高位元組。

    b. intValue  & 0xff : 由 intValue 二進位碼的邏輯運算取出低位元組。














4. 準備好傳輸的位元組,就可以依序丟到串列埠了,但由於資料是連續不間斷的傳輸,資料又被分成兩個傳輸組,為了讓接收端 ( Processing ) 知道哪裡是資料的開端,在每個讀值前應要加上一個可供判別的標頭字元,例如 'H' 字元,讓傳輸資料依序為 標頭字元、高位元組、低位元組,如此重複。

















以上完成 Arduino 端的設定,便可以直接燒入單晶片,Arduino便會不間斷的將虛擬感測值送到串列埠。





$ Processing 端 $


首先 Processing 64 bit 版本不支援 Serial 傳輸應用,所以若要使用 Serial  librery,必須下載 Processing 32 bit 的版本。



1. import Serial 的 library ,並宣告 Serial 物件 Ard。

















2. 在 setup() 中,建構 Serial 物件,Serial.list() 會將連上電腦 com port 的裝置都列出來,通常沒有連其他裝置的話, Arduino 會列在第一個,在此例中其名稱為 COM4,此名稱應該與 Arduino 燒綠時的串列埠名稱相同。後面指定傳輸鮑率。
















3. 建立一個陣列 Rec[],用來暫存串列埠抓來的值。並利用自訂函數 receive() 接收串列埠的值。由於感測值的大小 mapping 為視窗的高度,而陣列 Rec[] 的長度為視窗的寬度,因此陣列中的每一個元素便可以對應到視窗中的一個座標,以此達成讀值的視覺化輸出。
















4.  自訂的函數 receive() 中,預設接收值 Val 為 -1,直到確實收到值在回傳。 由於已經知道三個位元組為一個完整的感測資料,透過 p.available() 確認 Serial buffer 中有足夠的位元組再開始接收值。以 p.read() 抓取 buffer 中的資料,直到抓到標頭字元確認讀值的起始端。每從 buffer read 一次,buffer 後面的資料便會向前遞補,因此抓到標頭字元後,就可以知道下兩次 read 分別為資料高位元組與低位元組。將兩者合併為 16 位元資料完成感測值還原。
















5. 在迴圈 draw() 中,每次僅  receive() 一個感測值,並從視窗左方出現,剩餘舊資料全部向視窗右側平移,將所有資料點連接為曲線,視覺上便可以看到如弦波的波形並呈現資料持續刷新。





2014年5月8日 星期四

將 Processing 程式完整打包成 jar 檔案

上午10:28 Posted by Unknown , , No comments

Processing 這款開源軟體,對於從事科技藝術的創作工作者來說肯定不陌生,以其繪圖功能見長,即便不是專業的程式設計師,透過幾行簡單的程式碼,就可以產生有趣的視覺輸出,而越來越豐富的 library 資源與社群,使得以 processing 開發圖形介面、遊戲、網頁或是 android app 程式都日趨方便。


Processing 支援跨平台,其程式語言以 JAVA 為基礎,程式編輯腳本 (script) 被稱為 sketch,並儲存為 *.pde 檔,當程式撰寫完成,雖然可以直接按下 run 執行,但其實我們更應該將程式打包成一可獨立運作的應用程序,這時候第一個想到的就是 Processing 內建的 Export Application 功能。 較舊的 Processing 版本,支援跨平台的匯出,也就是在 Windows下編輯好的程式,可以匯出檔案給 Macintosh或是 Linux 系統執行,但自 Processing 2.1 起,匯出功能僅針對開發者的作業系統生成出相應的檔案,如在 Windows (x86) 下就只產生 *.exe 檔,附帶一包相連的 lib 資料夾,雖然多了一些匯出 JAVA 的功能,但我覺得還是不太方便。

Processing 2.1+ 匯出功能
Processing 舊版匯出功能















執行匯出的 *.exe 檔仍然會去抓 lib 資料夾裡的 java(*.jar) 檔案,表示這 Processing 小程式仍然不是獨立運作,這還蠻詭異的 ....... 既然是基於 JAVA 語言的開發,而 JAVA 的優勢之一便是其可跨平台執行的能力,怎麼搞了半天還是綁手綁腳呢? 我們需要更完整的打包方法。  

lib 資料夾裡的 *.jar 檔
不太方便的 *.exe 檔


為了打包 Processing 程式,網路上可找到非常實用的小工具:

1. SvgExe.jar
2. ProcessingLibrary.jar

主要的打包流程是利用 SvgExe.jar 這個程式將 Processing 匯出時產生的 *.jar 檔案與 Processing 的原生資料庫( ProcessingLibrary.jar) 整合一起匯出,便可產生一份跨平台執行的獨立 *.jar 檔案,只要是有安裝較新版 JRE 的系統,基本上以滑鼠雙擊就可以執行開啟,非常方便。

當你在 Processing 執行匯出時,在 lib 資料夾中會產生一份與原檔名相同的 *.jar 檔,將其與下載的 ProcessingLibrary.jar 一同匯入 (Add)  SvgExe.jar 中:

在 Step 1: Classes 中將 *.jar 檔案 Add 進來


對於較單純的應用, Step 1 完之後可直接跳到 Step 5: Output,在此輸入主要類別名與檔案輸出路徑,基本上這些也都跟原檔名相同,按下GO,便可產生打包好的 *.jar 檔案:

Step 5: Output 設定



雙擊 *.jar 檔彈出繪圖視窗,直接執行 Processing 的繪圖內容,大功告成。

產生的 *.jar 檔案與執行視窗



更詳細的說明,可以參考這裡: