[아두이노] 구부림(flex) 센서와 관절 제어

IOT/아두이노|2019. 7. 26. 09:00

[아두이노] 구부림(flex) 센서와 관절 제어



지난 시간까지 주변 사물을 관찰하는 실험을 하였는데 문득 손을 보고 손의 움직임을 관찰하다가 한번 아두이노 시각으로 표현하고 싶은더군요. 그래서 손가락 관절을 제어하는 실험에 대해 상상하다가 예전에 공부했던 손가락의 움직임을 감지 할 수 있는 센서가 떠올랐네요. 실제 구부림 센서가 없어 실제로 실험은 못하지만 구부림 센서을 대해 간단히 이야기만 하고 가상시뮬레이터로 구부림 센서를 대신 할 부품으로 가변저항을 이용하여 가상시뮬레이터에서 손가락 관절 제어 실험을 해 보겠습니다.

1. 구부림(flex) 센서


구부림 센서는 구부림의 정도에 따라서 저항값이 달라집니다. 이 값을 통해서 구부림의 각도를 계산해 낼 수 있습니다.

int val = analogRead(A0);
int angle = map(val,입력최소값,출력최대값,0,90);

위와 같이 설정하면 구부림 값에 따라 0~90도의 각도를 만들어 낼 수 있습니다. 이 값을 통해서 Servo모터의 회전 시킬 수 있습니다.

2. 손가락 관절 제어


관철의 관찰 대상은 인간의 손으로 하겠습니다.


위 사진을 보시면 집게손가락을 보시면 세개의 관절이 있습니다. 이 부분의 동작을 관찰해 볼까요.

이제 주먹을 쥐어 볼까요.



자세히 보시면 각 마디의 관절이 약 90도 각도로 회전되어 있는 걸 보실 꺼에요.

이 관찰을 통해서 여러분들은 아두이노시각으로 표현한다면 뭐가 있을까요. 각도를 제어할 수 있는 Servo모터가 떠오르지 않나요.

집게손가락의 관절의 각도를 손이 쫙 펴진 상태에서 0도라면 주먹이 쥐어지는 과정은 서서히 3개의 관절의 각도를 증가시켜 90도 회전을 하게되면 주먹이 쥐어지는 형태가 됩니다. 이 원리를 잘 이용하게 손가락의 관절 제어를 할 수 있게 되겠죠.

오늘 다루게 되는 구부림센서를 이용하여 구부림의 각도값을 통해 3개의 관절(Servo모터)을 이용하면 손가락을 움직이게 할 수 잇겠죠.

이제 좀 더 자세히 알아보도록 하죠.

3. 구부림센서를 이용한 손가락 관절 제어


  • 준비물 : Servo모터 3개, 구부림센서, 저항 1m옴, 아두이노 우노
  • 내용 : A0는 구부림센서값을 읽기 위한 핀이고, Servo모터는 5,6,7핀을 이용한다.

실제 구부림 센서가 없기 때문에 회로도만 간단히 소개합니다.


위 회로도로 만들게 된다면 구부림의 정도에 따라서 3개의 Servo모터를 회전시키면 손가락 관절을 회전시키는 결과를 얻게 됩니다.

4. 가상시뮬레이터에서 손가락 관절 제어 실험


  • 준비물 : Servo모터 3개, 가변저항 1개, 아두이노 우노
  • 내용 : A0는 구부림센서값을 읽기 위한 핀이고, Servo모터는 5,6,7핀을 이용한다.

가상시뮬레이터에서는 구부림 센서는 없습니다. 예전에 post로 이야기 했듯이 없는 부품은 유사한 같은 느낌의 부품을 이용해서 실험을 하라고 했죠. 이번에는 가변저항을 이용하여 구부림 센서의 값을 대신하면 유사한 실험을 할 수 있게 됩니다.

위 회로도에서 보는 것처럼 가변저항의 Vcc, Gnd 핀을 연결하면 가변저항을 회전시키면 1023~0의 아날로그 신호의 변화가 일어나고가 발생합니다. 반대로 연결하면 0~1023의 아날로그 신호 변화가 일어납니다. 이 부분을 주위해주세요.

5. 코딩


손가락 과절 제어는 가상시뮬레이터에서 가변저항의 값을 각도로 바꾸기 위해서는 1023~0의 아날로그 신호값을 0~90도의 각도값을 만들고 그 각도만큼 회전시키면 됩니다.

int angle = map(analogRead(A0),0,1023,0,90);  

map()함수로 0~1023의 아날로그값을 0~90의 각도값으로 변환시킵니다. angle값으로 Servo모터를 회전시키면 됩니다.

servo1.write(angle);     
servo2.write(angle);     
servo3.write(angle);     

이렇게 가변저항을 통해 얻은 회전각을 3개의 Servomotor의 회전값으로 실행시켜면 자연스럽게 손가락 관절을 움직이게 됩니다.

종합해보면,

#include <Servo.h>

Servo servo[3];
const byte servoPin[3] = {5,6,7};

void setup()
{ 
  for(int i=0;i<3;i++){
    servo[i].attach(servoPin[i]); 
    servo[i].write(0);
  }  
  delay(1000);
}

void loop()
{  
  int angle = map(analogRead(A0),0,1023,0,90);  
  for(int i=0;i<3;i++){
    servo[i].write(angle);     
  }
  delay(50);
}

손가락 관절 제어라고 했는데 거창한 코딩을 기대하고 post를 보셨다면 크게 실망하셨을 거라 생각됩니다. 이걸로 손가락 관절이 제어가 되는지 의구심을 갖을 수 있겠죠.

시뮬레이터 결과로 간단히 설명 드리겠습니다.

6. 결과





위 결과를 보면 저게 손가락 관절이야 하실 꺼에요. 손가락 위치로 한번 재 배치 해 볼까요.


위 그림이 어느정도 회전에 대한 손가락 관절을 이해하셨나요.


7. 구부림 센서의 한계


구부림 센서는 손가락 한개의 구부림 각도 한개만 만들어 냅니다. 즉, 3개의 관절을 같은 각도로 제어하기 때문에 단순한 움직임만 만들어 냅니다. 정교한 손가락 움직임을 만들어 낼 수 없습니다. 손가락 3개의 관절 중 2개의 관절만 구부려도 구부림 센서는 이 값을 가지고 3개의 관절을 동일한 각도로 회전시키는 문제가 발생합니다.

처음에는 구부림 센서와 같은 부품으로 간단히 손가락을 움직이게 하고 나중에 능력이 된다면 근육센서로 신경값을 분석하여 손가락을 움직이는 실험을 해보세요. 나중에 기회가 된다면 꼭 해보고 싶은 실험입니다. 문제는 근육센서가 비싸고 단순히 Servo모터로 손가락을 제어하는것도 좀 부자연 스럽고 어느정도 로봇손에 관련된 고급 모터 부품을 쓴다면 재미로 실험하기에는 가격 부담이 크네요.

마무리


구부림 센서를 이용한 손가락 관절 제어는 구글검색을 하시면 쉽게 post들을 찾아 볼 수 있습니다. 대개 장갑에 구부림 센서를 부착하여 장갑을 낀 상태로 주먹을 쥐면 주먹을 쥔 각도만큼 구부림 센서는 구부러지고 그 구부림 각도만큼 아두이노에서 센서값을 읽고 회전각을 만들어서 구부린 각도만큼 회전을 시키면 실제 센서가 부착된 장갑에서 손동작과 유사하게 로봇손을 움직이게 할 수 있게 됩니다.

실제로 Servo모터가 16개가 있고 다수 Servo모터를 컨트롤 할 수 있는 모터쉴드랑 다섯개의 구부림 센서가 있다면 로봇손 전부를 움직이게 하는 실험을 할 수 있을 꺼에요. 하지만 제가 가지고 있는 부품이 Servo모터 1개뿐이라 실제 관절 실험을 못해 봤네요. 하지만 가상시뮬레이터로 어느정도 유사한 실험을 할 수 있는 것에 만족합니다.

댓글()

[아두이노] 아두이노 주차장 출입구 차량차단장치

IOT/아두이노|2019. 7. 25. 09:00

[아두이노] 아두이노 주차장 출입구 차량차단장치



일상을 주제로 주변에 상황을 아두이노로 표현하는 실험을 계속 진행 중입니다. 오늘은 아파트나 일반 주차장 또는 특정 건물에 차량을 진입하는 입구 쪽에 차량 차단장치가 설치되어 있는 곳들이 많습니다. 이 차량 차단장치를 아두이노적 시각으로 한번 실험을 해보도록 하겠습니다.

1. 주차장 출입구 차량차단장치 설정



위와 같이 상황은 근접감지센서를 다른 것을 이용해야 하는데 실험에서는 초음파센서를 이용하여 근접감지를 하도록 상황을 만들고 차량이 출입구 쪽으로 다가오면 근접감지가 되고 그 감지되었을 때 차량차단막기 올라가게 됩니다. 차량차단막이 내려져 있을 때는 Red LED에 불이 들어와 있는 상태이고 차단막이 올라가면 Green LED에 불이 들어오고 차량이 통과된다는 설정입니다.

2. 아두이노 주차장 출입구 차량차단장치 회로도


  • 준비물 : 적색 LED 1개, 녹색 LED 1개, 저항 220옴 2개, 초음파센서, Servo모터, 아두이노우노
  • 내용 : 8,9번핀은 LED에 연결하고 초음파센서핀은 7번, Servo모터은 6번 핀에 연결하시오.

실제 초음파센서는 4핀이지만 가상시뮬레이터에서는 3핀 초음파센서로 7번핀으로 Trig, Echo 역활을 수행합니다.


회로도는 복잡하지 않죠. 위 회로도는 각 역활은 아래 그림으로 이해하시면 되겠습니다.


3. 코딩



사전학습으로 Servo모터와 초음파센서에 대해서 한번 읽고 오세요. 사용하실 수 있는 분들은 그냥 넘어가셔도 됩니다.

[설계]

  • 초기신호등은 적색LED에 불이 들어온 상태가 되면 차량 정지를 의미한다.
  • 초음파센서로 근접거리가 일정거리이하가 되면 차량감지로 간주한다.
  • 차량감지가 되면 차량차단막이 올라간다는 느낌으로 Servo모터를 90도 회전한다.
  • Servo모터가 90도 회전이 되었다가 간주한 시점에 "적색->녹색" 등으로 변환시킨다.
  • 차량은 통과하는데 통과가 완료되었다고 생각되는 감지 시간을 기준으로 차단만은 일정시간 유지한다.
  • 마지막 차량 감지된 시간을 기준으로 일정시간 차단막이 올라가 있다가 차량이 통과했다고 생각 되는 시간에 차단막이 내려온다는 느낌으로 Servo모터를 0도로 회전시킨다.
  • 0도 회전을 할 때 "녹색->적색"등으로 변환시킨다.(정지)

차량감지

거리를 측정은 위 초음파센서 post의 거리 계산를 함수로 거리값을 구하게 됩니다.

int CalDistance (int Pin){  //초음파센서(3핀) 예제를 그대로 외부함수로 빼냄
  pinMode(Pin,OUTPUT); //출력모드로  사용
  digitalWrite(Pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(Pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(Pin,LOW);   
  
  pinMode(Pin,INPUT);    //입력모드로 사용
  int duration = pulseIn(Pin, HIGH);  
  int distance = duration / 57.5;  //가상시뮬레이션의 오차율을 줄이기 위해 이걸로 테스트 함.
  return distance; //거리값 리턴
}

거리를 구하게 되면 차량이 감지판정은 if문으로 만든다.

if(m_distance<50){ //50cm미터 미만일 때 차량 감지로 간주             
  차량감지 후 동작;
}

차량이 감지 후 동작은

  if(m_distance<50){   //50cm미만일 때 차단막 올리기 동작       
    if(state==false){  //초기값은 state=false상태로 처음에는 이 if조건문이 무조건 참이 된다.
      servo.write(90); //감지 되었기 때문에 Servo모터 90도 회전
      delay(2000);   //2초 동안 강제로 Servo모터가 회전하도록 설정
      state=true;     //다시 이 if조건문을 수행할 필요가 없기 때문에 state=true 설정     
      digitalWrite(rPin, LOW); //차단막이 올라갔으니 적색등 끄기
      digitalWrite(gPin, HIGH); //차량 통행하도록 녹색등 켜기
    }
    timeVal=millis(); //마지막 차량 감지된 시간을 저장함
  }  
  if(state==true){ //차단막 내리기 동작
    if(millis()-timeVal>=2000){ //마지막 차량 감지된 시간을 기준으로 차단막 유지시간이 지났는가 체크
      servo.write(0); //차단막 내리기
      state=false; //차단막이 내려졌으니 이 if조건문을 수행할 필요가 없기 때문에 state=fasle 설정
      digitalWrite(gPin, LOW); //차단막이 내려오니깐 녹색등은 끄기
      digitalWrite(rPin, HIGH); //차량 정지하도록 적색등 켜기
    }    
  } 

주석을 일일히 다 달아놓았으니깐 주석을 살펴보시기 바랍니다. 왜! if문을 state의 상태값으로 두가진 차단막 올리기/내리기 동작을 구분하였을 까요. 두 동작을 구분을 지어서 동작하게 하기 위해서요. 자세히 보시면 만약 차단막이 올라간 상태에서 계속 현재 차량이 감지되거나 계속 새로운 차량이 들어올 경우는 계속 차단막 올리는 명령을 내려야 합니다. 이미 올라갔는데 구지 반복 명령을 내릴 필요는 없이 현상태만 유지하면 됩니다. 그래서 if문으로 올라갔으면 내려가는 상황만 체크하면 되지 올라가는 상황을 동작할 필요없는 없습니다. 여기서, timeVal은 올라가는 상황 코딩 안에 다 넣지 않고 밖에다 빼낸 이유는 계속 초음파센서로 감지했을 때 마지막으로 감지된 시간을 기준으로 차단막의 유지시간을 결정하게 하기 위해서 입니다.

종합해보면,

#include <Servo.h>

Servo servo;
const byte servoPin = 6;
const byte pingPin = 7;
const byte gPin = 8;
const byte rPin = 9;
int m_distance=0;
boolean state = false;
unsigned long timeVal=0;

void setup()
{
  pinMode(gPin, OUTPUT);
  pinMode(rPin, OUTPUT);  

  servo.attach(servoPin); 
  servo.write(0);
  delay(1000);
  
  digitalWrite(rPin, HIGH);
}

void loop()
{
  m_distance=CalDistance(7);  //초음파센서로 거리계산함수  
  if(m_distance<50){            
    if(state==false){
      servo.write(90);
      delay(2000); 
      state=true;          
      digitalWrite(rPin, LOW);
      digitalWrite(gPin, HIGH);
    }
    timeVal=millis();
  }  
  if(state==true){
    if(millis()-timeVal>=2000){
      servo.write(0);
      state=false;
      digitalWrite(gPin, LOW);
      digitalWrite(rPin, HIGH);
    }    
  }  
}

int CalDistance (int Pin){  //초음파센서(3핀) 예제를 그대로 외부함수로 빼냄
  pinMode(Pin,OUTPUT); //출력모드로  사용
  digitalWrite(Pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(Pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(Pin,LOW);   
  
  pinMode(Pin,INPUT);    //입력모드로 사용
  int duration = pulseIn(Pin, HIGH);  
  int distance = duration / 57.5;  //가상시뮬레이션의 오차율을 줄이기 위해 이걸로 테스트 함.
  return distance; //거리값 리턴
}

loop()함수 내 로직만 설계에 맞게 코딩만 하면 오늘 코딩의 전부입니다. loop()함수 내 코딩은 몇줄 안되지만 설계의 내용을 다 포함되어 있네요. 글보다 코딩이 더 짧은 케이스네요.

4. 결과


가상시뮬레이터에서 결과입니다.


5. 실제 테스트


정상적으로 동작하는지 실제로 구현해 봤네요.

1) 아두이노 주차장 출입구 차량차단장치 회로도


4핀짜리 초음파센서로 대충 실제 제작 모습과 동일하게 디자인 했네요.


위 회로도를 실제 배치하고 선을 연결하니깐 아래와 같은 모습으로 좀 지져분하게 되었습니다.


2) 코딩



위의 가상시뮬레이터 코딩에서 newPing 라이브러리를 사용해서 그부분만 수정했습니다. 그리고 초음파센서가 4핀이니 trig, echo 핀을 정확하게 지정해 줘야 합니다.

코딩은 가상시뮬레이터 소스에서 newPing 라이브러리를 이용한 방식과 4핀 초음파센서 부분만 수정하면 됩니다.
그리고 주의할 점은 실제 초음파센서에서는 거리 측정값이 오류가 발생합니다. 이 오류를 해결하기 위해서 초음파센서값에 대한 평균값을 구해서 오류값을 막을 수 도 있지만 간단하게 오류값을 무시하는 방식을 취했습니다.

  if(m_distance>오류값 && m_distance<감지거리){       
      동작;
  }

제가 쓰는 초음파센서는 가끔 0과 10이하의 값들이 찍히네요. 그래서 실험에서는 10이상의 값들만 거리 판정을 내렸네요. 만약 결과가 정상적으로 나오지 않는다면 거리측정값을 시리얼통신을 통해 시리얼모니터로 그 결과를 찍어보시고 여러분들이 원하는 값으로 수정해 주시면 됩니다.

수정해보면,

#include <NewPing.h>
#include <Servo.h>

Servo servo;
const byte servoPin = 7; 
const byte gPin = 8;
const byte rPin = 9;
int m_distance=0; //거리
boolean state = false; //차단막 제어를 위한 상태값
unsigned long timeVal=0;

NewPing sonar(6, 5, 200); // (Trig, Echo. 거리제한)

void setup() {
  
  Serial.begin(9600);
  pinMode(gPin, OUTPUT);
  pinMode(rPin, OUTPUT);  

  servo.attach(servoPin); 
  servo.write(0);
  delay(1000);
  
  digitalWrite(rPin, HIGH);
}

void loop() {  
  delay(50);            
  m_distance=sonar.ping_cm();  //초음파센서로 거리계산함수  
  Serial.println(m_distance);
  if(m_distance>10 && m_distance<20){  //20cm는 의미가 있는 것은 아닙니다.
    if(state==false){
      servo.write(90);
      delay(2000); 
      state=true;          
      digitalWrite(rPin, LOW);
      digitalWrite(gPin, HIGH);      
    }
    timeVal=millis();
  }  
  if(state==true){
    if(millis()-timeVal>=3000){
      servo.write(0);
      state=false;
      digitalWrite(gPin, LOW);
      digitalWrite(rPin, HIGH);
    }    
  } 
}

3) 결과


10cm이하는 차량감지로 판정을 하지 않습니다. 그리고 20cm미만일 때 차량감지 파전을 내렸네요. 스마트폰으로 촬영하기 위해서 거리값을 최대한 줄여서 실험 했네요.


마무리


최근에 계속 아두이노적 시각으로 주변환경을 관찰하면서 하나씩 post 주제를 선정하고 있네요. 지하주차장에 차량차단막이 되어 있는 곳을 들어 갈 때 차량을 감지하고 차단막이 올라가면서 옆에 신호등이 "적색->녹색" 등으로 바뀌는 상황을 보면서 "아! 이걸 주제로 표현해봐야지!"하고 이렇게 실험하게 되었네요.

여러분들도 한번 주변환경을 관찰하고 저처럼 실험을 해보세요.


댓글()

[아두이노] 스위치버턴+Servo Motor 제어

IOT/아두이노|2019. 4. 21. 09:00

[아두이노] 스위치버턴+Servo Motor 제어



어제 조이스틱으로 Servo Motor를 실험을 했는데 포스트를 본신 분들이 그냥 글을 보는거로만 끝내는 것이 좀 그런 것 같아서 조이스틱의 느낌을 스위치 버턴으로 최대한 살려서 비슷하게 가상시뮬레이터에서 테스트를 해 볼 수 있도록 변경을 해 봤습니다. 원리는 동일하고 스위치 버턴 2개로 조이스틱의 좌/우측의 회전 값으로 표현을 했고 스위치 버턴 1개로 조이스틱의 스위치 버턴으로 표현을 해서 총 3개의 스위치 버턴으로 조이스틱의 느낌을 담아 회로도와 코딩을 수정했네요. 위에 공개회로도를 클릭하시면 바로 가상시뮬레이터를 실행 할 수 있는 창이 나오니깐 가상시뮬레이터로 직접 스위치를 눌러 Servo Motor를 회전 시켜보세요. 대충 어떤 느낌으로 회전이 이루어지는지 체험을 하실 수 있습니다. 이제 본격으로 어떻게 표현 했는지 살펴보도록 하죠.

1. 회로도 구성


  • 준비물 : 스위치 버턴 3개, Red LED 1개, 저항 220옴 1개, Servo Motor 1개, 아두이노우노
  • 내용 : Servo Motor핀은 7번에 연결, 스위치 버턴은 3,4,5 번핀에 연결, Red LED은 12번 핀으로 연결


어제 포스팅한 조이스틱 + Servo Motor 회로에서 조이스틱 부분만 스위치 3개로 대신 표현한 회로도 입니다. 나머지는 동일하고 스위치 버턴 3개만 변경 했네요.

3. 코딩


  • 사용함수 : pinMode(), digitalRead(), delay(), servo.attach(), servo.write(), map()
  • 내용 : 스위치 버턴 2개로 Servo Motor를 회전 시키고 나머지 한개로 Red LED를 제어 깜박이게 해보자.

함수

  • pinMode(사용핀, 입/출력모드) : 해당 사용핀을 어떤 모드로 사용할지 결정한다. INPUT_PULLUP모드로 설정(내부저항을 사용할 예정)
  • digitalRead(디지털핀) : 디지털 신호를 읽음(0 or 1)
  • digitalWrite(출력핀, HIGH/LOW) : 출력핀에 HIGH(5V) or LOW(0)를 출력
  • delay(시간값) : 시간값만큼 대기한다.

Servo Motor 함수

#include <Servo.h> 
  • Servo servo : 서보모터 객체 선언
  • servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
  • servo.write(회전각) : 회전각 만큰 서보모터를 회전 시킴

[ 조이스틱 소스 ]

#include <Servo.h> 

Servo servo;

const int AXIS_X = A0;
const int AXIS_Y = A1;
const int SW_P = 3; 

const int servoPin = 7;
const int redLed = 12;

int m_Angle = 0;
 
void setup() {
  servo.attach(servoPin);
  pinMode(redLed, OUTPUT);
  pinMode(SW_P,INPUT_PULLUP);
}
 
void loop() {
  m_Angle = map(analogRead(AXIS_X),0,1023,0,180);
  digitalWrite(redLed,digitalRead(SW_P));
  servo.write(m_Angle); 
  delay(100); 
  
}

변경 후, 어떻게 코딩이 되었는지 볼까요.

[스위치 버턴 소스]

#include <Servo.h> 

Servo servo;

const int AXIS_X1 = 5;
const int AXIS_X2 = 4;
const int SW_P = 3; 

const int servoPin = 7;
const int redLed = 12;

int m_Angle = 0;

void setup()
{
  servo.attach(servoPin);
  pinMode(redLed, OUTPUT);
  pinMode(AXIS_X1, INPUT_PULLUP);
  pinMode(AXIS_X2, INPUT_PULLUP);
  pinMode(SW_P, INPUT_PULLUP);
}

void loop()
{    
  if(digitalRead(AXIS_X1) == 0){  
    if(m_Angle<180){
      m_Angle=m_Angle+10; 
    }          
  }
  else if(digitalRead(AXIS_X2) == 0){ 
    if(m_Angle>0){
      m_Angle=m_Angle-10; 
    }      
  }
  else{
    digitalWrite(redLed,digitalRead(SW_P));
  }
  servo.write(m_Angle);
  delay(100);
}

pinMode()함수로 두개의 스위치 버턴의 모드를 풀업저항모드로 설정을 했고 loop()함수 내부의 코드가 좀 바뀌었네요.

조이스틱보다 코딩이 좀 길어졌죠. if~else if 문을 사용했습니다. AXIS_X1과 AXIS_X2 로 두개의 변수를 설정해서 방향 스위치를 만들었습니다. AXIS_X1 방향이 정방향이면 AXIS_X2은 역방향이 됩니다.

이제 회전 시킬려면 스위치를 눌렀을 때 상황을 만들어 줘야 합니다. 정방향 스위치를 눌렀는지 역방향 스위치를 눌렀는지를 IF문으로 표현 하면 됩니다. 참고로 내부풀업저항을 이용 했기 때문에 초기 상태는 '1'의 상태입니다. 여기서 스위치를 누르면 '0'의 상태가 됨으로 스위치가 눌렀을 때 '0'인가 라고 IF문에서 체크하면 됩니다.

다음과 같은 코딩으로 표현이 되겠죠.

if(digitalRead(AXIS_X1) == 0) {
    정방향 10도 회전;
}
else if(digitalRead(AXIS_X2) == 0){  
    역방향 10도 회전;
}

이제 두 개의 방향스위치로 해당 스위치를 누르면 10도씩 회전하게 만들려면 m_Angle(각) 변수를 하나 만들어 놓고 이 변수값을 10씩 변화를 주면 됩니다.

  • 정방향 => m_Angle=m_Angle+10;
  • 역방향 => m_Angle=m_Angle-10;

간단하게 이 표현으로 스위치를 눌렀을 때 정방향은 +10이 되고 역방향은 -10이 되도록 위 문장을 코딩을 하면 간단하게 회전 시킬 각을 만들 수 있습니다.

그런데 Servo Motor은 0~180도 회전만 할 수 있습니다. 아무리 스위치 버턴을 눌러도 180도 이상 증가하면 안되고 0도이하로 감소해도 안됩니다.

if(m_Angle<180)  m_Angle=m_Angle+10; 

if(m_Angle>0)  m_Angle=m_Angle-10; 

이렇게 if문으로 각에 대해 제한을 두면 아무리 스위치를 눌러도 180이상 0이하로 m_Angle 값은 더이상 변하지 않게 됩니다.

종합해보면,

  if(digitalRead(AXIS_X1) == 0){  
    if(m_Angle<180) m_Angle=m_Angle+10; 
  }
  else if(digitalRead(AXIS_X2) == 0){ 
    if(m_Angle>0) m_Angle=m_Angle-10;     
  }
    else{
    digitalWrite(redLed,digitalRead(SW_P));
  }

이렇게 방향 스위치를 누르지 않았다면 else 문을 수행하는데 SW_P핀의 스위치가 상태값에 따른 redLed핀의 출력을 만들어 내면 마무리가 됩니다. 그냥 else에서 SW_P핀의 스위치가 눌러졌는지 체크하고 그냥 넘어가는 표현이라고 생각하시면 될 듯 싶네요.
이 부분을 else if()문으로 눌러졌는지 물어 보는 코딩을 해도 되지만 그냥 스위치 상태값으로 LED를 상태를 결정하기 때문에 묻는 조건식은 생략 했네요.

4. 결과


가상시뮬레이터 결과 영상입니다.


이것도 실제로 표현을 해서 어떻게 돌아가는지 돌려봤네요.


마무리


어제는 조이스틱으로 Servo Motor를 제어 했고 오늘은 스위치 버턴으로 같은 느낌으로 Servo Motor를 제어를 해 보았습니다. 뭔가를 표현학 그게 동작하는 모습을 봐야 어떤 느낌인지 이해가 빠를 꺼에요. 원래 Servo Motor 회전 제어를 예전 포스트에서 리모콘으로 조정했던 포스트였습니다. 아마도 Bluetooth 통신 포스트를 할 때도 LED나 Servo Motor를 가지고 제어를 하지 않을까 싶네요. 가장 시각적으로 표현하기 쉬운 부품이니깐요.

아무튼 어제 배운 조이스틱의 내용이 중요하니깐 꼭 조이스틱의 동작 원리를 이해하셨으면 합니다. 그리고 오늘 다룬 내용은 같은 의미로 스위치 버턴이 대신한 응용편으로 복습이라고 생각하시면 될 듯 싶네요.

여러분들도 다른 것을 이용하여 비슷한 동작을 수행하시고 싶으시다면 어떤 부품으로 비슷한 느낌을 표현할 수 있을지 상상의 나래를 펼쳐 보세요.


댓글()

[아두이노] 조이스틱 + Servo Motor 제어

IOT/아두이노|2019. 4. 20. 09:00

[아두이노] 조이스틱 + Servo Motor 제어



조이스틱으로 뭔가를 조정하는 응용 예제로 소개할 만한게 없나 고민하다가 지금까지 포스트 한 것 중에서 찾다가 조정 느낌이 느낄 수 있는 LED로 실험 했다가 그냥 포스트에 올리는 걸 포기했네요. 다른 부품으로 찾다가 Servo Motor를 제어를 해보는게 재밌 소재가 될 것 같고 따로 코딩하는 부분도 별로 없을 것 같아 실험 주제로 결정 했네요. 코딩도 아주 초 간단하게 원리만 표현 했고 지난 시간에 조이스틱 조정기 값을 읽는 것을 배웠으니깐 이제는 그 값을 기준으로 Servo Motor의 Angle을 정하면 되니깐 별로 어렵지 않고 재밌는 포스팅이 될 것 같네요.

자! 그러면 Servo Motor를 조이스틱으로 제어를 해 봅시다.

1. 회로도 구성


  • 준비물 : 조이스틱 1개, Servo Motor 1개, Red LED 1개, 저항 220옴 1개, 아두이노우노
  • 내용 : A0, A1 핀은 x,y 방향 아날로그 신호를 받고 5번핀은 스위치 신호를 받도록 선은 연결하고 7번핀에 Servo Motor 출력핀으로 연결하고 12번핀은 Red LED의 출력으로 사용한다.

[ Fritzing 디자인 ]


회로도를 보시면 좀 복잡해 보일 수 있는데, 조이스틱에서 방향 신호(VRX, VRY)와 스위치 신호(SW) 선만 아날로그 핀과 디지털 핀에 연결 하고, Servo Motor핀과 LED 핀들은 사용하고자 하는 디지털 핀을 선택해서 연결하시면 됩니다. 나머지는 다 전원에 관련된 핀이라 회로도 그림만 좀 복잡해 보이고 선 연결은 어렵지 않습니다.

2. 코딩



함수

  • pinMode(사용핀, 입/출력모드) : 해당 사용핀을 어떤 모드로 사용할지 결정한다. INPUT_PULLUP모드로 설정(내부저항을 사용할 예정)
  • analogRead(아날로그핀) : 아날로그 신호를 읽음(0~1023)
  • digitalRead(디지털핀) : 디지털 신호를 읽음(0 or 1)
  • digitalWrite(출력핀, HIGH/LOW) : 출력핀에 HIGH(5V) or LOW(0)를 출력
  • delay(시간값) : 시간값만큼 대기한다.
  • map(입력값,입력최소,입력최대,출력최소,출력최대) : 입력값이 입력 최소~최대범위가 출력 최소~최대에 매칭되어 출력

Servo Motor 함수

#include <Servo.h> 
  • Servo servo : 서보모터 객체 선언
  • servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
  • servo.write(회전각) : 회전각 만큼 서보모터를 회전 시킴

설계

조이스틱의 어떤 값으로 Servo Motor를 제어 해야 할까요. X 방향, Y 방향, 스위치 버턴 이 세가지 신호 중에 어떤 것을 사용할까요. 여기서, X, Y 값 중에 하나를 선택하면 됩니다. Servo Motor 한개가 하나의 축 방향이라고 생각하면서 접근하시면 됩니다.

간단히, 저는 X 방향 아날로그 신호로 Servo Motor를 제어 해볼까 합니다.

그러면 조정값을 어떻게 Servo Motor랑 매칭해야 할까요. 한번 생각을 해보세요.

조이스틱이 처음 어떤 상태로 어떤 값을 초기값으로 되어 있나요. 조이스틱의 현재 상태와 현재 신호를 곰곰히 생각 해보시면 그 안에 답이 있습니다. 바로, 조이스틱은 방향 아날로그 신호값이 중앙값으로 처음 시작 합니다. 즉, 0~1023의 아날로그 신호에서 512이라는 중심값이 초기 상태로 아날로그 신호로 시작 합니다.

이때 왼쪽은 0~512 사이가 되고 오른쪽은 512~1023 사이의 값으로 좌우 방향을 나타낼 수 있습니다. 이 원리를 이용하시면 쉽게 해결 됩니다. 512를 기점으로 좌우 신호 값으로 Angle를 표현하면 되니깐요. 여기서 0~1023의 아날로그 신호값에서 0도가 0의 값이 되고 180도는 1023이 되게 하면 중앙값 512은 90도가 되고 이 개념을 가지고 코딩을 하면 됩니다.

그렇게 하면, 처음 시작은 중앙값 512로 Servo Motor은 90도에서 시작하게 되고 아날로그 신호가 0에 가까울 수록 0도에 가까워지고 아날로그 신호가 1023에 가까울 수록 180도에 가까워지겠죠.

여기서, 문제점은 0을 0도로 1023을 180도로 어떻게 표현 할까요. map()함수를 이용하면 됩니다. 은근히 자주 사용하는 함수인데 여기에서도 사용 하네요.

m_Angle = map(analogRead(AXIS_X),0,1023,0,180);

AXIS_X핀의 아날로그 신호값이 0~1023 범위에서 출력 0~180범위에 어느 정도의 위치가 되는지 알아서 해당 값을 찾아주는 함수입니다. 만약, 512값이 입력신호로 들어오면 출력 0~180 범위에서 90이라는 값의 위치하니깐 90이 반환되어 나옵니다. 즉, 입력신호값을 0~180사이의 값으로 자동으로 변환시켜주는 함수인 셈이죠.

이 한줄로 조이스틱의 X방향의 아날로그 신호를 각도로 만들어 낼 수 있겠죠.

 servo.write(m_Angle); 
 delay(100); 

아날로그 신호 512일 때, Servo.write(90)함수로 90도를 Servo Motor가 회전하게 됩니다. delay()함수는 Servo Motor가 충분이 회전할 시간값을 줘야 하기때문에 같이 코딩 해야 합니다. 100은 0.1초인데 더 짧게 하시면 좋겠죠. 그냥 이전 포스트 소스를 가져다가 표현한거라 딜레이 시간은 그냥 뒀네요. 원래 저 값은 특정한 각도로 회전 한뒤에 다시 다른 특정한 각도로 회전할 때까지의 충분한 시간 값인데 조이스틱 조정기에서는 이렇게 큰 시간값은 필요 없습니다. 각도 값 범위가 변화율은 크지 않기 때문에 짧게 시간을 주면 됩니다. 100은 아주 큰 값이지만 조정기로 해본 결과 그냥 사용해도 제 실험에서는 상관 없어서 그냥 뒀네요.

결론은 조이스틱의 X방향 신호값을 읽어와서 map()함수로 각도를 만들어 내고 servo.write()로 각도를 출력하는 코딩으로 딱 두줄이면 조이스틱으로 Servo Motor를 움직이게 할 수 있게 됩니다.

그러면, 전체 소스를 살펴봅시다.

[ 소스 ]

#include <Servo.h> 

Servo servo;

const int AXIS_X = A0;
const int AXIS_Y = A1;
const int SW_P = 3; 

const int servoPin = 7;
const int redLed = 12;

int m_Angle = 0;
 
void setup() {
  servo.attach(servoPin);
  pinMode(redLed, OUTPUT);
  pinMode(SW_P,INPUT_PULLUP);
}
 
void loop() {
  m_Angle = map(analogRead(AXIS_X),0,1023,0,180);
  digitalWrite(redLed,digitalRead(SW_P));
  servo.write(m_Angle); 
  delay(100);   
}

변수가 코딩의 절반을 차지 하네요. 정작 로직 코딩은 몇 줄 안되는 데 말이죠.

  digitalWrite(redLed,digitalRead(SW_P));

서비스 코딩으로 스위치 버턴을 누르면 12번에 연결된 Red LED가 꺼지도록 했네요. 조이스틱 스위치 버턴은 내부풀업저항을 이용하기 때문에 초기값은 '1'이 됩니다. 그래서 전원이 공급되면 Red LED에 불이 들어오게 됩니다. 그리고 스위치를 누르면 '0'의 상태가 되어서 Red LED는 불이 꺼지게 됩니다.

4. 결과




5. 추가 내용


위 실험 코딩은 그냥 조정기의 0~1023값을 단적으로 0~180도로 나눠서 조정한 거라서 실제로 뭔가 조정하거나 정밀 제어 할 때는 사용하지 않는 코딩입니다. 단순히 조정기를 움직일 때 그 움직임 값으로 Servo Motor를 움직이게 한 것 뿐이니깐요.

원래는 조정기에 대한 코딩을 제대로 하실려면은 조정기를 움직일 때에 채터링 문제 또는 조정값의 범위라던가 조정기의 수치에 따른 회전 속도라든가 고려해야할 것이 산더미로 많습니다. 거기에 조종기의 중심값의 보정 작업도 추가 해야 합니다.

이런것들을 다 코딩하면 회전 하나의 동작을 하기 위해서 수십 줄의 코딩으로 늘어나게 됩니다. 배보다 배꼽이 더 커지겠죠. 여기서 배워야할 것은 Servo Motor를 조이스틱으로 회전할 수 있는 기초 원리를 배우는게 목적이기 때문에 간단하게 원리만 전달하는 코딩만 한 것이니깐 여러분들이 실제로 뭔가를 제어할려면 많은 부분을 생각하고 코딩 하셔야 합니다.

그리고, 여러분들이 이 기초를 이해 하셨다면 조정기에 필요한 부분이 뭐가 있을지 생각하고 여러가지 상황들을 제시하고 그 문제를 해결하면서 코딩을 늘려가셨으면 합니다.

마무리


조이스틱으로 Servo Motor를 제어할 수 있게 되었네요. 한개의 Servo Motor를 제어 했지만 여기서 Y방향 값도 같이 적용을 하여 Motor 두개를 사용한다면 어떻게 될까요. x와 y방향으로 평면의 좌표로 이동할 수 있게 됩니다. 3D 프린터나 평면 그림을 그리는 프린트기 같은 걸 만들 수 있겠죠. 여기에 사용되는 Motor는 Stepper Motor로 360도를 회전하는 것을 사용합니다. 어찌 되었든 조정기로 뭔가를 움직이는 물체를 제작 가능해 지겠죠.

마지막으로, 뭔가 로봇 같은 관절 제어 쪽으로 상상력을 발휘해 보세요. 재밌는 상상력들을 많이 해 보셨으면 합니다.

예를들어, 로봇 손가락을 상상해 보세요. 자신의 손을 쫙 펴보세요. 손가락 하나에는 3마디로 구성 되어 있잖아요. 이 각 마디와 마디 사이에는 관절이 있습니다. 이 3개의 관절을 Servo Motor가 대신 한다고 상상을 해보세요. 손을 다시 주먹을 쥐어보세요. 한마디가 구부러질 때 몇도의 각도가 되나요. 각 관절이 90도에 가깝게 꺽기게 됩니다. 그러면 Servo모터로 3개의 관절을 동일하게 angle을 0도에서 서서히 90도에 가깝게 일정한 속도로 회전 시키면 어떻게 될까요. 주먹을 쥐는 형태로 자연스럽게 구브러 지겠죠. 다시 펼 때는 90도에서 서서히 0도로 3개의 Servo모터를 회전 시키면 어떻게 될까요 서서히 손가락을 펴는 모습이 되겠죠.
직접 자신의 손을 주먹을 쥐었다 폈다를 반복하면서 머리속에서 손가락 관절의 각도를 그려 보세요. 그 원리를 깨우치게 되면 로봇 손가락 관절 제어를 할 수 있게 됩니다.

일상의 모습을 보고 우리는 그걸 코딩화 할 수 있습니다. 일상의 모습을 코딩화 하기 위해서는 끊임없이 상상력을 끌어 올려야 하고요. 계속 자신에게 어떤 것을 배우고 다룰 수 있게 되면 다음에 뭘 배우지 보다는 이것을 이용하여 뭘 할 수 있지 하고 2개 이상의 응용할 수 있는 상상력 훈련을 하셨으면 합니다.


댓글()

[아두이노] 서보모터 제어

IOT/아두이노|2019. 2. 19. 09:18

[아두이노] 서보모터 제어



이전 시간까지는 칩과 관련해서 제어를 하다보니 복잡한 주제를 포스팅 했었습니다. 이번에는 좀 쉬운 주제를 다루고자 합니다. 기본 서보모터 동작 제어만 다루기 때문에 별거 아니라고 생각 할 수 있지만 서보모터 제어는 나중에 아두이노로 작품을 만들때 가장 많이 사용하는 부품입니다. 그리고 사용 목적에 따라서 코딩 능력을 필요로 하는 부분이기도 하기 때문에 사용목적에 따라 좀 어려울 질 수 있는 부품이기도 합니다. 하지만 오늘 포스팅은 단순한 제어를 통해 서보모터를 체엄하도록 하죠.

1. 서보모터



서보모터는 Vcc(5V), Gnd, Signal(입력신호)로 3핀으로 구성되어 있습니다. 아두이노에서 Signal핀에 신호를 보냄으로써 각도를 제어하게 됩니다.

2. 회로도 구성


  • 준비물 : 서보모터 1개, 아두이노우노
  • 내용 : 서보모터 회전 시키자


가장 간단한 회로도 입니다. Vcc은 5V에 Gnd은 Gnd에 Signal은 9핀에 연결하면 완료입니다. 원래 모터제어를 할때는 모터쉴드보드가 필요합니다. 하지만 서보모터의 경우는 아두이노에서 직접 제어해도 됩니다.

3. 코딩


  • 사용함수 : attach(사용핀), write(각도), writeMicroseconds(각도시간값)
  • 내용 : 간단히 180도 회전만 시키자.

설계

(1) 서보모터객체 선언

서보모터를 사용하기 위해서는 제어함수들이 있는 Servo클래스를 객체로 선언합니다. 이말은 예전에 변수 선언과 같은 의미로 생각하시면 됩니다. Servo 클래스를 하나의 변수에 저장되었다고 생각하시면 됩니다.

#include <Servo.h> 
Servo servo1; 

Servo.h 파일에 있는 Servo 클래스를 servo1이라는 객체변수로 선언합니다. 그래서 servo1 객체를 통해서 Servo 클래스 안에 함수들을 사용하게 됩니다. 쉽게말해서 servo1.함수() 이렇게 Servo클래스의 함수를 사용할 수 있게 됩니다.

(2) 서보모터객체 연결

servo1.attach(사용핀);
servo1.attach(사용핀,min,max); //Ardunio min = 544, max=2400

attach()함수는 해당핀을 서보모터를 제어하는 핀으로 사용하겠다는 의미로 받아 들이시면 됩니다. 여기서, 두가지 방식이 있는데 첫번째, 사용핀만 인자로 넘겨주면 기본값으로 자동으로 세팅됩니다. 두번째, min, max로 범위를 따로 지정해 줄 수 있습니다. 두번째 방식이 좀 더 서보모터를 정교하게 제어할 수 있습니다.

과거 tinkercad.com 사이트로 옮기전 사이트에서는 서보모터가 다른 종류가 한개 더 있었는데 그 모터에서는 min, max값을 1000, 2000으로 줘서 1500일때 90도 회전이였습니다. 가상시뮬에터에서는 그냥 544, 2400으로 맞춰서 실험했네요. 서보모터마다 제어하는 각도가 차이가 있기 때문에 조정 작업이 필요합니다. 그래서 범위를 지정해서 하실때는 사용하시는 서보모터의 종류에 따라 다르니깐 꼭 보정을 하셔야 합니다.

(3) 서보모터객체 제어

servo1.write(180); 
servo1.writeMicroseconds(2400);

여기에서 write(각도)로 제어하시면 서보모터 기종에 따라서 약간 차이를 보이게 됩니다. 정확하게 제어가 안될 수 있습니다. 하지만 writeMicroseconds(2400)로 제어하시면 오차율을 줄일 수 있습니다. 하지만 보정을 해야하기 때문에 좀 귀찮은 점이 있지요.

(3) delay()함수 사용

delay(2000);

180도를 회전 시킬려고 하는데 일정 시간이 필요합니다.

만약에,

servo1.write(180); 
delay(500);
servo1.write(0); 
delay(500);

delay(500)으로 할 경우 0.5초동안 180도로 갈려고 회전하는 도중에 딜레이 시간이 끝나면 0도로 가는 명령라인이 실행됩니다. 그러면 180도 회전이 안된 상태에서 다시 0도로 회전되어 버리는 현상이 발생합니다. 그래서 각도별 delay()함수로 시간 조정이 필요합니다.

코딩을 하면

첫번째,

#include <Servo.h> 

Servo servo1; 
int servoPin1 = 9;

void setup() 
{ 
    servo1.attach(servoPin1); 
}

void loop() 
{
    servo1.write(180);     
    delay(2000);
    servo1.write(0);     
        delay(2000);
}

두번째,

#include <Servo.h> 
 
Servo servo1; 
int servoPin1 = 9;

void setup() 
{ 
    servo1.attach(servoPin2,544,2400); //Ardunio min = 544, max=2400
}


void loop() 
{
    servo1.writeMicroseconds(2400);
    delay(2000);
    servo1.writeMicroseconds(544);
    delay(2000);   
}

5. 결과




가장 빠르게 작업했네요.

마무리


그러면 서보모터로 제어할 수 있는게 뭐가 있을까요. 지금까지 다룬 부품중에 이거랑 같이 쓸 수 있는게 뭐가 있을지 곰곰히 생각해 보세요.

가령 초음파센서를 여기에다 붙이여 어떻게 될까요. 서보모터로 각도가 제어된다면 거기에 초음파센서가 부착된다면 초음파 센서를 회전 시킬 수 있다는 의미가 됩니다. 회전 각도에 따른 거리측정이 이루어지면 어떤 일이 할 수 있을가요. 가장 많이 알려진게 레이더 입니다. 즉, 각도별 거리를 측정해서 모니터에 초음파센서에 감지된 장애물을 표시 할 수 있습니다. 또 하나는 자율주행차 입니다. 주변의 장애물을 측정해서 미로 같은 곳을 주행하면서 혼자서 피해다니면서 주행을 할 수 있게 되겠죠. 또 한가지는 180도 각도의 거리를 측정하여 그 거리들을 일정한 간격으로 게속 누적 측정한다면 그 데이터로 3D Rendering도 수행이 가능합니다. 그외로 서보모터와 초음파센서가 결합하면 다양한 표현이 가능합니다.

그러면 서보모터만으로 제어할 수 있는 다른 것들은 뭐가 있을까요. 이 글을 읽으시는 당신의 가장 가까운 곳에 정답이 있습니다. 바로 당신이 하는 행동을 비슷하게 제어할 수 있습니다. "뭘까요!" 그것은 바로 관절입니다. 관절 꺽기가 된다는 것이죠. 당신의 손을 바라보세요. 그리고 둘째(집게) 손가락을 한번 구브렸다 펴보세요. 둘째 손가락은 3마디이고 그 중간 관절을 중심으로 각 마디가 구브러졌다 펴질 꺼에요. 바로 그게 회전입니다. 그 관절 부위를 서보모터로 제어한다고 생각하시면 됩니다. 손가락 하나를 제어하게 되면 손을 제어할 수 있게 됩니다. 그러면 한쪽 팔을 제어할 수 있게 되고 그러면 로보팔을 만드는데 이용하면 되겠죠. 보다 정교하게 제어하고 싶을때 360도 회전하면서 하고 싶다면 스템모터로 제어하면 됩니다.

기본 제어로 180도 회전이였지만 좀 더 전문적으로 제어하기 위해서는 현실에서 곤충의 보행을 관찰하면서 관절의 각도를 계산해서 4,6,8축 로못으로 보행을 시킬 수 있고 사람의 손 동작의 움직일때 각 마디의 각도를 관찰하여 각도를 계산하시면 로봇팔을 만들 수 있습니다.

마지막으로 본인의 움직임의 각도를 측정해서 그걸 아두이노로 서보모터로 회전시켜 비슷하게 표현 하는 것을 상상해 보세요. 참고로 여러개의 서보모터를 아두이노에서 제어가 가능하지만 서보모터의 전류를 아두이노에서 전부 공급하면 안됩니다. 가상시뮬레이터에서는 가능하지만 현실에서는 모터에게 전류를 순간 많이 공급하면 아두이노에 불안정 전류 공급이 이루어질 수 있기 때문에 안좋습니다. 외부전류 공급을 추천 드려며 많은 모터를 제어하고자 할때는 따로 여러모터를 한번에 제어할 수 있는 보드가 있는데 오래전에 본거라 보드 이름이 생각 안나네요. 한번 구글링 하셔서 여러 모터를 제어할 수 있는 보드를 찾아보세요.


댓글()