์ด ๊ธ์ ์๋์ด๋ ธ๋ฅผ ์ด์ฉํ ์ธ๊ทธ์จ์ด ๊ธฐ๋ฐ ์ค๋ค๋ฆฌ๊ธฐ ๋ก๋ด ์ ์์ ๊ณผ์ ์ ๋ํด์ ์ธ๋ถ์ ์ธ ๊ธ์ ๋๋ค.
> ๊ธ์ ์์ฑํ๊ธฐ์ ์์ ์ด ์ค๋ค๋ฆฌ๊ธฐ ๋ก๋ด์ ์ด๋ฆ์ ์ค์ค์ด๋ผ๊ณ ๋ถ๋ฅด๋๋ก ํ๊ณ ์ ํ๋ค.
์ด๋ ์ฌ์ฉ๋ ํ์ผ์ ๋ค์ ๊นํ ๋งํฌ์ ์ ์ฅํ์๋ค.
https://github.com/MOSW626/Tug_of_War_Robot_Project_-2022-.git
GitHub - MOSW626/Tug_of_War_Robot_Project_-2022-
Contribute to MOSW626/Tug_of_War_Robot_Project_-2022- development by creating an account on GitHub.
github.com
- ์ค์ค์ด 1ํธ


> MPU6050 ๋ค๋ฃจ๊ธฐ(22.05.23.)
๊ฐ์ฅ ๋จผ์ github์ด๋ ์ ํ๋ธ์์ ์ด๋ฏธ ์งํํ ์ ํ ๋ฐธ๋ฐ์ฑ ๊ด๋ จ ์ฝ๋๋ฅผ ๋ฐ์ ๋ค์๊ณผ ๊ฐ์ด ์งํํด ๋ณด์๋ค.
// ์๋์ด๋
ธ ๋ฐธ๋ฐ์ฑ๋ก๋ด ํ๋ก๊ทธ๋จ (์ฐธ์กฐ ๋ฐ ์ถ์ฒ : circuitdigest.com)
#include "I2Cdev.h"
#include <PID_v1.h>
#include "MPU6050_6Axis_MotionApps20.h"
MPU6050 mpu;
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorFloat gravity; // [x, y, z] gravity vector
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
// ์๋์ 4๊ฐ์ ๊ฐ์ ๋ณธ์ธ์ ๋ก๋ด์ ๋ง๊ฒ ํ๋ํฉ๋๋ค.
/*********ํ๋ผ๋ฉํฐ ํ๋ ์์*********/
double setpoint= 220; //๋ก๋ด์ด ์ง๋ฉด์์ ํํ์ ์ ์งํ๋ ์ํ์ ๊ฐ์
๋๋ค.
//๋ค์์ PID ์ ์ด๊ธฐ์ Kp, Ki, Kd ํ๋ผ๋ฉํ๋ฅผ ์ค์ ํฉ๋๋ค. ์๋์ ์์๋๋ก ์ค์ ํฉ๋๋ค.
double Kp = 21;
double Kd = 0.8;
double Ki = 10;
/******ํ๋ผ๋ฉํฐ ํ๋ ๋*********/
double input, output;
PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
volatile bool mpuInterrupt = false; // MPU6050์ ์ธํฐ๋ฝํธ ๋ฐ์์ ๋ฌด ํ์ธ
void dmpDataReady()
{
mpuInterrupt = true;
}
void setup() {
Serial.begin(115200);
// MPU6050 ์ด๊ธฐํ
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
// MPU6050 ํต์ ํ์ธ
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// DMP ์ด๊ธฐํ
devStatus = mpu.dmpInitialize();
// ๊ธฐ๋ณธ ์ต์
๊ฐ ์ค์
mpu.setXGyroOffset(220); // ์ํ์ผ๋์ ๊ธฐ๋ณธ x์ถ ์์ด๋ก๊ฐ
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1688);
// ์ ์๋์ํ๋ ๊ฒฝ์ฐ
if (devStatus == 0)
{
// DMP ๊ฐ๋
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// ์๋์ด๋
ธ ์ธํฐ๋ฝํธ ์ค์
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// DMP ์ฌ์ฉ๊ฐ๋ฅ ์ํ ์ค์
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// ํจํท์ฌ์ด์ฆ ๊ฐ์ ธ์ค๊ธฐ
packetSize = mpu.dmpGetFIFOPacketSize();
//PID ์ค์
pid.SetMode(AUTOMATIC);
pid.SetSampleTime(10);
pid.SetOutputLimits(-255, 255);
}
else
{
// ์ค๋ฅ์
// 1 = ์ด๊ธฐ ๋ฉ๋ชจ๋ฆฌ ์๋ฌ
// 2 = DMP ์ค์ ์ค๋ฅ
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
//๋ชจํฐ ์ถ๋ ฅํ ์ด๊ธฐํ
pinMode (6, OUTPUT);
pinMode (9, OUTPUT);
pinMode (10, OUTPUT);
pinMode (11, OUTPUT);
//๋ชจํฐ ๋์ OFF
analogWrite(6,LOW);
analogWrite(9,LOW);
analogWrite(10,LOW);
analogWrite(11,LOW);
}
void loop() {
// ์ค๋ฅ์ ์์
์ค์ง
if (!dmpReady) return;
// MPU ์ธํฐ๋ฝํธ๋ ํจํท ๋๊ธฐ
while (!mpuInterrupt && fifoCount < packetSize)
{
//MPU6050 ๋ฐ์ดํฐ๊ฐ ์๋ ๊ฒฝ์ฐ PID ๊ณ์ฐ
pid.Compute();
//์๋ฆฌ์ผ ๋ชจ๋ํฐ๋ก ํ์ฌ ์ํ ์ถ๋ ฅ
Serial.print(input); Serial.print(" =>"); Serial.println(output);
if (input>150 && input<250){//๋ก๋ด์ด ๊ธฐ์ธ์ด์ง๋ ๊ฒฝ์ฐ(๊ฐ๋ ๋ฒ์๋ด์์๋ง)
if (output>0) //์์ผ๋ก ๊ธฐ์ธ์ด์ง๋ ๊ฒฝ์ฐ
Forward(); //์ ์ง
else if (output<0) //๋ค๋ก ๊ธฐ์ธ์ด์ง๋ ๊ฒฝ์ฐ
Reverse(); //ํ์ง
}
else //๋ก๋ด์ด ๊ธฐ์ธ์ด์ง์ง ์์ ๊ฒฝ์ฐ
Stop(); //๋ชจํฐ ์ ์ง
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024)
{
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
}
else if (mpuIntStatus & 0x02)
{
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
mpu.dmpGetQuaternion(&q, fifoBuffer); //get value for q
mpu.dmpGetGravity(&gravity, &q); //get value for gravity
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); //get value for ypr
input = ypr[1] * 180/M_PI + 180;
}
}
void Forward() //์ ์ง
{
analogWrite(6,output);
analogWrite(9,0);
analogWrite(10,output);
analogWrite(11,0);
Serial.print("F");
}
void Reverse() //ํ์ง
{
analogWrite(6,0);
analogWrite(9,output*-1);
analogWrite(10,0);
analogWrite(11,output*-1);
Serial.print("R");
}
void Stop() //์ ์ง
{
analogWrite(6,0);
analogWrite(9,0);
analogWrite(10,0);
analogWrite(11,0);
Serial.print("S");
}
์ด ๊ณผ์ ์์ ๋ก๋ด์ imu์์์ ๊ฐ์ ์ดํดํ์ง ๋ชปํด ์ผ์๋ง์ ๋ฏ์ด๋ณด๊ณ ์ ํ์๋ค.
> MPU6050 ๋ค๋ฃจ๊ธฐ(22.06.01.)
mpu6050์ ๊ฐ์ ์ป์ด๋ณด๊ธฐ ์ํด์ ์ฌ์ฉํด ๋ณธ ์ฝ๋์ด๋ค.
#include<Wire.h>
const int MPU=0x68; //MPU 6050 ์ I2C ๊ธฐ๋ณธ ์ฃผ์
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
void setup(){
Wire.begin(); //Wire ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด๊ธฐํ
Wire.beginTransmission(MPU); //MPU๋ก ๋ฐ์ดํฐ ์ ์ก ์์
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); //MPU-6050 ์์ ๋ชจ๋๋ก
Wire.endTransmission(true);
Serial.begin(115200);
Serial.println("CLEARDATA");
Serial.println("LABEL, AcZ, GyX, GyY");
}
void loop(){
Wire.beginTransmission(MPU); //๋ฐ์ดํฐ ์ ์ก์์
Wire.write(0x3B); // register 0x3B (ACCEL_XOUT_H), ํ์ ๋ฐ์ดํฐ ๊ธฐ๋ก
Wire.endTransmission(false); //์ฐ๊ฒฐ์ ์ง
Wire.requestFrom(MPU,14,true); //MPU์ ๋ฐ์ดํฐ ์์ฒญ
//๋ฐ์ดํฐ ํ ๋ฐ์ดํธ ์ฉ ์ฝ์ด์ ๋ฐํ
AcX=Wire.read()<<8|Wire.read();
AcY=Wire.read()<<8|Wire.read();
AcZ=Wire.read()<<8|Wire.read();
Tmp=Wire.read()<<8|Wire.read();
GyX=Wire.read()<<8|Wire.read();
GyY=Wire.read()<<8|Wire.read();
GyZ=Wire.read()<<8|Wire.read();
//์๋ฆฌ์ผ ๋ชจ๋ํฐ์ ์ถ๋ ฅ
Serial.print("DATA,");
Serial.print(AcZ);
Serial.print(",");
Serial.print(GyX);
Serial.print(",");
Serial.print(GyY);
Serial.println();
delay(333);
}
๊ทธ๋ฆฌ๊ณ ์ด ๊ฐ๋ค์ด ์ฃผ๋ก ์ฐ์ธ๋ค๋ ๊ฒ์ ์๊ณ ์์์ง๋ง, ๊ทธ์ ์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์๋ณด๋ ์ฝ๋์ด๋ค.
#include<Wire.h>
const int MPU=0x68; //MPU 6050 ์ I2C ๊ธฐ๋ณธ ์ฃผ์
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
void setup(){
Wire.begin(); //Wire ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด๊ธฐํ
Wire.beginTransmission(MPU); //MPU๋ก ๋ฐ์ดํฐ ์ ์ก ์์
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); //MPU-6050 ์์ ๋ชจ๋๋ก
Wire.endTransmission(true);
Serial.begin(9600);
Serial.println("CLEARDATA");
Serial.println("LABEL,AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ");
}
void loop(){
Wire.beginTransmission(MPU); //๋ฐ์ดํฐ ์ ์ก์์
Wire.write(0x3B); // register 0x3B (ACCEL_XOUT_H), ํ์ ๋ฐ์ดํฐ ๊ธฐ๋ก
Wire.endTransmission(false); //์ฐ๊ฒฐ์ ์ง
Wire.requestFrom(MPU,14,true); //MPU์ ๋ฐ์ดํฐ ์์ฒญ
//๋ฐ์ดํฐ ํ ๋ฐ์ดํธ ์ฉ ์ฝ์ด์ ๋ฐํ
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("DATA,");
Serial.print(AcX);
Serial.print(",");
Serial.print(AcY);
Serial.print(",");
Serial.print(AcZ);
Serial.print(",");
Serial.print(Tmp/340.00+36.53);
Serial.print(",");
Serial.print(GyX);
Serial.print(",");
Serial.print(GyY);
Serial.print(",");
Serial.println(GyZ);
delay(333);
}
๊ฐ์ ๋ฐ์๋ณด๋, ๋ฐ์ดํฐ๊ฐ ๋งค์ฐ ๋ค์ฅ๋ ์ฅํ์ฌ ์นผ๋งํํฐ๋ฅผ ๊ณต๋ถํ๊ธฐ ์์ํ๋ค.
> ์นผ๋งํํฐ์ ์ดํด(22.06.02.)
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. This software may be distributed and modified under the terms of the GNU General Public License version 2 (GPL2) as published by the Free Software Foundation and appearing in the file GPL2.TXT included in the packaging of this file. Please note that GPL2 Section 2[b] requires that all works based on this software must also be made publicly available under the terms of the GPL2 ("Copyleft").
Contact information
-------------------
Kristian Lauszus, TKJ Electronics
Web : http://www.tkjelectronics.com
e-mail : kristianl@tkjelectronics.com
*/
#include <Wire.h>
#include <Kalman.h>
// Source: https://github.com/TKJElectronics/KalmanFilter
#define RESTRICT_PITCH
// Comment out to restrict roll to ±90deg instead - please read:http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdfKalman
kalmanX; // Create the Kalman instancesKalman
kalmanY;
/* IMU Data */
double accX, accY, accZ;double gyroX, gyroY, gyroZ;int16_t tempRaw;
double gyroXangle, gyroYangle; // Angle calculate using the gyro only
double compAngleX, compAngleY; // Calculated angle using a complementary filter
double kalAngleX, kalAngleY; // Calculated angle using a Kalman filter
uint32_t timer;
uint8_t i2cData[14]; // Buffer for I2C data
// TODO: Make calibration routine
void setup() {
Serial.begin(115200); Wire.begin();
#if ARDUINO >= 157
Wire.setClock(400000UL); // Set I2C frequency to 400kHz
#else
TWBR = ((F_CPU / 400000UL) - 16) / 2; // Set I2C frequency to 400kHz
#endif
i2cData[0] = 7; // Set the sample rate to 1000Hz - 8kHz/(7+1) = 1000Hz
i2cData[1] = 0x00; // Disable FSYNC and set 260 Hz Acc filtering, 256 Hz Gyro filtering, 8 KHz sampling
i2cData[2] = 0x00; // Set Gyro Full Scale Range to ±250deg/s
i2cData[3] = 0x00; // Set Accelerometer Full Scale Range to ±2g
while (i2cWrite(0x19, i2cData, 4, false)); // Write to all four registers at once
while (i2cWrite(0x6B, 0x01, true)); // PLL with X axis gyroscope reference and disable sleep mode
while (i2cRead(0x75, i2cData, 1)); if (i2cData[0] != 0x68) { // Read "WHO_AM_I" register
Serial.print(F("Error reading sensor")); while (1); }
delay(100); // Wait for sensor to stabilize
/* Set kalman and gyro starting angle */
while (i2cRead(0x3B, i2cData, 6)); accX = (i2cData[0] << 8) | i2cData[1]; accY = (i2cData[2] << 8) | i2cData[3]; accZ = (i2cData[4] << 8) | i2cData[5];
// Source: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf eq. 25 and eq. 26
// atan2 outputs the value of -π to π (radians) - see http://en.wikipedia.org/wiki/Atan2
// It is then converted from radians to degrees
#ifdef RESTRICT_PITCH
// Eq. 25 and 26
double roll = atan2(accY, accZ) * RAD_TO_DEG; double pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG;
#else
// Eq. 28 and 29
double roll = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG; double pitch = atan2(-accX, accZ) * RAD_TO_DEG;
#endif
kalmanX.setAngle(roll); // Set starting angle
kalmanY.setAngle(pitch); gyroXangle = roll; gyroYangle = pitch; compAngleX = roll; compAngleY = pitch;
timer = micros();}
void loop() {
/* Update all the values */
while (i2cRead(0x3B, i2cData, 14)); accX = ((i2cData[0] << 8) | i2cData[1]); accY = ((i2cData[2] << 8) | i2cData[3]); accZ = ((i2cData[4] << 8) | i2cData[5]); tempRaw = (i2cData[6] << 8) | i2cData[7]; gyroX = (i2cData[8] << 8) | i2cData[9]; gyroY = (i2cData[10] << 8) | i2cData[11]; gyroZ = (i2cData[12] << 8) | i2cData[13];
double dt = (double)(micros() - timer) / 1000000; // Calculate delta time
timer = micros();
// Source: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf eq. 25 and eq. 26
// atan2 outputs the value of -π to π (radians) - see http://en.wikipedia.org/wiki/Atan2
// It is then converted from radians to degrees
#ifdef RESTRICT_PITCH
// Eq. 25 and 26
double roll = atan2(accY, accZ) * RAD_TO_DEG; double pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG;#else
// Eq. 28 and 29
double roll = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG; double pitch = atan2(-accX, accZ) * RAD_TO_DEG;#endif
double gyroXrate = gyroX / 131.0; // Convert to deg/s
double gyroYrate = gyroY / 131.0; // Convert to deg/s
#ifdef RESTRICT_PITCH
// This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees
if ((roll < -90 && kalAngleX > 90) || (roll > 90 && kalAngleX < -90)) { kalmanX.setAngle(roll); compAngleX = roll; kalAngleX = roll; gyroXangle = roll; } else
kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt); // Calculate the angle using a Kalman filter
if (abs(kalAngleX) > 90) gyroYrate = -gyroYrate; // Invert rate, so it fits the restriced accelerometer reading
kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt);#else
// This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees
if ((pitch < -90 && kalAngleY > 90) || (pitch > 90 && kalAngleY < -90)) { kalmanY.setAngle(pitch); compAngleY = pitch; kalAngleY = pitch; gyroYangle = pitch; } else
kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt); // Calculate the angle using a Kalman filter
if (abs(kalAngleY) > 90)
gyroXrate = -gyroXrate; // Invert rate, so it fits the restriced accelerometer reading
kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt); // Calculate the angle using a Kalman filter
#endif
gyroXangle += gyroXrate * dt; // Calculate gyro angle without any filter
gyroYangle += gyroYrate * dt; //gyroXangle += kalmanX.getRate() * dt; // Calculate gyro angle using the unbiased rate
//gyroYangle += kalmanY.getRate() * dt;
compAngleX = 0.93 * (compAngleX + gyroXrate * dt) + 0.07 * roll; // Calculate the angle using a Complimentary filter
compAngleY = 0.93 * (compAngleY + gyroYrate * dt) + 0.07 * pitch;
// Reset the gyro angle when it has drifted too much
if (gyroXangle < -180 || gyroXangle > 180) gyroXangle = kalAngleX; if (gyroYangle < -180 || gyroYangle > 180) gyroYangle = kalAngleY;
/* Print Data */
#if 0 // Set to 1 to activate Serial.print(accX); Serial.print("\t"); Serial.print(accY); Serial.print("\t"); Serial.print(accZ); Serial.print("\t"); Serial.print(gyroX);
Serial.print("\t"); Serial.print(gyroY); Serial.print("\t"); Serial.print(gyroZ); Serial.print("\t"); Serial.print("\t");
#endif
Serial.print(roll); Serial.print("\t"); Serial.print(gyroXangle); Serial.print("\t"); Serial.print(compAngleX); Serial.print("\t"); Serial.print(kalAngleX); Serial.print("\t");
Serial.print("\t");
Serial.print(pitch); Serial.print("\t"); Serial.print(gyroYangle); Serial.print("\t"); Serial.print(compAngleY); Serial.print("\t"); Serial.print(kalAngleY); Serial.print("\t");
#if 0 // Set to 1 to print the temperature
Serial.print("\t");
double temperature = (double)tempRaw / 340.0 + 36.53; Serial.print(temperature); Serial.print("\t");
#endif
Serial.print("\r\n"); delay(2);}
๋จผ์ ์ด ์ฝ๋๋ฅผ ํตํด ์ดํดํด๋ณด๊ณ ์ ํ์์ผ๋, ์ ๋๋ก ์ดํดํ์ง ๋ชปํ ์ํ์์ ๊ท ํ ์ก์ ๋ ๊ฐ๋ก๋ก ๊ธด ๋ชจ์ต์ด ๋ถ์์ ํ๊ฒ ํ๋ ์์ธ์์ ์ดํดํ๊ณ ์ค์ค์ด 2ํธ๋ก ์ ๊ทธ๋ ์ด๋๋ฅผ ์งํํ์๋ค.
- ์ค์ค์ด 2ํธ
์ธ๋ก ํํ๋ก ๋ฐ๊พธ๊ณ , ํน์ ๊ฐ๋๋ฅผ ์ ์งํ ์ ์๋๋ก ํ์๋ค.


> ๊ท ํ ์ก๊ธฐ(22.06.12.)
์ง์ mpu6050์์ ์ป์ ๊ฐ์ ํ ๋๋ก ์ง ์ฝ๋์ด๋ค.
// ์ฝ๋ ์ค๋ช
: ๊ฐ์ ๊ธฐ์ค์ผ๋ก ํด์ ์์ง์๋ง ๊ฐ๋จํ ์ ์ดํ๊ธฐ
// โ MPU 6050 ์์ Z์๋, X๊ฐ์๋, Y๊ฐ์๋ ๊ฐ์ ๋ฐ๊ธฐ
// โก AcZ <= 22000 ์ผ ๋, ๋ชจํฐ ๊ฐ๋์ ๋ํ ํจ์ ์คํ
// โก-1 GyX < 600 && GyY < 50 => ๋ชจํฐ ์๋๋ delta(GyY)๊ฐ์ ๋น๋ก
// โก-2 GyX > 600 && GyY > 50 => ๋ชจํฐ ์๋๋ delta(GyY)๊ฐ์ ๋น๋ก
// -1*delta(GyY)*0.8 = output
// delta(GyY) = GyY(i) - GyY(f)
//----------libraries------------
#include<Wire.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
const int MPU=0x68; //MPU 6050 ์ I2C ๊ธฐ๋ณธ ์ฃผ์
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
int output;
void setup(){
Wire.begin(); //Wire ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด๊ธฐํ
Wire.beginTransmission(MPU); //MPU๋ก ๋ฐ์ดํฐ ์ ์ก ์์
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); //MPU-6050 ์์ ๋ชจ๋๋ก
Wire.endTransmission(true);
Serial.begin(115200);
Serial.println("CLEARDATA");
Serial.println("LABEL, AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ");
//๋ชจํฐ ์ถ๋ ฅํ ์ด๊ธฐํ
pinMode (6, OUTPUT);
pinMode (9, OUTPUT);
pinMode (10, OUTPUT);
pinMode (11, OUTPUT);
//๋ชจํฐ ๋์ OFF
analogWrite(6,LOW);
analogWrite(9,LOW);
analogWrite(10,LOW);
analogWrite(11,LOW);
}
void loop(){
Wire.beginTransmission(MPU); //๋ฐ์ดํฐ ์ ์ก์์
Wire.write(0x3B); // register 0x3B (ACCEL_XOUT_H), ํ์ ๋ฐ์ดํฐ ๊ธฐ๋ก
Wire.endTransmission(false); //์ฐ๊ฒฐ์ ์ง
Wire.requestFrom(MPU,14,true); //MPU์ ๋ฐ์ดํฐ ์์ฒญ
//๋ฐ์ดํฐ ํ ๋ฐ์ดํธ ์ฉ ์ฝ์ด์ ๋ฐํ
AcX=Wire.read()<<8|Wire.read();
AcY=Wire.read()<<8|Wire.read();
AcZ=Wire.read()<<8|Wire.read();
Tmp=Wire.read()<<8|Wire.read();
GyX=Wire.read()<<8|Wire.read();
GyY=Wire.read()<<8|Wire.read();
GyZ=Wire.read()<<8|Wire.read();
//์๋ฆฌ์ผ ๋ชจ๋ํฐ์ ์ถ๋ ฅ
Serial.print("DATA,");
Serial.print(AcX);
Serial.print(",");
Serial.print(AcY);
Serial.print(",");
Serial.print(AcZ);
Serial.print(",");
Serial.print(Tmp);
Serial.print(",");
Serial.print(GyX);
Serial.print(",");
Serial.print(GyY);
Serial.print(",");
Serial.print(GyZ);
Serial.println();
delay(333);
}
ํ์ง๋ง, ์ ๋๋ก ์๋ํ์ง ์์๊ณ , ํ ๋ธ๋ก๊ทธ๋ฅผ ์ฐพ์์ ๊ทธ ๋ธ๋ก๊ทธ์ ์๋ ์ฝ๋๋ฅผ ์คํํ๊ณ ์ ์๋ํจ์ ์ฐ์ ํ์ธํ๊ฒ ๋์๋ค.
/*
arduino ==> ์
ํ๋ฐธ๋ฐ์ฑ๋ก๋ด์ ๋ง๋ค์ด ์คํํด๋ณธ ์ฝ๋
*/
//------------------------------------------------------------
// PID ์ ์ด, ๋ชจํฐ ์ ์ด, MPU6050์ผ์ ๊ฐ์ ๋ฐ๊ธฐ ์ํ ์ ์ธ๋ฌธ
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
//------------------------------------------------------------
#define OUTPUT_TEAPOT 1 // Processing์ ํตํด MPU6050 ์ผ์๋ฅผ Visualize ํ๊ณ ์ถ์ ๊ฒฝ์ฐ 1, ์๋๋ฉด 0์ผ๋ก ์ ์ธํฉ๋๋ค
#define MIN_ABS_SPEED 30 // ๋ชจํฐ์ ์ต์ ์๋๋ฅผ ์ค์ ํฉ๋๋ค. 0 ~ 255 ๊ฐ ์ค ์ ํ
#define OUTPUT_READABLE_YAWPITCHROLL // Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
#define INTERRUPT_PIN 2 // MPU6050 ์ผ์์ INT ํ์ด ๊ฝํ์๋ ๋ฒํธ๋ฅผ ์ค์ ํฉ๋๋ค. ๋ณดํต 2๋ฒ
#define LED_PIN 13 // Arudino Uno์ 13๋ฒํ LED๋ฅผ ๋์ ์ค์ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๋ ค๊ณ ์ ์ธํฉ๋๋ค
//------------------------------------------------------------
//MPU ๊ฐ์ฒด๋ฅผ ์ ์ธํฉ๋๋ค
MPU6050 mpu;
// MPU control/status vars
bool blinkState = false; // LED๋ฅผ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๊ธฐ ์ํ ๋ณ์
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
//------------------------------------------------------------
// MPU6050 ์ผ์๋ฅผ ํตํด ์ฟผํฐ๋์ธ๊ณผ ์ค์ผ๋ฌ๊ฐ, Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
//------------------------------------------------------------
// Processing์ผ๋ก MPU6050 ์ผ์๋ฅผ Visualize ํ๊ธฐ ์ํ ๋ณ์
// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
//------------------------------------------------------------
// PID ์ ์ด์ฉ ๋ณ์ ์ ์ธ
double kp = 60.;
double ki = 200.;
double kd = 1.5;
// ๊ธฐ์ธ์ผ ๊ฐ๋ ์ ํ
// ์ ๊ฐ ๋ง๋ ๋ฐธ๋ฐ์ฑ๋ก๋ด์๋ 184.0๋๊ฐ ๊ฐ์ฅ ์ต์ ์ ํํ๊ฐ๋์์ต๋๋ค
// ๊ฐ๋๊ฐ 180๋๋ฅผ ๊ธฐ์ค์ผ๋ก +-๋ฅผ ์ค์ ํด์ฃผ์๋ฉด ๋ฉ๋๋ค
double originalSetpoint = 180;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.3;
// PID ์ ์ด์ฉ input, output ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค
double input, output;
// PID๊ฐ์ ์ค์ ํด์ค๋ค
PID pid(&input, &output, &setpoint, kp, ki, kd, DIRECT);
// ๋ชจํฐ ์ ์ด์ฉ ๋ณ์ ์ ์ธ
// EnA, EnB๋ ์๋์ ์ด์ฉ(pwm), IN1,2,3,4๋ ๋ฐฉํฅ์ ์ด์ฉ ํ์
๋๋ค
int ENA = 10;
int IN1 = 12;
int IN2 = 6;
int IN3 = 9;
int IN4 = 8;
int ENB = 11;
// motorController ๊ฐ์ฒด ์์ฑ, ๋งจ ๋ ํ๋ผ๋ฏธํฐ 1,1์ ๊ฐ๊ฐ ์ข์ธก, ์ฐ์ธก๋ชจํฐ์ ์ต๋์๋(%) ์
๋๋ค.
LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, 1, 1);
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
void setup(){
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
Serial.begin(115200);
while (!Serial); // wait for Leonardo enumeration, others continue immediately
// initialize device
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// ํค์
๋ ฅ์ ๊ธฐ๋ค๋ฆฌ๋ ์ฝ๋. ์ฃผ์์ฒ๋ฆฌํ์ต๋๋ค
// wait for ready
//Serial.println(F("\nSend any character to begin DMP programming and demo: "));
//while (Serial.available() && Serial.read()); // empty buffer
//while (!Serial.available()); // wait for data
//while (Serial.available() && Serial.read()); // empty buffer again
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
// devStatus ๊ฐ์ด 0 ์ด๋ฉด ์ ์์๋, 0์ด ์๋๋ฉด ์ค์๋์
๋๋ค
// make sure it worked (returns 0 if so)
if (devStatus == 0){
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
// MPU6050 ์ผ์๊ฐ ์ ์ญ์๋ํ๋ฉด PID ์ ์ด์ฉ ์ฝ๋๋ฅผ ์ด๊ธฐํํฉ๋๋ค
pid.SetMode(AUTOMATIC);
pid.SetSampleTime(10);
pid.SetOutputLimits(-255, 255);
}
else{ // MPU6050 ์ผ์๊ฐ ์ค์๋ํ ๊ฒฝ์ฐ
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
// ๋ก๋ด์ด ์๋ ์ค 13๋ฒ LED๋ฅผ ๊น๋นก๊ฑฐ๋ฆฌ๊ธฐ ์ํด OUTPUT์ผ๋ก ์ด๊ธฐํํฉ๋๋ค
pinMode(LED_PIN, OUTPUT);
}
void loop(){
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize){
//no mpu data - performing PID calculations and output to motors
pid.Compute(); // ๋ฃจํ๋ฅผ ๋๋ฉด์ pid ๊ฐ์ ์
๋ฐ์ดํธํฉ๋๋ค
motorController.move(output, MIN_ABS_SPEED); // pid ์ฐ์ฐ์ผ๋ก ๋์จ output ๊ฐ์ motorController๋ก ์ ์กํฉ๋๋ค. (๋ชจํฐ์ ์ด)
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// MPU6050 ์ผ์๊ฐ ์ ์์๋ํ๋ ๊ฒฝ์ฐ์๋ง PID์ ์ด๋ฅผ ํด์ผํ๋ฏ๋ก ์๋์ ๊ฐ์ด if-else๋ฌธ์ ์์ฑํฉ๋๋ค
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024){
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
}
// MPU6050 ์ผ์๊ฐ ์ ์์๋ํ๋ ๊ฒฝ์ฐ
else if (mpuIntStatus & 0x02){
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
#ifdef OUTPUT_READABLE_YAWPITCHROLL // ์ผ์๋ฅผ ํตํด ๊ตฌํ Yaw, Pitch, Roll ๊ฐ์ Serial Monitor์ ํ์ํฉ๋๋ค
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
#endif
// PID ์ ์ด๋ฅผ ํ๊ธฐ ์ํด input ๋ณ์์ Pitch ๊ฐ์ ๋ฃ์ต๋๋ค
input = ypr[1] * 180/M_PI + 180;
#ifdef OUTPUT_TEAPOT // Processing์ผ๋ก MPU6050์ผ์์ ์์ง์์ Visualize ํ๊ธฐ ์ํ ์ฝ๋
// display quaternion values in InvenSense Teapot demo format:
teapotPacket[2] = fifoBuffer[0];
teapotPacket[3] = fifoBuffer[1];
teapotPacket[4] = fifoBuffer[4];
teapotPacket[5] = fifoBuffer[5];
teapotPacket[6] = fifoBuffer[8];
teapotPacket[7] = fifoBuffer[9];
teapotPacket[8] = fifoBuffer[12];
teapotPacket[9] = fifoBuffer[13];
Serial.write(teapotPacket, 14);
teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
#endif
}
}
// [์ถ์ฒ] [Arduino] ์๋์ด๋
ธ Uno ํ์ฉ ์์ ์ฝ๋์
๋๋ค(4) - Self Balancing Robot, ์
ํ๋ฐธ๋ฐ์ฑ ๋ก๋ด ๋ง๋ค๊ธฐ|์์ฑ์ edward0im
์ดํ ์ฐ๋ฆฌ์ ์ํฉ์ ๋ง์ถ์ด ๋ค์ ์์ ํ ์ฝ๋์ด๋ค.
/*
arduino ==> ์
ํ๋ฐธ๋ฐ์ฑ๋ก๋ด์ ๋ง๋ค์ด ์คํํด๋ณธ ์ฝ๋
*/
//------------------------------------------------------------
// PID ์ ์ด, ๋ชจํฐ ์ ์ด, MPU6050์ผ์ ๊ฐ์ ๋ฐ๊ธฐ ์ํ ์ ์ธ๋ฌธ
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
//------------------------------------------------------------
#define OUTPUT_TEAPOT 1 // Processing์ ํตํด MPU6050 ์ผ์๋ฅผ Visualize ํ๊ณ ์ถ์ ๊ฒฝ์ฐ 1, ์๋๋ฉด 0์ผ๋ก ์ ์ธํฉ๋๋ค
#define MIN_ABS_SPEED 30 // ๋ชจํฐ์ ์ต์ ์๋๋ฅผ ์ค์ ํฉ๋๋ค. 0 ~ 255 ๊ฐ ์ค ์ ํ
#define OUTPUT_READABLE_YAWPITCHROLL // Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
#define INTERRUPT_PIN 2 // MPU6050 ์ผ์์ INT ํ์ด ๊ฝํ์๋ ๋ฒํธ๋ฅผ ์ค์ ํฉ๋๋ค. ๋ณดํต 2๋ฒ
#define LED_PIN 13 // Arudino Uno์ 13๋ฒํ LED๋ฅผ ๋์ ์ค์ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๋ ค๊ณ ์ ์ธํฉ๋๋ค
//------------------------------------------------------------
//MPU ๊ฐ์ฒด๋ฅผ ์ ์ธํฉ๋๋ค
MPU6050 mpu;
// MPU control/status vars
bool blinkState = false; // LED๋ฅผ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๊ธฐ ์ํ ๋ณ์
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
//------------------------------------------------------------
// MPU6050 ์ผ์๋ฅผ ํตํด ์ฟผํฐ๋์ธ๊ณผ ์ค์ผ๋ฌ๊ฐ, Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
//------------------------------------------------------------
// Processing์ผ๋ก MPU6050 ์ผ์๋ฅผ Visualize ํ๊ธฐ ์ํ ๋ณ์
// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
//------------------------------------------------------------
// PID ์ ์ด์ฉ ๋ณ์ ์ ์ธ
double kp = 2;
double ki = 0.8;
double kd = 10;
// ๊ธฐ์ธ์ผ ๊ฐ๋ ์ ํ
// ์ ๊ฐ ๋ง๋ ๋ฐธ๋ฐ์ฑ๋ก๋ด์๋ 184.0๋๊ฐ ๊ฐ์ฅ ์ต์ ์ ํํ๊ฐ๋์์ต๋๋ค
// ๊ฐ๋๊ฐ 180๋๋ฅผ ๊ธฐ์ค์ผ๋ก +-๋ฅผ ์ค์ ํด์ฃผ์๋ฉด ๋ฉ๋๋ค
double originalSetpoint = 270;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.3;
// PID ์ ์ด์ฉ input, output ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค
double input, output;
// PID๊ฐ์ ์ค์ ํด์ค๋ค
PID pid(&input, &output, &setpoint, kp, ki, kd, DIRECT);
// ๋ชจํฐ ์ ์ด์ฉ ๋ณ์ ์ ์ธ
// EnA, EnB๋ ์๋์ ์ด์ฉ(pwm), IN1,2,3,4๋ ๋ฐฉํฅ์ ์ด์ฉ ํ์
๋๋ค
int ENA = 10;
int IN1 = 12;
int IN2 = 6;
int IN3 = 9;
int IN4 = 8;
int ENB = 11;
// motorController ๊ฐ์ฒด ์์ฑ, ๋งจ ๋ ํ๋ผ๋ฏธํฐ 1,1์ ๊ฐ๊ฐ ์ข์ธก, ์ฐ์ธก๋ชจํฐ์ ์ต๋์๋(%) ์
๋๋ค.
LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, 1, 1);
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
void setup(){
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
Serial.begin(115200);
while (!Serial); // wait for Leonardo enumeration, others continue immediately
// initialize device
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// ํค์
๋ ฅ์ ๊ธฐ๋ค๋ฆฌ๋ ์ฝ๋. ์ฃผ์์ฒ๋ฆฌํ์ต๋๋ค
// wait for ready
//Serial.println(F("\nSend any character to begin DMP programming and demo: "));
//while (Serial.available() && Serial.read()); // empty buffer
//while (!Serial.available()); // wait for data
//while (Serial.available() && Serial.read()); // empty buffer again
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
// devStatus ๊ฐ์ด 0 ์ด๋ฉด ์ ์์๋, 0์ด ์๋๋ฉด ์ค์๋์
๋๋ค
// make sure it worked (returns 0 if so)
if (devStatus == 0){
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
// MPU6050 ์ผ์๊ฐ ์ ์ญ์๋ํ๋ฉด PID ์ ์ด์ฉ ์ฝ๋๋ฅผ ์ด๊ธฐํํฉ๋๋ค
pid.SetMode(AUTOMATIC);
pid.SetSampleTime(10);
pid.SetOutputLimits(-255, 255);
}
else{ // MPU6050 ์ผ์๊ฐ ์ค์๋ํ ๊ฒฝ์ฐ
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
// ๋ก๋ด์ด ์๋ ์ค 13๋ฒ LED๋ฅผ ๊น๋นก๊ฑฐ๋ฆฌ๊ธฐ ์ํด OUTPUT์ผ๋ก ์ด๊ธฐํํฉ๋๋ค
pinMode(LED_PIN, OUTPUT);
}
void loop(){
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize){
//no mpu data - performing PID calculations and output to motors
pid.Compute(); // ๋ฃจํ๋ฅผ ๋๋ฉด์ pid ๊ฐ์ ์
๋ฐ์ดํธํฉ๋๋ค
motorController.move(output, MIN_ABS_SPEED); // pid ์ฐ์ฐ์ผ๋ก ๋์จ output ๊ฐ์ motorController๋ก ์ ์กํฉ๋๋ค. (๋ชจํฐ์ ์ด)
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// MPU6050 ์ผ์๊ฐ ์ ์์๋ํ๋ ๊ฒฝ์ฐ์๋ง PID์ ์ด๋ฅผ ํด์ผํ๋ฏ๋ก ์๋์ ๊ฐ์ด if-else๋ฌธ์ ์์ฑํฉ๋๋ค
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024){
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
}
// MPU6050 ์ผ์๊ฐ ์ ์์๋ํ๋ ๊ฒฝ์ฐ
else if (mpuIntStatus & 0x02){
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
#ifdef OUTPUT_READABLE_YAWPITCHROLL // ์ผ์๋ฅผ ํตํด ๊ตฌํ Yaw, Pitch, Roll ๊ฐ์ Serial Monitor์ ํ์ํฉ๋๋ค
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
#endif
// PID ์ ์ด๋ฅผ ํ๊ธฐ ์ํด input ๋ณ์์ Pitch ๊ฐ์ ๋ฃ์ต๋๋ค
input = ypr[1] * 180/M_PI + 180;
#ifdef OUTPUT_TEAPOT // Processing์ผ๋ก MPU6050์ผ์์ ์์ง์์ Visualize ํ๊ธฐ ์ํ ์ฝ๋
// display quaternion values in InvenSense Teapot demo format:
teapotPacket[2] = fifoBuffer[0];
teapotPacket[3] = fifoBuffer[1];
teapotPacket[4] = fifoBuffer[4];
teapotPacket[5] = fifoBuffer[5];
teapotPacket[6] = fifoBuffer[8];
teapotPacket[7] = fifoBuffer[9];
teapotPacket[8] = fifoBuffer[12];
teapotPacket[9] = fifoBuffer[13];
Serial.write(teapotPacket, 14);
teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
#endif
}
}
// [์ถ์ฒ] [Arduino] ์๋์ด๋
ธ Uno ํ์ฉ ์์ ์ฝ๋์
๋๋ค(4) - Self Balancing Robot, ์
ํ๋ฐธ๋ฐ์ฑ ๋ก๋ด ๋ง๋ค๊ธฐ|์์ฑ์ edward0im
> ๊ท ํ ์ก๊ธฐ(22.06.14.)
์์ ์ฝ๋๋ฅผ ์กฐ๊ธ ์์ ํ๋ ๊ณผ์ ์ ๊ฑฐ์ณค๋ค.
/*
arduino ==> ์
ํ๋ฐธ๋ฐ์ฑ๋ก๋ด์ ๋ง๋ค์ด ์คํํด๋ณธ ์ฝ๋
*/
//------------------------------------------------------------
// PID ์ ์ด, ๋ชจํฐ ์ ์ด, MPU6050์ผ์ ๊ฐ์ ๋ฐ๊ธฐ ์ํ ์ ์ธ๋ฌธ
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
//------------------------------------------------------------
#define OUTPUT_TEAPOT 1 // Processing์ ํตํด MPU6050 ์ผ์๋ฅผ Visualize ํ๊ณ ์ถ์ ๊ฒฝ์ฐ 1, ์๋๋ฉด 0์ผ๋ก ์ ์ธํฉ๋๋ค
#define MIN_ABS_SPEED 30 // ๋ชจํฐ์ ์ต์ ์๋๋ฅผ ์ค์ ํฉ๋๋ค. 0 ~ 255 ๊ฐ ์ค ์ ํ
#define OUTPUT_READABLE_YAWPITCHROLL // Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
#define INTERRUPT_PIN 2 // MPU6050 ์ผ์์ INT ํ์ด ๊ฝํ์๋ ๋ฒํธ๋ฅผ ์ค์ ํฉ๋๋ค. ๋ณดํต 2๋ฒ
#define LED_PIN 13 // Arudino Uno์ 13๋ฒํ LED๋ฅผ ๋์ ์ค์ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๋ ค๊ณ ์ ์ธํฉ๋๋ค
//------------------------------------------------------------
//MPU ๊ฐ์ฒด๋ฅผ ์ ์ธํฉ๋๋ค
MPU6050 mpu;
// MPU control/status vars
bool blinkState = false; // LED๋ฅผ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๊ธฐ ์ํ ๋ณ์
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
//------------------------------------------------------------
// MPU6050 ์ผ์๋ฅผ ํตํด ์ฟผํฐ๋์ธ๊ณผ ์ค์ผ๋ฌ๊ฐ, Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
//------------------------------------------------------------
// Processing์ผ๋ก MPU6050 ์ผ์๋ฅผ Visualize ํ๊ธฐ ์ํ ๋ณ์
// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
//------------------------------------------------------------
// PID ์ ์ด์ฉ ๋ณ์ ์ ์ธ
double kp = 2.8;
double ki = 0.5;
double kd = 15;
// ๊ธฐ์ธ์ผ ๊ฐ๋ ์ ํ
// ์ ๊ฐ ๋ง๋ ๋ฐธ๋ฐ์ฑ๋ก๋ด์๋ 184.0๋๊ฐ ๊ฐ์ฅ ์ต์ ์ ํํ๊ฐ๋์์ต๋๋ค
// ๊ฐ๋๊ฐ 180๋๋ฅผ ๊ธฐ์ค์ผ๋ก +-๋ฅผ ์ค์ ํด์ฃผ์๋ฉด ๋ฉ๋๋ค
double originalSetpoint = 270;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.3;
// PID ์ ์ด์ฉ input, output ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค
double input, output;
// PID๊ฐ์ ์ค์ ํด์ค๋ค
PID pid(&input, &output, &setpoint, kp, ki, kd, DIRECT);
// ๋ชจํฐ ์ ์ด์ฉ ๋ณ์ ์ ์ธ
// EnA, EnB๋ ์๋์ ์ด์ฉ(pwm), IN1,2,3,4๋ ๋ฐฉํฅ์ ์ด์ฉ ํ์
๋๋ค
int ENA = 10;
int IN1 = 12;
int IN2 = 6;
int IN3 = 9;
int IN4 = 8;
int ENB = 11;
// motorController ๊ฐ์ฒด ์์ฑ, ๋งจ ๋ ํ๋ผ๋ฏธํฐ 1,1์ ๊ฐ๊ฐ ์ข์ธก, ์ฐ์ธก๋ชจํฐ์ ์ต๋์๋(%) ์
๋๋ค.
LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, 1, 1);
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
void setup(){
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
Serial.begin(115200);
while (!Serial); // wait for Leonardo enumeration, others continue immediately
// initialize device
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// ํค์
๋ ฅ์ ๊ธฐ๋ค๋ฆฌ๋ ์ฝ๋. ์ฃผ์์ฒ๋ฆฌํ์ต๋๋ค
// wait for ready
//Serial.println(F("\nSend any character to begin DMP programming and demo: "));
//while (Serial.available() && Serial.read()); // empty buffer
//while (!Serial.available()); // wait for data
//while (Serial.available() && Serial.read()); // empty buffer again
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
// devStatus ๊ฐ์ด 0 ์ด๋ฉด ์ ์์๋, 0์ด ์๋๋ฉด ์ค์๋์
๋๋ค
// make sure it worked (returns 0 if so)
if (devStatus == 0){
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
// MPU6050 ์ผ์๊ฐ ์ ์ญ์๋ํ๋ฉด PID ์ ์ด์ฉ ์ฝ๋๋ฅผ ์ด๊ธฐํํฉ๋๋ค
pid.SetMode(AUTOMATIC);
pid.SetSampleTime(10);
pid.SetOutputLimits(-255, 255);
}
else{ // MPU6050 ์ผ์๊ฐ ์ค์๋ํ ๊ฒฝ์ฐ
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
// ๋ก๋ด์ด ์๋ ์ค 13๋ฒ LED๋ฅผ ๊น๋นก๊ฑฐ๋ฆฌ๊ธฐ ์ํด OUTPUT์ผ๋ก ์ด๊ธฐํํฉ๋๋ค
pinMode(LED_PIN, OUTPUT);
}
void loop(){
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize){
//no mpu data - performing PID calculations and output to motors
pid.Compute(); // ๋ฃจํ๋ฅผ ๋๋ฉด์ pid ๊ฐ์ ์
๋ฐ์ดํธํฉ๋๋ค
motorController.move(output, MIN_ABS_SPEED); // pid ์ฐ์ฐ์ผ๋ก ๋์จ output ๊ฐ์ motorController๋ก ์ ์กํฉ๋๋ค. (๋ชจํฐ์ ์ด)
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// MPU6050 ์ผ์๊ฐ ์ ์์๋ํ๋ ๊ฒฝ์ฐ์๋ง PID์ ์ด๋ฅผ ํด์ผํ๋ฏ๋ก ์๋์ ๊ฐ์ด if-else๋ฌธ์ ์์ฑํฉ๋๋ค
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024){
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
}
// MPU6050 ์ผ์๊ฐ ์ ์์๋ํ๋ ๊ฒฝ์ฐ
else if (mpuIntStatus & 0x02){
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
#ifdef OUTPUT_READABLE_YAWPITCHROLL // ์ผ์๋ฅผ ํตํด ๊ตฌํ Yaw, Pitch, Roll ๊ฐ์ Serial Monitor์ ํ์ํฉ๋๋ค
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
#endif
// PID ์ ์ด๋ฅผ ํ๊ธฐ ์ํด input ๋ณ์์ Pitch ๊ฐ์ ๋ฃ์ต๋๋ค
input = ypr[1] * 180/M_PI + 180;
#ifdef OUTPUT_TEAPOT // Processing์ผ๋ก MPU6050์ผ์์ ์์ง์์ Visualize ํ๊ธฐ ์ํ ์ฝ๋
// display quaternion values in InvenSense Teapot demo format:
teapotPacket[2] = fifoBuffer[0];
teapotPacket[3] = fifoBuffer[1];
teapotPacket[4] = fifoBuffer[4];
teapotPacket[5] = fifoBuffer[5];
teapotPacket[6] = fifoBuffer[8];
teapotPacket[7] = fifoBuffer[9];
teapotPacket[8] = fifoBuffer[12];
teapotPacket[9] = fifoBuffer[13];
Serial.write(teapotPacket, 14);
teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
#endif
}
}
// [์ถ์ฒ] [Arduino] ์๋์ด๋
ธ Uno ํ์ฉ ์์ ์ฝ๋์
๋๋ค(4) - Self Balancing Robot, ์
ํ๋ฐธ๋ฐ์ฑ ๋ก๋ด ๋ง๋ค๊ธฐ|์์ฑ์ edward0im
์ด๋ ์งํํ ์คํ์ ๋ํ ์์๋ค์ด๋ค.
> ๊ท ํ ์ก๊ธฐ(22.06.16.)
์ดํ ์ฐ๋ฆฌ๋ค์๊ฒ ๋ง๋ ์ฝ๋๋ก ๋ฐ๊พธ์๋ค.
/*
์ฝ๋ ์ค๋ช
: ๊ฐ์ ๊ธฐ์ค์ผ๋ก ํด์ ์์ง์๋ง ๊ฐ๋จํ ์ ์ดํ๊ธฐ
โ MPU 6050 ์์ Z์๋, X๊ฐ์๋, Y๊ฐ์๋ ๊ฐ์ ๋ฐ๊ธฐ
โก AcZ <= 22000 ์ผ ๋, ๋ชจํฐ ๊ฐ๋์ ๋ํ ํจ์ ์คํ
โก-1 GyX < 600 && GyY < 50 => ๋ชจํฐ ์๋๋ delta(GyY)๊ฐ์ ๋น๋ก
โก-2 GyX > 600 && GyY > 50 => ๋ชจํฐ ์๋๋ delta(GyY)๊ฐ์ ๋น๋ก
-1*delta(GyY)*0.8 = output
delta(GyY) = GyY(i) - GyY(f)
*/
//----------libraries------------
#include <Wire.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include <PID_v1.h>
//------------------------------------------------------------
#define OUTPUT_TEAPOT 1 // Processing์ ํตํด MPU6050 ์ผ์๋ฅผ Visualize ํ๊ณ ์ถ์ ๊ฒฝ์ฐ 1, ์๋๋ฉด 0์ผ๋ก ์ ์ธํฉ๋๋ค
#define MIN_ABS_SPEED 30 // ๋ชจํฐ์ ์ต์ ์๋๋ฅผ ์ค์ ํฉ๋๋ค. 0 ~ 255 ๊ฐ ์ค ์ ํ
#define OUTPUT_READABLE_YAWPITCHROLL // Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
#define INTERRUPT_PIN 2 // MPU6050 ์ผ์์ INT ํ์ด ๊ฝํ์๋ ๋ฒํธ๋ฅผ ์ค์ ํฉ๋๋ค. ๋ณดํต 2๋ฒ
#define LED_PIN 13 // Arudino Uno์ 13๋ฒํ LED๋ฅผ ๋์ ์ค์ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๋ ค๊ณ ์ ์ธํฉ๋๋ค
//------------------------------------------------------------
//MPU ๊ฐ์ฒด๋ฅผ ์ ์ธํฉ๋๋ค
MPU6050 mpu;
// MPU control/status vars
bool blinkState = false; // LED๋ฅผ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๊ธฐ ์ํ ๋ณ์
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
//------------------------------------------------------------
// MPU6050 ์ผ์๋ฅผ ํตํด ์ฟผํฐ๋์ธ๊ณผ ์ค์ผ๋ฌ๊ฐ, Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
//------------------------------------------------------------
// Processing์ผ๋ก MPU6050 ์ผ์๋ฅผ Visualize ํ๊ธฐ ์ํ ๋ณ์
// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
//------------------------------------------------------------
// PID ์ ์ด์ฉ ๋ณ์ ์ ์ธ
double kp = 2.8;
double ki = 0.5;
double kd = 15;
// ๊ธฐ์ธ์ผ ๊ฐ๋ ์ ํ
// ์ ๊ฐ ๋ง๋ ๋ฐธ๋ฐ์ฑ๋ก๋ด์๋ 184.0๋๊ฐ ๊ฐ์ฅ ์ต์ ์ ํํ๊ฐ๋์์ต๋๋ค
// ๊ฐ๋๊ฐ 180๋๋ฅผ ๊ธฐ์ค์ผ๋ก +-๋ฅผ ์ค์ ํด์ฃผ์๋ฉด ๋ฉ๋๋ค
double originalSetpoint = 270;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.3;
// PID ์ ์ด์ฉ input, output ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค
double input, output;
// PID๊ฐ์ ์ค์ ํด์ค๋ค
PID pid(&input, &output, &setpoint, kp, ki, kd, DIRECT);
// ๋ชจํฐ ์ ์ด์ฉ ๋ณ์ ์ ์ธ
// EnA, EnB๋ ์๋์ ์ด์ฉ(pwm), IN1,2,3,4๋ ๋ฐฉํฅ์ ์ด์ฉ ํ์
๋๋ค
int ENA = 10;
int IN1 = 12;
int IN2 = 6;
int IN3 = 9;
int IN4 = 8;
int ENB = 11;
// motorController ๊ฐ์ฒด ์์ฑ, ๋งจ ๋ ํ๋ผ๋ฏธํฐ 1,1์ ๊ฐ๊ฐ ์ข์ธก, ์ฐ์ธก๋ชจํฐ์ ์ต๋์๋(%) ์
๋๋ค.
LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, 1, 1);
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
void setup(){
Wire.begin(); //Wire ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด๊ธฐํ
Wire.beginTransmission(MPU); //MPU๋ก ๋ฐ์ดํฐ ์ ์ก ์์
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); //MPU-6050 ์์ ๋ชจ๋๋ก
Wire.endTransmission(true);
Serial.begin(115200);
Serial.println("CLEARDATA");
Serial.println("LABEL, AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ");
//๋ชจํฐ ์ถ๋ ฅํ ์ด๊ธฐํ
pinMode (6, OUTPUT);
pinMode (9, OUTPUT);
pinMode (10, OUTPUT);
pinMode (11, OUTPUT);
//๋ชจํฐ ๋์ OFF
analogWrite(6,LOW);
analogWrite(9,LOW);
analogWrite(10,LOW);
analogWrite(11,LOW);
}
void loop(){
Wire.beginTransmission(MPU); //๋ฐ์ดํฐ ์ ์ก์์
Wire.write(0x3B); // register 0x3B (ACCEL_XOUT_H), ํ์ ๋ฐ์ดํฐ ๊ธฐ๋ก
Wire.endTransmission(false); //์ฐ๊ฒฐ์ ์ง
Wire.requestFrom(MPU,14,true); //MPU์ ๋ฐ์ดํฐ ์์ฒญ
//๋ฐ์ดํฐ ํ ๋ฐ์ดํธ ์ฉ ์ฝ์ด์ ๋ฐํ
AcX=Wire.read()<<8|Wire.read();
AcY=Wire.read()<<8|Wire.read();
AcZ=Wire.read()<<8|Wire.read();
Tmp=Wire.read()<<8|Wire.read();
GyX=Wire.read()<<8|Wire.read();
GyY=Wire.read()<<8|Wire.read();
GyZ=Wire.read()<<8|Wire.read();
//์๋ฆฌ์ผ ๋ชจ๋ํฐ์ ์ถ๋ ฅ
Serial.print("DATA,");
Serial.print(AcX);
Serial.print(",");
Serial.print(AcY);
Serial.print(",");
Serial.print(AcZ);
Serial.print(",");
Serial.print(Tmp);
Serial.print(",");
Serial.print(GyX);
Serial.print(",");
Serial.print(GyY);
Serial.print(",");
Serial.print(GyZ);
Serial.println();
delay(333);
}
- ์ค์ค์ด 3ํธ
์ง๊ธ๊น์ง์ ๋ชจ์ต์ ์ค ๋ฌถ๊ธฐ๊ฐ ์ด๋ ค์ ๊ธฐ ๋๋ฌธ์ ์ค์ ๋ฌถ์ ์ ์๋ ์์น๋ฅผ ์ค์ ํ๊ณ , ๋ถํ์ ์์น๋ฅผ ๊ณ ์ ํํ์ฌ ๋ฌด๊ฒ ์ค์ฌ์ ๊ณ ์ ํ์๋ค.



> ๊ท ํ ์ก๊ธฐ(22.06.17.)
์๋ฆฌ์ผ์์ ๋ ์ด ๊ฑธ๋ฆฌ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ฌ, ๊ตณ์ด mpu์ ๊ฐ์ ์ฝ์ด์ ๋ณด์ฌ์ฃผ๋ ๋ถ๋ถ์ ์์ด๋ค.
// arduino ==> ์
ํ๋ฐธ๋ฐ์ฑ๋ก๋ด์ ๋ง๋ค์ด ์คํํด๋ณธ ์ฝ๋
//------------------------------------------------------------
// PID ์ ์ด, ๋ชจํฐ ์ ์ด, MPU6050์ผ์ ๊ฐ์ ๋ฐ๊ธฐ ์ํ ์ ์ธ๋ฌธ
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
//------------------------------------------------------------
#define OUTPUT_TEAPOT 1 // Processing์ ํตํด MPU6050 ์ผ์๋ฅผ Visualize ํ๊ณ ์ถ์ ๊ฒฝ์ฐ 1, ์๋๋ฉด 0์ผ๋ก ์ ์ธํฉ๋๋ค
#define MIN_ABS_SPEED 30 // ๋ชจํฐ์ ์ต์ ์๋๋ฅผ ์ค์ ํฉ๋๋ค. 0 ~ 255 ๊ฐ ์ค ์ ํ
#define OUTPUT_READABLE_YAWPITCHROLL // Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
#define INTERRUPT_PIN 2 // MPU6050 ์ผ์์ INT ํ์ด ๊ฝํ์๋ ๋ฒํธ๋ฅผ ์ค์ ํฉ๋๋ค. ๋ณดํต 2๋ฒ
#define LED_PIN 13 // Arudino Uno์ 13๋ฒํ LED๋ฅผ ๋์ ์ค์ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๋ ค๊ณ ์ ์ธํฉ๋๋ค
//------------------------------------------------------------
//MPU ๊ฐ์ฒด๋ฅผ ์ ์ธํฉ๋๋ค
MPU6050 mpu;
// MPU control/status vars
bool blinkState = false; // LED๋ฅผ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๊ธฐ ์ํ ๋ณ์
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
//------------------------------------------------------------
// MPU6050 ์ผ์๋ฅผ ํตํด ์ฟผํฐ๋์ธ๊ณผ ์ค์ผ๋ฌ๊ฐ, Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
//------------------------------------------------------------
// Processing์ผ๋ก MPU6050 ์ผ์๋ฅผ Visualize ํ๊ธฐ ์ํ ๋ณ์
// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
//------------------------------------------------------------
// PID ์ ์ด์ฉ ๋ณ์ ์ ์ธ
double kp = 2.8;
double ki = 0.5;
double kd = 15;
// ๊ธฐ์ธ์ผ ๊ฐ๋ ์ ํ
// ์ ๊ฐ ๋ง๋ ๋ฐธ๋ฐ์ฑ๋ก๋ด์๋ 184.0๋๊ฐ ๊ฐ์ฅ ์ต์ ์ ํํ๊ฐ๋์์ต๋๋ค
// ๊ฐ๋๊ฐ 180๋๋ฅผ ๊ธฐ์ค์ผ๋ก +-๋ฅผ ์ค์ ํด์ฃผ์๋ฉด ๋ฉ๋๋ค
double originalSetpoint = 273;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.3;
// PID ์ ์ด์ฉ input, output ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค
double input, output;
// PID๊ฐ์ ์ค์ ํด์ค๋ค
PID pid(&input, &output, &setpoint, kp, ki, kd, DIRECT);
// ๋ชจํฐ ์ ์ด์ฉ ๋ณ์ ์ ์ธ
// EnA, EnB๋ ์๋์ ์ด์ฉ(pwm), IN1,2,3,4๋ ๋ฐฉํฅ์ ์ด์ฉ ํ์
๋๋ค
int ENA = 10;
int IN1 = 12;
int IN2 = 6;
int IN3 = 9;
int IN4 = 8;
int ENB = 11;
// motorController ๊ฐ์ฒด ์์ฑ, ๋งจ ๋ ํ๋ผ๋ฏธํฐ 1,1์ ๊ฐ๊ฐ ์ข์ธก, ์ฐ์ธก๋ชจํฐ์ ์ต๋์๋(%) ์
๋๋ค.
LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, 1, 1);
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
void setup(){
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
Serial.begin(115200);
while (!Serial); // wait for Leonardo enumeration, others continue immediately
// initialize device
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// ํค์
๋ ฅ์ ๊ธฐ๋ค๋ฆฌ๋ ์ฝ๋. ์ฃผ์์ฒ๋ฆฌํ์ต๋๋ค
// wait for ready
//Serial.println(F("\nSend any character to begin DMP programming and demo: "));
//while (Serial.available() && Serial.read()); // empty buffer
//while (!Serial.available()); // wait for data
//while (Serial.available() && Serial.read()); // empty buffer again
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
// devStatus ๊ฐ์ด 0 ์ด๋ฉด ์ ์์๋, 0์ด ์๋๋ฉด ์ค์๋์
๋๋ค
// make sure it worked (returns 0 if so)
if (devStatus == 0){
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
// MPU6050 ์ผ์๊ฐ ์ ์ญ์๋ํ๋ฉด PID ์ ์ด์ฉ ์ฝ๋๋ฅผ ์ด๊ธฐํํฉ๋๋ค
pid.SetMode(AUTOMATIC);
pid.SetSampleTime(10);
pid.SetOutputLimits(-255, 255);
}
else{ // MPU6050 ์ผ์๊ฐ ์ค์๋ํ ๊ฒฝ์ฐ
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
// ๋ก๋ด์ด ์๋ ์ค 13๋ฒ LED๋ฅผ ๊น๋นก๊ฑฐ๋ฆฌ๊ธฐ ์ํด OUTPUT์ผ๋ก ์ด๊ธฐํํฉ๋๋ค
pinMode(LED_PIN, OUTPUT);
}
void loop(){
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize){
//no mpu data - performing PID calculations and output to motors
pid.Compute(); // ๋ฃจํ๋ฅผ ๋๋ฉด์ pid ๊ฐ์ ์
๋ฐ์ดํธํฉ๋๋ค
motorController.move(output, MIN_ABS_SPEED); // pid ์ฐ์ฐ์ผ๋ก ๋์จ output ๊ฐ์ motorController๋ก ์ ์กํฉ๋๋ค. (๋ชจํฐ์ ์ด)
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// MPU6050 ์ผ์๊ฐ ์ ์์๋ํ๋ ๊ฒฝ์ฐ์๋ง PID์ ์ด๋ฅผ ํด์ผํ๋ฏ๋ก ์๋์ ๊ฐ์ด if-else๋ฌธ์ ์์ฑํฉ๋๋ค
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024){
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
}
// MPU6050 ์ผ์๊ฐ ์ ์์๋ํ๋ ๊ฒฝ์ฐ
else if (mpuIntStatus & 0x02){
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
#ifdef OUTPUT_READABLE_YAWPITCHROLL // ์ผ์๋ฅผ ํตํด ๊ตฌํ Yaw, Pitch, Roll ๊ฐ์ Serial Monitor์ ํ์ํฉ๋๋ค
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
#endif
// PID ์ ์ด๋ฅผ ํ๊ธฐ ์ํด input ๋ณ์์ Pitch ๊ฐ์ ๋ฃ์ต๋๋ค
input = ypr[1] * 180/M_PI + 180;
#ifdef OUTPUT_TEAPOT // Processing์ผ๋ก MPU6050์ผ์์ ์์ง์์ Visualize ํ๊ธฐ ์ํ ์ฝ๋
// display quaternion values in InvenSense Teapot demo format:
teapotPacket[2] = fifoBuffer[0];
teapotPacket[3] = fifoBuffer[1];
teapotPacket[4] = fifoBuffer[4];
teapotPacket[5] = fifoBuffer[5];
teapotPacket[6] = fifoBuffer[8];
teapotPacket[7] = fifoBuffer[9];
teapotPacket[8] = fifoBuffer[12];
teapotPacket[9] = fifoBuffer[13];
Serial.write(teapotPacket, 14);
teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
#endif
}
}
// [์ถ์ฒ] [Arduino] ์๋์ด๋
ธ Uno ํ์ฉ ์์ ์ฝ๋์
๋๋ค(4) - Self Balancing Robot, ์
ํ๋ฐธ๋ฐ์ฑ ๋ก๋ด ๋ง๋ค๊ธฐ|์์ฑ์ edward0im
- ์ค์ค์ด 4ํธ
์ค์ค์ด 3ํธ๋ ๋ด๊ตฌ๋๊ฐ ๋ถ์กฑํ์ฌ ์คํ ์ค์ ๊ต์ฅํ ๋ถ์์ ํ์ฌ์ ๋ด๊ตฌ๋๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด์ ๋ณผํธ, ๋ํธ ์ฒด๊ฒฐ์ ํตํด ๊ธ๋ฃจ๊ฑด์ ํตํ ๋ถ์ฐฉ๋ณด๋ค ์ด๋ก ์ธํ ๋ถํ ํผ์์ ๋ง์๋ค.

> PID ์ก๊ธฐ(22.07.17.)
// arduino ==> ์
ํ๋ฐธ๋ฐ์ฑ๋ก๋ด์ ๋ง๋ค์ด ์คํํด๋ณธ ์ฝ๋
//------------------------------------------------------------
// PID ์ ์ด, ๋ชจํฐ ์ ์ด, MPU6050์ผ์ ๊ฐ์ ๋ฐ๊ธฐ ์ํ ์ ์ธ๋ฌธ
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
//------------------------------------------------------------
#define OUTPUT_TEAPOT 1 // Processing์ ํตํด MPU6050 ์ผ์๋ฅผ Visualize ํ๊ณ ์ถ์ ๊ฒฝ์ฐ 1, ์๋๋ฉด 0์ผ๋ก ์ ์ธํฉ๋๋ค
#define MIN_ABS_SPEED 30 // ๋ชจํฐ์ ์ต์ ์๋๋ฅผ ์ค์ ํฉ๋๋ค. 0 ~ 255 ๊ฐ ์ค ์ ํ
#define OUTPUT_READABLE_YAWPITCHROLL // Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
#define INTERRUPT_PIN 2 // MPU6050 ์ผ์์ INT ํ์ด ๊ฝํ์๋ ๋ฒํธ๋ฅผ ์ค์ ํฉ๋๋ค. ๋ณดํต 2๋ฒ
#define LED_PIN 13 // Arudino Uno์ 13๋ฒํ LED๋ฅผ ๋์ ์ค์ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๋ ค๊ณ ์ ์ธํฉ๋๋ค
//------------------------------------------------------------
//MPU ๊ฐ์ฒด๋ฅผ ์ ์ธํฉ๋๋ค
MPU6050 mpu;
// MPU control/status vars
bool blinkState = false; // LED๋ฅผ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๊ธฐ ์ํ ๋ณ์
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
//------------------------------------------------------------
// MPU6050 ์ผ์๋ฅผ ํตํด ์ฟผํฐ๋์ธ๊ณผ ์ค์ผ๋ฌ๊ฐ, Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
//------------------------------------------------------------
// Processing์ผ๋ก MPU6050 ์ผ์๋ฅผ Visualize ํ๊ธฐ ์ํ ๋ณ์
// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
//------------------------------------------------------------
// PID ์ ์ด์ฉ ๋ณ์ ์ ์ธ
double kp = 40;
double ki = 100;
double kd = 0.8;
// ๊ธฐ์ธ์ผ ๊ฐ๋ ์ ํ
// ์ ๊ฐ ๋ง๋ ๋ฐธ๋ฐ์ฑ๋ก๋ด์๋ 184.0๋๊ฐ ๊ฐ์ฅ ์ต์ ์ ํํ๊ฐ๋์์ต๋๋ค
// ๊ฐ๋๊ฐ 180๋๋ฅผ ๊ธฐ์ค์ผ๋ก +-๋ฅผ ์ค์ ํด์ฃผ์๋ฉด ๋ฉ๋๋ค
double originalSetpoint = 207;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.3;
// PID ์ ์ด์ฉ input, output ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค
double input, output;
// PID๊ฐ์ ์ค์ ํด์ค๋ค
PID pid(&input, &output, &setpoint, kp, ki, kd, DIRECT);
// ๋ชจํฐ ์ ์ด์ฉ ๋ณ์ ์ ์ธ
// EnA, EnB๋ ์๋์ ์ด์ฉ(pwm), IN1,2,3,4๋ ๋ฐฉํฅ์ ์ด์ฉ ํ์
๋๋ค
int ENA = 10;
int IN1 = 12;
int IN2 = 6;
int IN3 = 9;
int IN4 = 8;
int ENB = 11;
// motorController ๊ฐ์ฒด ์์ฑ, ๋งจ ๋ ํ๋ผ๋ฏธํฐ 1,1์ ๊ฐ๊ฐ ์ข์ธก, ์ฐ์ธก๋ชจํฐ์ ์ต๋์๋(%) ์
๋๋ค.
LMotorController motorController(ENA, IN2, IN1, ENB, IN4, IN3, 1, 1);
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
void setup(){
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
Serial.begin(115200);
while (!Serial); // wait for Leonardo enumeration, others continue immediately
// initialize device
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// ํค์
๋ ฅ์ ๊ธฐ๋ค๋ฆฌ๋ ์ฝ๋. ์ฃผ์์ฒ๋ฆฌํ์ต๋๋ค
// wait for ready
//Serial.println(F("\nSend any character to begin DMP programming and demo: "));
//while (Serial.available() && Serial.read()); // empty buffer
//while (!Serial.available()); // wait for data
//while (Serial.available() && Serial.read()); // empty buffer again
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
// devStatus ๊ฐ์ด 0 ์ด๋ฉด ์ ์์๋, 0์ด ์๋๋ฉด ์ค์๋์
๋๋ค
// make sure it worked (returns 0 if so)
if (devStatus == 0){
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
// MPU6050 ์ผ์๊ฐ ์ ์ญ์๋ํ๋ฉด PID ์ ์ด์ฉ ์ฝ๋๋ฅผ ์ด๊ธฐํํฉ๋๋ค
pid.SetMode(AUTOMATIC);
pid.SetSampleTime(10);
pid.SetOutputLimits(-255, 255);
}
else{ // MPU6050 ์ผ์๊ฐ ์ค์๋ํ ๊ฒฝ์ฐ
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
// ๋ก๋ด์ด ์๋ ์ค 13๋ฒ LED๋ฅผ ๊น๋นก๊ฑฐ๋ฆฌ๊ธฐ ์ํด OUTPUT์ผ๋ก ์ด๊ธฐํํฉ๋๋ค
pinMode(LED_PIN, OUTPUT);
}
void loop(){
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize){
//no mpu data - performing PID calculations and output to motors
pid.Compute(); // ๋ฃจํ๋ฅผ ๋๋ฉด์ pid ๊ฐ์ ์
๋ฐ์ดํธํฉ๋๋ค
motorController.move(output, MIN_ABS_SPEED); // pid ์ฐ์ฐ์ผ๋ก ๋์จ output ๊ฐ์ motorController๋ก ์ ์กํฉ๋๋ค. (๋ชจํฐ์ ์ด)
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// MPU6050 ์ผ์๊ฐ ์ ์์๋ํ๋ ๊ฒฝ์ฐ์๋ง PID์ ์ด๋ฅผ ํด์ผํ๋ฏ๋ก ์๋์ ๊ฐ์ด if-else๋ฌธ์ ์์ฑํฉ๋๋ค
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024){
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
}
// MPU6050 ์ผ์๊ฐ ์ ์์๋ํ๋ ๊ฒฝ์ฐ
else if (mpuIntStatus & 0x02){
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
#ifdef OUTPUT_READABLE_YAWPITCHROLL // ์ผ์๋ฅผ ํตํด ๊ตฌํ Yaw, Pitch, Roll ๊ฐ์ Serial Monitor์ ํ์ํฉ๋๋ค
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
#endif
// PID ์ ์ด๋ฅผ ํ๊ธฐ ์ํด input ๋ณ์์ Pitch ๊ฐ์ ๋ฃ์ต๋๋ค
input = ypr[1] * 180/M_PI + 180;
#ifdef OUTPUT_TEAPOT // Processing์ผ๋ก MPU6050์ผ์์ ์์ง์์ Visualize ํ๊ธฐ ์ํ ์ฝ๋
// display quaternion values in InvenSense Teapot demo format:
teapotPacket[2] = fifoBuffer[0];
teapotPacket[3] = fifoBuffer[1];
teapotPacket[4] = fifoBuffer[4];
teapotPacket[5] = fifoBuffer[5];
teapotPacket[6] = fifoBuffer[8];
teapotPacket[7] = fifoBuffer[9];
teapotPacket[8] = fifoBuffer[12];
teapotPacket[9] = fifoBuffer[13];
Serial.write(teapotPacket, 14);
teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
#endif
}
}
// [์ถ์ฒ] [Arduino] ์๋์ด๋
ธ Uno ํ์ฉ ์์ ์ฝ๋์
๋๋ค(4) - Self Balancing Robot, ์
ํ๋ฐธ๋ฐ์ฑ ๋ก๋ด ๋ง๋ค๊ธฐ|์์ฑ์ edward0im
์ค์ค์ด 4ํธ์ ์์ง์ ์ ์งํ๋ ๋ชจ์ต์ด๋ค.
๋ํ ์์ธ๋ฅผ ์ ์งํ๋ ๋ชจ์ต์ด๋ค.
์ด๋ ๊ฒ ๋ก๋ด์ ๋ค ๋ง๋ค๊ณ , ์ด์ ๋ฐํ๋ฅผ ํ๊ธฐ ์ํด ์ค๋น๋ฅผ ์งํํ๋ค.