블로그 이미지
래머
오늘도 열심히 개발하는 개발자입니다.

calendar

1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Notice

2015. 12. 11. 20:55 아두이노작품


위에 사진을 보시는 것처럼 2종의 배터리 전원공급부가 보입니다. 9V충전지는 아두이노 자체에 전력을 공급하기 위한 용도입니다. 충전건전지는 모터드라이버에 전원을 공급하기 위한 용도입니다. 9V전지와 건전지 4개는 Gnd를 공유하고 있습니다.


각 모터마다 필요로 하는 전류 및 전압의 크기가 있습니다. 모터의 정격전류 및 전압인데 충분히 공급되지 못하면 모터는 움직이지 않게 될겁니다.

보여주신 연결상으로 별다른 문제는 보이지 않는듯합니다. 제공한 소스또한 제가 직접 테스트를 해서 문제가 없다는 것을 확인했기 때문에 핀연결이 올바르게 되었다면 모터가 작동해야 하는게 정상입니다.


자세한 내용은 모르겠지만 9V건전지 만으로 전원을 공급하시는경우라면 전력부족으로 모터가 가동되지 않을 수 있습니다. 모터가 움직이기에 충분한 전력을 공급하셔야 합니다. 참고로 일반 알카라인 건전지의 경우 공급가능한 전류의 양이 너무적기 때문에 사용하시는 모터를 구동하기에 부족할 가능성이 높습니다.

충전지 계열을 이용하시는게 좋을겁니다.

아래는 소스를 업로드 했을때 밸런싱 로봇의 상태입니다.

보시는 것처럼 바로 작동이 되야 합니다. (댓글에도 달았지만, 기존에 제가 작성해둔 소스에서 LED컨트롤을 위해서 두개의 핀 9, 10번을 사용하고 있었습니다. 만드신 밸런스 로봇이 사용하는것과 중복되게핀을 사용중이라 제대로 작동이 안될 수 있을것 같은데, 일단 LED제어하는 부분을 비활성화한 소스를 업로드 해놨습니다. 해당 소스를 사용하시면될것 같습니다.)



'아두이노작품' 카테고리의 다른 글

밸런싱 로봇 소스 사용법  (15) 2015.12.07
아두이노 4륜구동  (0) 2015.05.23
밸런싱 로봇 만들어 보기  (105) 2015.05.09
충전팩 충방전기 만들어보기  (1) 2015.04.27
라인트레이서 만들어보기  (1) 2015.03.25
posted by 래머
2015. 12. 7. 20:54 아두이노작품

일단 여기에서 소스를 다운로드 합니다.

소스다운로드(Git)


압축을 푸시면 아래와 같은 내용물들이 보일겁니다.


Libraries폴더에는 아두이노용 라이브러리들이 들어 있습니다.


비주얼스튜디오용소스에는 비주얼스튜디어용 밸런싱 로봇소스가 있습니다.

아두이노스케치용소스에는 순수 아두이노 스케치용 소스가 들어 있습니다.


일단 Libraries폴더를 보면 아래와 같은 내용물들이 보입니다.



폴더가 몇개 보이는데요, 3개의 폴더들을 아두이노스케치 프로그램의 라이브러리 폴더에 복사해 넣어야 합니다.


아두이노 아이콘에서 오른버튼 클릭하시고 속성에 들어가시면 아두이노 스케치 설치위치로 이동할 수 있습니다.



폴더내용물을 보시면 아래에 보이는것처럼 libraries폴더가 보일겁니다. 저기 안에 밸런싱로봇 소스의 Libraries폴더안에 있던 3개 폴더를 복사해 넣으면 됩니다.



아래 그림처럼 3개의 폴더를 복사해 넣습니다.



이제 라이브러리 설치는 끝났습니다. 아두이노 스케치 프로그램을 실행중이라면 일단 종료를 해주세요.

밸런싱 로봇 소스로 다시 돌아와서 아두이노용 소스 폴더로 들어가보면

아래와 같은 파일들이 보일겁니다.



저기위에 표시해놓은 Blancer.ino파일을 아두이노 스케치 프로그램으로 열어주시면 됩니다.


소스를 보시면 다음과 같은 부분이 있을겁니다.


CDCMotorDriver는 모터드라이버를 제어하기위해 만든클래스 입니다. 생성자의 인자로 핀번호를 받고 있는데요


입력인자순서되로


모터1핀1, 모터1핀2, 모터2핀1, 모터2핀2, 모터1PWM핀, 모터2PWM핀

입니다.


이6개의 핀번호를 구성하신 회로와 맞게 수정하신다음에 아두이노에 업로드 하시고 사용하시면됩니다.

소스에 첨부된 설명.xlxs파일에보시면 간략한 설명이 포함되어 있습니다.

읽어 보시면될것 같습니다.

'아두이노작품' 카테고리의 다른 글

밸런싱로봇 전력공급  (0) 2015.12.11
아두이노 4륜구동  (0) 2015.05.23
밸런싱 로봇 만들어 보기  (105) 2015.05.09
충전팩 충방전기 만들어보기  (1) 2015.04.27
라인트레이서 만들어보기  (1) 2015.03.25
posted by 래머
2015. 5. 23. 23:52 아두이노작품
이번에는 4륜차를 만들어보기로 했습니다.
회피차나 라인트레이서같은 2륜 차들은 구조적인 문제 때문인지 직선주행이 잘안되는경향이 있습니다.
바닥의 기울기나, 보조바퀴의 방향에 따라서 주행방향이 틀어지는 문제가 있지요.
지자기 센서를 통해서 방향밸런스 조정을 시도해봤지만 지자기 센서의 오류로 인해서

그마저도 쉽지 않은듯합니다. 금속부근이나 자기장이 있는곳에서는 어김없이 측정에 오류가 발생하는것같습니다.


제어보드는 역시나 아두이노 나노를 사용했습니다. 4개의 모터를 제어하기위해서 2개의 l9110s모터 드라이버를 사용했습니다.

이모터 드라이버의 장점은 900mA까지 지속적인 전류 공급이 가능하고, 매우 작은 크기라는 것입니다.

사진상에는 3개의 l9110s모터 드라이버가 보이는데, 2개는 기어드 모터를 제어하는용도고, 나머지 빨간색한개는 나중에 워터펌프에 연결하려고 붙여뒀습니다.

아두이노 카페에 어떤분이 만든 사륜 물주기차를 보고 참 재미있어 보여서 저도 한번따라 해보기로 했습니다.

워터펌프는 아직 도착을 하지 않아서 나중에 붙여야 겠네요.

빨리 물좀 뿌려보고 싶네요.

선들이 매우 지저분하죠? 납땜하기 너무 귀찮아서 대충 점퍼선으로 덕지덕지 연결했습니다.모터드라이버제어선 연결하고나니 나노의 디지털핀이 거의다

소모되네요.


전체 모습입니다. 6v배터리 팩으로 모터의 전원을 공급하고, 아두이노에는 휴대폰 충전용 파워뱅크를 연결해서 전원을 공급합니다.

이번차체는 크기기 커서 그런지 공간이 충분하네요.


자체의 제어와 워터펌프의 제어는 휴대폰의 앱으로 하려고 합니다. 예전에 탱크만들면서 만들었던 제어앱을 모든 아두이노 작품들에 잘 우려먹고 있네요.

hc-06블루투스 모듈은 여기붙었다 저기 붙었다. 잘도 왔다갔다 하고 있습니다. 모듈이 하나 더있기는 한데, 블루투스 페어링하고 하는게 귀찮아서 그냥 한개를

돌려쓰고 있습니다.


차체의 메인프레임을 구성하는 하얀판때기는 ps판이라는 프라스틱판인데, 처음사용해보는 재질입니다.

비교적 견고하고, 가공도 잘되는 편이네요. 알루미늄판을 써서 좀더 튼튼하게 만들고 싶었지만, 적당한사이즈를 찾지못해서 프로스틱판으로 결정햇습니다.

기어드모터와 차체는 역시나 어린이용 나무블럭에 구멍을 뚫어서 고정했습니다.

저번에 밸런싱 로봇처럼 정밀하게 구멍을 뚫지 않아도 되서 한결수월했던것 같네요.

자 노란색 TT기어드 모터를 고정하기 위한 적절한 브라켓이 있었으면 좋겠는데, 아무리 찾아봐도 적당한 브라켓이 없습니다.

3d 프린터라도 있으면 좋을텐데 그점이 아쉽네요.



현재 주행테스트까지 마쳤습니다.

4륜이라서 그런지 굉장히 힘이 좋네요.





'아두이노작품' 카테고리의 다른 글

밸런싱로봇 전력공급  (0) 2015.12.11
밸런싱 로봇 소스 사용법  (15) 2015.12.07
밸런싱 로봇 만들어 보기  (105) 2015.05.09
충전팩 충방전기 만들어보기  (1) 2015.04.27
라인트레이서 만들어보기  (1) 2015.03.25
posted by 래머
2015. 5. 9. 08:48 아두이노작품

이번에는 밸런싱 로봇을 만들어보기로 했습니다.

밸런싱 로봇을 만들면서 가장힘들었던점은 기구부를 만드는 거였네요.

마땅히 기구부를 구할 방법이 없다보니 힘들게 겨우 만들었습니다.


아래 밸런싱 로봇의 기본원리는 차체에 부착된 mpu6050센서로 부터 차체의 y축에 대한 기울기를 측정하고, 차체가 기울었다면 기운방향으로

차체를 이동시켜서 중심을 잡는것입니다.


먼저 제어 회로쪽입니다.

나노 호환 모듈과 hc-06 블루투스, mpu6050 6축 자이로 가속도 센서 및 TB6612FNG듀얼채널 모터 드라이버를 사용했습니다.

점퍼선을 없애기 위해서 모두 pcb에 배선을 했네요.  노란색 콘덴서는 680마이크로 패럿짜리 전원부에 결선되어 있습니다.

납땜 하는것 너무 싫어 하는데 암튼 몇시간에 걸쳐서 완성했습니다. 


일단 기구부와 조립한 모습입니다.

기어드 모터는 저렴한 TT모터라고 2륜 구동차 구매 하면서 딸려온것을 활용했습니다.

가장힘들었던점은 TT모터를 고정하는 브라켓이 마땅한게 없어서 거의 못만들뻔했네요.

아래 사진에 보이는 배선들은 전원부 배선과 모터로 연결되는 선들입니다. 하네스 커넥터를 사용해서 깔끔하게 연결했습니다.

기어드 모터를 고정시키는 브라켓은 굴러다니고 있는 어린이용 나무블럭을 활용해서 만들었습니다. ㅋㅋㅋㅋㅋㅋㅋㅋ

마침 사이즈가 적당하지 않았다면 밸런싱 로봇은 물건너 가지 않았을까 생각이 드네요.


2개의 건전지 홀더를 사용했는데, 아래쪽은 4개의 AA건전지 위에 보이는것은 9V건전지용홀더입니다.

상판은 회로의 PCB를 그대로 서포터를 사용해서  고정하고 하판은 포맥스판을 대충 잘라서 나사로 고정했습니다.

건전지 홀더는 양면테이프를 활용해서 접착을 시켰네요.


모터드라이버의 출력은 헤더핀에 연결하고 각모터에 헤더핀용 점퍼선을 납땜해서 연결했습니다. 모터 방향이 잘못연결된경우 편리하게 다시 배선할 수 있네요.

TB6612FNG모터 드라이버는 알리에서 개당 3000원정도에 4개를 구했했는데, 한동안 짱박혀 있다가 이제야 써먹어 보내요.

저 모터 드라이버의 장점은 1.2A의 비교적 쓸만한 정도의 전류공급이 가능하고 무엇보다 매우 작은 크기라는 것입니다.


거의 아두이노 호환 보드를 사용중인데, 역시 알리에서 구매했습니다. 저렴하게 다량구매해서 잘써먹고 있네요.


mpu6050 6축 자이로 가속도 센서입니다. 각도 측정할때 많이 사용되죠, 관련된 칼만필터나 이런것도 있어서 손쉽게 사용가능합니다.


휴대폰의 앱을 통해서 제어를 하기 위해서 hc-06블루투스 모듈을 사용했습니다. 


밸런싱 로봇용 전용앱을 만들어야 하는데, 일단하나부터 열까지 새로 만들자면 너무 일이 많아지니 기존에 탱크나, 2wd카에서 사용했던 앱에 쓸쩍 다리 하나 걸쳐서

완성했습니다.

PID제어를 하고 있는데, 관련 상수를 손쉽게 조정할 수 있는 인터페이스를 추가했고,

일단 전, 후, 좌, 우 제어가 되도록 하기위한 조정버튼도 추가했습니다.








드디어 완성된 밸런싱 로봇!!



불안정하지만 조정기능도 추가했습니다. 

일단 수작업한 차체라 밸런스가 잘맞지 않네요, LED까지 추가했더니 밸런스 균형이 더 나빠진것 같습니다.



밸런싱 로봇 제어 소스입니다. 도움이 되지 않을것 같지만 일단 첨부해둡니다.


http://eltgroup.tistory.com/297 글에 소스 사용법에 대해서 간단히 설명해두었습니다.


회로도입니다.


안드로이드와 블루투스 제어쪽이 빠진 소스입니다.

TB6612FNG 모터드라이버용 소스 : 다운로드










L298N회로

안드로이드와 블루투스 제어쪽이 빠진 소스입니다.

L298N모터드라이버용 소스 ; 소스다운로드





L298N에 공급하는 5v는 L298N에 독립적으로 로직전압을 공급하도록 셋팅한경우에만 공급합니다.




posted by 래머
2015. 4. 27. 14:00 아두이노작품

얼마전에 저가 rc카를 하나 구매를 했는데

같이 딸려온 rc충전팩이 영 시원찮습니다.

오래방치해서 수명이 다됐거나, 아니면 구라 용량인지 몇시간충전해도 단 몇분이면 rc카가 멈추는 희안한 사태가 벌어져서

버릴까 하다가 그냥 남는 부품도 있고 해서 배터리 충방전기를 만들어 보기로 했습니다.

같이 딸려온 충전아답터는 보니까 과충전 방지 기능이나 전류 제어 같은 기능은 전혀 없는듯하고 그냥

계속 일정 전압과 전류만 지속적으로 공급해주는 단순한 타입인듯한데,

요즘들어 배터리를 많이 가지고 놀다보니 배터리에 대해서 여러가지 정보를 많이 접한지라

왠지 무서워서

충전 시간이라도 제어되는 장치를 만들어 보기로 했습니다.


일단 전체 장치 구성입니다. 왠지 허접해 보이지만

아두이노 나노 호환 모듈을 통해서 제어부를 구성했습니다.

2개의 릴레이 모듈을 사용했는데 각기 전류 흐름을 제어하는 스위치입니다.

1개의 릴레이는 방전부로 가는 통로를 제어하고 있고

나머지 한개의 릴레이는 전압측정부로 가는 흐름을 제어합니다.

마지막 릴레이는 충전전류를 제어하는 역할입니다.



오른쪽 기판에는 3개의 시멘트 저항과 2개의 일반 저항 및 스위치 2개가 있습니다.

2개의 시멘트 저항은 직렬구성되어 있고 릴레이 스위치 및 배터리와 연결되어 배터리 방전회로를 구성하고 있습니다.

한개의 시멘트저항과 2개의 일반저항은 전압측정을 위한 회로를 구성합니다.

배터리 전압을 대략 1/2로 분압해서 아두이노의 아날로그 입력을 통해서 전압을 측정합니다. 

배터리 방전시 일정이하로 떨어질경우 방전을 중지하기 위한 구성입니다.

저항의 오차에 의해서 정밀한 전압측정은 되지 않지만 딱히 다른 방법이 없으니 ㅎㅎ

각 스위치는 충전시간을 선택하는 충전모드 선택과 충방전 루틴을 시작/종료 시키는 역할을 합니다.


아래 사진에 보이는 가변저항은 원래 방전회로를 구성하기 위해서 사용했던 것인데, 실사용결과 완충된 배터리 방전시 하얀 연기를 내뿜으며 불이 날뻔해서

오른쪽의 시멘트 저항으로 교체를 했습니다. 대략 210ma로 방전을 하게 되어 있고 배터리 팩의 전압이 5v이하가 되는 시점에 방전을 종료하도록했습니다(배터리 셀당 대략 1v정도). 전압측정부의 시멘트 저항은 일반저항의 전력초과로 타버리는 것을 방지하기 위한 차원으로 직렬연결해뒀습니다.

1/2분압회로에서 안그래도 저항의 오차가 있는데 시멘트저항이 오차를 증가 시키겠지만 일반저항의 저항값이 매우 크므로(10k Ohm) 크게 신경쓰지 않아도 될정도이지 않나 생각됩니다.


현재 충방전기의 상태를 표시하기 위해서 16 * 2 lcd 디스플레이를 사용했습니다. 오른쪽 2채널릴레이는 위에서 설명했지만

전압측정부와 충전아답터로부터 오는 전류를 제어하는 역할입니다.


아래는 실제 작동영상입니다.




충방전기 소스는 다음과 같습니다.


def.h//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef DEF_H_

#define DEF_H_


#define BATT_PIN A0        //전압측정핀

#define LELAY_PIN 12        //방전릴레이 제어핀

#define LIMIT_VOLTAGE 5.0f    //방전종지전압


#define BATT_CHK_PIN 3    //전압측정릴레이 제어핀

#define CHARGE_CPIN 4    //충전전류제어 릴레이 핀


#define CHARGE_TIME1 (60 * 60 * 3.64f)    //700mAh배터리팩 충전시간

#define CHARGE_TIME2 (60 * 60 * 5.2f)    //1300mAh배터리팩 충전시간


#define MODE_1 0        //700mAh충전모드

#define MODE_2 1        //1300mmAh충전모드


//충반전기 상태정의

#define STATUS_WAIT 0        //아무런 처리를 하고 있지않음

#define STATUS_DIS_CHARGE 1    //방전중

#define STATUS_CHARGE 2        //충전중

#define STATUS_IDLE 3        //충반전완료됨


#define MODE_PIN 6            //충전시간 선택 버튼 핀

#define STARTSTOP_PIN 7        //충방전 시작/종료 버튼 핀


#endif


CMyTime.h//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef MYTIME_H_

#define MYTIME_H_


//시간측정기

class CMyTime

{

long m_lTime;

public:

CMyTime()

{

m_lTime = millis();

}


//현재 시간을 캡쳐함

void Capture()

{

m_lTime = millis();

}


//이전에 capture함수를 호출한 시점부터 이함수를 호출한시점까지의 경과 시간을 초단위로 구함

float GetPassedTime()

{

long lpassedTime = millis() - m_lTime;


return (float)lpassedTime * (1.0f / 1000.0f);

}

};

#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <Wire.h>

#include <LiquidCrystal_I2C.h>


#include <inttypes.h>

#include <MemoryFree.h>

#include "def.h"

#include "CMyTime.h"


LiquidCrystal_I2C g_LCD(0x27,16,2);


//전압측정

class CVoltMng

{

public:


void Init()

{

pinMode(BATT_CHK_PIN, OUTPUT);

Enable(false);

}


void Enable(bool bEnable)

{

//digitalWrite(BATT_CHK_PIN, bEnable ? HIGH : LOW);

digitalWrite(BATT_CHK_PIN, bEnable ? LOW : HIGH);

}


float GetVoltage()

{

return ((float)analogRead(BATT_PIN) / 1024.0f) * 5.0f * 2.0f;

}

};


CVoltMng g_VoltMng;


//충전 제어

class CCharger

{

bool m_bEnabled;

bool m_bCompleted;

CMyTime m_CheckTime;

CMyTime m_ScrUpdateTime;

int m_iFlag;

int m_iMode;

float m_fChargeTime;

public:

CCharger()

{

m_fChargeTime = CHARGE_TIME1;

m_iMode = MODE_1;

Clear();

}


void Clear()

{

m_bEnabled = false;

m_bCompleted = false;

}


int GetMode() {return m_iMode;}


void SelectMode1()

{

m_iMode = MODE_1;

m_fChargeTime = CHARGE_TIME1;

}


void SelectMode2()

{

m_iMode = MODE_2;

m_fChargeTime = CHARGE_TIME2;

}


void Init()

{

m_bEnabled = false;

m_bCompleted = false;

pinMode(CHARGE_CPIN, OUTPUT);

EnableRelay(false);

}


bool IsCompleted() {return m_bCompleted;}


void Start()

{

EnableRelay(true);

m_CheckTime.Capture();

m_ScrUpdateTime.Capture();

m_bEnabled = true;

m_iFlag = 0;

}


void Update()

{

if (!m_bEnabled)

return;


if (m_bCompleted)

return;


if (m_ScrUpdateTime.GetPassedTime() >= 1.0f)

{

m_ScrUpdateTime.Capture();

g_LCD.clear();

g_LCD.home();


float fPassedTime = m_CheckTime.GetPassedTime();


long min = (long)(fPassedTime / 60.0f);

long sec = (long)(fPassedTime - (min * 60));


switch(m_iFlag)

{

case 0:

g_LCD.print(min);

g_LCD.print("m ");

g_LCD.print(sec);

g_LCD.print("s");

if (m_iMode == MODE_1)

g_LCD.print(" 700mAh");

else

g_LCD.print(" 1300mAh");

g_LCD.setCursor(0, 1);

g_LCD.print("charging.");


++m_iFlag;

break;

case 1:

g_LCD.print(min);

g_LCD.print("m ");

g_LCD.print(sec);

g_LCD.print("s");

if (m_iMode == MODE_1)

g_LCD.print(" 700mAh");

else

g_LCD.print(" 1300mAh");

g_LCD.setCursor(0, 1);

g_LCD.print("charging..");

++m_iFlag;

break;

case 2:

g_LCD.print(min);

g_LCD.print("m ");

g_LCD.print(sec);

g_LCD.print("s");

if (m_iMode == MODE_1)

g_LCD.print(" 700mAh");

else

g_LCD.print(" 1300mAh");

g_LCD.setCursor(0, 1);

g_LCD.print("charging...");

m_iFlag = 0;

break;

}

}


if (m_CheckTime.GetPassedTime() >= m_fChargeTime)

{

m_bCompleted = true;

EnableRelay(false);

g_LCD.clear();

g_LCD.home();

g_LCD.print("charging completed.");

}

}


void EnableRelay(bool bEnable)

{

digitalWrite(CHARGE_CPIN, bEnable ? LOW : HIGH);

}

};


CCharger g_Charger;


//방전제어

class CDisCharger

{

bool m_bEnabled;

CMyTime m_CheckTime;

CMyTime m_TotalTime;

CMyTime m_EndStepTime;


bool m_bDisChargeCompleted;

int m_iEndStep;

float m_fResultTime;

public:

CDisCharger()

{

Clear();

}


void Clear()

{

m_bEnabled = true;

m_bDisChargeCompleted = false;

m_iEndStep = 0;

m_fResultTime = 0.0f;

}


void Init()

{

m_bEnabled = true;

m_bDisChargeCompleted = false;

m_iEndStep = 0;

m_fResultTime = 0.0f;

m_CheckTime.Capture();

m_TotalTime.Capture();

pinMode(LELAY_PIN, OUTPUT);

EnableDischarger(false);

}


/*

배터리가 지속적으로 방전시 방전이 거의 완료되는 시점에 전압이 급격히 떨어지는데

그렇다고 해서 완전히 방전된것이 아니다.

방전을 중지시키고 약간 대기하게 되면 다시 전압과 전류가  올라가는데

그 시점에 대한 처리

5초간 방전, 2초간 휴식을 계속반복하면서 배터리 전압을 측정해서 이때에 전압이 허용범위이하까지 떨어지면

완전방전되었다고 가정함. 

*/

void ProcessEndStep()

{

if (m_iEndStep == 1)

{

if (m_EndStepTime.GetPassedTime() < 5.0f)

return;

}

else

{

if (m_EndStepTime.GetPassedTime() < 2.0f)

return;

}


m_EndStepTime.Capture();


if (m_iEndStep == 1)

{

EnableDischarger(false);

delay(150);

g_VoltMng.Enable(true);

delay(150);


float fCurVolt = g_VoltMng.GetVoltage();

g_VoltMng.Enable(false);

delay(150);

g_LCD.clear();

g_LCD.home();

g_LCD.print(fCurVolt, 3);


if (m_bDisChargeCompleted)

g_LCD.print("V, end");

else

g_LCD.print("V");


g_LCD.setCursor(0, 1);

g_LCD.print("Time = ");


if (!m_bDisChargeCompleted)

g_LCD.print(m_TotalTime.GetPassedTime() / 60.0f, 2);

else

g_LCD.print(m_fResultTime / 60.0f, 2);


g_LCD.print("min");


if (fCurVolt <= LIMIT_VOLTAGE)

{

m_iEndStep = 3;

return;

}


EnableDischarger(true);


m_iEndStep = 2;

}

else if (m_iEndStep == 2)

{

m_iEndStep = 1;

EnableDischarger(false);

}

}


void Update()

{

if (!m_bEnabled)

return;


if (m_CheckTime.GetPassedTime() < 10.0f)

return;


m_CheckTime.Capture();


if (m_iEndStep == 1 || m_iEndStep == 2)

{

ProcessEndStep();

}

else

{

EnableDischarger(false);

delay(150);

g_VoltMng.Enable(true);

delay(150);

float fCurVolt = g_VoltMng.GetVoltage();

g_VoltMng.Enable(false);

delay(150);

EnableDischarger(true);


if (!m_bDisChargeCompleted && fCurVolt <= LIMIT_VOLTAGE)

{

EnableDischarger(false);

if (m_iEndStep == 3)

{

m_bDisChargeCompleted = true;

m_fResultTime = m_TotalTime.GetPassedTime();

}

else

{

m_iEndStep = 1;

m_EndStepTime.Capture();

}

}


g_LCD.clear();

g_LCD.home();

g_LCD.print(fCurVolt, 3);


if (m_bDisChargeCompleted)

g_LCD.print("V, end");

else

g_LCD.print("V");


switch (g_Charger.GetMode())

{

case MODE_1:

g_LCD.print(", 700mAh");

break;

case MODE_2:

g_LCD.print(", 1300mAh");

break;

}

}


g_LCD.setCursor(0, 1);

g_LCD.print("Time = ");


if (!m_bDisChargeCompleted)

g_LCD.print(m_TotalTime.GetPassedTime() / 60.0f, 2);

else

g_LCD.print(m_fResultTime / 60.0f, 2);


g_LCD.print("min");

}


void EnableDischarger(bool bEnable)

{

digitalWrite(LELAY_PIN, bEnable? HIGH : LOW);

}


bool IsCompleted() {return m_bDisChargeCompleted;}

};


CDisCharger g_DisCharger;


//버튼 스위치 입력처리

class CInputMng

{

bool m_bNowDelay;

CMyTime m_Time;

int m_iPin;

public:

CInputMng(int pin)

{

m_bNowDelay = false;

m_iPin = pin;

m_Time.Capture();

}


void Init()

{

pinMode(m_iPin, INPUT_PULLUP);

}


bool Check()

{

if (m_bNowDelay)

{

if (m_Time.GetPassedTime() > 1.0f)

{

m_bNowDelay = false;

}


return false;

}


if (digitalRead(m_iPin) == LOW)

{

m_bNowDelay = true;

m_Time.Capture();


return true;

}


return false;

}

};


CInputMng g_InputMode(MODE_PIN);

CInputMng g_StartStop(STARTSTOP_PIN);


int g_iCurMode = STATUS_WAIT;


//충반전 루틴 정지

void StopProcessing()

{

g_DisCharger.Init();

g_Charger.Init();

g_iCurMode = STATUS_WAIT;


g_LCD.clear();

g_LCD.home();

g_LCD.print("Stop Processing..");

g_LCD.setCursor(0, 1);

if (g_Charger.GetMode() == MODE_1)

g_LCD.print("700mAh..");

else

g_LCD.print("1300mAh..");

}


//충방전 루틴 시작

void StartProcessing()

{

g_DisCharger.Init();

g_Charger.Init();


g_InputMode.Init();

g_StartStop.Init();


g_DisCharger.EnableDischarger(true);


g_LCD.clear();

g_LCD.home();

g_LCD.print("Discharging..");


g_iCurMode = STATUS_DIS_CHARGE;

}


void setup()

{


  /* add setup code here */


g_LCD.init();

g_LCD.backlight();

g_VoltMng.Init();

g_InputMode.Init();

g_StartStop.Init();


StopProcessing();

}


void loop()

{

if (g_StartStop.Check())

{

if (g_iCurMode != STATUS_WAIT)

{

StopProcessing();

return;

}

else

{

StartProcessing();

return;

}

}


if (g_InputMode.Check())

{

switch(g_Charger.GetMode())

{

case MODE_1:

g_Charger.SelectMode2();

break;

case MODE_2:

default:

g_Charger.SelectMode1();

break;

}


StopProcessing();

return;

}


  /* add main program code here */

if (g_iCurMode == STATUS_DIS_CHARGE)

{

g_DisCharger.Update();


if (g_DisCharger.IsCompleted())

{

g_VoltMng.Enable(false);

g_DisCharger.EnableDischarger(false);

delay(1000);

g_iCurMode = STATUS_CHARGE;

g_Charger.Start();

}

}

else if (g_iCurMode == STATUS_CHARGE)

{

g_Charger.Update();

if (g_Charger.IsCompleted())

{

g_iCurMode = STATUS_IDLE;

}

}

else if (g_iCurMode == STATUS_IDLE)

{

delay(1000);

}

}


'아두이노작품' 카테고리의 다른 글

아두이노 4륜구동  (0) 2015.05.23
밸런싱 로봇 만들어 보기  (105) 2015.05.09
라인트레이서 만들어보기  (1) 2015.03.25
아두이노 스파이더 로봇만들어보기  (0) 2015.02.22
아두이노 탱크 만들어보기  (0) 2015.02.22
posted by 래머
2015. 3. 25. 12:45 아두이노작품

굉장히 빠르고 정밀하게 제어되는 라인트레이서를 보고 저도 만들어보고 싶어 시도를 해봤습니다.


2015-05-17일 최종 튜닝했습니다.

라인트래킹제어 알고리즘을 좀 수정했고, 트랙을 좀손봤습니다(검정테이프질). 운행가능한 최대 속도로 운행될수 있게 했습니다.


센서는 newtc의 AM-IRS4D라는 모듈을 이용햇습니다. 4개의 적외선 센서를 장착하고 있습니다.

가변저항으로 감도를 조정할 수 있으며, 0또는 1로 신호를 출력하게 됩니다.



제어는 아두이노 프로미니 호환 모듈과 ET-DCM이라는 모터드라이버를 사용했습니다. 프로그램은 기존 자율주행 자동차등에서 사용했던 프로그램을 조금 수정하고

라인트래킹 제어루틴을 추가했습니다.


차체는 아카데미 과학의 라인X-II라는 것을 사용했습니다. 구입시 같이 딸려오는 기판과 각종 회로부품들은 잘 짱박아 뒀습니다.


아래는 완성 후 주행영상입니다.

원래 계획은 굉장히 빠르고 정밀하게 제어되는 라인트레이서였는데

그것은 저의 허황된 꿈일 뿐이었습니다.

정밀제어를 위해서는 좀더 많은 수의 센서로부터 입력을 받아야할듯하고, 차체도 정밀제어가 가능하지 않으면 안된다는 것을 알았습니다.

아카데미의 차체는 pwm응당특성이 별로 좋지 않습니다.

그냥 한번만들어봤다는것에 만족하기로 했습니다.


allowfullscreen>


값을 튜닝후



//소스-----------------------------------------------------------------------------------

class CMyTime

{

long m_lTime;

public:

CMyTime()

{

m_lTime = millis();

}


//현재 시간캡쳐

void Capture()

{

m_lTime = millis();

}


//이전에 capture한 시점부터 이함수를 호출하는 시점까지의 경과 시간을 초단위로 구함

float GetPassedTime()

{

long lpassedTime = millis() - m_lTime;


return (float)lpassedTime * (1.0f / 1000.0f);

}

};


#define LINE_PIN_1 2

#define LINE_PIN_2 3

#define LINE_PIN_3 4

#define LINE_PIN_4 7


#define LINE_LEFT_OUTER 0x08

#define LINE_LEFT_INNER 0x04

#define LINE_RIGHT_INNER 0x02

#define LINE_RIGHT_OUTER 0x01


#define LINE_STATUS_ALL (LINE_LEFT_OUTER | LINE_LEFT_INNER | LINE_RIGHT_INNER | LINE_RIGHT_OUTER)


class CLineCheker

{

protected:

char m_cPin1;

char m_cPin2;

char m_cPin3;

char m_cPin4;

public:


CLineCheker(char pin1, char pin2, char pin3, char pin4)

{

m_cPin1 = pin1;

m_cPin2 = pin2;

m_cPin3 = pin3;

m_cPin4 = pin4;

}


void Init()

{

pinMode(m_cPin1, INPUT);

pinMode(m_cPin2, INPUT);

pinMode(m_cPin3, INPUT);

pinMode(m_cPin4, INPUT);

}


char Check()

{

char ret = 0;


if (CheckLine1())

ret |=  LINE_LEFT_OUTER;


if (CheckLine2())

ret |=  LINE_LEFT_INNER;


if (CheckLine3())

ret |=  LINE_RIGHT_INNER;


if (CheckLine4())

ret |=  LINE_RIGHT_OUTER;


return ret;

}


bool CheckLine1()

{

return digitalRead(m_cPin1) == LOW;

}


bool CheckLine2()

{

return digitalRead(m_cPin2) == LOW;

}


bool CheckLine3()

{

return digitalRead(m_cPin3) == LOW;

}


bool CheckLine4()

{

return digitalRead(m_cPin4) == LOW;

}

};


#define LT_UPDATE_TIME (1.0f / 30.0f)


#define LT_DIR_CENTER 0

#define LT_DIR_LEFT 1

#define LT_DIR_RIGHT 2


class CLineTracker

{

protected:

bool m_bEnabled;

CLineCheker m_Checker;

unsigned char m_cLastLeftSpeed, m_cLastRightSpeed;

char m_cLtDir;

char m_cLastStatus;

CMyTime m_Time;

bool m_bEndMark;

float m_fSpeedScale;


float m_fTurnSpeedSlow;

float m_fTurnSpeedQuick;


public:

CLineTracker() : m_Checker(LINE_PIN_1, LINE_PIN_2, LINE_PIN_3, LINE_PIN_4)

{

m_fSpeedScale = 1.0f;

Clear();

}


void Clear()

{

m_bEnabled = false;

m_cLtDir = LT_DIR_CENTER;

m_cLastStatus = 0;

m_bEndMark = false;

m_cLastLeftSpeed = (unsigned char)(255 * m_fSpeedScale);

m_cLastRightSpeed = (unsigned char)(255 * m_fSpeedScale);


m_fTurnSpeedSlow = (255.0f * (54.0f/100.0f));

m_fTurnSpeedQuick = (255.0f * (7.0f/100.0f));

}


void Enable(bool bEnable)

{

Stop();

m_bEnabled = bEnable;

}


bool IsEnabled() {return m_bEnabled;}


char GetLastStatus() {return m_cLastStatus;}

float GetSpeedScale() {return m_fSpeedScale;}

void SetSpeedScale(float f) {m_fSpeedScale = f;}


void SetTurnSpeedSlow(float fPer)

{

m_fTurnSpeedSlow = fPer * 0.01f * 255.0f;

}

void SetTurnSpeedQuick(float fPer)

{

m_fTurnSpeedQuick = fPer * 0.01f * 255.0f;

}


void Init()

{

m_Checker.Init();

m_Time.Capture();

}


void Move(int left, int right)

{

if (left > 255)

left = 255;

if (right > 255)

right = 255;


m_cLastLeftSpeed = (unsigned char)left;

m_cLastRightSpeed = (unsigned char)right;


//모터 드라이버 제어

g_Motor.SmoothTurn(left, right, true);

}


void Stop()

{

g_Motor.StopAll();

Clear();

}


void Forward()

{

m_cLtDir = LT_DIR_CENTER;

Move((int)(255 * m_fSpeedScale), (int)(255 * m_fSpeedScale));

}


void TurnLeft(int speed)

{

m_cLtDir = LT_DIR_LEFT;

//Move((int)(speed * m_fSpeedScale), int(255 * m_fSpeedScale));

Move(speed, 255);

}


void TurnRight(int speed)

{

m_cLtDir = LT_DIR_RIGHT;

//Move((int)(255 * m_fSpeedScale), (int)(speed * m_fSpeedScale));

Move(255, speed);

}

void MoveLastStatus()

{

Move(m_cLastLeftSpeed, m_cLastRightSpeed);

}


char Update()

{

if (!m_bEnabled)

return 0;


if (m_Time.GetPassedTime() < LT_UPDATE_TIME)

return m_cLastStatus;


m_Time.Capture();


m_cLastStatus = m_Checker.Check();



if (m_cLastStatus == LINE_LEFT_OUTER)

{//크게 좌회전

m_bEndMark = false;

TurnLeft(m_fTurnSpeedQuick);

}

else if (m_cLastStatus == LINE_LEFT_INNER)

{//좌회전

m_bEndMark = false;

TurnLeft(m_fTurnSpeedSlow);

}

else if (m_cLastStatus == LINE_RIGHT_INNER)

{//우회전

m_bEndMark = false;

TurnRight(m_fTurnSpeedSlow);

}

else if (m_cLastStatus == LINE_RIGHT_OUTER)

{//크게 우회전

m_bEndMark = false;

TurnRight(m_fTurnSpeedQuick);

}

else if (m_cLastStatus == 0) //아무런 라인인식안됨

{

/*if (m_bEndMark)

Stop();

else*/

Forward();

}

else if (m_cLastStatus == (LINE_LEFT_OUTER | LINE_LEFT_INNER | LINE_RIGHT_OUTER | LINE_RIGHT_INNER))

{

m_bEndMark = true;

Forward();

}

else if ((m_cLastStatus & (LINE_LEFT_OUTER | LINE_LEFT_INNER)) == (LINE_LEFT_OUTER | LINE_LEFT_INNER))

{

m_bEndMark = false;

TurnLeft(m_fTurnSpeedQuick);

}

else if ((m_cLastStatus & (LINE_RIGHT_OUTER | LINE_RIGHT_INNER)) == (LINE_RIGHT_OUTER | LINE_RIGHT_INNER))

{

m_bEndMark = false;

TurnRight(m_fTurnSpeedQuick);

}

else

{

m_bEndMark = false;

MoveLastStatus();

}


return m_cLastStatus;

}

};

posted by 래머
2015. 2. 22. 23:40 아두이노작품


각종 탱크 부품을 구하다가 발견한 신기한 녀석이다.

매우 흥미롭군 ㅎㅎ



일전에 제작한 탱크의 소스를 조금만 수정하면 정말 괜찮은 작품이 나올듯하다.


주문해서 도착한 스파이더 로봇

껍데기는 이렇게 생겼다.



열심히 조립하고, 정말 조립은 귀찮고 짜증나는 과정인듯하다.

이게 몸통이인데, 미리 드릴로 구멍을 뚫었다. 흔한 장난감용 모터 2개가 달려 있는데, 저번에 장난감

탱크의 모터와 비슷한게 힘이 별로 없을듯하다, 별로 마음에 안든다.




이건 다리다




두개를 결합하면 이렇게 된다.




기판을 고정시킬 포맥스 판을 정밀 제단하고,

한치의 오차도 없이 치수를 제어 구멍을 뚫었다.




기판위에 아두이노 나노를 고정시키고, 연결핀을 배치했다.




원래 눈이 있는곳인데, 여기에 초음파 센서를 글루건을 이용해서 고정시켰다.




전원은 이것 두개면 충분하겠지



AAA4개용 배터리 케이스다.




포맥스판과 스파이더로봇의 본체는 pCB서포트를 이용해서 고정하기로 했다.




음 이것들 다올라가면 또 탱크처럼 빌빌거리는거 아니겠지.


탱크때는 서보모터를 이용해서 장애물 검출 방향을 결정했었는데, 이번에는 힘들듯하여

생각하다, 지자기 센서를 이용해보기로 했다. 그나저나 너무 쪼그매서 어떻게 고정시키나. 생각해보니 핀도 잘못납땜한것같다. 핀이 아래쪽으로 향하게 해서 납떕했으면 기판위에 소켓만들고 바로 꽂을수 있었을텐데..



일단 소프트웨어 작업은 일차적으로 마무리 했는데, 주문한 모터 드라이버가 설이 끝나야 도착할거 같다.


완성된 작품은 차후에


-------2015-03-09----------------

모든 구성완료 후 테스트해보니, 모터가 힘이 없어서 빌빌....그냥망함 

스파이더로봇 프레임에 맞는 토크 높은 모터를 구해서 장착해봤지만 역시나 빌빌....한번더 망함

현존하는 어떠한 모터를 달아도 안될것같다. 결국 스파이더 로봇만들기는 포기를 했다.


대신에 스파이더 로봇구성을 위해 만들었던 회로는 2륜 구동 차량을 구매해서, 거기에 그대로 이식을 시켰다.

탱크를 만들면서 느낀거지만, 자동주행을 완벽하게 해보겠다고 센서를 덕지 덕지 붙였는데 결론은

헛짓거리였다는 것이다. 차량의 모든 방향을 커버할 수 있도록 센서를 촘촘하게 배치하지 않는 이상은 난해한 코스에서 무조건 해딩이다.

아래 이륜차는 하나의 초음파 센서만 장착했지만 탱크보다 훨씬 나아 보인다.






'아두이노작품' 카테고리의 다른 글

아두이노 4륜구동  (0) 2015.05.23
밸런싱 로봇 만들어 보기  (105) 2015.05.09
충전팩 충방전기 만들어보기  (1) 2015.04.27
라인트레이서 만들어보기  (1) 2015.03.25
아두이노 탱크 만들어보기  (0) 2015.02.22
posted by 래머
2015. 2. 22. 23:34 아두이노작품


탱크 자동주행 영상


아두이노라는 재미있는 녀석을 반결한 기념으로 탱크를 만들어보기로 했다.

조향가능한 RC카는 아무래도 제작하는데 더 힘들듯하니 만만한 탱크로...


일단 탱크 본체를 구해야 하는데, 만만한 녀석이 있나

마트 가서 보니 음 별로 없네..

그냥

이걸로 들고왔다.

크기가 좀 작은듯한데



음 조립하기 너무 귀찮은것같다. 아무튼 열심히 조립하고, 모터에 노이즈 감소용 세라믹 콘덴서를 납땜했다. 동시에 점퍼선도 납떔해주고..




상판에 튀어나온 부분은 칼로 썰어 버리고   서보모터를 달았다.



초음파센서로 장애물을 검출할수 있게 프라스틱 케이스에 글루건을 이용해서 고정시키고



다시 케이스를 서보모터에 고정시켰다.



모터를 제어할 모터 드라이버, ET-DCM이라는 모델인데

찾다찾다 없어서 구입한건데, 나중에 보니 다른것들도 많이 있었다. 내가 못찾은거였음..

아무튼 나름 쓸만한것 같다. 




쪼그만 프라스틱 케이스에 우노는 좀 큰것 같으니 나노를 구입해서 넣기로 했다.


일단 센서 테스트 ㅎㅎ

기본적인 작동 알고리즘은 센서에 일정이하의 거리가 검출되면 서보 모터를 조정해서 주변을 살펴서

회전 방향을 결정하는거다.



탱크의 모터와 서보모터에는 이녀석으로 전원을 공급하기로 했다.



탱크를 조정은 안드로이드 폰으로 하기로 했다.

하는일이 이거이니 만큼 유니티와 NGUI를 통해서 작업을.. 순수 안드로이드는 UI작업하기가 너무 귀찮은듯하다.




이렇게 해서 우여곡절끝에 일단 완성을..

음 근데 뭐지.. 왠지 탱크가 누더기 같다. 기분탓인가... 작동은 잘되는데

뭔가 좀...

그리고 서보모터는 왜 저렇게 튀지!! 

전진 후진은 잘되는데, 좌회전 우회전이 잘안된다.

모터 힘이 너무 없는것 같다.




자동주행시 서보 모터 제어가 정말 중요한 문제인데, 지 마음되로 튄다.

희안하게도 블루투스 연결만 되면 튀기 시작한다.

일단 해결은 잘됐지만, 이것때문에 몇일째 골머리릴 싸맸다.



뭔가 허무하다. 수십만원 투자해서 만든탱크인데, 누더기라니

차선책을 찾아야한다.

그와중에 이런녀석을 발견했다.

오호!! 힘좋게 생겼군. 일단 본체를 이것으로 바꾸기로 했다.



저번에 누더기 탱크를 교훈삼아, 이번에는 좀 제대로 만들어보기로 했는데,


일단 각종 기판들을 고정시킬 판때기가 필요하다.

재료를 알아보던중 알게된 포맥스 판때기다, 그전에 아크릴 판으로 치수재고 드릴로 구멍뚫다가 아작나는바람에 피눈물을 흘렸다.

이녀석은 가공도 쉽고 여로모로 아크릴판보다 좋은점이 많은것같다.



열심히 치수재고 드릴로 구멍뚫고..

초정밀 가공이란 바로 이런것인가(사실 아래 사진에 나온건 다른 작품에 사용할 판때기다 사진을 못찍어서.. 이걸로라도)




여기저기 널부러져 있던 부품들을 가능한 기판에 직접시키고




뭔가 매우 정밀해 보인다.



몇가지 기능을 추가할려다 보니, 다층 구조를 만들 수밖에 없었다.


초음파 센서의 단점을 보충해보고자 적외선 센서도 추가하기로 했다(사진이 없어서 좀 빌려옴).

탱크의 좌/우측 장애물을 검출하게 될것이다. 더불어 자동주행 알고리즘도 더욱 개선되었다.




이전에 아두이노에 전원을 공급하던 8.4V 니켈 수소 충전지는 너무 빨리 닳아서

이걸로 전원 공급을 대체 하기로 했다. 테스트결과 아주 넉넉하니 마음에 든다.



일반 USB케이블은 너무 뻑뻑해서, 요것도 같이 구매했다.



우연찬게 발견한 재미있는 녀석이다. 아주 조그만 모터와 프로팰러인데

무슨 쿼드콥터에 사용되는 녀석이라한다.

아무튼 마음에 들어서 구입했다.

어디에 쓸까 하다가 그냥 탱크의 뒷편에서 풍력을 공급하는 역할을 맡기기로 했다.




새로 구입한 ROVER-5 라는 녀석이 생각보다 전기를 엄청나게 잡아 먹는다.

1.2V AA충전지 4개로는 빌빌거린다, 더구나 모터 드라이버가 공급하는 전력이 최대 2A밖에(채널당 1A) 안되기 때문에 더욱더 힘을 못 받는다.

스펙상으로는 Rover 5의 각 모터는 최대 2.5A를 필요로 한다고 나와있다. 

그래서 일단은 1.2V충전지 6개를 연결하기로 하고, 서보모터와는 전원을 분리했다.

서보모터의 전원을 어떻게 공급할까 고민하다가 그냥 전원을 하나 더연결하기로 했다.


마침 집에서 놀고 있는 갤4 배터리가 있어서 이걸 활용하기로 했다.



완성품

사용하는 센서의 양이 많아져 3개의 아두이노 나노를 사용했다. 

A,B,C 3개의 아두이노가 A<->B간 i2c통신을 B<->C간 시리얼 통신을 하며, 데이터를 주고 받는다.

센서를 많이 달았지만, 장애물 검출 영역이 제한적이고 센서의 검출 조건도 제한 적이라

자동 주행시 애매한 조건에서 헤딩을 많이 한다. 결론은 센서로 해결하자면 끝이 없다는것.




탱크 수동조작 영상





posted by 래머
prev 1 next