#include <DFRobot_BMI160.h>
#include <Wire.h>
DFRobot_BMI160 bmi160;
const int8_t i2c_addr = 0x68;
const float ACCEL_SCALE = 1.0f / 16384.0f;
const float GYRO_SCALE = 1.0f / 16.4f;
const float D2R = 0.01745329f; // 180도를 파이값으로 나눈 것
float roll = 0.0f, pitch = 0.0f, yaw = 0.0f;
float base_grx = 0, base_gry = 0, base_grz = 0;
unsigned long lastTime;
void setup() {
Serial.begin(115200);
if ((bmi160.softReset() != BMI160_OK) || (bmi160.I2cInit(i2c_addr) != BMI160_OK)) {
Serial.println("init false");
while(1);
}
//====== Gyro Offet Calibrating ======
int samples = 100;
for(int i=0; i < samples; i++) {
int16_t data[6] = {0};
bmi160.getAccelGyroData(data);
base_grx += data[0];
base_gry += data[1];
base_grz += data[2];
delay(10);
}
base_grx /= samples;
base_gry /= samples;
base_grz /= samples;
//====================================
lastTime = micros();
}
void loop() {
unsigned long currentTime = micros();
float dt = (currentTime - lastTime) * 0.000001f;
if(dt <= 0.0f || dt > 0.1f) {
lastTime = currentTime;
return;
}
lastTime = currentTime;
int16_t data[6] = {0};
if (bmi160.getAccelGyroData(data) == 0) {
float grx = ((data[0] - base_grx) * GYRO_SCALE) * D2R; // Radian, 비율
float gry = ((data[1] - base_gry) * GYRO_SCALE) * D2R; // Radian, 비율
float grz = ((data[2] - base_grz) * GYRO_SCALE) * D2R; // Radian, 비율
float ax = data[3] * ACCEL_SCALE; // 중력가속도 값, 비율
float ay = data[4] * ACCEL_SCALE; // 중력가속도 값, 비율
float az = data[5] * ACCEL_SCALE; // 중력가속도 값, 비율
// 중력가속도계 기준으로 Roll, Pitch 값 설정
float accRoll = atan2(ay, az);
float accPitch = atan2(-ax, sqrt(ay * ay + az * az));
// 상보필터를 적용하여 데이터 보완 (Gyro 데이터 96% + 가속도 데이터 4% 적용)
roll = 0.96f * (roll + grx * dt) + 0.04f * accRoll;
pitch = 0.96f * (pitch + gry * dt) + 0.04f * accPitch;
// YAW 값은 보완할 수 있는 자료가 없으므로 Gyro 데이터에 지연값 누적 처리
// 지자계 센서 데이터로 보완 가능, BMI160에는 없음
yaw += grz * dt; // Radian
Serial.print(roll, 4); Serial.print(","); // roll : radian
Serial.print(pitch, 4); Serial.print(","); // pitch : radian
Serial.println(yaw, 4); // yaw : radian
}
}