【教學】 Arduino 101結合 Processing 互動滾球遊戲

本文章要告訴您如何結合 Processing IDE 與 Arduino 101 開發板來設計一個滾球遊戲。藉由 Arduino 101 開發板上的加速度計與陀螺儀感測器來感測板子於空間中的傾斜、位移與轉動的狀態,藉此控制球在畫面上的四處移動,就好像零食乖乖附贈的小玩具一樣。

作者/攝影 曾吉弘
時間   2小時
難度

★★★★★

材料表
  • Arduino 101開發板(亦可使用KEYES UNO陀螺儀/加速度感測器 作為取代)
  • 個人電腦 (作業系統可用 Windows, Mac OSX 與 Linux,本範例使用 Windows 7)
  • Processing IDE  (2.0 or 3.0 皆可,本範例使用 2.2.1)
  • Arduino IDE 1.6.x 版以上

 

本範例是阿吉老師於海洋大學機械系學期課程的課堂小挑戰,修改 Arduino官網的範例來製作一個滾球遊戲,藉由搖擺 Arduino 101 開發板來控制 Processing 畫面上的球移動,這運用到了Arduino 101的加速度計與陀螺儀感測器,並理解如何透過序列通訊讓 Arduino 與 Processing (您的PC) 彼此溝通,後續要改成藍牙也是沒有問題的。

這類型的遊戲通稱為 labyrinth,玩法都差不多就是控制球走到終點,iOS 或 Android 都有非常多可以下載,您可以多載幾個來玩並把一些可怕的功能加入本範例中。

實際操作影片:

 

Arduino官網範例的實際執行影片:

 

Processing code (重要程式碼說明列於註解)

Processing 會讀取來自指定序列埠的 Arduino 所送來的資料,也就是加速度計與陀螺儀感測器值(Arduino 101 有內建的濾波器函式先處理過了,不然應該會抖動地相當嚴重)。

接著根據這些數值來決定灰球的XY位移,畫面上有陷阱(黑色球)與過關點(黃色球),當揮球與陷阱重疊達10像素(這個值您可以自行調整,不然一碰到邊邊就死掉也太嚴苛了吧……)時就會回到起點(畫面中央的紅色方塊)。

過關條件是灰球”完全”位在過關點中就會顯示紅色 WIN !!!」 字樣,當然這個條件您也可以自行調整。

[pastacode lang=”java” manual=”import%20processing.serial.*%3B%0A%0ASerial%20myPort%3B%0Afloat%20x%3D0%3B%0A%0Afloat%20y%3D0%3B%0A%0Afloat%20yaw%20%3D%200.0%3B%20%20%20%20%20%2F%2F%E9%99%80%E8%9E%BA%E5%84%80Z%E8%BB%B8%0A%0Afloat%20pitch%20%3D%200.0%3B%20%20%20%20%2F%2F%E9%99%80%E8%9E%BA%E5%84%80X%E8%BB%B8%0A%0Afloat%20roll%20%3D%200.0%3B%20%20%20%20%20%20%2F%2F%E9%99%80%E8%9E%BA%E5%84%80Y%E8%BB%B8%20%0Avoid%20setup()%0A%0A%7B%0A%0A%20size(600%2C%20500%2C%20P3D)%3B%0A%20myPort%20%3D%20new%20Serial(this%2C%20%22COM3%22%2C%209600)%3B%20%2F%2F%E8%AB%8B%E6%94%B9%E7%82%BA%E6%82%A8%E9%9B%BB%E8%85%A6%E4%B8%8A%E5%B0%8D%E6%87%89%E7%9A%84COM%20port%0A%20%2F%2F%20if%20you%20know%20the%20serial%20port%20name%0A%0A%20%2F%2FmyPort%20%3D%20new%20Serial(this%2C%20%22COM5%3A%22%2C%209600)%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Windows%0A%0A%20%2F%2FmyPort%20%3D%20new%20Serial(this%2C%20%22%2Fdev%2FttyACM0%22%2C%209600)%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Linux%0A%0A%20%2F%2FmyPort%20%3D%20new%20Serial(this%2C%20%22%2Fdev%2Fcu.usbmodem1217321%22%2C%209600)%3B%20%20%2F%2F%20Mac%0A%20textSize(16)%3B%20%2F%2F%20set%20text%20size%0A%0A%20textMode(SHAPE)%3B%20%2F%2F%20set%20text%20mode%20to%20shape%0A%0A%7D%0Avoid%20draw()%0A%0A%7B%0A%0A%20serialEvent()%3B%20%20%2F%2F%E5%91%BC%E5%8F%AB%E6%9C%AC%E5%87%BD%E5%BC%8F%EF%BC%8C%E8%AE%80%E5%8F%96%E5%BA%8F%E5%88%97%E5%9F%A0%E8%A8%8A%E6%81%AF%0A%0A%20background(255)%3B%20%2F%2F%E8%83%8C%E6%99%AF%E8%A8%AD%E7%82%BA%E7%99%BD%E8%89%B2%0A%0A%20lights()%3B%0A%20translate(width%2F2%2C%20height%2F2)%3B%20%2F%2F%20set%20position%20to%20centre%0A%20pushMatrix()%3B%20%2F%2F%20begin%20object%0A%20float%20c1%20%3D%20cos(radians(roll))%3B%0A%0A%20float%20s1%20%3D%20sin(radians(roll))%3B%0A%0A%20float%20c2%20%3D%20cos(radians(pitch))%3B%0A%0A%20float%20s2%20%3D%20sin(radians(pitch))%3B%0A%0A%20float%20c3%20%3D%20cos(radians(yaw))%3B%0A%0A%20float%20s3%20%3D%20sin(radians(yaw))%3B%0A%0A%20applyMatrix(%20c2*c3%2C%20s1*s3%2Bc1*c3*s2%2C%20c3*s1*s2-c1*s3%2C%200%2C%0A%0A%20-s2%2C%20c1*c2%2C%20c2*s1%2C%200%2C%0A%0A%20c2*s3%2C%20c1*s2*s3-c3*s1%2C%20c1*c3%2Bs1*s2*s3%2C%200%2C%0A%0A%200%2C%200%2C%200%2C%201)%3B%0A%20popMatrix()%3B%20%2F%2F%20end%20of%20object%0A%20%2F%2F%E9%A1%AF%E7%A4%BA%E5%BA%A7%E6%A8%99%E8%A8%8A%E6%81%AF%0A%0A%20print(x)%3B%0A%0A%20print(%22%5Ct%22)%3B%0A%0A%20print(y)%3B%0A%0A%20println(%22%5Ct%22)%3B%0A%20background(255)%3B%0A%0A%20fill(255%2C%200%2C%200)%3B%20%0A%20float%20dx%3Droll*0.11111%3B%0A%0A%20float%20dy%3D-pitch*0.11111%3B%0A%0A%20x%3Dx%2Bdx%3B%20%20%20%2F%2Ffloat%20x%3D0%20out%20of%20void%20draw%0A%0A%20y%3Dy%2Bdy%3B%20%20%20%2F%2Ffloat%20y%3D0%20out%20of%20void%20draw%0A%20%2F%2F%E9%82%8A%E7%B7%A3%E5%81%B5%E6%B8%AC%EF%BC%8C%E5%9C%A8%E6%AD%A4%E8%A8%AD%E5%AE%9A%E8%AE%93%E7%90%83%E4%B8%8D%E6%9C%83%E5%87%BA%E7%95%8C%EF%BC%8C%E4%BD%86%E6%82%A8%E5%8F%AF%E4%BB%A5%E6%94%B9%E6%8E%89%0A%0A%20if%20(x%3E285)%20%7B%0A%0A%20%20%20x%3D285%3B%0A%0A%20%7D%20else%20if%20(x%3C-285)%20%7B%0A%0A%20%20%20x%3D-285%3B%0A%0A%20%7D%0A%0A%20if%20(y%3E235)%20%7B%0A%0A%20%20%20y%3D235%3B%0A%0A%20%7D%20else%20if%20(y%3C-235)%20%7B%0A%0A%20%20%20y%3D-235%3B%0A%0A%20%7D%0A%20fill(192%2C%20192%2C%20192)%3B%20%20%20%20%20%20%2F%2F%E7%81%B0%E8%89%B2%0A%0A%20ellipse(x%2C%20y%2C%2030%2C%2030)%3B%20%20%20%2F%2F%E6%A0%B9%E6%93%9Ax%20y%E8%AE%8A%E6%95%B8%E5%80%BC%E6%B1%BA%E5%AE%9A%E7%81%B0%E7%90%83%E4%BD%8D%E7%BD%AE%0A%0A%20fill(255%2C%200%2C%200)%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%E7%B4%85%E8%89%B2%20%20%0A%0A%20rect(-25%2C%20-25%2C%2050%2C%2050)%3B%20%2F%2F%E8%B5%B7%E5%A7%8B%E9%BB%9E%0A%0A%20fill(255%2C%20215%2C%200)%3B%20%20%20%20%20%20%20%20%2F%2F%E5%9C%9F%E9%BB%83%E8%89%B2%0A%0A%20ellipse(300-25%2C%20250-25%2C%2050%2C%2050)%3B%20%2F%2F%E9%81%8E%E9%97%9C%E9%BB%9E%E4%BD%8D%E7%BD%AEl%0A%0A%20fill(0%2C%200%2C%200)%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%E9%BB%91%E8%89%B2%0A%0A%20ellipse(-300%2B25%2C%20-250%2B25%2C%2050%2C%2050)%3B%2F%2F%E9%99%B7%E9%98%B1%E4%BD%8D%E7%BD%AE%0A%20%2F%2F%E6%98%AF%E5%90%A6%E6%8E%89%E5%85%A5%E9%99%B7%E9%98%B1%EF%BC%8C%E5%A6%82%E6%9E%9C%E9%87%8D%E7%96%8A%E5%88%B0%E4%B8%80%E5%AE%9A%E6%AF%94%E4%BE%8B%E5%89%87%E5%9B%9E%E5%88%B0%E8%B5%B7%E5%A7%8B%E9%BB%9E%E9%87%8D%E4%BE%86%0A%0A%20if%20(sqrt(pow((x%2B275)%2C%202)%2Bpow((y%2B225)%2C%202))%3C%3D10)%20%7B%0A%0A%20%20%20x%3D0%3B%0A%0A%20%20%20y%3D0%3B%0A%0A%20%7D%0A%20%2F%2F%E6%98%AF%E5%90%A6%E9%81%8E%E9%97%9C%EF%BC%8C%E5%89%87%E9%A1%AF%E7%A4%BA%E7%9B%B8%E9%97%9C%E8%A8%8A%E6%81%AF%0A%0A%20if%20(sqrt(pow((x-275)%2C%202)%2Bpow((y-225)%2C%202))%3C%3D10)%20%7B%0A%0A%20%20%20textSize(64)%3B%0A%0A%20%20%20fill(255%2C%200%2C%200)%3B%0A%0A%20%20%20text(%22WIN!!!%22%2C%20130%2C%20-125)%3B%20%20%20%2F%2F%E9%A1%AF%E7%A4%BA%E5%8B%9D%E5%88%A9%E8%A8%8A%E6%81%AF%0A%0A%20%7D%0A%0A%7D%0Avoid%20serialEvent()%0A%0A%7B%0A%0A%20int%20newLine%20%3D%2013%3B%20%2F%2FASCII%E7%B7%A8%E7%A2%BC%E4%B8%AD%E7%9A%84%E6%8F%9B%E8%A1%8C%E5%AD%97%E5%85%83%3A%20%5Cn%0A%0A%20String%20message%3B%0A%0A%20do%20%7B%0A%0A%20%20%20message%20%3D%20myPort.readStringUntil(newLine)%3B%20%2F%2F%E8%AE%80%E5%8F%96%E5%BA%8F%E5%88%97%E5%9F%A0%E8%A8%8A%E6%81%AF%E7%9B%B4%E5%88%B0%E8%AE%80%E5%88%B0%20%5Cn%20%E7%82%BA%E6%AD%A2%0A%0A%20%20%20if%20(message%20!%3D%20null)%20%7B%20%20%2F%2F%E5%A6%82%E6%9E%9C%E6%9C%89%E8%A8%8A%E6%81%AF%E9%80%B2%E4%BE%86%0A%0A%20%20%20%20%20String%5B%5D%20list%20%3D%20split(trim(message)%2C%20%22%20%22)%3B%0A%0A%20%20%20%20%20%2F%2Ftrim()%E6%9C%83%E5%85%88%E7%A7%BB%E9%99%A4%20message%E5%AD%97%E4%B8%B2%E9%A0%AD%E5%B0%BE%E7%9A%84%E7%A9%BA%E7%99%BD%0A%0A%20%20%20%20%20%2F%2F%E5%86%8D%E7%94%B1split()%E4%BB%A5%E2%80%9D%20%E2%80%9C%E4%BE%86%E5%88%87%E5%89%B2message%E4%B8%A6%E5%AD%98%E5%85%A5list%EF%BC%8C%E9%80%99%E8%88%87Arduino%20code%E4%B8%AD%E7%9A%84%20Serial.print()%E5%B0%8D%E6%87%89%0A%0A%20%20%20%20%20%2F%2Fhttps%3A%2F%2Fprocessing.org%2Freference%2Ftrim_.html%0A%0A%20%20%20%20%20%2F%2Fhttps%3A%2F%2Fprocessing.org%2Freference%2Fsplit_.html%0A%0A%20%20%20%20%20if%20(list.length%20%3E%3D%204%20%26%26%20list%5B0%5D.equals(%22Orientation%3A%22))%20%7B%0A%0A%20%20%20%20%20%2F%2F%E6%AA%A2%E6%9F%A5%E5%88%86%E5%89%B2%E5%BE%8C%E7%9A%84%E9%99%A3%E5%88%97%E9%95%B7%E5%BA%A6%E6%98%AF%E5%90%A6%E6%AD%A3%E7%A2%BA%EF%BC%8C%E4%B8%94%E7%AC%AC%E4%B8%80%E5%80%8B%E5%85%83%E7%B4%A0%E6%98%AF%E5%90%A6%E7%AD%89%E6%96%BC%E2%80%9DOrientation%0A%0A%20%20%20%20%20%2F%2F%E4%BB%A3%E8%A1%A8%E6%8A%93%E5%88%B0%E7%AC%AC%E4%B8%80%E7%AD%86%E8%A8%8A%E6%81%AF%E7%9A%84%E4%BD%8D%E7%BD%AE%EF%BC%8C%E5%BE%8C%E7%BA%8C%E5%8F%96%20list%5B1%5D~list%5B3%5D%E6%89%8D%E6%9C%83%E6%AD%A3%E7%A2%BA%0A%0A%20%20%20%20%20%20%20yaw%20%3D%20float(list%5B1%5D)%3B%20%20%2F%2F%E5%8F%96%E5%BE%97%20yaw%0A%0A%20%20%20%20%20%20%20pitch%20%3D%20float(list%5B2%5D)%3B%20%2F%2F%E5%8F%96%E5%BE%97%20pitch%0A%0A%20%20%20%20%20%20%20roll%20%3D%20float(list%5B3%5D)%3B%20%20%20%20%2F%2F%E5%8F%96%E5%BE%97%20roll%0A%0A%20%20%20%20%20%7D%0A%0A%20%20%20%7D%0A%0A%20%7D%0A%0A%20while%20(message%20!%3D%20null)%3B%0A%0A%7D” message=”Processing code” highlight=”” provider=”manual”/]

 

Arduino code

(來自https://www.arduino.cc/en/Tutorial/Genuino101CurieIMUOrientationVisualiser,未修改)

[pastacode lang=”java” manual=”%23include%20%3CCurieIMU.h%3E%0A%23include%20%3CMadgwickAHRS.h%3E%20%20%20%2F%2F%E6%BF%BE%E6%B3%A2%E5%87%BD%E5%BC%8F%E5%BA%AB%0A%0A%2F%2Fhttp%3A%2F%2Fx-io.co.uk%2Fopen-source-imu-and-ahrs-algorithms%2F%0AMadgwick%20filter%3B%20%20%20%2F%2F%E5%AE%A3%E5%91%8A%E6%BF%BE%E6%B3%A2%E5%99%A8%E7%89%A9%E4%BB%B6%0A%0Aunsigned%20long%20microsPerReading%2C%20microsPrevious%3B%0A%0Afloat%20accelScale%2C%20gyroScale%3B%0Avoid%20setup()%20%7B%0A%20Serial.begin(9600)%3B%20%20%20%2F%2F%E5%BB%BA%E7%AB%8B%E5%B0%8DProcessing%E7%9A%84%E5%BA%8F%E5%88%97%E9%80%9A%E8%A8%8A%0A%20%2F%2F%E5%95%9F%E5%8B%95IMU%E8%88%87filter%0A%20CurieIMU.begin()%3B%0A%20CurieIMU.setGyroRate(25)%3B%0A%20CurieIMU.setAccelerometerRate(25)%3B%0A%20filter.begin(25)%3B%0A%20%2F%2F%E5%B0%87%E5%8A%A0%E9%80%9F%E5%BA%A6%E8%A8%88%E6%95%B8%E5%80%BC%E7%AF%84%E5%9C%8D%E8%A8%AD%E5%AE%9A%E7%82%BA2G%0A%0A%20CurieIMU.setAccelerometerRange(2)%3B%0A%20%2F%2F%E5%B0%87%E9%99%80%E8%9E%BA%E5%84%80%E6%95%B8%E5%80%BC%E7%AF%84%E5%9C%8D%E8%A8%AD%E5%AE%9A%E7%82%BA%20250%20degrees%2Fsecond%0A%0A%20CurieIMU.setGyroRange(250)%3B%0A%20%2F%2F%20initialize%20variables%20to%20pace%20updates%20to%20correct%20rate%0A%0A%20microsPerReading%20%3D%201000000%20%2F%2025%3B%0A%20microsPrevious%20%3D%20micros()%3B%0A%7D%0A%0Avoid%20loop()%20%7B%0A%20int%20aix%2C%20aiy%2C%20aiz%3B%0A%20int%20gix%2C%20giy%2C%20giz%3B%0A%20float%20ax%2C%20ay%2C%20az%3B%0A%20float%20gx%2C%20gy%2C%20gz%3B%0A%20float%20roll%2C%20pitch%2C%20heading%3B%0A%0A%20unsigned%20long%20microsNow%3B%0A%20%2F%2F%20check%20if%20it’s%20time%20to%20read%20data%20and%20update%20the%20filter%0A%0A%20microsNow%20%3D%20micros()%3B%0A%20if%20(microsNow%20-%20microsPrevious%20%3E%3D%20microsPerReading)%20%7B%0A%20%20%20%2F%2F%E8%AE%80%E5%8F%96CurieIMU%E7%9A%84raw%E5%80%BC%0A%20%20%20CurieIMU.readMotionSensor(aix%2C%20aiy%2C%20aiz%2C%20gix%2C%20giy%2C%20giz)%3B%0A%20%20%20%2F%2F%E5%B0%87%20raw%E5%80%BC%E5%88%86%E5%88%A5%E8%BD%89%E7%82%BA%E5%B0%8D%E6%87%89%E7%9A%84%E5%96%AE%E4%BD%8D%EF%BC%9A%E5%8A%A0%E9%80%9F%E5%BA%A6%E8%A8%88(g)%20%2F%20%E9%99%80%E8%9E%BA%E5%84%80(degrees%2Fsecond)%0A%0A%20%20%20ax%20%3D%20convertRawAcceleration(aix)%3B%0A%20%20%20ay%20%3D%20convertRawAcceleration(aiy)%3B%0A%20%20%20az%20%3D%20convertRawAcceleration(aiz)%3B%0A%0A%20%20%20gx%20%3D%20convertRawGyro(gix)%3B%0A%20%20%20gy%20%3D%20convertRawGyro(giy)%3B%0A%20%20%20gz%20%3D%20convertRawGyro(giz)%3B%0A%20%20%20%2F%2F%E8%A6%81%E6%B1%82%20filter%20%E5%8F%96%E5%BE%97%20IMU%E5%80%BC%E9%80%B2%E8%A1%8C%E8%A8%88%E7%AE%97%0A%0A%20%20%20filter.updateIMU(gx%2C%20gy%2C%20gz%2C%20ax%2C%20ay%2C%20az)%3B%0A%20%20%20%2F%2F%E9%A1%AF%E7%A4%BA%20filter%20%E8%99%95%E7%90%86%E5%BE%8C%E7%9A%84XYZ%E8%BB%B8%E5%90%91%E8%A8%8A%E6%81%AF%2C%20X%3A%20pitch%2C%20Y%3Aroll%2C%20Z%3A%20heading%20%3D%20yaw%0A%0A%20%20%20roll%20%3D%20filter.getRoll()%3B%0A%0A%20%20%20pitch%20%3D%20filter.getPitch()%3B%0A%0A%20%20%20heading%20%3D%20filter.getYaw()%3B%0A%0A%20%20%20%2F%2F%E4%BB%A5%E4%B8%8B%E9%96%8B%E5%A7%8B%E7%99%BC%E9%80%81%E8%B3%87%E6%96%99%0A%0A%20%20%20Serial.print(%22Orientation%3A%20%22)%3B%0A%0A%20%20%20Serial.print(heading)%3B%0A%0A%20%20%20Serial.print(%22%20%22)%3B%0A%0A%20%20%20Serial.print(pitch)%3B%0A%0A%20%20%20Serial.print(%22%20%22)%3B%0A%0A%20%20%20Serial.println(roll)%3B%0A%20%20%20%2F%2F%20increment%20previous%20time%2C%20so%20we%20keep%20proper%20pace%0A%0A%20%20%20microsPrevious%20%3D%20microsPrevious%20%2B%20microsPerReading%3B%0A%0A%20%7D%0A%0A%7D%0Afloat%20convertRawAcceleration(int%20aRaw)%20%7B%0A%0A%20%20%20%2F%2F%E7%94%B1%E6%96%BC%E5%B0%87%E5%8A%A0%E9%80%9F%E5%BA%A6%E8%A8%88%E6%95%B8%E5%80%BC%E7%AF%84%E5%9C%8D%E8%A8%AD%E5%AE%9A%E7%82%BA2G%2C%20%E5%9C%A8%E6%AD%A4%E9%80%B2%E8%A1%8C%E8%BD%89%E6%8F%9B%0A%0A%20%20%20%2F%2F%20-2g%E5%B0%8D%E6%87%89%E5%88%B0%20raw%20%E5%80%BC%E7%9A%84%20-32768%2C%20%2B2g%20%E5%89%87%E6%98%AF%2032767%0A%20%20%2F%2F%20since%20we%20are%20using%202G%20range%0A%0A%20%2F%2F%20-2g%20maps%20to%20a%20raw%20value%20of%20-32768%0A%0A%20%2F%2F%20%2B2g%20maps%20to%20a%20raw%20value%20of%2032767%0A%0A%20%0A%0A%20float%20a%20%3D%20(aRaw%20*%202.0)%20%2F%2032768.0%3B%0A%0A%20return%20a%3B%0A%0A%7D%0Afloat%20convertRawGyro(int%20gRaw)%20%7B%0A%0A%20%2F%2F%E7%94%B1%E6%96%BC%E5%B0%87%E9%99%80%E8%9E%BA%E5%84%80%E6%95%B8%E5%80%BC%E7%AF%84%E5%9C%8D%E8%A8%AD%E5%AE%9A%E7%82%BA250%20degrees%2Fseconds%2C%20%E5%9C%A8%E6%AD%A4%E9%80%B2%E8%A1%8C%E8%BD%89%E6%8F%9B%0A%0A%20%2F%2F%20-250%20%E5%B0%8D%E6%87%89%E5%88%B0%20raw%20%E5%80%BC%E7%9A%84%20-32768%2C%20%2B250%20%E5%89%87%E6%98%AF%2032767%0A%0A%20%0A%0A%20float%20g%20%3D%20(gRaw%20*%20250.0)%20%2F%2032768.0%3B%0A%0A%20return%20g%3B%0A%0A%7D” message=”42″ highlight=”Arduino 101 code” provider=”manual”/]

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *