์ค์ค์ด 9ํธ๋ฅผ ๊ฐ์ง๊ณ ๊ฒฐ๊ณผ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ์์ ์คํ์ ์งํํ์๋ค.
python์ ์ด์ฉํด ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ์ด์ฉํด์ ์๋์ด๋ ธ์์ ๋ชฉํ๊ฐ๊ณผ ํ์ฌ ๊ฐ๋๋ฅผ ๋ฐ์ผ๋ฉฐ ์ด๋ฅผ ๋ ธํธ๋ถ์ csv๋ก ์ ์ฅํ๋ค.
์๋์ด๋ ธ ์ฝ๋
#include <I2Cdev.h>
// arduino ==> ์
ํ๋ฐธ๋ฐ์ฑ๋ก๋ด์ ๋ง๋ค์ด ์คํํด๋ณธ ์ฝ๋
//----------------------------- -------------------------------
// PID ์ ์ด, ๋ชจํฐ ์ ์ด, MPU6050์ผ์ ๊ฐ์ ๋ฐ๊ธฐ ์ํ ์ ์ธ๋ฌธ
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
//MPU ๊ฐ์ฒด๋ฅผ ์ ์ธํฉ๋๋ค
MPU6050 mpu;
//------------------------------------------------------------
#define OUTPUT_READABLE_YAWPITCHROLL // Yaw, Pitch, Roll ๊ฐ์ ์ป๊ธฐ ์ํด ์ ์ธํฉ๋๋ค
#define INTERRUPT_PIN 2 // MPU6050 ์ผ์์ INT ํ์ด ๊ฝํ์๋ ๋ฒํธ๋ฅผ ์ค์ ํฉ๋๋ค. ๋ณดํต 2๋ฒ
//#define LED_PIN 13 // Arudino Uno์ 13๋ฒํ LED๋ฅผ ๋์ ์ค์ ๋ฐ์ง๊ฑฐ๋ฆฌ๊ฒ ํ๋ ค๊ณ ์ ์ธํฉ๋๋ค
//int led = 13;
unsigned long timer; // ์๊ฐ ์ ์
extern volatile unsigned long timer0_millis;
// ================================================================
// === i2c SETUP Items ===
// ================================================================
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
void i2cSetup() {
// 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
}
// ================================================================
// === MPU DMP SETUP ===
// ================================================================
#define OUTPUT_TEAPOT 1 // Processing์ ํตํด MPU6050 ์ผ์๋ฅผ Visualize ํ๊ณ ์ถ์ ๊ฒฝ์ฐ 1, ์๋๋ฉด 0์ผ๋ก ์ ์ธํฉ๋๋ค
//------------------------------------------------------------
// 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
// ================================================================
// === PID Setup ===
// ================================================================
double originalSetpoint;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.3;
double fKp = 10000, fKi = 200, fKd = 1.6; //front Kpid 50 > 75
double bKp = 75, bKi = 200, bKd = 0.8; //back Kpid 32 > 20
/*
1. ๋จผ์ Kp๋ฅผ ์กฐ์ ํฉ๋๋ค.
Kp๊ฐ์ด ๋๋ฌด ์์ผ๋ฉด ๋ก๋ด์ด ์ฝ๊ฒ ๋์ด์ง๋ฉฐ
Kp๊ฐ์ด ๋๋ฌด ํฌ๋ฉด ๋ก๋ด์ด ์๋ค๋ก ์ฌํ๊ฒ ํ๋ค๋ฆฌ๊ฒ ๋ฉ๋๋ค.
๋ก๋ด์ด ์๋ค๋ก ์กฐ๊ธ์ฉ ํ๋ค๋ฆฌ๋ ์ํ๊ฐ ์ต์ ์ ์ํ์
๋๋ค.
2. Kp๊ฐ ์ค์ ๋๋ฉด ๋ค์์ Kd๋ฅผ ์กฐ์ ํฉ๋๋ค.
์ต์ ์ Kd๊ฐ์ ๋ก๋ด์ด ์์ ์ ์ ์งํ๋ ๋์ ์ง๋์ ๊ฐ์์ํค๋ฉฐ
์์ผ๋ก ๋ฐ์ด๋ ๋ก๋ด์ด ๋ฐ๋ก ๋ณต๊ท๋๊ฒ ํฉ๋๋ค.
3. ๋ง์ง๋ง์ผ๋ก Ki๋ฅผ ์กฐ์ ํฉ๋๋ค.
Kp์ Kd๊ฐ ์ค์ ๋๋๋ผ๋ ์์ ๋ ์ํ๋ก ๋๋ฌํ๋ ๋์ ์ง๋์ ํ๊ฒ ๋ฉ๋๋ค.
์ต์ ์ Ki๊ฐ์ ๋ก๋ด์ด ์์ ๋๋ ๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ์ ๋จ์ถ์ํฌ ์ ์์ต๋๋ค.
*/
// PID ์ ์ด์ฉ input, output ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค
double input, output;
// PID๊ฐ์ ์ค์ ํด์ค๋ค
PID pid(&input, &output, &setpoint, fKp, fKi, fKd, DIRECT);
void PID_RL() {
/* double gap = input - setpoint; // Pitch ๊ฐ์ back์ผ๋ก ๊ฐ ์๋ก ๊ฐ์ด ์ปค์ง๋ค.
// ๋์ฐ๋ฉด gap์ ์์, ์ผ์ด์๋ฉด gap์ ์์์ ๊ฐ์ ์ง๋๊ฒ ๋จ.
if (setpoint >= -30) {
pid.SetTunings(bKp, bKi, bKd);
} else if (setpoint < -30) {
if (gap <= 0) // ๋ค๋ก ๋์
{
pid.SetTunings(bKp, bKi, bKd);
} else if (gap > 0) // ์์ผ๋ก ๊ฐ
{
pid.SetTunings(fKp, fKi, fKd);
}
}
*/
pid.SetTunings(fKp, fKi, fKd);
}
// ================================================================
// === Motor Setup ===
// ================================================================
#define MIN_ABS_SPEED 200 // ๋ชจํฐ์ ์ต์ ์๋๋ฅผ ์ค์ ํฉ๋๋ค. 0 ~ 255 ๊ฐ ์ค ์ ํ
// ๋ชจํฐ ์ ์ด์ฉ ๋ณ์ ์ ์ธ
// 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, 100, 100);
// ================================================================
// === Visualize ===
// ================================================================
//------------------------------------------------------------
// 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' };
void MPU6050_connect() {
// 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"));
// 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(")"));
}
}
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
// ================================================================
// === MPU6050 ===
// ================================================================
void PID_update_and_motor_control() {
// 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๋ก ์ ์กํฉ๋๋ค. (๋ชจํฐ์ ์ด)
}
}
void etc_need_setup() {
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
}
void Get_ypr_input() {
// 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-1;
}
void check_imu_working() {
// 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) {
Get_ypr_input();
PID_RL();
MPU6050_visualize();
}
}
void MPU6050_visualize() {
#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
}
// ================================================================
// === Rebound ===
// ================================================================
void Rebound(double theta_0, double theta_b, unsigned long time_start, unsigned long time_b, unsigned long T)
// theta_0๋ ๊ธฐ๋ณธ ์ธํ
๊ฐ๋, theta_b๋ ๋ฐ์ด๋ ๋ ๊ฐ๋, time_start๋ ๋ฐ๋ ์์ ํ์ด๋ฐ, time_b๋ ๋ฐ์ด๋ ๋ ๊ฐ๋์ ์ ์ง ์๊ฐ, T์ ์ฃผ๊ธฐ
{
timer = millis();
setpoint = theta_0; // degree(90) = 0, degree(60) = -30
if (timer >= time_start && timer <= time_start + time_b) {
setpoint = theta_b;
digitalWrite(LED_BUILTIN, HIGH);
}
if (timer >= T) {
timer0_millis = 0;
}
digitalWrite(LED_BUILTIN, LOW);
}
// ================================================================
// === Setup ===
// ================================================================
void setup() {
i2cSetup();
Serial.begin(115200);
Serial.print("test Theta_set,Theta_input, test\n");
while (!Serial); // wait for Leonardo enumeration, others continue immediately
MPU6050_connect();
// ๋ก๋ด์ด ์๋ ์ค 13๋ฒ LED๋ฅผ ๊น๋นก๊ฑฐ๋ฆฌ๊ธฐ ์ํด OUTPUT์ผ๋ก ์ด๊ธฐํํฉ๋๋ค
// pinMode(LED_PIN, OUTPUT);
//pinMode(led, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
}
// ================================================================
// === Loop ===
// ================================================================
void loop() {
Rebound(0,0, 1000, 72, 2000);
PID_update_and_motor_control();
etc_need_setup();
check_imu_working();
// Serial.println(timer);
// Serial.println(",");
// Serial.println(setpoint);
Serial.print("test ");
Serial.print(setpoint);
Serial.print(",");
Serial.print(input);
Serial.print(" test\n");
}
ํ์ด์ฌ
# com6 : ๋ชจํฐ๋ถ
# com8 : ์ผ์๋ถ
import time
from datetime import datetime
from multiprocessing import Pool, Process
import os
import serial
port = 'com28'
rate = 115200
def getSensor(name):
arduino = serial.Serial(port, rate)
time.sleep(1)
now = datetime.now()
title = str(now.strftime('%Y-%m-%d %H.%M.%S')) + ".csv"
f = open(title, 'w')
f.close()
while True:
f = open(title, 'a')
get = arduino.readline()
try:
# _ = arduino.readline() # trash0
get = get.decode('utf-8')
except:
continue
print("get", get)
print(get.split())
if len(get.split()) <= 1:
continue
data = get.split()[1]
print("data:", data)
f.write(data+"\n")
f.close()
if __name__ == '__main__':
print('pid of main:', os.getpid())
p1 = Process(target=getSensor, args=("proc_sensor",))
p1.start()
p1.join()
์ด๋ ๊ฒ ์คํํ๋ฉด์ ์์ ๋๊ธฐ ์คํ์ ๋๋ฆ ์ฑ๊ณต์ ์ผ๋ก ๋๋ฌ๋ค. ํ์ง๋ง ์ ๋ง๋ก ์ค๋ค๋ฆฌ๊ธฐ ์ํฉ์ ์ด์ธ๋ฆฌ๋ ๊ฒ์ ๋๋ฅด๋ ์คํ์ฅ์น์์ ์๋ ์ ์งํ๋ ๊ฐ๋์์ ํํ์ ์ด๋ฃจ๋ ์ํฉ์์ ๊ทธ ์์๋ฅผ ๋ฐ๋์ ํตํด ๋์ด์ฌ๋ฆฌ๋ ์ํฉ์ด๋ค. ๋ฐ๋ผ์ ๊ทธ ์คํ์ ์งํํ๊ณ ์ ํ์์ง๋ง, ์์๊ฐ ์ข์ฐ๋ก ํ๋ค๋ฆฌ๋ฉฐ ๋ท ๋ถ๋ถ์์ ๋ ์์์ ์ฃผ๊ธฐ๋ ์ผ์ ํ๋ฐ ์ค์ ๊ธธ์ด๊ฐ ์งง์์ ธ ๋ฐ๋์ด ์ฌํด ์คํ ๊ฒฐ๊ณผ๊ฐ ์ข์ง ๋ชปํ ์ , ์ ์ ์ ์ด ์ํฅ์ ์ฃผ๋ ๋ฑ์ ๋ฌธ์ ๋ก ์ธํด์ ์ ๋๋ก ์คํ์ด ์ด๋ ค์ ๋๋ฌผ์ ๋จธ๊ธ๊ณ ๊ฒฐ๊ณผ๋ค์ ํ๊ธฐํ๊ณ ์ฌ์คํ, ๋ค์ ์ฌ์คํ์ ์งํํ๋ค.
์์ ๊ฐ์ด ์์ ๋ก ๋ฐ์ดํฐ๋ ์ ๋ฆฌํด๋ณด๋ฉฐ ์คํ์ ์งํํ๋ฉฐ, ๊ฐ์ด๋๋ฅผ ์ธ์ฐ๊ณ ์ค์ ํ์ฑ์ ์ค์ด๊ธฐ ์ํด์ ๋ค์ํ ์ค์ ์ฌ์ฉํด ๊ฒฐ๊ตญ ๋์์ค๊ณผ ๊ฐ์ ์ค๋ก ๋ฐ๊พธ๋ ๋ฑ ์ฌ๋ฌ ๊ณ ๋์ ๊ฑฐ์ณ ๊ฒฐ๊ตญ ๋ฐํ๋ฅผ ํ๊ฒ ๋์๋ค.