728x90

밸런싱 예제를 살펴봤으니

 

 

모터를 사용해보려고 한다.

 

그런데 모터를 사용하려면 모터 드라이버를 써줘야 한다고 하더라

 

 

 

 

이전에는 모터를 정밀하게 제어하려면 필요하다고만 알았지

 

정확하게 왜 필요한지는 잘 몰랐다.

 

잠깐 검색한 자료

 

다음 링크에서 모터 드라이버의 역활에 대해서 설명해주고 있다.

 

ref : https://blog.naver.com/ycpiglet/222050336319

 

 

 

 

 

 

그래도 모터드라이버 준비하기전에 한번 돌아가는지 궁금해서

 

아래 처럼 준비해보고 돌려봤다.

 

 

 

 

 

 

 

잘 돌아간다.

 

 

 

 

 

아까 본 글에서는

 

아두이노 출력 핀에서는 20~30mA밖에 못내서 안된다고 하던데

 

왜그런가 찾아보았다.

 

 

 

 

 

 

다음 링크에 따르면

 

ref : https://electronics.stackexchange.com/questions/67092/how-much-current-can-i-draw-from-the-arduinos-pins

 

아두이노 uno보드의 atmega328p는

 

일반 핀에서 40mA가 나오는게 맞지만

 

내가 사용한 Vcc핀에서는 200mA가 나와서 모터가 동작한듯 하다.

 

 

 

그러면 정말 일반 입출력핀에서 안되는지 보자

 

 

5번 핀으로 출력하는 예제

int output = 5; 

void setup() {
  pinMode(output, OUTPUT);
}

void loop() {
  analogWrite(output, 100);
}

 

 

진짜 5번 핀으로 연결시켰더니 동작되질 않는다....

 

 

 

 

 

오실로스코프 상에서는 제대로 나오고 있는걸 봐선

 

정말 전류가 부족해서 그런것같다.

 

 

 

 

 

 

 

 

 

나중에는 아래 링크 처럼

 

모터 드라이버랑 연결해서 돌려봐야겠다.

 

ref : https://codingrun.com/111

 

 

 

 

 

 

 

 

 

 

 

300x250
728x90

일단 mpu6050 의 dmp 기능으로 roll, pitch, yaw 출력되는 예제를 돌려보고

 

이번에는 밸런싱 로봇 예제를 살펴보았다.

 

ref : https://maker.pro/arduino/projects/build-arduino-self-balancing-robot

 

 

 

 

 

 

 

이 예제에서는 아두이노 나노를 기반으로 L298N 모터 드라이버와 mpu6050 등이 연결되어잇다.

 

 

잠시 L298N 모터 드라이버를 다시 살펴보면

 

 

다음 링크에서 L298N 모터 드라이버의 핀에 대해서 잘 설명해주고 있다.

 

ref :https://m.blog.naver.com/PostView.nhn?blogId=eduino&logNo=221030701469&proxyReferer=https:%2F%2Fwww.google.com%2F

 

 

나노(나는 우노보드를 사용할거지만)와 연결하는 핀이 ENA/ENB, IN1, IN2, IN3, IN4가 있는데

 

ENA와 ENB는 PWM 신호로 각 모터의 속도 제어용

 

중간에 4개의 IN1 ~ 4는 방향 지정용이라 한다.

 

 

 

 

모터 드라이버는 잠깐 나두고

 

일단 아두이노 소스를 보면 다음과 같다.

 

 

 

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 MIN_ABS_SPEED 20

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

//PID
double originalSetpoint = 173;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.1;
double input, output;

//adjust these values to fit your own design
double Kp = 50;   
double Kd = 1.4;
double Ki = 60;
PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);

double motorSpeedFactorLeft = 0.6;
double motorSpeedFactorRight = 0.5;
//MOTOR CONTROLLER
int ENA = 5;
int IN1 = 6;
int IN2 = 7;
int IN3 = 8;
int IN4 = 9;
int ENB = 10;
LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, motorSpeedFactorLeft, motorSpeedFactorRight);

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();
TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif

mpu.initialize();

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

// make sure it worked (returns 0 if so)
if (devStatus == 0)
{
// turn on the DMP, now that it's ready
mpu.setDMPEnabled(true);

// enable Arduino interrupt detection
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();

// set our DMP Ready flag so the main loop() function knows it's okay to use it
dmpReady = true;

// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();

//setup PID
pid.SetMode(AUTOMATIC);
pid.SetSampleTime(10);
pid.SetOutputLimits(-255, 255); 
}
else
{
// 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(")"));
}
}


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();
motorController.move(output, MIN_ABS_SPEED);

}

// 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);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
input = ypr[1] * 180/M_PI + 180;
}
}

 

 

 

처음에는 사용하는 라이브러리들 인클루드, 정의할것들 정의해주고

 

 

 

 

 

 

 

mpu 6050 관련 변수 선언

 

 

쿼터니언, 오일러각, 중력 벡터 변수 선언

 

 

 

PID 관련 변수들도 선언하여

pid 인스턴스 생성

 

 

 

 

모터 제어기 변수 선언,

인스턴스도 초기화

타이머와 인터럽트 관련 내용

 

 

 

이제 셋업문으로 들어와보자

 

"I2Cdev.h" 내부를 보면 I2CDEV_IMPLEMENTATION이 정의되어 있으므로

 

Wire.begin()과 TWBR = 24;이 수행된다.

 

이후 시리얼 통신 시작

 

 

 

 

이후는 mpu6050 초기화과정

 

이전에 봤던 mpu6050 예제와 동일

 

 

 

 

 

mpu6050이 잘 초기화되면

 

DMP 사용할 준비와

pid 변수들이 설정된다.

 

초기화 실패시 에러 출력

 

 

 

135 : dmp 데이터가 준비안되면 시간이 조금 지난뒤 다시수행

 

138 ~ 145 : mpu 인터럽트가 발생안하고 fifo데이터가 패킷 사이즈보다 작은 경우

        => PID 계산, 모터 이동

 

나머지는 인터럽트 플래그 리셋, 인터럽트 상태와 fifocount 취득

 

 

오버플로 체크

- 피포 데이터가 너무 쌓이거나 인터럽트 상태가 0x10인 경우 mpu 리셋

 

 

dmp 데이터가 준비되면,

1. fifo 관련 읽고, 카운팅 루틴

2. fifo 데이터로 쿼터니언 계산 -> 중력 가속도 계산 -> 오일러각 계산

3. 오일러각 출력+

 

 

 

 

 

대강 코드는 이정도로 정리하고

 

Pid와 LmotorController는 조금봐야될거같다.

 

 

일단 PID부터

 

 

 

 

 

 

PID  헤더는 대충 pass

 

 

 

 

 

PID 클래스

 

pid 게인, 제어 방향, 시간, 포인터 변수 등을 지정

 

 

 

 

1. now 처음 시간 취득

2. timechange : 시간 변화 취득

3. 샘플링 타임보다 같거나 크면 -> 시간이 충분히 지나면 pid 로직 수행

4. ino 파일에서 전달받은 myInput을 input으로 초기화

* myInput은 ypr에서 피치각

5. setPoint - input으로 오차 구함. setPoint는 균형 상태에서의 피치각

6. 적분항 Iterm, 미분항 DTerm 계산

7. pid 출력 계산 

 

 

 

ino 파일에서 pid.compute()를 호출하여 pid 출력이 계산되면

 

이대로 모터 컨트롤러가 이동시킨다.

 

 

 

 

우선 모터 컨트롤러 클래스

 

우선 모터 A, B 상수와 핀 번호들이 지정된다.

 

 

 

 

ino 파일에서 호출한 move 함수

 

pid의 output이 여기서는 speed로 사용된다.

 

속도가 음수면 -1 방향으로 

 

음수 시

 speed가 minAbsSpeed보다 크면 minAbsSpeed

 speed가 -255보다 크면 speed 사용

 => - minAbsSpeed(= -20) > speed  > -255

  -> speed가 -20보다 크면 -20 사용

  -> speed가 -255보다 작으면 -255사용

 

속도의 범위 20 ~ 255, -20 ~ -255

 

 

ena, enb로 realSpeed * 모터 상수로 pwm 신호가 전달

 

 

 

대강 소스 흐름은 이정도만 보면 될거같다.

 

구현이 문제지만..

300x250
728x90

상보필터 complementary filter

- 각 센서 데이터에 alpha 만큼의 가중치를 줘서 서로 보완하는 필터

- 예시 : 각 변화율 자이로 x에 가속도 x 값을 같이 반영해서 롤 각을 구함

 

 

 

 

raw mpu데이터로 roll, pitch, yaw를 출력하도록

 

상보 필터 예제를 찾다가

 

ref : https://blog.naver.com/PostView.nhn?blogId=ysahn2k&logNo=221385063966&categoryNo=0&parentCategoryNo=0&viewDate=¤tPage=1&postListTopCurrentPage=1&from=postView

 

위 링크에서 예제와 잘 설명이 되어있더라

 

 

 

원본 소스에서 주석 처리되거나 필요없는 부분을 제거해서 돌려봤

 

 

으나..

 

생각보다 롤, 피치, 요가 잘 나오지 않는다.

 

 

 

#include <Wire.h>
#define MPU6050_AXOFFSET 158
#define MPU6050_AYOFFSET 9
#define MPU6050_AZOFFSET -91
#define MPU6050_GXOFFSET 19
#define MPU6050_GYOFFSET -42
#define MPU6050_GZOFFSET -26

long sampling_timer;
const int MPU_addr=0x68;  // I2C address of the MPU-6050

int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ; // Raw data of MPU6050
float GAcX, GAcY, GAcZ; // Convert accelerometer to gravity value
float Cal_GyX,Cal_GyY,Cal_GyZ; // Pitch, Roll & Yaw of Gyroscope applied time factor
float acc_pitch, acc_roll, acc_yaw; // Pitch, Roll & Yaw from Accelerometer
float angle_pitch, angle_roll, angle_yaw; // Angle of Pitch, Roll, & Yaw
float alpha = 0.96; // Complementary constant

void setup(){
  Wire.begin();
  init_MPU6050();
  Serial.begin(9600);
}

void loop(){

  // Read raw data of MPU6050
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
  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)

  // Raw data of accelerometer corrected by offset value
//  AcX -= MPU6050_AXOFFSET;
//  AcY -= MPU6050_AYOFFSET;
//  AcZ -= MPU6050_AZOFFSET;

  // Convert accelerometer to gravity value
  GAcX = (float) AcX / 4096.0;
  GAcY = (float) AcY / 4096.0;
  GAcZ = (float) AcZ / 4096.0;

  // Calculate Pitch, Roll & Yaw from Accelerometer value
  // Reference are 
  // https://engineering.stackexchange.com/questions/3348/calculating-pitch-yaw-and-roll-from-mag-acc-and-gyro-data
  // https://www.dfrobot.com/wiki/index.php/How_to_Use_a_Three-Axis_Accelerometer_for_Tilt_Sensing
  acc_pitch = atan ((GAcY - (float)MPU6050_AYOFFSET/4096.0) / sqrt(GAcX * GAcX + GAcZ * GAcZ)) * 57.29577951; // 180 / PI = 57.29577951
  acc_roll = - atan ((GAcX - (float)MPU6050_AXOFFSET/4096.0) / sqrt(GAcY * GAcY + GAcZ * GAcZ)) * 57.29577951; 
  acc_yaw = atan (sqrt(GAcX * GAcX + GAcZ * GAcZ) / (GAcZ - (float)MPU6050_AZOFFSET/4096.0)) * 57.29577951; 



  // Calculate Pitch, Roll & Yaw by Complementary Filter
  // Reference is http://www.geekmomprojects.com/gyroscopes-and-accelerometers-on-a-chip/
  // Filtered Angle = α × (Gyroscope Angle) + (1 − α) × (Accelerometer Angle)     
  // where α = τ/(τ + Δt)   and   (Gyroscope Angle) = (Last Measured Filtered Angle) + ω×Δt
  // Δt = sampling rate, τ = time constant greater than timescale of typical accelerometer noise
  angle_pitch = alpha * (((float)(GyX - MPU6050_GXOFFSET) * 0.000244140625) + angle_pitch) + (1 - alpha) * acc_pitch;
  angle_roll = alpha * (((float)(GyY - MPU6050_GYOFFSET) * 0.000244140625) + angle_roll) + (1 - alpha) * acc_roll;
  angle_yaw += (float)(GyZ - MPU6050_GZOFFSET) * 0.000244140625; // Accelerometer doesn't have yaw value
  


  // Print value of Pitch, Roll & Yaw reflected Complementary Filter
  Serial.print(" | angle_pitch = "); Serial.print(angle_pitch);
  Serial.print(" | angle_roll = "); Serial.print(angle_roll);
  Serial.print(" | angle_yaw = "); Serial.println(angle_yaw);



  // Sampling Timer
  while(micros() - sampling_timer < 4000);
  sampling_timer = micros(); //Reset the sampling timer  
}

void init_MPU6050(){
  //MPU6050 Initializing & Reset
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

  //MPU6050 Clock Type
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0x03);     // Selection Clock 'PLL with Z axis gyroscope reference'
  Wire.endTransmission(true);

  //MPU6050 Gyroscope Configuration Setting
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x1B);  // Gyroscope Configuration register
  Wire.write(0x18);     // FS_SEL=3, Full Scale Range = +/- 2000 [degree/sec]
  Wire.endTransmission(true);

  //MPU6050 Accelerometer Configuration Setting
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x1C);  // Accelerometer Configuration register
  Wire.write(0x10);     // AFS_SEL=2, Full Scale Range = +/- 8 [g]
  Wire.endTransmission(true);

  //MPU6050 DLPF(Digital Low Pass Filter)
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x1A);  // DLPF_CFG register
  Wire.write(0x00);     // Accel BW 260Hz, Delay 0ms / Gyro BW 256Hz, Delay 0.98ms, Fs 8KHz 
  Wire.endTransmission(true);
}



//https://blog.naver.com/PostView.nhn?blogId=ysahn2k&logNo=221385063966&categoryNo=0&parentCategoryNo=0&viewDate=&currentPage=1&postListTopCurrentPage=1&from=postView

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

생각해보니 mpu6050에서 dmp 기능으로 

 

오일러 각을 잘 출력해주는데 잊고있었다.

 

ref : https://www.teachmemicro.com/orientation-arduino-mpu6050/

 

 

 

그동안 써왔던 핀 연결에다가 인터럽트 핀도 D2에 연결하자

 

 

 

 

electrocats의 mpu6050 라이브러리를 다운받고

 

 

 

 

 

DMP 기능으로 오일러각 출력하는 예제가 있다.

 

 

 

 

중간에 이 줄을 남겨두면 키 입력받을때까지 동작을 안하니 지워주고

 

업로드하자

 

 

 

 

 

 

 

 

 

 

 

 

값은 yaw pitch roll 순으로 나온다

 

1. yaw

 

 

2. pitch

 

 

 

 

3. roll

 

 

 

 

 

 

 

 

 

 

 

300x250
728x90

 

 

gyro xyz : 각 축 방향에 대한 각 변화량

acc xyz : 각 축 방향의 가속도

 

 

 

mpu 6050의 각 축에 대해서 잘 나온 그림이 있어 가져왔다.

 

https://www.electronicwings.com/sensors-modules/mpu6050-gyroscope-accelerometer-temperature-sensor-module

 

 

 

평행이동과 회전 운동의 관계

ref : https://m.blog.naver.com/PostView.nhn?blogId=kb986213&logNo=70190081308&targetKeyword=&targetRecommendationCode=1

 

 

 

 

1. 센서를 가만히 나둔 경우

 

기본적으로 gyro xyz는 각 변화량으로 축 방향으로 변화가 있을때만 값의 변화가 크게 일어난다.

-> 가만히 있으면 g xyz는 큰 변화는 없음

 

acc xyz는 각 축 방향에 대한 가속도를 출력하고 있으나 센서가 움직이지 않는 상태에서는 중력가속도의 영향으로

-> acc z축값만 크고 다른 값들은 크게 변하지 않는다.

 

=> 센서에 동작이 없으므로 ax, ay/ gxyz는 큰 변화없이  az만 15000정도로 유지

 

 

 

 

 

2. 회전 시킨 경우

2.1 gyro

회전하는 순간의 각 속도가 gx, gy, gz로 출력

-> 회전하는 동안만  속도가 0이 아니나 회전을 멈춘 순간 원상태로 돌아옴

2.2 acc

가속도계는 중력 가속도의 영향으로 x/y축 방향으로 양방향 음방향 회전에 따라 값이 유지

 

 

 

 

 

 

 

3. 선운동의 경우

 

3.1 x, y 선운동

 

 

 

 

 

3.2 z축 선운동

 

15000 정도 되던 acc z값이

내려가는 순간 -32768 까지 떨어짐

이후 돌아옴

 

 

 

 

 

 

300x250
728x90

지난번 zumi에서와 비슷한 방법으로

 

파이3에 opencv를 설치하면

 

piwheel에 등록된 opencv-python 버전을 설치하면 되겠다.

 

버전을 지정안해주면 계속 opencv 소스 자체를 받아서 빌드하더라

 

 

 

 

파이에서 파이썬3을 실행하니 지금 3.5.3버전이 깔려있다고 한다.

 

 

파이 휠에서 python3.5용 제공되는 최신 opencv whl 파일은 4.1.1.26이니 이버전으로 지정해서 설치하면 되겠다.

* pi zero는 armv6l이나 pi3은 armv7l

 

 

 

버전 지정안해서 소스 다운받을때와는 달리 whl 파일을 다운받는다.

 

 

소스 코드를 다운받고 빌드하는 과정이 없다보니 금방 끝난다.

 

 

 

 

파이3에서 영상 스트리밍을 하려고하는데 

ref : throwexception.tistory.com/889

 

다음 에러가 뜬다.

 

libf77blas 라이브러리가 설치안되서 그런것같다.

* blas 라이브러리는 선형대수 처리 lib

 

 

 

 

검색해보니 blas를 설치안해서 그런듯하다

 

blas를 설치해주자

 

ref : https://github.com/numpy/numpy/issues/14772

 

 

이번에는 jasper 라이브러리가 문제다

 

 

jasper도 설치해주자

* jasper는 JPEG 포멧 코덱 라이브러리라고한다.

* http://www.linuxfromscratch.org/blfs/view/svn/general/jasper.html

 

 

 

으아 이번에는 QtGUI가 문제다

* QT : GUI 라이브러리

 

 

 

 

qt도 설치

 

 

 

 

 

 

pip로 설치하면서 필요한 의존라이브러리가 제대로 설치안된게 문제다

ref : https://discuss.96boards.org/t/opencv-3-2-install-dependencies-error/2139

 

 

# GUI (if you want to use GTK instead of Qt, replace ‘qt5-default’ with ‘libgtkglext1-dev’ and remove ‘-DWITH_QT=ON’ option in CMake):
sudo apt-get install -y qt5-default
sudo apt-get install -y libqt5opengl5-dev
sudo apt-get install -y python3-pyqt5.qtopengl python3-opengl

 

 

# Media I/O:
sudo apt-get install -y zlib1g-dev libjpeg-dev libwebp-dev libpng-dev libtiff5-dev libopenexr-dev libgdal-dev
sudo apt-get install -y libjpeg62-turbo-dev libtiff5-dev

 

 

# Video I/O:
sudo apt-get install -y libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-dev libtheora-dev libvorbis-dev libxvidcore-dev libx264-dev yasm libopencore-amrnb-dev libopencore-amrwb-dev libv4l-dev libxine2-dev
sudo apt-get install -y libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
sudo apt-get install -y libxvidcore-dev libx264-dev

 

 

# gstreamer, opengl and vtk
sudo apt-get install -y libgstreamer1.0-0
sudo apt-get install -y freeglut3-dev libglew-dev libglm-dev mesa-common-dev
sudo apt-get install -y libvtk6-qt-dev python-vtk6

 

 

# Parallelism and linear algebra libraries:
sudo apt-get install -y libtbb-dev libeigen3-dev
sudo apt-get install -y libcblas-dev gfortran
sudo apt-get install -y libblas-dev liblapack-dev

 

 

 

 

이 명령어로 다 설치해보자

 

 

 

sudo apt-get install -y qt5-default libqt5opengl5-dev python3-pyqt5.qtopengl python3-opengl libtbb-dev libeigen3-dev libcblas-dev gfortran libblas-dev liblapack-dev libgstreamer1.0-0 freeglut3-dev libglew-dev libglm-dev mesa-common-dev libvtk6-qt-dev python-vtk6  zlib1g-dev libjpeg-dev libwebp-dev libpng-dev libtiff5-dev libopenexr-dev libgdal-dev libjpeg62-turbo-dev libtiff5-dev

 

 

 

이것도 안되고

 

sudo apt-get upgrade 했더니 이번에는

 

undefined symobl: cbblas_sgemm 에러가 뜬다..

 

 

 

여기서 numpy를 삭제하고 

 

재설치해보라길래 

 

삭제는 해봤는데

 

python3-numpy는 그대로 남아있더라

 

pip로 설치한것과 apt-get 으로 설치한게 다른데 위치해서 충돌난듯하다.

 

 

 

이번에는 실행은 된것같지만

 

libQtTest가 없다고 한다.

 

아까 설치했던것같은데??

 

 

 

일단 qttest도 설치해주고

 

 

 

 

 

이제서야 된다 ㅠㅜ

 

 

 

 

 

 

300x250
728x90

아두이노

- mpu6050 시리얼 출력 예제 업로드

ref : https://throwexception.tistory.com/919?category=856640

 

파이

- 아두이노 데이터 리드에서 프린트 하는 예제 사용

ref : throwexception.tistory.com/920

 

 

나중에 파이에서 연산하고

아두이노로 제어하는 간단한 로봇을 만드는게 목표

 

 

 

 

 

아두이노 데이터 리드

- 아두이노에서 전달받은 데이터를 utf-8로 받으니. utf-8로 못바꾸는 데이터가 있다고 한다. 대신 ascii로 했더니 잘된다.

#!/usr/bin/env python3
import serial

if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
    ser.flush()

    while True:
        if ser.in_waiting > 0:
            line = ser.readline().decode("ascii").rstrip()
            print(line)

 

 

 

 

 

스트리밍이 되는데로

 

imu 회전에 따라 값변화에 대해 다시 써야지

 

 

 

 

 

300x250
728x90

지난번과 마찬가지로

다음 링크의 나머지 내용들

 

https://roboticsbackend.com/raspberry-pi-arduino-serial-communication/

 

 

 

 

 

다시 아두이노에 새 펌웨어를 올려야되는데

 

파이에서 꽂은걸 다시 PC에다 연결해서 올리기 번거로우니

 

라즈베리파이에서 바로 업로드할수 있도록 arduino sketch를 설치하려고한다.

 

 

 

arm 아키텍처인건 아는데 32bit은지 64비트인지 몰라

 

검색해보니

 

uname -m 할때

 

armv7l이 뜨면 32비트라고 한다.

 

 

armv7l이뜨니 32비트 아두이노 스캐치 다운

 

 

 

 

 

압축 다풀고 설치해주자

 

 

 

 

 

스케치에서 다음 코드를 아두이노로 업로드 해줍시다.

 

파이에서 보낸 데이터를 다시 에코하는 예제

void setup() {
  Serial.begin(9600);
}
void loop() {
  if (Serial.available() > 0) {
    String data = Serial.readStringUntil('\n');
    Serial.print("You sent me: ");
    Serial.println(data);
  }
}

 

 

 

 

 

 

아두이노에는 보낸 스트링을 에코해주는 펌웨어를 올렸다면

 

파이에다가는 스트링을 보내고

아두이노에서 에코해준 문자열을 출력

 

#!/usr/bin/env python3
import serial
import time
if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
    ser.flush()
    while True:
        ser.write(b"Hello from Raspberry Pi!\n")
        line = ser.readline().decode('utf-8').rstrip()
        print(line)
        time.sleep(1)

 

 

아두이노에서 잘 에코해주고 있는것 같긴한데

 

왜 일부 line 변수에서는 You sent me : 가 빠져서 출력되는지는 잘 모르겠다.

 

 

 

300x250
728x90

링크

- ref : https://roboticsbackend.com/raspberry-pi-arduino-serial-communication/

 

 

위 글에서 아두이노와 라즈베리파이를 USB로 연결하여 직렬 통신을 하는 방법을 설명해주고 있습니다

 

 

보통 아두이노와 라즈베리파이 간의 통신에 대한 글들은

GPIO를 이용한 I2C 통신에 대해 서술하고 있지만

 ref : https://dronebotworkshop.com/i2c-arduino-raspberry-pi/

전압 컨버터가 없어 그냥 USB로 사용하겠습니다.

 

 

 

연결 방법은

 

아두이노 USB단자를 파이 USB에 연결하면 끝

 

 

 

아두이노 코드

void setup() {
  Serial.begin(9600);
}
void loop() {
  Serial.println("Hello from Arduino!");
  delay(1000);
}

 

 

 

아두이노 포트 확인

- 아두이노가 ttyACM0로 연결되어있내요.

 

 

 

 

 

파이썬 시리얼 통신 라이브러리 설치

pip install pyserial

 

 

파이썬 코드

#!/usr/bin/env python3
import serial
if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
    ser.flush()
    while True:
        if ser.in_waiting > 0:
            line = ser.readline().decode('utf-8').rstrip()
            print(line)

 

vim에서는 복붙이 잘안되다보니 nano 에디터로 위 코드를 복붙했습니다.

 

 

 

 

 

 

300x250
728x90

mpu6050 이론

- 센서 레지스터 내용을 잘 정리했더라

ref : https://blog.naver.com/mokhwasomssi/221801280305

 

 

 

아두이노 cc 

- mpu 6050 사용 예제 일부

ref : https://create.arduino.cc/projecthub/Raushancpr/mpu6050-configuration-with-arduino-1a3dcf

 

 

 

Fritzing file that shows how to wire the GY-521 breakout board to an Arduino Uno.In this tutorial we will make use only of the first four pins: VCC, GND, SDA, and SCL.

-> 위 프리징 이미지는 GY-521 보드(mpu 6050 센서 내장)와 아두이노 우노 연결 예시를 보여줌

 

First, we connect the module’s VCC to the Arduino’s 5V pin. Then, the module’s GND is connected to one of the Arduino’s GND pins.Next, we have to set up the I2C connection between the module and the Arduino.

-> VCC는 아두이노 5v, GND는 아두이노 GND, 나머지 SDA와 SCL로 아두이노와 i2c 연결

 

Most Arduino Uno variants have an SCL and SDA pin. If you have such an Arduino Uno, just connect SCL to SCL and SDA to SDA.

-> 모듈 SCL은 아두이노 SCL, 모듈 SDA는 아두이노 SDA와 연결해야함

 

 If you can’t find an SCL and SDA pin on your Arduino, you have to use other pins. Unfortunately, you cannot use just any pin.

-> 아두이노에 별도 SCL, SDA 핀이없으니 대신 다른 핀 사용

 

 For each type of Arduino, SCL and SDA are tied to different pins:Arduino Uno, Arduino Ethernet, Arduino Nano: A4 (SDA), A5 (SCL)Arduino Mega2560: 20 (SDA), 21 (SCL)Arduino Leonardo: 2 (SDA), 3 (SCL)Arduino Due: 20 (SDA), 21 (SCL)So, if you have an Arduino Uno without SCL and SDL pins, then connect the Arduino’s A4 pin to the module’s SDA pin. Next, connect the Arduino’s A5 pin to the module’s SCL pin.

-> (메가2560, 레오나르도, 듀오 내용은 뺌)

우노 보드에 사용시 SDA핀을 우노 A4핀에 연결

SCL은 우노의 A5핀에 연결

 

 Example source code:We make use of the Arduino platform’s in-built library (Wire) to establish an I2C connection between the Arduino Uno and the GY-521 sensor. At the beginning of our source code, the Wire library’s header file is included.

-> 아두이노에서 제공하는 "Wire.h" 라이브러리로 I2C 통신 수행. 해더 파일로 포함시킴

 

 

 Next, we define and declare some variables.

-> 사용할 변수 선언, 정의

- MPU6050 주소, 센서 값 담을 변수, 임시 문자열 변수

 

 

 Then, a convert-function is defined. The convert-function makes sure that all sensor values have the same width when they are printed out to the serial monitor later.

-> int16 타입의 센서 값을 문자열 출력하도록 변환 함수 정의

 

 

 

 

 In the setup function, a serial connection is established. Moreover, we start our first I2C transmission to the GY-521 board to wake it up from sleep mode.

-> 시리얼 통신을 시작하고,

보드와 I2C 통신도 시작. 

 * I2C 통신 데이터는 8비트 길이

 

 

 

wire.beginTransmission(addr)

-> I2C 슬레이브 장치의 주소로 통신 시작

wire.write(0x6B)

wire.write(0)

-> 0x6B 번지에 0 쓰기 => GY-521 보드에 전원인가

 

 

 

 The MPU-6050 has many registers which can be read. Fourteen of these registers contain the sensor values that we need. As a first step, we tell the GY-521 module where we are going to start reading (“Wire.write(0x3B);”). Then, we request to read 14 registers (“Wire.requestFrom(MPU_ADDR, 7*2, true);”).

-> 0x3B 부터 읽음.

ACC X의 H 값은 0x3B에서 읽을수 있기 때문

 

 

 

 

 If you are wondering, why 14 registers are read instead of 7 registers, the reason is quite simple: Each sensor value has a size of 2 byte. As each register has a size of one byte, a single sensor value must be retrieved by accessing two registers. The first register contains the so-called “high byte” and the second register contains the “low byte”.

-> 레지스터가 7개이나 14레지스터를 읽는 이유. 한 센서 값은 2바이트로 되서(첫쨰는 하이바이트, 둘째는 로바이트). 

 

 

 

  Wire.requestFrom(MPU_ADDR, 7*2, true); // request a total of 7*2=14 registers

-> 14바이트 요청

 

첫 두바이트는 acc x(H, L)

 

 

wire.read()로 우선 acc x H 읽기

이후 시프트해서 acc x L는 0으로

 

 

 

Ax H를 시프트 후, wire.read()하여 acc x L값을 읽고 or연산

=> 가속도 X값 완성 

 

acc X는 16비트(2바이트)로 int16에 담음

 

 

 

 

 

 

* MPU6050 레지스터맵 - ACC + TEMP + GYRO 번지

 

 

 

 Next, all values are retrieved and printed out to the serial connection. At the end of the loop function, a delay of one second is added in order to avoid flooding the serial monitor with messges

-> 직렬 통신으로 출력을 마치면, 너무 빨리 출력되지 않도록 딜레이 줌

 

#include "Wire.h" // This library allows you to communicate with I2C devices.


const int MPU_ADDR = 0x68; // I2C address of the MPU-6050. If AD0 pin is set to HIGH, the I2C address will be 0x69.
int16_t accelerometer_x, accelerometer_y, accelerometer_z; // variables for accelerometer raw data
int16_t gyro_x, gyro_y, gyro_z; // variables for gyro raw data
int16_t temperature; // variables for temperature data
char tmp_str[7]; // temporary variable used in convert function

char* convert_int16_to_str(int16_t i) { // converts int16 to string. Moreover, resulting strings will have the same length in the debug monitor.
  sprintf(tmp_str, "%6d", i);
  return tmp_str;
}



void setup() {
  Serial.begin(9600);
  Wire.begin();
  Wire.beginTransmission(MPU_ADDR); // Begins a transmission to the I2C slave (GY-521 board)
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0); // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
}



void loop() {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H) [MPU-6000 and MPU-6050 Register Map and Descriptions Revision 4.2, p.40]
  Wire.endTransmission(false); // the parameter indicates that the Arduino will send a restart. As a result, the connection is kept active.
  Wire.requestFrom(MPU_ADDR, 7*2, true); // request a total of 7*2=14 registers
  
  // "Wire.read()<<8 | Wire.read();" means two registers are read and stored in the same variable
  accelerometer_x = Wire.read()<<8 | Wire.read(); // reading registers: 0x3B (ACCEL_XOUT_H) and 0x3C (ACCEL_XOUT_L)
  accelerometer_y = Wire.read()<<8 | Wire.read(); // reading registers: 0x3D (ACCEL_YOUT_H) and 0x3E (ACCEL_YOUT_L)
  accelerometer_z = Wire.read()<<8 | Wire.read(); // reading registers: 0x3F (ACCEL_ZOUT_H) and 0x40 (ACCEL_ZOUT_L)
  temperature = Wire.read()<<8 | Wire.read(); // reading registers: 0x41 (TEMP_OUT_H) and 0x42 (TEMP_OUT_L)
  gyro_x = Wire.read()<<8 | Wire.read(); // reading registers: 0x43 (GYRO_XOUT_H) and 0x44 (GYRO_XOUT_L)
  gyro_y = Wire.read()<<8 | Wire.read(); // reading registers: 0x45 (GYRO_YOUT_H) and 0x46 (GYRO_YOUT_L)
  gyro_z = Wire.read()<<8 | Wire.read(); // reading registers: 0x47 (GYRO_ZOUT_H) and 0x48 (GYRO_ZOUT_L)
  
  // print out data
  Serial.print("aX = "); Serial.print(convert_int16_to_str(accelerometer_x));
  Serial.print(" | aY = "); Serial.print(convert_int16_to_str(accelerometer_y));
  Serial.print(" | aZ = "); Serial.print(convert_int16_to_str(accelerometer_z));
  // the following equation was taken from the documentation [MPU-6000/MPU-6050 Register Map and Description, p.30]
  Serial.print(" | tmp = "); Serial.print(temperature/340.00+36.53);
  Serial.print(" | gX = "); Serial.print(convert_int16_to_str(gyro_x));
  Serial.print(" | gY = "); Serial.print(convert_int16_to_str(gyro_y));
  Serial.print(" | gZ = "); Serial.print(convert_int16_to_str(gyro_z));
  Serial.println();
  
  // delay
  delay(100);
}

 

 

 

 

 

 

 

 

300x250
728x90

최근 회로에 대해서 간단히 배우기 전까지

 

 

라즈베리파이나 아두이노의 전원에 대한 개념이 전혀 없었다.

 

 

그냥 마이크로 usb(파이)/usb1.1(우노)를 꽂으면 그게 그냥 데이터 전송도하고 전원도 되는가보다 했다.

 

 

 

 

그런데 아두이노에는 이 외에 2가지 전원을 주는 방법이 더 있더라

 

 

1. usb 전원

2. 파워 잭

3. Vin 단자 

 

 

파워 잭이야 우리가 자주보는 이런 단자

 

아두이노 회로도 상에서 POWERSUPPLY_DC21MMX 로 나오고 있다.

ref : https://throwexception.tistory.com/866

ref : www.youtube.com/watch?v=h_jTHMpEBAY

 

 

8핀 헤더에 8번 핀이 VIN 단자로 여기로 5V 전압을 받아도 아두이노가 동작된다.

 

 

 

 

 

 

 

 

라즈베리파이3의  그라운드와 5V 단자로 아두이노에 전원 주도록 해봤습니다.

 

파이 5V 출력을 아두이노 VIN과 연결하고

파이 GND와 아두이노 GND를 연결.

 

https://docs.microsoft.com/ko-kr/windows/iot-core/learn-about-hardware/pinmappings/pinmappingsrpi

 

아두이노에는 블링크 예제 펌웨어를 업로드 해줍시다.

 

 

 

 

 

 

희미하지만 위 예제대로 파이 전원으로 잘 블링크 됩니다.

 

 

 

300x250

+ Recent posts