IBM Developer Day 2018์—์„œ ๋ฐฐํฌํ•œ IoT ๋ฑƒ์ง€๋Š” ESP32 Devkit์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฐํฌ ๋‹น์‹œ ์„ค์น˜๋œ ํ–‰์‚ฌ์šฉ ์†Œํ”„ํŠธ์›จ์–ด ๋Œ€์‹  ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ์ด ํฌํŒ…๋œ ํŽŒ์›จ์–ด๋ฅผ ์„ค์น˜ํ•˜๋ฉด IoT ๋ฑƒ์ง€๋ฅผ ๊ฐœ๋ฐœ ๋ณด๋“œ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ์˜ machine.I2C ๋ชจ๋“ˆ์„ ์ด์šฉํ•˜์—ฌ ์ž์ด๋กœ ์„ผ์„œ์™€ ํ†ต์‹ ํ•˜๊ณ  x,y,z 3์ถ•์— ๋Œ€ํ•œ ๊ธฐ์šธ๊ธฐ ์ •๋ณด๋ฅผ ์–ป๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค.

ํ•™์Šต ๋ชฉํ‘œ

์ด ํŠœํ† ๋ฆฌ์–ผ์„ ๋งˆ์น˜๊ฒŒ ๋˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • REPL์„ ์ด์šฉํ•˜์—ฌ IoT Badge์— ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ ์ฝ”๋“œ ์‹คํ–‰
  • ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ ์ฝ”๋“œ๋กœ I2C ํ†ต์‹ 
  • ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ ์ฝ”๋“œ์™€ MPU6050๋กœ X,Y,Z 3์ถ• ๊ฐ๋„ ์ธก์ •

์‚ฌ์ „ ์ค€๋น„ ์‚ฌํ•ญ

  1. Developer Day 2018 IoT Badge ํŽŒ์›จ์–ด ์„ค์น˜ํ•˜๊ธฐ
  2. Developer Day 2018 IoT Badge์— ๋‚˜๋งŒ์˜ ํŒŒ์ด์ฌ ์ฝ”๋“œ ์‹คํ–‰ํ•˜๊ธฐ
  3. IBM Developer Day 2018 IoT Badge
  4. ๋ฐ์ดํ„ฐ ํ†ต์‹ ์šฉ USB 2.0 Micro B Type ์ผ€์ด๋ธ” (๋งˆ์ดํฌ๋กœ 5ํ•€)
  5. J7๋ฒˆ 6ํ•€ 2.54mm Female ์†Œ์ผ“ ํ—ค๋” ์—ฐ๊ฒฐ
  6. GY-521 (MPU6050 Breakout ๋ณด๋“œ)

์†Œ์š” ์‹œ๊ฐ„

์ด ํŠœํ† ๋ฆฌ์–ผ์„ ์™„๋ฃŒํ•˜๊ธฐ๊นŒ์ง€ ๋Œ€๋žต 30๋ถ„ ์ •๋„๊ฐ€ ์†Œ์š”๋ฉ๋‹ˆ๋‹ค.

๋‹จ๊ณ„

MPU6050 ์„ผ์„œ ์ •๋ณด

GY-521 ๋ณด๋“œ๋Š” MPU6050 ์„ผ์„œ๋ฅผ ๋ฐ”๋กœ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋ณธ์ ์ธ ํšŒ๋กœ๊ฐ€ ํฌํ•จ๋œ Breakout Board ์ž…๋‹ˆ๋‹ค. MPU6050์˜ I2C ๋ฐ SPI ํ†ต์‹ ์„ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋„๋ก VCC, GND, SCL, SDA, XDA, XCL, AD0, INT 8ํ•€์œผ๋กœ ๋…ธ์ถœ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณธ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” I2C ๋ฐฉ์‹์œผ๋กœ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค.

์ธก์ • ๊ธฐ์ค€์€ MPU6050์˜ ๋ฌผ๋ฆฌ์  ์œ„์น˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ•˜๋ฉฐ ๊ฐ ์ถ•์— ๋Œ€ํ•œ ์ •๋ณด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

MPU6050 ์„ผ์„œ ์—ฐ๊ฒฐ ๋ฐ I2C ์ดˆ๊ธฐํ™” ํ•˜๊ธฐ

I2C ํ†ต์‹ ์„ ์œ„ํ•œ Pin์€ SCL๊ณผ SDA์ด๋ฉฐ iot badge์˜ J7์— ์—ฐ๊ฒฐ๋˜์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑ ํ•ฉ๋‹ˆ๋‹ค.

ESP32 MPU6050 Description
VCC VDD v3.3
GND GND Ground
GPIO26 SCL Serial Clock
GPIO25 SDA Serial Data

MPU6050์€ 8Pin์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ J7 ์ปค๋„ฅํ„ฐ๋Š” 6Pin์ž…๋‹ˆ๋‹ค. ์„œ๋กœ ๋งž์ง€ ์•Š์ง€๋งŒ I2C๋ฅผ ์ด์šฉํ•˜๋Š” VCC, GND, SCL, SDA๋Š” ์ด์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋‚˜๋จธ์ง€ 4Pin ๋ถ€๋ถ„์€ ๋ณธ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ESP32 MPU6050 Description
GPIO33 XDA AUX Data
GPIO27 XCL AUX Clock
N/C AD0 Select Slave Address (1K Pull-down)
N/C INT Interrupt

๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ์˜ machine.I2C ๋ชจ๋“ˆ์„ ์ด์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋กœ I2C ํ†ต์‹ ์„ ์œ„ํ•œ ์ค€๋น„๊ฐ€ ์™„๋ฃŒ ๋ฉ๋‹ˆ๋‹ค.

from machine import I2C, Pin
import time

SCL = Pin(26) # SCL
SDA  = Pin(25) # SDA
i2c = I2C(scl=SCL, sda=SDA)

์ด์ œ MPU6050์—์„œ ์‚ฌ์šฉํ•˜๋Š” Register์— ๋Œ€ํ•ด ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. MPU6050์ด ์‚ฌ์šฉํ•˜๋Š” Register๋Š” 0x0D ๋ถ€ํ„ฐ 0x75 ๊นŒ์ง€ 82๊ฐœ์— ๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋ณธ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ์‹ค์Šต์— ํ•„์š”ํ•œ Register๋ฅผ ์„ ํƒํ•˜์—ฌ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ฉฐ, ๋” ๋งŽ์€ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ MPU6050 Register Map๋ฅผ ์ฐธ๊ณ  ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก Register๋ฅผ ์ƒ์ˆ˜๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

MPU6050_CONFIG       = 0x1a
MPU6050_GYRO_CONFIG  = 0x1b
MPU6050_ACCEL_CONFIG = 0x1c
MPU6050_SMPLRT_DIV = 0x19

MPU6050_I2C_MST_STATUS = 0x36
MPU6050_INT_STATUS = 0x3a
MPU6050_ACCEL_XOUT_H = 0x3b
MPU6050_ACCEL_XOUT_L = 0x3c
MPU6050_ACCEL_YOUT_H = 0x3d
MPU6050_ACCEL_YOUT_L = 0x3e
MPU6050_ACCEL_ZOUT_H = 0x3f
MPU6050_ACCEL_ZOUT_L = 0x40
MPU6050_TEMP_OUT_H = 0x41
MPU6050_TEMP_OUT_L = 0x42
MPU6050_GYRO_XOUT_H = 0x43
MPU6050_GYRO_XOUT_L = 0x44
MPU6050_GYRO_YOUT_H = 0x45
MPU6050_GYRO_YOUT_L = 0x46
MPU6050_GYRO_ZOUT_H = 0x47
MPU6050_GYRO_ZOUT_L = 0x48
MPU6050_PWR_MGMT_1 = 0x6b
MPU6050_PWR_MGMT_2 = 0x6c
MPU6050_WHO_AM_I = 0x75

MPU6050_ACCEL_XOUT = 0x3b
MPU6050_ACCEL_YOUT = 0x3d
MPU6050_ACCEL_ZOUT = 0x3f
MPU6050_TEMP_OUT = 0x41
MPU6050_GYRO_XOUT = 0x43
MPU6050_GYRO_YOUT = 0x45
MPU6050_GYRO_ZOUT = 0x47

I2C ๋ฐ์ดํ„ฐ ํ†ต์‹ 

MPU6050์„ผ์„œ์™€ ํ†ต์‹ ์€ SPI์™€ I2C ๋ฐฉ์‹์„ ์„ ํƒํ•ด์„œ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณธ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” I2C๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. I2C๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ์ž…์žฅ์—์„œ Master์™€ Slave๋กœ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. iot badge๊ฐ€ ์„ผ์„œ๋ฅผ ์ œ์–ดํ•˜๋ฏ€๋กœ Master, MPU6050 ์„ผ์„œ๊ฐ€ Slave๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

I2C ํ†ต์‹ ์„ ์œ„ํ•ด์„œ๋Š” Slave์— ๋Œ€ํ•œ ์ฃผ์†Œ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•œ๋ฐ, MPU6050 ์„ผ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •ํ•ด์ ธ ์žˆ์œผ๋ฉฐ AD0 Pin์˜ ์ƒํƒœ ๊ฐ’์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

AD0 Slave Address Hex
HIGH 1101001 0x69
LOW 1101000 0x68

๊ธฐ๋ณธ์ ์œผ๋กœ GY-521 ๋ณด๋“œ์—์„œ๋Š” AD0 Pin์„ 1K ์ €ํ•ญ์œผ๋กœ Pull-down ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ Slave ์ฃผ์†Œ ๊ฐ’์€ 0x68์ด ๋ฉ๋‹ˆ๋‹ค. iot badge์˜ J7๊ณผ ์„ผ์„œ ๋ณด๋“œ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒฝ์šฐ AD0 Pin์ด ์ง์ ‘ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š๊ณ  ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ Slave Address๋ฅผ 0x69๋กœ ์ ‘๊ทผํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๋ณ„๋„์˜ Jump Cable์„ AD0 Pin์— ์—ฐ๊ฒฐ ํ›„ HIGH ๊ฐ’์„ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

MPU6050์ด ์ดˆ๊ธฐํ™” ๋˜๋Š” ๊ฒฝ์šฐ PWR_MGMT_1์™€ WHO_AM_I Register๋ฅผ ์ œ์™ธํ•˜๋ฉด ๋ชจ๋“  Register์˜ ๊ฐ’์€ 0x00์œผ๋กœ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, PWR_MGMT_1์˜ ๊ฒฝ์šฐ 0x40์œผ๋กœ ์ดˆ๊ธฐํ™” ๋˜๋Š”๋ฐ ์ด๋Š” Sleep ๋ชจ๋“œ๋กœ ์ง„์ž…๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ฒ˜์Œ ๋ฆฌ์…‹ ํ›„ Sleep ๋ชจ๋“œ๋ฅผ ํ•ด์ œ ํ•˜๋Š” ๊ฒƒ์ด ์ฒซ ๋ฒˆ์งธ ๋ช…๋ น์ด ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ์ค€๋น„๊ฐ€ ๋˜์—ˆ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹จ๊ณ„๋กœ MPU6050๊ณผ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค.

  • Master์—์„œ Slave๋กœ PWR_MGMT_1 ๋กœ 0x00 ์ „๋‹ฌํ•˜์—ฌ Wake up
  • Master์—์„œ Slave์— MPU6050_WHO_AM_I ๊ฐ’์„ ์š”์ฒญํ•˜์—ฌ 0x68์„ ์ˆ˜์‹ ํ•˜๋Š”์ง€ ํ™•์ธ

๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ์—์„œ๋Š” I2C ํ†ต์‹  ์ดˆ๊ธฐํ™”๋กœ ์–ป์€ instance์˜ writeto_mem() ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด Slave์˜ Register์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ณ  readfrom_mem() ํ•จ์ˆ˜๋กœ Slave์˜ Register์˜ ์ •๋ณด๋ฅผ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์ด์šฉํ•˜์—ฌ ํ•„์š”ํ•œ ๋ช…๋ น ์‹œํ€€์Šค์„ ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ ์ฝ”๋“œ๋กœ ๊ตฌ์„ฑํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

sl_addr = 0x68 # slave address
i2c.writeto_mem(sl_addr, MPU6050_PWR_MGMT_1, bytearray([0x00]))
data = i2c.readfrom_mem(sl_addr, MPU6050_WHO_AM_I, 1)[0]
if data != 0x68:
    raise Exception('MPU6050 initialize failure')

๋งŒ์•ฝ, MPU6050_ACCEL_XOUT์™€ ๊ฐ™์ด High, Low ๋‘ ๋ฐ”์ดํŠธ๋กœ ๋‚˜๋ˆ„์–ด์ ธ ์žˆ๋Š” ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด 2 byte ๊ฐ’์„ ์ฝ๊ณ  ์ด๋ฅผ 16 bits signed ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import ustruct
data = i2c.readfrom_mem(sl_addr, MPU6050_ACCEL_XOUT_H, 2)
acc_x = ustruct.unpack('>h', data)[0]

์‚ฌ์‹ค MPU6050์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฐ’์€ ๊ฐ€์†๋„ 3๊ฐœ, ์˜จ๋„, ๊ฐ์†๋„ 3๊ฐœ ์ด 7๊ฐœ์˜ 2 byte ๋ฐ์ดํ„ฐ์˜ ์ฃผ์†Œ๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ํ•œ ๋ฒˆ์— ํ†ต์‹  ํ•  ๋•Œ 14 ๋ฐ”์ดํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ณ  ์ด๋ฅผ ๊ฐ๊ฐ ๋ถ„๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

data = i2c.readfrom_mem(sl_addr, MPU6050_ACCEL_XOUT_H, 14)
acc_x, acc_y, acc_z, tp, gyro_x, gyro_y, gyro_z = ustruct.unpack('>hhhhhhh', data)

์ฝ์–ด ๋“ค์ธ ๊ฐ’ ์ค‘ ์˜จ๋„ ๊ฐ’์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ณ€ํ™˜ ์ˆ˜์‹์„ ์ด์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Temperature in degrees C = (TEMP_OUT Register Value as a signed quantity)/340 + 36.53

์ด๋ฅผ ํŒŒ์ด์ฌ ์ฝ”๋“œ๋กœ ๋ฐ˜์˜ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

def measure():
    data = i2c.readfrom_mem(sl_addr, MPU6050_ACCEL_XOUT_H, 14)
    acc_x, acc_y, acc_z, tp, gyro_x, gyro_y, gyro_z = ustruct.unpack('>hhhhhhh', data)

    # Convert in degrees C
    tp = tp/340 + 36.53

    return (acc_x, acc_y, acc_z, tp, gyro_x, gyro_y, gyro_z)

์„ผ์„œ ์ •๋ณด ํ‘œ์‹œ

๋‹ค์Œ๊ณผ ๊ฐ™์ด measure() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์„ผ์„œ ์ •๋ณด๋ฅผ ์ถœ๋ ฅ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

while True:
    acc_x, acc_y, acc_z, temp, gyro_x, gyro_y, gyro_z = measure()
    
    print('{},{},{}'.format(acc_x, acc_y, acc_z))
    # print('{},{},{}'.format(gx, gy, gz))
    time.sleep_ms(10)

๊ทธ๋ฆฌ๊ณ  ์ด๋ ‡๊ฒŒ ์ถœ๋ ฅ๋˜๋Š” ๊ฐ’์„ Ardino ํ”„๋กœ๊ทธ๋žจ์˜ Serial Plotter๋ฅผ ์ด์šฉํ•˜๋ฉด ์ด๋ฅผ ์‹œ๊ณ„์—ด ์ฐจํŠธ๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„ผ์„œ๋กœ ์ธก์ •ํ•œ ๊ฐ’์ด Analog ๊ฐ’์ด๋ผ ์ผ์ •ํ•œ ๋ฒ”์œ„ ์•ˆ์—์„œ ๊ฐ’์ด ํ”๋“ค๋ฆฌ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์ผ์ • ๊ณผ๊ฑฐ ์‹œ์  ๋ถ€ํ„ฐ ํ˜„์žฌ๊นŒ์ง€์˜ ํ‰๊ท  ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ณค ํ•ฉ๋‹ˆ๋‹ค. ๋ณธ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ์ง€์ˆ˜ ์ด๋™ ํ‰๊ท (Exponential Moving Average)์ด๋ผ๋Š” ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. ์ง€์ˆ˜ ์ด๋™ ํ‰๊ท ์— ๋Œ€ํ•œ ์ˆ˜์‹์œผ๋กœ ๊ธฐ๊ฐ„์„ N์ด๋ผ ํ•  ๋•Œ ํ‰ํ™œ ๊ณ„์ˆ˜ k๋Š” 2 / (N+1)์ด ๋˜๊ณ , tํšŒ์ฐจ ์ง€์ˆ˜ ์ด๋™ ํ‰๊ท  ๊ฐ’ EMA(t)๋Š” Value(t) x k + EMA(t-1) x (1.0 – k) ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์ด์šฉํ•˜์—ฌ N์ด 19์ผ ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ ์šฉ ํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

def ema(prev, curr):
    # Exponential Moving Average
    # EMA(t) = Value(t)*k + EMA(y) * (1-k)
    # k = 2 / (N+1)
    # k = 0.1 where N = 19
    return curr * 0.1 + prev * 0.9

๊ทธ๋ฆฌ๊ณ , ์ด๋ฅผ ์ ์šฉํ•˜๋ฉด

ax = 0;ay = 0;az = 0
while True:

    acc_x, acc_y, acc_z, temp, gyro_x, gyro_y, gyro_z = measure()
    
    ax = ema(ax, acc_x)
    ay = ema(ay, acc_y)
    az = ema(az, acc_z)

    print('{},{},{}'.format(ax, ay, az))
    time.sleep_ms(10)

์ด ๊ฒฝ์šฐ ๊ทธ๋ž˜ํ”„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ข€ ๋” ์•ˆ์ •์ ์œผ๋กœ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ€์†๋„๊ณ„(Accelerometer) ๊ฐ’ ์ธก์ •

์œ„์˜ ๊ทธ๋ž˜ํ”„๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์‹ค์ œ MPU6050์˜ X, Y, Z ์ถ•์— ๋Œ€ํ•œ ๊ฐ€์†๋„๊ณ„ ๊ฐ’์ด ์–ด๋–ป๊ฒŒ ์ธก์ •๋˜๋Š”์ง€ ๋ˆˆ์œผ๋กœ ํ™•์ธ ํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ‰์ƒ์— ๋”ฐ๋ผ ๊ฐ๊ฐ ๋‹ค๋ฅธ ์ถ•์˜ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

์ƒ‰๊น” ์ถ•
ํŒŒ๋ž‘ X
๋นจ๊ฐ• Y
์ดˆ๋ก Z

IDLE ์ƒํƒœ์˜ ๊ทธ๋ฆผ์—์„œ X์™€ Y๋Š” 0์— ๊ทผ์ ‘ํ•˜์ง€๋งŒ Z ์ถ•์€ ๊ฐ’์ด 18,000 ์ •๋„๋ฅผ ์œ ์ง€ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” MPU6050 ์„ผ์„œ์˜ ๊ฐ€์†๋„๊ณ„๊ฐ€ ์ง€๊ตฌ ์ค‘๋ ฅ์— ๋ฐ˜์‘ํ•˜๋Š” ๊ฒƒ์œผ๋กœ MPU6050์˜ ๊ฐ€์†๋„๊ณ„ ๊ธฐ๋ณธ scale ๊ฐ’์ด ยฑ2G ์ด๋ฏ€๋กœ ๊ฐ’ 18,000 ์ •๋„๋Š” ๋Œ€๋žต 1G๋ฅผ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

X ์ถ• ์ด๋™

๋‹ค์Œ ๊ทธ๋ฆผ์€ IDLE ์ƒํƒœ์—์„œ X ์ถ•์„ ์œ—์ชฝ์œผ๋กœ ํ–ฅํ•  ๋•Œ์— ๋Œ€ํ•œ ๊ทธ๋ž˜ํ”„ ์ž…๋‹ˆ๋‹ค.

X์ถ•์„ ์˜๋ฏธํ•˜๋Š” ํŒŒ๋ž€์ƒ‰ ๊ทธ๋ž˜ํ”„๊ฐ€ ์ƒ์Šนํ•˜๊ณ  ์ƒ๋Œ€์ ์œผ๋กœ Z์ถ•(์ดˆ๋ก)์ด ๋‚ฎ์•„์ง€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ X ์ถ•์„ ์•„๋žซ์ชฝ์œผ๋กœ ํ–ฅํ•  ๋•Œ ๊ทธ๋ž˜ํ”„์ž…๋‹ˆ๋‹ค.

X์ถ•์ด ๋‚ฎ์•„์ง€๋ฉด์„œ 0์— ๊ฐ€๊นŒ์›Œ์ง€๋ฉด Z์ถ•์ด ์ƒ์Šนํ•˜๋‹ค๊ฐ€ X์ถ•์ด ์Œ์ˆ˜๋กœ ์ „ํ™˜๋˜์–ด -1G ๊ฐ’์ด ๋˜๋ฉด ๋‹ค์‹œ 0์œผ๋กœ ๋‚ฎ์•„์ง€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Y ์ถ• ์ด๋™

๋‹ค์Œ ๊ทธ๋ฆผ์€ IDLE ์ƒํƒœ์—์„œ Y ์ถ•์„ ์œ—์ชฝ์œผ๋กœ ํ–ฅํ•  ๋•Œ์— ๋Œ€ํ•œ ๊ทธ๋ž˜ํ”„ ์ž…๋‹ˆ๋‹ค.

Y์ถ•์„ ์˜๋ฏธํ•˜๋Š” ๋นจ๊ฐ„์ƒ‰ ๊ทธ๋ž˜ํ”„๊ฐ€ ์ƒ์Šนํ•˜๊ณ  ์ƒ๋Œ€์ ์œผ๋กœ Z์ถ•(์ดˆ๋ก)์ด ๋‚ฎ์•„์ง€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ Y ์ถ•์„ ์•„๋žซ์ชฝ์œผ๋กœ ํ–ฅํ•  ๋•Œ ๊ทธ๋ž˜ํ”„์ž…๋‹ˆ๋‹ค.

Z ์ถ• ์ด๋™

๋‹ค์Œ ๊ทธ๋ฆผ์€ IDLE ์ƒํƒœ์—์„œ Z์ถ•์„ ์•„๋ž˜๋กœ ํ–ฅํ•  ๋•Œ์— ๋Œ€ํ•œ ๊ทธ๋ž˜ํ”„ ์ž…๋‹ˆ๋‹ค.

Z์ถ•์ด ๋‚ฎ์•„์ง€๋ฉด์„œ -1G์— ๊ฐ€๊นŒ์›Œ์ง€๋ฉด์„œ X, Y ์ถ•๋„ ๊ฐ™์ด ๋‚ฎ์•„์ง€๋Š” ๋“ฏ ํ•˜์ง€๋งŒ Z์ถ•์ด ์Œ์ˆ˜๋กœ ์ „ํ™˜๋˜์–ด -1G ๊ฐ’์ด ๋˜๋ฉด ๋‹ค์‹œ 0์œผ๋กœ ์ˆ˜๋ ตํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž์ด๋กœ์Šค์ฝ”ํ”„(Gyroscope) ๊ฐ’ ์ธก์ •

๊ฐ€์†๋„๊ณ„์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

gx = 0;gy = 0;gz = 0
while True:

    acc_x, acc_y, acc_z, temp, gyro_x, gyro_y, gyro_z = measure()
    
    gx = ema(gx, gyro_x)
    gy = ema(gy, gyro_y)
    gz = ema(gz, gyro_z)

    print('{},{},{}'.format(gx, gy, gz))
    time.sleep_ms(10)

MPU6050์˜ ์ž์ด๋กœ์Šค์ฝ”ํ”„ ๊ธฐ๋ณธ scale ๊ฐ’์€ ยฑ250 DPS ์ž…๋‹ˆ๋‹ค. DPS๋ž€ Degree per second๋กœ์„œ ์ดˆ๋‹น ํšŒ์ „ ๊ฐ์„ ์˜๋ฏธํ•˜๋ฉฐ ๊ฐ์†๋„๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. Full-scale ๊ฐ’์„ ์ ์šฉํ•˜๋ฉด ์„ผ์„œ ์ธก์ • ๊ฐ’ 100์ด ๋Œ€๋žต 0.76 DPS ์ •๋„๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๋‹จ์œ„ ์‹œ๊ฐ„๋‹น ํšŒ์ „ํ•œ ๊ฐ๋„์ด๋ฏ€๋กœ ์ง€์†์ ์œผ๋กœ ํšŒ์ „ํ•˜๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ์ธก์ • ๊ฐ’์€ 0์œผ๋กœ ์ˆ˜๋ ดํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ธก์ •ํ•œ ๊ฐ์†๋„๋งŒ์œผ๋กœ๋Š” ํ•ด๋‹น ๋ฌผ์ฒด๊ฐ€ ์–ด๋Š ๋ฐฉํ–ฅ์œผ๋กœ ํšŒ์ „ํ•˜๊ณ  ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋งŒ ์•Œ ์ˆ˜ ์žˆ์„ ๋ฟ ์–ผ๋งˆ๋‚˜ ํšŒ์ „ํ•œ ๊ฒƒ์ธ์ง€๋Š” ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

X ์ถ• ๊ฐ์†๋„ ์ธก์ •

X ์ถ•์„ ๊ธฐ์ค€์œผ๋กœ ๋ฐ˜์‹œ๊ณ„ ๋ฐฉํ–ฅ์œผ๋กœ ํšŒ์ „ ํ›„ ๋‹ค์‹œ ์‹œ๊ณ„ ๋ฐฉํ–ฅ์œผ๋กœ ๋ณต๊ท€ํ•œ ๊ทธ๋ž˜ํ”„์ž…๋‹ˆ๋‹ค.

16,000 ๊ฐ€๊นŒ์ด ์ด๋™ํ–ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ -16,000 ์ •๋„๋กœ ๊ฐ’์ด ๋ณ€ํ–ˆ์œผ๋ฉฐ ํ™˜์‚ฐํ•˜๋ฉด +120 DPS์—์„œ -120 DPS ๋งŒํผ ๋ณ€๋™๋œ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Y ์ถ• ๊ฐ์†๋„ ์ธก์ •

Y ์ถ•์„ ๊ธฐ์ค€์œผ๋กœ ๋ฐ˜์‹œ๊ณ„ ๋ฐฉํ–ฅ์œผ๋กœ ํšŒ์ „ ํ›„ ๋‹ค์‹œ ์‹œ๊ณ„ ๋ฐฉํ–ฅ์œผ๋กœ ๋ณต๊ท€ํ•œ ๊ทธ๋ž˜ํ”„์ž…๋‹ˆ๋‹ค.

12,000 ๊ฐ€๊นŒ์ด ์ด๋™ํ–ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ -12,000 ์ •๋„๋กœ ๊ฐ’์ด ๋ณ€ํ–ˆ์œผ๋ฉฐ ํ™˜์‚ฐํ•˜๋ฉด +91 DPS์—์„œ ๋‹ค์‹œ -91 DPS ๋งŒํผ ๊ธฐ์šธ์–ด์ง„ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Z ์ถ• ๊ฐ์†๋„ ์ธก์ •

Z ์ถ•์„ ๊ธฐ์ค€์œผ๋กœ ๋ฐ˜์‹œ๊ณ„ ๋ฐฉํ–ฅ์œผ๋กœ ํšŒ์ „ ํ›„ ๋‹ค์‹œ ์‹œ๊ณ„ ๋ฐฉํ–ฅ์œผ๋กœ ๋ณต๊ท€ํ•œ ๊ทธ๋ž˜ํ”„์ž…๋‹ˆ๋‹ค.

12,000 ๊ฐ€๊นŒ์ด ์ด๋™ํ–ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ -12,000 ์ •๋„๋กœ ๊ฐ’์ด ๋ณ€ํ–ˆ์œผ๋ฉฐ ํ™˜์‚ฐํ•˜๋ฉด +91 DPS์—์„œ ๋‹ค์‹œ -91 DPS ๋งŒํผ ๊ธฐ์šธ์–ด์ง„ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Full scale ๋ฐ˜์˜

์„ผ์„œ๋กœ ์ธก์ •ํ•œ ๊ฐ’์ด raw data๊ฐ€ ์•„๋‹Œ Full scale ๊ฐ’์— ๋”ฐ๋ผ ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

# Full scale range & sensitivity
MPU6050_AFS_SEL = {
    0: (2, 16384),
    1: (4, 8192),
    2: (8, 4096),
    3: (16, 2048),
}

MPU6050_FS_SEL = {
    0: (250, 131),
    1: (500, 65.5),
    2: (1000, 32.8),
    3: (2000, 16.4),
}

def acc_full_scale_range():
    cfg = i2c.readfrom_mem(sl_addr, MPU6050_ACCEL_CONFIG, 1)[0]
    return MPU6050_AFS_SEL.get((cfg >> 3) & 0x03, MPU6050_AFS_SEL[0])[0]

def gyro_full_scale_range():
    cfg = i2c.readfrom_mem(sl_addr, MPU6050_GYRO_CONFIG, 1)[0]
    return MPU6050_FS_SEL.get((cfg >> 3) & 0x03, MPU6050_FS_SEL[0])[0]

afs_range = acc_full_scale_range()
gfs_range = gyro_full_scale_range()

def measure_scaled():
    acc_x, acc_y, acc_z, temp, gyro_x, gyro_y, gyro_z = measure()
    
    # LSB Sensitivity : 16384 LSB/g
    # max: 32767 # 0x7f
    # min: -32768 # 0x80

    acc_x = acc_x * afs_range / 32768
    acc_y = acc_y * afs_range / 32768
    acc_z = acc_z * afs_range / 32768

    gyro_x = gyro_x * gfs_range / 32768
    gyro_y = gyro_y * gfs_range / 32768
    gyro_z = gyro_z * gfs_range / 32768

    return (acc_x, acc_y, acc_z, temp, gyro_x, gyro_y, gyro_z)

์ด๋ฅผ Sensitivity๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋‹ค์‹œ ์ •๋ฆฌํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

def acc_full_scale_sensitivity():
    cfg = i2c.readfrom_mem(sl_addr, MPU6050_ACCEL_CONFIG, 1)[0]
    return MPU6050_AFS_SEL.get((cfg >> 3) & 0x03, MPU6050_AFS_SEL[0])[1]

def gyro_full_scale_sensitivity():
    cfg = i2c.readfrom_mem(sl_addr, MPU6050_GYRO_CONFIG, 1)[0]
    return MPU6050_FS_SEL.get((cfg >> 3) & 0x03, MPU6050_FS_SEL[0])[1]

afs_sensitivity = acc_full_scale_sensitivity()
gfs_sensitivity = gyro_full_scale_sensitivity()

def measure_scaled():
    data = i2c.readfrom_mem(sl_addr, MPU6050_ACCEL_XOUT_H, 14)
    measured = list(ustruct.unpack('>hhhhhhh', data))

    measured[0:3] = [(m * afs_range / 32768) for m in measured[0:3]]
    measured[3] = measured[3]/340 + 36.53 # Convert in degrees C
    measured[4:] = [(m * gfs_range / 32768) for m in measured[4:]]

    return tuple(measured)
gx = 0;gy = 0;gz = 0
while True:

    acc_x, acc_y, acc_z, temp, gyro_x, gyro_y, gyro_z = measure_scaled()
    
    ax = ema(ax, acc_x)
    ay = ema(ay, acc_y)
    az = ema(az, acc_z)

    print('{},{},{}'.format(ax, ay, az))
    time.sleep_ms(10)

Roll & Pitch

์ถ•์— ๋Œ€ํ•œ ๊ฐ€์†๋„์™€ ๊ฐ์†๋„ ๊ฐ’์„ ์•Œ๋ฉด ๊ธฐ๊ธฐ๊ฐ€ ์–ผ๋งŒํผ ๊ธฐ์šธ์–ด์ง€๋Š”์ง€ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค๋งŒ, ์ธก์ • ๊ฐ’์— ๋Œ€ํ•œ ์˜ค์ฐจ ๋ฐ ๋ฐฉ์œ„๊ฐ ์ธก์ •์— ๋”ฐ๋ฅธ ์ ๋ถ„ ๊ณ„์‚ฐ๋“ฑ์ด ํ•„์š”ํ•˜๋ฏ€๋กœ ๋ณธ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด X, Y ์ถ• ๊ธฐ์šธ์–ด์ง ์ •๋„๋งŒ ํ™•์ธ ํ•ฉ๋‹ˆ๋‹ค.

import math
RADIANS_TO_DEGREES = 180/math.pi

def calc_acc_angles(acc_x, acc_y, acc_z):
    angle_x = math.atan(acc_x / math.sqrt(math.pow(acc_y, 2) + math.pow(acc_z, 2))) * RADIANS_TO_DEGREES
    angle_y = math.atan(acc_y / math.sqrt(math.pow(acc_x, 2) + math.pow(acc_z, 2))) * RADIANS_TO_DEGREES
    angle_z = math.atan(math.sqrt(math.pow(acc_x, 2) + math.pow(acc_y, 2)) / acc_z) * RADIANS_TO_DEGREES

    return (angle_x, angle_y, angle_z)

ax = 0;ay = 0;az = 0
while True:

    acc_x, acc_y, acc_z, temp, gyro_x, gyro_y, gyro_z = measure()
    
    ax = ema(ax, acc_x)
    ay = ema(ay, acc_y)
    az = ema(az, acc_z)

    pitch, roll, z = calc_acc_angles(ax, ay, az)

    print('{},{},{}'.format(pitch, roll, z))

    time.sleep_ms(10)

๊ณต ๊ตด๋ฆฌ๊ธฐ ๊ฒŒ์ž„ ๋งŒ๋“ค๊ธฐ

uGFX๋ฅผ ์ด์šฉํ•˜์—ฌ ํ™”๋ฉด์— ๊ฐ„๋‹จํ•œ ๊ฒŒ์ž„์„ ๋งŒ๋“ค์–ด ๋ณด๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์–€์ƒ‰ ํ™”๋ฉด์— ๋นจ๊ฐ„ ๊ณต์ด ๊ฐ€์šด๋ฐ ๋‚˜ํƒ€๋‚˜๋ฉด iot badge๋ฅผ ๊ธฐ์šธ์—ฌ ๊ณต์ด ๊ธฐ์šธ์–ด์ง„ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์šธ์–ด์ง„ ๊ฐ๋„์— ๋”ฐ๋ผ ๊ณต์˜ ์ด๋™ ๋‹จ์œ„๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ ์•ฝ๊ฐ„์˜ ๊ฐ€๊ฐ์† ํšจ๊ณผ๋ฅผ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

ugfx.init()
ugfx.set_default_font('IBMPlexMono_Bold24')
ugfx.clear()
ugfx.Label(40, 0, 240, 60, text='MPU6050 Demo')

ugfx.set_default_font('IBMPlexMono_Regular24')
l = ugfx.Label(40, 60, 240, 120, text='')

# Wait 3 seconds
for i in range(3):
    l.text('Waits {}'.format('.'*i))
    time.sleep(1)

ugfx.clear()

width = ugfx.width()
height = ugfx.height()

px = width//2
py = height//2
cx = px; cy = py
ax = 0;ay = 0;az = 0
gx = 0;gy = 0;gz = 0
while True:

    acc_x, acc_y, acc_z, temp, gyro_x, gyro_y, gyro_z = measure_scaled()
    
    ax = ema(ax, acc_x)
    ay = ema(ay, acc_y)
    az = ema(az, acc_z)

    gx = ema(gx, gyro_x)
    gy = ema(gy, gyro_y)
    gz = ema(gz, gyro_z)

    #print('{},{},{}'.format(ax, ay, az))
    # print('{},{},{}'.format(gx, gy, gz))
    # print('{},{},{},{},{},{}'.format(ax, ay, az, gx, gy, gz))

    pitch, roll, z = calc_acc_angles(ax, ay, az)
    pitch = int(pitch)
    roll = int(roll)
    
    # Move ball
    cx += roll//5
    cy += pitch//5
    
    # Border
    if cx < 0:
        cx = 0
    if cy < 0:
        cy = 0
    if cx >= width:
        cx = width-1
    if cy >= height:
        cy = height-1

    # Erase
    if px != cx or py != cy:
        #ugfx.pixel(px, py, ugfx.WHITE)
        #ugfx.area(px-5, py-5, 10, 10, ugfx.WHITE)
        ugfx.fill_circle(px, py, 5, ugfx.WHITE)

    # Draw
    #ugfx.pixel(cx, cy, ugfx.BLACK)
    #ugfx.area(cx-5, cy-5, 10, 10, ugfx.RED)
    ugfx.fill_circle(cx, cy, 5, ugfx.RED)

    # Renew
    px = cx; py = cy

iot badge์—์„œ ์‹คํ–‰ ์ค‘์ธ ๋ชจ์Šต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋งบ์Œ๋ง

์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” IBM Developer Day 2018 IoT ๋ฑƒ์ง€์™€ MPU6050 ์„ผ์„œ ์—ฐ๋™ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.