2016년 3월 19일 토요일

다이나믹셀 TTL 전원 케이블 작업

가끔 다이나믹셀 TTL 전원 케이블을 직접 만들어야 하는 경우가 있다.
이때는 다음 레퍼런스를 참고하여, power와 GND 선을 구분해 만든다.


참고로 TTL 선이 짧아서 다른 선과 연결해 붙일 때는, 엔드의 방향이 서로 엊갈리게 연결해야 함에 주의한다. 
OpenCM에 전원을 인가할 때는 다음 다이어그램을 참고한다. 


다이나믹셀 데이지 체인 결속할 때는 다음 다이어그램을 참고한다.

2016년 3월 16일 수요일

실내에서 드론 테스트 방법

넓은 드론 테스트 장소가 없는 곳에서 드론을 개발하기 위해서는 약간 머리를 써야 한다. 다음 영상은 실내에서 드론을 끈으로 묶어 두고, 제어 테스트를 하는 모습이다. 


2016년 3월 6일 일요일

아두이노 기반 6 DoF 로봇암 조립 방법 및 제어

오늘은 6축의 자유도를 가진 6 DoF (자유도) Robot Arm Kit 조립 및 개발 방법을 정리해 본다. 6축 자유도는 사람의 팔 구조와 매우 유사한 동작을 할 수 있다.


사용할 로봇암은 Oak Studio 6 DoF 이다. 여기에 사용된 MG 996R 서보 모터 제원은 다음과 같다.

Weight- 55g 
Dimension 40.7*19.7*42.9mm 
Stall torque 10kg/cm 
Operating speed 0.20sec/60degree(4.8v) 
Operating voltage 4.8-7.2V 
Temperature range 0_ 55  
Servo Plug: JR (Fits JR and Futaba)


이 모터는 토크가 그리 크지 않아, 큰 부하를 처리하지는 못한다. 이와 관련해, 입력 전압이 최대 7.2볼트라 토크에 한계가 있다. 그러므로, 가벼운 부하를 다루는 데 적합하다. 참고로, 큰 부하를 모터에 가하면, 아두이노 보드에 인터럽트가 발생하여, 모든 동작이 정지하게 된다.

이와 관련된 제작 방법은 다음과 같다.

1. 로봇암 조립

로봇암의 부품은 매우 많다. 그러므로, 관절에 해당하는 서보 모터 회전 각도 등을 고려해 순서대로 잘 생각한 후 결속한다.

특히, 브라켓(bracket)과 서보 모터를 결속은, 회전 각도가 잘못되어 있을 경우, 다시 결속된 것을 해체하고 회전 각도를 조정한 후, 다시 결속해야 하므로 주의한다. 

다음은 결속한 순서대로 표시한 그림이다. 






다음은 완성된 모습이다. 


이렇게 작업할 때 걸린 시간은 대략 3~4시간 정도이다. 우선 결속해야 할 브라켓, 나사와 너트가 너무 많다. 또한, 잘못 결속했을 때 몇번의 시행착오도 종종 발생한다.

사실 아직 마무리 못한 것이 있다. 바닥 회전판의 서보 모터 각도를 잘 못 맞추어 다시 나사 풀고 결속해야 한다. 실수할 때마다, 이웃 브라켓에 연결된 여덜개의 나사와 너트도 다시 풀고, 결속해야 하는 삽질을 계속해야 한다. 그러니, 잘 생각해서 작업하자.

머리 나쁘면 삽질이다

2. 로봇암 제어
이제 로봇암을 제어해 보도록 하자. 이 부분은 다음 레퍼런스를 참고하였다. 


우선 아두이노 보드 입력 핀과 각 서보모터의 신호, GND, VCC핀을 연결한다. 서보의 VCC는 7.2볼트 배터리에 연결한다. 참고로, 아두이노 5볼트에 연결하면, 전압이 약해, 제대로 동작하지 않는다. 

서보 신호 핀과 연결은 다음과 같다. 

바닥 서보: 3번 핀
어깨 서보: 5번 핀 
윗팔 서보: 6번 핀
손목 굽힘 서보: 9번 핀
손목 회전 서보: 10번 핀
손 그립(grip) 서보: 11번 핀

결선 후, 다음과 같이 코딩한다. 단, 각 관절 모터의 최대, 최소 각도는 조립전에 확인하여, 제한값을 두도록 한다. 그렇지 않으면, 프레임 간에 간섭이 생겨, 프레임이나 모터가 망가질 수 있다. 

#include <Servo.h>

#define NO_OF_FIELDS 6

Servo base, shoulder, foreArm, wristFlex, wristRotate, claw;

int inCommand[NO_OF_FIELDS];
int ledPin = 13;

void EchoData(int echoWord[])
{
 Serial.print("*");
 float total;
 for (int i = 0; i < NO_OF_FIELDS; i++)
 {
  Serial.print((int)echoWord[i]); 
  total+= inCommand[i];
  Serial.print(",");
 }
 Serial.print((int)total);
 Serial.print(",");
 Serial.print("*");
 Serial.print("&");
}

void ClearOldData(int oldData[])
{
 for (int i = 0; i < NO_OF_FIELDS; i++)
 {
  oldData[i] = 0;
 }
}

void setup()
{
 Serial. begin(38400);

 base.attach(3);
 shoulder.attach(5);
 foreArm.attach(6);
 wristFlex.attach(9);
 wristRotate.attach(10);
 claw.attach(11);

 //initialise all arms 
 base.write(20);      // 시계반대방향. 0 - 100, 20
 shoulder.write(30);  // 30 - 180, 30
 foreArm.write(10);   // 0 - 100, 10
 wristFlex.write(40); // 0 - 150, 40
 wristRotate.write(0);  // 0 - 180, 0
 claw.write(130);       // 90 - 140,  

 pinMode(ledPin, OUTPUT);
}

void demo()
{
 //initialise all arms
 for(int i = 20; i < 70; i += 1)
 {
   base.write(i);      // 시계반대방향. 0 - 100, 20
   delay(50);
 }
 for(int j = 70; j > 20; j -= 1)
 {
   base.write(j);      // 시계반대방향. 0 - 100, 20
   delay(50); 
 }

 for(int i = 30; i < 70; i += 1)
 {
   shoulder.write(i);  // 30 - 180, 30
   delay(50);
 }
 for(int j = 70; j > 30; j -= 1)
 {
   shoulder.write(j);  // 30 - 180, 30
   delay(50);  
 }

 foreArm.write(10);   // 0 - 100, 10
 wristFlex.write(40); // 0 - 150, 40
 wristRotate.write(0);  // 0 - 180, 0

 for(int i = 90; i < 150; i += 1)
 { 
   claw.write(i);       // 90 - 140,    
   delay(50);
 }
 for(int j = 150; j > 90; j -= 1)
 {
   claw.write(j);       // 90 - 140,    
   delay(50);
 }
}

void control()
{
 if(Serial.available())
 {
  for(int i = 0; i < NO_OF_FIELDS; i++)
  {
   inCommand[i] = Serial.parseFloat();
  }

  unsigned long time1 = millis();

  while (Serial.read() != -1)
  {
   //loops here until serial buffer becomes empty
   //Serial.println(millis() - time1);
   digitalWrite(ledPin, HIGH);
  }

  digitalWrite(ledPin,LOW); 

  //echo the incoming command sequence
  EchoData(inCommand);

  //now do something with the inCommand[] array.... 
  base.write(inCommand[0]);
  shoulder.write(inCommand[1]);
  foreArm.write(inCommand[2]);
  wristFlex.write(180 - inCommand[3]);
  wristRotate.write(inCommand[4]);
  claw.write(inCommand[5]);

  //clear the inCommand[] array
  ClearOldData(inCommand);
 }
}

void loop()
{  
  demo();
}

현재는 demo() 함수가 호출되고, 제대로 하였다면, 다음과 같이 로봇암이 제어되는 것을 확인할 수 있을 것이다. 



이제 시리얼 모니터(Serial monitor)에서 38400 bps로 속도를 맞춰 놓고, 6개의 각도에 대한 숫자를 다음과 같이 입력해 본다. 

20.0 30.0 10.0 40.0 0.0 130.0

제대로 입력되면, 정상적으로 로봇암이 해당 각도대로 제어될 것이다. 

3. 마무리
6 DoF 로봇암은 사람 팔과 자유도가 비슷한다. 그러므로, 로봇암을 마운팅한 로버 등을 이용하면, 탐사나 인스펙션 중에 장애물을 제거하거나, 스위치를 조작하거나, 개체를 획득하기 쉽다.

다만, 테스트해 본 결과로는 여기서 사용하는 6 DoF robot arm은 매우 가벼운 장애물만 제거하거나 옮길 수 있었다. 무게가 있는 장애물은 처리하기가 곤란하기 때문에, 이 경우에는 좀 더 높은 토크를 지원하는 서보를 사용해야 한다.

2016년 3월 5일 토요일

메카넘휠(mecanum wheel) 활용 로버

메카넘휠(mecanum wheel)을 사용하면, 본체의 방향이 고정된 상황에서 좌우상하 및 대각선 이동이 가능하다. 메카넘휠은 다음과 같이 생겼다.



참고로, 메카넘휠의 아이디어는 1973년 Bengt Ilon이란 스웨덴 개발자로 부터 왔다. 그는 스웨덴 mercanum AB 사의 엔지니어였다. US Navy는 Ilon의 특허를 구매하여, 1980년대에 Panama 시티에서 배로 물품을 이동하기 할 때 적용할 수 있도록 그 기술을 연구하였다. 이후 1997년에 Airtrax 사와 몇몇 회사들은 이 특허 기술을 US Navy로 부터 $2,500로 권리를 구매하였다. 현재는 물류 운송, 항공/군용/병원 캐리어 등 많은 분야에 응용되고 있다.

이런 메카넘휠을 이용해 3차원 이미지 스캔 시 안정적인 조사용 로버를 개발할 수 있다. 다음은 이를 간단한 테스트 영상이다.