2015. 10. 13.

DIY 3axis gimbal - 1 ( MPU 6050 Sensor로 3축 각도 알아내기 )

<구글에서 '3 axis gimbal'을 검색한 결과>
<Search result for '3-axis gimbal' on Google>

 동영상을 흔들림 없이 촬영하려면 어떻게 해야 할까? 물론 사람이 조심조심하면서 촬영할 수 있겠지만 힘들것이다. 그러나 3축 짐벌이라는 도구를 사용하면 흔들림 없이 깨끗한 촬영이 가능하다. 액션캠이 대중화 되면서 3축 짐벌 또한 수요가 증가하고 있다. 나 역시 깨끗한 촬영을 하고 싶었고 3축 짐벌의 가격을 찾아보았다. 하지만 3축 짐벌은 그 가격이 매우 비싸다...

 How do we shoot a video without shaking? Of course, you can shoot a video by your hands. But it will be very hard. However, you can easily shoot video without shaking by using a tool called  '3-axis gimbal'. As the action cam's popularity has increased, 3-axis's demand has increased too. I also wanted a clear video and search a price of 3-axis gimbal. However, the price was very very expensive...

그럼 내가 만들면 되지 뭐.

Well.. Maybe D.I.Y is better.



<나만의 목표>

1. 먼저 목표 가격은 인건비를 제외하고 재료비만 20만원 아래로 할 것이다. 
2. 모터는 서보모터를 이용한다.
3. MPU 6050 센서를 이용하므로 100만원대 제품 정도의 정밀도는 아니지만 가격대비 좋은 성능을 내도록 만든다.  

<My own goal>

1. Price of material is lower than 173 dollars.
2. Using digital servo motor
3. MPU 6050 is very cheap sensor. So I will do my best for good performance.


<MPU 6050 sensor>

 MPU 6050은 3축 가속도 센서+자이로 센서로 이루어진 센서로 2가지 값을 센싱할 수 있다. 

▲ 가속도 센서는 중력가속도를 기반으로 X,Y,Z축에 대한 직선 가속도를 측정한다. 외력에 의해 측정값이 왜곡될 수 있지만 시간이 흘러도 오차가 커지지 않는 다는 장점이 있다. 

▲ 자이로 센서는 X,Y,Z축의 각속도 변화량을 측정한다. 그런데 각도의 미분값 = 각속도이므로 각도를 알기 위해서는 이 값을 적분해줘야 한다. 그런데 적분을 하게 되면 누적오차가 쌓이기 마련이다. 가속도 센서보다는 실제 센서의 움직임과 같은 값을 내지만 누적 오차 때문에 시간이 흐를 수록 오차가 커지게 된다.

 따라서 필터를 이용해 이 둘의 장점만을 따오는 식으로 사용한다. 즉 실제 센서의 움직임과 같으면서 누적오차가 없는 결과가 나오게 되는 것이다. 보통 계산 주기 때문에 계산량이 상대적으로 가볍고 만들기 쉬운 상보필터를 많이 쓰는데, 칼만필터를 적용한다면 더 좋다고 한다. (여기서 다시 확인하는 칼만필터의 위력) 그러나 칼만필터는 계산량이 많아 계산 주기가 길어지기 때문에 Arduino Uno와 같은 보드로는 실시간제어가 힘들다는 문제가 있다. 어찌되었든 코드를 찾는게 문제였다.

 MPU 6050 is a sensor that made of 3-axis acceleration sensor and 3-axis gyro sensor. So MPU 6050 can measure acceleration and gyro together.

The acceleration sensor is based on gravity. It measure an acceleration from each X,Y,Z axis. An acceleration value may be distorted by an external force, but acceleration sensor has an advantage that the error does not increase overtime.

The gyro sensor measure a angular speed from each X,Y,Z axis. However, because of angular speed is differential of angle, we have to integrating this value. But the problem is because of integration, there is an accumulated error. The gyro sensor could measure almost corrected value from real movement. But it still have an accumulated error.

 So we have to use a filter for taking each advantage from 2 sensors. If we use a filter, we can always get a correct value from MPU 6050. Kalman filter is a popular filter. But it could heavy to arduino. So I have to find another solution from internet.



하지만 언제나 Google에는 해결책이 있다..!!

But there is always a solution in Google!!




 아래는 MPU 6050과 관련된 자료를 찾다가 발견한 사이트이다. 이는 아두이노 공식 홈페이지에서도 추천하는 방법이다.

 먼저 i2cdevlib 를 다운 받는다. 그리고 그 폴더안에 Arduino라는 폴더가 있다. Arduino 폴더 안에 있는 'MPU6050' 폴더와 'I2Cdev' 란 폴더를 원래 아두이노가 설치된 폴더의 libraries 폴더 안에 복사한다. 라이브러리를 추가하기 위함이다. 
 그리고 예제코드인 MPU6050_DMP6.ino 를 다운받아 컴파일을 해본다. 오류 없이 잘 된다면 일단 소프트웨어는 준비가 완료되었다. 이 예제코드는 센서안에 있는 DMP(Digital Motion Processor)로 부터 가속도와 자이로값을 토대로 쿼터니안값을 계산하고 이것을 이용해 원하는 값으로 변환하여 내뱉는 코드이다. Jeff Rowberg란 사람이 i2cdevlib란 라이브러리를 만들어 놓아서 그걸 이용해 만들었다고 한다. 

 코드 내용을 보면 쿼터니안 값, 오일러 각도 값, 가속도 각도 값, yaw pitch roll 값 등을 출력하는 부분이 있으니 필요한 부분을 쓰면 될 듯 하다. 나는 3축짐벌을 만들어야 하므로 yaw, pitch, roll 값을 오일러 값으로 출력하는 부분을 이용했다. 아래는 내가 수정한 부분이다. 

 Below is the link that I found. That solution is recommended by Arduino website.

 First, we have to add some libraries. Download i2cdevlib. And there is a folder named 'Arduino'. In 'Arduino' folder, there is 'MPU6050' folder and 'I2Cdev' folder. Copy 'MPU6050','I2Cdev' folder and paste in 'libraries' folder that is inside your installed arduino IDE folder. 
 And download an example code MPU6050_DMP6.ino and compile. If there is no an error, the basic thing is done. 

 In this example code, you can select output value by #define part. You can get quaternion, euler angle, yaw pitch roll value, etc.. And I use yaw pitch roll value from an example code. And below is edited part.



<Servo motor SETUP> - 서보모터 셋업

// ================================================================
// ===                      INITIAL SETUP                       ===
// ================================================================
int servoPin1 = 3, servoPin2 = 5, servoPin3 = 11;
Servo servo1, servo2, servo3;
void setup() {
        servo1.attach(servoPin1); 
        servo2.attach(servoPin2);
        servo3.attach(servoPin3);
        servo1.write(0); //Init the servo1 angle to 0
        servo2.write(0); //Init the servo2 angle to 0
        servo3.write(0); //Init the servo2 angle to 0
cs



<Control Servo motor> - 각 각도값에 따라 서보모터 컨트롤

        #ifdef OUTPUT_READABLE_YAWPITCHROLL
            // display Euler angles in degrees
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
            Serial.print("ypr\t");
            Serial.print(ypr[0* 180/M_PI);
            servo1.write(map(ypr[0* 180/M_PI, -1801800180)); //Control servo1
            Serial.print("\t");
            Serial.print(ypr[1* 180/M_PI);            
            servo2.write(map(ypr[1* 180/M_PI, -90900180)); //Control servo2
            Serial.print("\t");
            Serial.println(ypr[2* 180/M_PI);
            servo3.write(map(ypr[2* 180/M_PI, -90900180)); //Control servo3
         
        #endif
cs



<Yaw, Pitch, Roll angle, from wikipedia > 

  그런데 ypr[0] (=Yaw)는 -180~+180이 되는데, ypr[1], ypr[2] (=Pitch, Roll)는 -90~+90 밖에 측정이 되지 않으므로 이에 맞게 서보모터가 움직일 수 있도록 map()을 사용하였다. 

 ypr[0] (=Yaw) can measure -180~+180. But ypr[1], ypr[2] (=Pitch, Roll) is only -90~90. So I used map().




<MPU 6050 핀 연결>

<MPU 6050 pin map>

▲ 5V <->VCC (You can also connect with 3.3V)
▲ GND <-> GND
▲ A5 <-> SCL
▲ A4 <-> SDA
▲ Digital pin 2 <-> INT (interrupt)

※나머지 핀은 연결하지 않는다.
※The remaining pins are not connected.


<dccDuino>

 Arduino Uno의 클론 보드인 dccDuino 이다. 마이크로 칩을 사용했다는 것과 암컷핀이 수컷핀으로 확장되어 암,수 핀이 모두 연결가능하며 전원핀도 확장되 었다는 것이 차이점이다. 인터넷에서 할인하여 5천원대에 구매했다. (정품의 약 1/6가격.. 그러나 성능은 동일하다. 왜 그런지는 밑에 나올 동영상을 보면 알게 될 것이다.) 다만 입력 전원이 조금 낮다는 점과 전용 드라이버를 다운받아야 PC에서 인식이 된다. 드라이버는 인터넷에서 쉽게 구할 수 있다. 귀찮으면 아래 링크로 다운로드 받자.








<데모 영상>


<MPU 6050 센서로 서보모터 컨트롤 하기>
<Servo motor control with MPU 6050 sensor>

 영상을 촬영하기 전 Yaw값이 steady state가 될 때까지 기다린 후 촬영하였다.(30초 정도 소요) 한번 steady state가 되면 그 이후로는 안정적인 값을 출력한다. Arduino Uno급 보드로도 충분히 정밀한 제어가 가능하다는 것을 알 수 있다.

 Before taking this video, I waited for Yaw value until steady state.(It takes maybe 30 seconds) In this video, we can sufficient accurate control with arduino-level board.
















<번외편>

<Extra>


<Hot Hand USB Wireless MIDI Controller>

 위 제품 영상중 2:02초 부터 나오는 장면을 보면 각축의 기울기에 따라 mapping된 knob값이 변경되는 것을 볼 수 있다. 
 저걸 보자마자 드는 생각..

 In that video from 2:02 seconds, the product control knobs by each axis angle.



저거 나도 충분히 가능한거 아냐?!

I think I can make like that!




  결론적으로 말하자면 가능하다. 물론 무선통신을 하는 건 블루투스 모듈이나 Xbee 모듈이 있어야 하므로 나중에 만들더라 치더라도 일단 기울기로 knob값 조절 기능은 가능하다.

 In conclusion, it is possible.



<MIDI Control>

        #ifdef OUTPUT_READABLE_YAWPITCHROLL
            // display Euler angles in degrees
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
            //Serial.print("ypr\t");
            //Serial.print(ypr[0] * 180/M_PI);
            midiControl(0xB01, map(ypr[0* 180/M_PI, -1801800127));
            delay(10);
            //Serial.print("\t");
            //Serial.print(ypr[1] * 180/M_PI);
            midiControl(0xB02, map(ypr[1* 180/M_PI, -90900127));
            delay(10);
            //Serial.print("\t");
            //Serial.println(ypr[2] * 180/M_PI);
            midiControl(0xB03, map(ypr[2* 180/M_PI, -90900127));
            delay(10);            
        #endif
cs

 위에서 Yaw, Pitch, Roll 값을 이용해 서버모터를 제어하던 부분에 'midiControl' 이란 함수로 변경하였다. 각각 PC 프로그램에서 1,2,3 채널로 인식이 되어 control 값이 입력이 된다. control 값은 0~127사이의 값을 가져야한다.

 Instead of controlling the server motor using Yaw, Pitch, and Roll values, I changed the function to 'midiControl'. It have 1,2,3 channel and input control value to computer. control value should 0~127.

<midiControl function>

void midiControl(int st, int cc, int val) {
  Serial.write(st);
  Serial.write(cc);
  Serial.write(val);
}
cs

  midiControl function은 MIDI 규격으로 출력하게 해주는 역할이며 첫째 인자는 보내는 값이 음인지 컨트롤 값인지를 구분하는 것이고 두번째 인자는 각 음의 높이 혹은 컨트롤 채널을 의미하며 마지막 인자는 음일 경우 속도를, 컨트롤일 경우 값을 의미한다. 그러나 USB를 이용해 PC와 serial 통신으로 MIDI 값을 전송하는 것이기 때문에 중간에 2 가지 프로그램이 필요하다.

 'midiControl function' make a MIDI serial data. 'st' is distinguish whether sending data is key or control. 'cc' is key level or control channel. 'val' is velocity or control value. But arduino uno use a usb for serial communication with computer. So we need 2 more program.






 첫째로는 아두이노와 같은 디바이스로 부터 받은 serial 값을 MIDI 통신 값으로 전달하는 다리 역할을 하는 프로그램이다.
'hairless-midiserial'이란 프로그램이며 리눅스, 맥, 윈도우 버젼이 있다.

First program is 'hairless-midiserial'. It change serial data to MIDI data. 
This program is available for Linux, Mac, and Windows versions.





Link : loopmidi

 두번째로는 가상 midi port 역할을 하는 'loopmidi'란 프로그램이다. 윈도우버젼만 있다.

Second program is 'loopmidi'. It is a virtual midi port. Mac does not need a virtual midi port.(Because Mac support this thing)





자, 신호가 가는 순서를 정리하자면 이렇다.
Well, the signal flow like below.

MPU6050  ->  Arduino  ->  hairless-midiserial  ->  loopmidi  ->  Abletonlive



<데모 영상> - Demo

<MPU 6050 센서로 Abletonlive 입력하기>
<Control Abletonlive with MPU 6050>

각 축에 따라 노브값이 바뀌는 것을 알 수 있다.
Abletonlive의 임의의 knob와 MIDI 신호를 mapping 하는 것은 youtube에 많이 나와 있다.
아두이노에서 보내는 MIDI 신호 값을 Abletonlive에서 mapping 시키는 작업은 필수이다. 

It can be seen that this knob value is changed by each axis angle.
How to mapping of a knob with MIDI signals are a lot of on youtube.
In Abletonlive, it is necessary to map the MIDI signal values sent by Arduino.

댓글 3개:

Unknown :

직접 옆에서 설명을 듣고 지켜보는건 더 굉장한데~ 요즘 바쁘지만서도 완성하면 엄청멋진 성과일듯!!!

Unknown :

안녕하세요!
my name is Yang, a design student from Germany. The theme of our semester project calls . it`s about the haptic and tactile quality in our daily objects. Thus i`m doing an interactive ball which changing the sound effect while the user moving or pressing the ball. I`ve chosen the MPU 6050 sensor (for rotation)and FSR resistive pressure sensor(for pressure). the final result would be great if it`s wireless. I`m trying working it out with XBEE and NRF24L01 Bluetooth sensors now. Or as alternative I will try to send the serial output as MIDI serial then with MIDI Bluetooth Port connect to Ableton as a MIDI Input.

I did a lot research about the mpu6050 and tried several examples and then arrived at your project))! I`ve replaced the codes of to the example code MPU6050_DMP6.ino, but i didnt know where should i put the part of to it? I`m using OS system and already downloaded the Hairless MIDI serial bridge programm. but no matter how many times i tried it doesn`t work out at all.(the green light spots of hairless program are blinking i assume there is signal tranfering from arduino to Max for live and Ableton).

My korean friend Mr.Park who studying computer science here helped me finding your contact on you website. unfortunately we couldn`t find it. Your support will mean a lot to me if you could contact me and give me some advice!
my email address is yang.ni@gmx.de
감사합니다!

TEWDA :

Hallo!

I received your kind comment. Usually most people simply ask for code, but you were different.
I want to say thank you first.

I made it two years ago when I was a student, so I do not remember it in detail.
However, I will send you the code file I have. ('DMP6_servo_test')

The sent file is a code by e-mail that moves servo motor using YAW, PITCH, ROLL value obtained from MPU6050. If you modify some of the sent files, you can use the angle value obtained from the MPU6050 as a MIDI file.





First, '#include ' on line 43 contains header files that move the servo motor. Therefore, delete it.


Secondly, the contents of lines 158, 159, 164 ~ 169 are also what set up the servo motor. Therefore, delete it.


Third, the contents on line 329, 333, and 337 are the motions of the servo motor. After deleting it, add the MIDI Control contents posted on the blog as shown below.

Ex)
midiControl (0xB0, 1, map (ypr [0] * 180 / M_PI, -180, 180, 0, 127));

I added three midiControl functions to each angle value.
A delay of at least 10 micro seconds is required between the midicontrol functions.


Finally, add the contents of the 'midiConrtrol' function posted on my blog at the end of the code, or at the very beginning, as shown in the example below.

Ex)
void midiControl (int st, int cc, int val) {
Serial.write (st);
Serial.write (cc);
Serial.write (val);
}




In addition, the 'midi_slide' file is a sample code that sends an analog variable resistance value to a MIDI signal.

After making some modifications to the sample code, make sure that the abletonlive recognizes the MIDI signal sent from Arduino.

Before that, abletonlive must recognize the Arduino as a single device, and after recognition, each MIDI signal sent from Arduino should be matched with any knob of abletonlive.

You need to do mapping in abletonlive's setting window. There are a lot of ways to do this on the Internet.



As a tip, the MPU6050 should be left for about 10 seconds after starting to stabilize initially.

I have already done a similar project with you and I used Bluetooth because it is easier to use Bluetooth which uses serial communication.



Since you are going to use Xbee and Bluetooth, I think you will understand it even if I explain it this way.

I hope my explanation helps you. I hope the project you are working on is well done.



Vielen Dank! Einen schönen Tag! :)