아두이노 미로게임 (MPU-6050 + 프로세싱)

MPU-6050 가속도 센서

MPU-6050은 x, y, z축의 자이로스코프 기울기와 가속도를 측정할 수 있는 센서입니다. 이 센서를 이용하면, 드론 및 3차원 탑승장치의 기울어짐이나 운동 상태를 모니터링 할 수 있습니다.
이 센서는 I2C 통신을 사용하여 데이터를 송수신합니다. 아두이노에 기본적으로 내장된 Wire 라이브러리가 I2C 통신을 지원하기 때문에 별다른 라이브러리를 따로 설치할 필요가 없습니다.

아두이노 미로게임 (MPU-6050 + 프로세싱)

MPU-6050 연결

센서와 아두이노의 SDA, SCL 단자를 서로 연결합니다.

아두이노 미로게임 (MPU-6050 + 프로세싱)

아두이노 소스 설치 및 작동 테스트

  1. 아두이노에 아래의 소스 코드를 업로드합니다.
    /*
    
      GY-521 자이로&가속도 센서 (MPU-6050)
      예제입니다.
    
      UNO R3 : http://kit128.com/goods/view?no=71
      GY-521 자이로&가속도 센서 (MPU-6050) 모듈 : http://kit128.com/goods/view?no=146
    
      출처: http://whiteat.com/Arduino
    
    */
    
    #include
    const int MPU = 0x68;  // I2C address of the MPU-6050
    int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;
    
    
    // 프로그램 시작 - 초기화 작업
    void setup()
    {
      Serial.begin(9600);     // 시리얼 통신 초기화
      Serial.println("Arduino Examples - GY-521 Gyro& Accelator (MPU-6050) Module");
      Serial.println("    http://docs.whiteat.com/?p=2662");
    
    
      Wire.begin();
      Wire.beginTransmission(MPU);
      Wire.write(0x6B);  // PWR_MGMT_1 register
      Wire.write(0);     // set to zero (wakes up the MPU-6050)
      Wire.endTransmission(true);
    
      // gyro scale
      Wire.beginTransmission(MPU);
      Wire.write(0x1B);  //
      Wire.write(0xF8);     //
      Wire.endTransmission(true);
    
      // acc scale
      Wire.beginTransmission(MPU);
      Wire.write(0x1C);  //
      Wire.write(0xF8);     //
      Wire.endTransmission(true);
    
    }
    
    void loop()
    {
      Wire.beginTransmission(MPU);
      Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
      Wire.endTransmission(false);
      Wire.requestFrom(MPU, 14, true);  // request a total of 14 registers
      AcX = Wire.read() << 8 | Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
      AcY = Wire.read() << 8 | Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
      AcZ = Wire.read() << 8 | Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
      Tmp = Wire.read() << 8 | Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
      GyX = Wire.read() << 8 | Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
      GyY = Wire.read() << 8 | Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
      GyZ = Wire.read() << 8 | Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
    
      /*
        Serial.print("AcX = "); Serial.print(AcX);
        Serial.print(" | AcY = "); Serial.print(AcY);
        Serial.print(" | AcZ = "); Serial.print(AcZ);
        Serial.print(" | Tmp = "); Serial.print(Tmp / 340.00 + 36.53);  //equation for temperature in degrees C from datasheet
        Serial.print(" | GyX = "); Serial.print(GyX);
        Serial.print(" | GyY = "); Serial.print(GyY);
        Serial.print(" | GyZ = "); Serial.println(GyZ);
        delay(333);
      */
    
    
      int xAxis = (AcX - 1090);
      int yAxis = (AcY - 930);
      int zAxis = (AcZ - 1000);
      
      Serial.print(xAxis);
      Serial.print(" ");
      Serial.print(yAxis);
      Serial.print(" ");
      Serial.print(zAxis);
      Serial.println(" ");
    
      delay(50);
    }
  2. 소스 코드를 업로드한 다음 시리얼 모니터를 열어보면, x, y, x 축 기울어짐 값이 출력되는 것을 볼 수 있습니다.
  3. 여기까지가 성공하였으면 아두이노 보드는 PC와 연결한 채로 둔다음, 아두이노 소프트웨어만 닫습니다. 이제 아두이노 보드가 전송하는 데이터를 다른 프로그램에서 받아서 처리하게 할 것입니다.

프로세싱 설치 및 게임 작동

  1. 아두이노에서 전송하는 기울기 데이터를 받아서 처리하는 게임 프로그램을 만들어 보겠습니다. https://processing.org/download/에 접속하여 프로세싱을 다운로드 받습니다.(processing-x.x.x.zip 파일 형태) 이미 설치되어 있다면 다운로드 단계는 생략합니다.
  2. 다운받은 zip 파일을 적당한 폴더에 압축을 풀어 놓습니다.
    아두이노 LM35 온도계 (with Processing)
  3. processing.exe 파일을 실행시킵니다.
    아두이노 LM35 온도계 (with Processing)
  4. 프로세싱에 아래의 소스 코드를 붙여넣습니다.
    import processing.serial.*;
    Serial myPort;
    
    float xRot = 0;
    float yRot = 0;
    
    int hWall =  30;
    int wall[][] = 
        {
        {-400, -400, 800, 0}, 
        {-400, -400, 0, 800}, 
        {-400, -200, 100, 0}, 
        {-300, -300, 0, 100}, 
        {-300, -300, 100, 0}, 
        {-200, -300, 0, 100}, 
        {-100, -300, 0, 200}, 
        {0, -300, 0, 100}, 
        {100, -300, 0, 400}, 
        {100, -300, 100, 0}, 
    
        {300, -300, 0, 200}, 
        {300, -300, 100, 0}, 
        {0, -200, 100, 0}, 
        {200, -200, 100, 0}, 
        {400, -400, 0, 800}, 
        {-300, -100, 200, 0}, 
        {100, -100, 100, 0}, 
        {-300, -100, 0, 200}, 
        {-200, -100, 0, 100}, 
        {0, -100, 0, 100}, 
    
        {0, 0, 100, 0}, 
        {-100, 0, 0, 100}, 
        {200, 0, 0, 100}, 
        {300, 0, 0, 100}, 
        {-400, 100, 100, 0}, 
        {-100, 100, 400, 0}, 
        {-200, 100, 0, 200}, 
        {-300, 100, 100, 0}, 
        {200, 200, 200, 0}, 
        {-300, 200, 0, 200}, 
    
        {-100, 200, 0, 100}, 
        {0, 200, 0, 200}, 
        {100, 200, 0, 100}, 
        {-200, 300, 100, 0}, 
        {100, 300, 300, 0}, 
        {-400, 400, 700, 0}
    };
    
    float xBall = 50;
    float yBall = -350;
    float vxBall = 0;
    float vyBall = 0;
    
    void setup() 
    {
        frameRate(40);
        size(1400, 800, P3D);
    
        //포트 검색 후 시리얼통신 실행
        println("Available serial ports:");
        println(Serial.list());
        myPort = new Serial(this, Serial.list()[0], 9600);
    
        setup2();
    }
    
    void setup2() 
    {
        xBall = 50;
        yBall = -350;
        vxBall = 0;
        vyBall = 0;
    }
    
    void draw() 
    {
        background(255);
    
        float fov = PI/10.0; 
        float cameraZ = (height/2.0) / tan(fov/2.0); 
        perspective(fov, float(width)/float(height), cameraZ/2.0, cameraZ*2.0); 
        camera(width/2, height/2, cameraZ, width/2, height/2, 0, 0, 1, 0);
        lights();
    
        translate(width/2, height/2);
        rotateX(1.0);
    
        pushMatrix();
    
        int nBuffer = 0;
        while  (myPort.available() > 0) 
        {
            String inBuffer = myPort.readString();
            String subBuffer[] = { "", "", ""};
            int max = 0;
            int len = inBuffer.length();
            for (int i = 0; i < len; i++) { if (inBuffer.charAt(i) == ' ') { max++; if (max >= 3) break;
                } else
                    subBuffer[max] = subBuffer[max] + inBuffer.charAt(i);
            }
            if (max != 3) break;
    
            int x = -int(subBuffer[1]);
            if (x > 0) x = max(x - 4, 0);
            if (x < 0) x = min(x + 4, 0); xRot = x/8000.0; xRot = constrain(xRot, -PI/7, PI/7); int y = -int(subBuffer[0]); if (y > 0) y = max(y - 4, 0);
            if (y < 0) y = min(y + 4, 0);           
            yRot = y/8000.0;
            yRot = constrain(yRot, -PI/7, PI/7);
        }
    
        noFill();
        stroke(0, 255, 0);
        {
            pushMatrix();
            translate(0, 0, 0);
            box(1024, 1024, 1);
            popMatrix();
        }
    
        rotateX(xRot);
        rotateY(yRot);
        fill(255);
        stroke(64);
    
        //바닥
        {
            pushMatrix();
            translate(0, 0, -1);
            box(800, 800, 1);
            popMatrix();
        }
    
        //벽
        for (int n = 0; n < wall.length; n++) { pushMatrix(); translate(wall[n][0] + wall[n][2]/2 - 2, wall[n][1] + wall[n][3]/2 - 2, hWall/2); box(wall[n][2] + 5, wall[n][3] + 5, hWall); popMatrix(); } vxBall += yRot * 5; vyBall -= xRot * 5; if (abs(vxBall) >= 20) vxBall = vxBall*20/abs(vxBall);
        if (abs(vyBall) >= 20) vyBall = vyBall*20/abs(vyBall);
        xBall += vxBall;
        yBall += vyBall;
    
        float tollerence = 23;
        for (int n = 0; n < wall.length; n++) { // horizontal Wall if (wall[n][2] > 0)
                if ((wall[n][0] - tollerence*0.9) < xBall && (wall[n][0] + wall[n][2] + tollerence*0.9) > xBall)
                {
                    if (abs(wall[n][1] - yBall) < tollerence)
                    {
                        if (wall[n][1] < yBall) { yBall += 2; vyBall = 0; } else { yBall -= 2; vyBall = 0; } } } // vertical Wall if (wall[n][3] > 0)
                if ((wall[n][1] - tollerence*0.9) < yBall && (wall[n][1] + wall[n][3] + tollerence*0.9) > yBall)
                {
                    if (abs(wall[n][0] - xBall) < tollerence)
                    {
                        if (wall[n][0] < xBall) { xBall += 2; vxBall = 0; } else { xBall -= 2; vxBall = 0; } } } } // 공 { fill(255, 0, 0); noStroke(); pushMatrix(); translate(xBall, yBall, 20); sphere(20); popMatrix(); } if (yBall > 420) setup2();
    
        popMatrix();
    }
  5. 키보드 [Ctrl+R] 키를 눌러 프로세싱 프로그램을 실행시키면 미로게임이 실행됩니다.
  6. 프로세싱 코딩에 대해 잘 알고 있다면 화면을 좀 더 예쁘게 꾸며서 나만의 게임을 만들 수도 있습니다.

목차