산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

MobBob:Android 스마트폰으로 제어되는 DIY Arduino 로봇

구성품 및 소모품

Arduino Nano R3
× 1
HC-05 블루투스 모듈
× 1
SG90 마이크로 서보 모터
× 4
4xAA 배터리 홀더
× 1
AA 배터리
× 4

필요한 도구 및 기계

납땜 인두(일반)
3D 프린터(일반)

앱 및 온라인 서비스

Arduino IDE

이 프로젝트 정보

웹사이트의 지침에 따라 이 로봇을 만들었습니다. Arduino와 Android의 공생을 위한 좋은 아이디어입니다.

MobBob은 스마트폰으로 제어되는 로봇입니다. MobBob은 스마트폰의 성능을 활용하여 음성 인식 및 컴퓨터 비전 기능을 갖춘 걷고 말하는 로봇입니다.

3D 프린팅 부품:https://www.thingiverse.com/thing:715688

Android.apk 파일:https://apkpure.com/mobbob/com.cevinius.MobBob

코드에서 다음을 수행할 수 있습니다.

- 빌드와 일치하도록 핀 변수 업데이트

- 서보 센터, 최소값 및 최대값 조정

- 엉덩이 서보가 장착되는 방식에 따라 "FRONT_JOINT_HIPS"를 1 또는 -1로 설정합니다. 몹밥 전면에 서보 악셀로 장착합니다. 이 구성의 경우 이 값을 1로 설정합니다.

<섹션 클래스="섹션 컨테이너 섹션 축소 가능" id="코드">

코드

<울>
  • 코드
  • 코드Arduino
    <사전>/* * ===============================================================* MobBob 제어 프로그램 - 소프트웨어 직렬 Bluetooth 버전 * Kevin Chan(일명 Cevinius) 작성 * ===============================================================* * 이 프로그램 직렬 명령을 사용하여 MobBob을 제어할 수 있습니다. 이 버전의 코드에서 * 명령은 상단 근처의 #define에 정의된 핀과 함께 소프트웨어 직렬 포트를 통해 수신됩니다. * 이것은 모든 Arduino 호환 보드를 사용할 수 있고 블루투스 카드를 소프트웨어 직렬용으로 설정된 핀에 꽂을 수 있음을 의미합니다. (DFRobot의 Bluno 보드용으로 설계된 이 버전의 다른 버전과 반대입니다.) * * 이 프로그램은 길고 2개의 주요 구성요소(부드러운 서보 애니메이션 프로그램 및 직렬 * 명령 파서 프로그램)를 포함합니다. * * 애니메이션 시스템 * ================* 애니메이션 프로그램은 서보 키프레임 배열을 매끄럽게 애니메이션하도록 설계되었습니다. 코드는 사용하기 쉽도록 최선을 다합니다. * * 애니메이션 시스템은 1개의 명령만 대기열에 넣습니다. 즉, 하나의 명령이 실행될 수 있고 * 및 하나의 명령이 대기할 수 있습니다. 더 많은 명령을 보내면 대기 중인 명령을 덮어씁니다. * * 애니메이션 시스템은 기본적으로 다음 애니메이션을 시작하기 전에 현재 애니메이션이 끝날 때까지 기다립니다. 이것은 * 애니메이션 데이터가 기본 포즈의 로봇으로 끝나면 모든 것이 원활하게 결합된다는 것을 의미합니다. 이를 지원하기 위해 애니메이션 시스템에는 로봇을 기본 포즈로 되돌리기 위해 애니메이션이 "마무리 시퀀스"를 가질 수 있는 기능도 있습니다. 이 기능은 앞으로/뒤로 걷기 애니메이션에 사용됩니다. * 해당 애니메이션에는 로봇을 기본 포즈로 되돌리는 최종 시퀀스가 ​​있습니다. * * 애니메이션 재생이 끝나면 애니메이션 시스템은 직렬 포트에 응답 문자열을 출력합니다. * 이렇게 하면 호출자가 요청한 애니메이션 재생이 완료되었는지 알 수 있습니다. 이것은 사용자가 애니메이션을 시퀀싱하는 데 유용합니다. - 다른 애니메이션을 시작하기 전에 하나가 끝날 때까지 기다리는 것입니다. * * 애니메이션 코드에는 조정할 수 있는 많은 변수가 있습니다. 예 업데이트 빈도, arduino 핀 등 * * 애니메이션 데이터 배열 형식도 손으로 쉽게 편집할 수 있도록 설계되었습니다. * * Command Parser * ==============* 이 시스템은 직렬을 통해 수신된 명령을 구문 분석하고 처리합니다. 명령에는 서보 위치를 직접 설정하기 위한 명령과 미리 정의된 애니메이션 및 걷기를 트리거하기 위한 명령이 포함됩니다. * * 따라서 걷기의 세부 사항에 대해 걱정하고 싶지 않은 사용자는 미리 정의된 걷기/애니메이션을 사용하면 됩니다. * 그리고 서보를 완벽하게 제어하려는 사용자(즉석에서 새 애니메이션을 만들기 위해)도 그렇게 할 수 있습니다. * * 위에서 언급했듯이 이러한 명령은 Arduino 직렬 모니터에서 대화식으로 사용할 수 있습니다. Bluetooth LE를 사용하여 * 보낼 수도 있습니다(Bluno 사용 시). 전화 앱은 Bluetooth LE를 통해 * Bluno로 명령을 보냅니다. * * 일반 명령어:* ----------------- * 준비/확인 확인:<확인> * 상태 확인. 컨트롤러가 작동하는지 확인하기 위해 응답이 즉시 반환됩니다. * * 서보 설정: * time - 지정된 각도로 트위닝할 시간, 0은 즉시 각도로 점프합니다. * leftHip - 중심에서 마이크로초. -ve는 힙 인, +ve는 힙 아웃 * leftFoot - 플랫에서 마이크로초입니다. -ve는 발 아래로, +ve는 발 위로 * rightHip - 중심에서 마이크로초입니다. -ve는 힙 인, +ve는 힙 아웃 * rightFoot - 플랫에서 마이크로초. -ve는 발 아래, +ve는 발 위로 * 이 명령은 서보를 완전히 제어하는 ​​데 사용됩니다. 지정된 기간 동안 * 현재 포즈에서 지정된 포즈로 로봇을 트위닝할 수 있습니다. * * 중지/리셋: * 현재 애니메이션 후 로봇을 중지합니다. 무한 루프 *로 설정된 애니메이션을 중지하는 데 사용할 수 있습니다. 이것은 또한 로봇을 기본 포즈(직선으로 서 있음)에 두는 데 사용할 수 있습니다. * * 즉시 중지: * 현재 애니메이션이 완료될 때까지 기다리지 않고 즉시 로봇을 중지합니다. 이것은 * 로봇의 현재 애니메이션을 중단합니다. 잠재적으로 로봇은 애니메이션 중간 * 및 불안정한 자세에 있을 수 있으므로 이를 사용할 때 주의하십시오. * * 표준 걷기 명령:* ----------------------- * 앞으로:, -1은 연속을 의미하고, 0 또는 매개변수 없음은 1번과 동일합니다. * 역방향:, -1은 연속을 의미하며 0 또는 매개변수가 없는 경우 1번과 같습니다. * 좌회전:, -1은 연속을 의미하고, 0 또는 매개변수가 없는 것은 1번과 같습니다. * 우회전:, -1은 연속을 의미하며 0 또는 매개변수가 없는 경우 1번과 같습니다. * * 재미있는 애니메이션 명령:* ----------------------- * Shake Head:, -1은 연속, 0 또는 매개변수 없음을 의미합니다. 1번과 같습니다. * * 바운스:, -1은 연속을 의미하며 0 또는 매개변수가 없는 경우 1회와 동일합니다. * * Wobble:, -1은 연속을 의미하고 매개변수가 0이거나 없는 것은 1회와 같습니다. * 왼쪽 워블:, -1은 연속을 의미하고, 0 또는 매개변수가 없는 것은 1번과 같습니다. * 오른쪽 워블:, -1은 연속을 의미하며, 0 또는 매개변수가 없는 것은 1번과 같습니다. * * Tap Feet:, -1은 연속을 의미하고, 0 또는 매개변수가 없는 것은 1회와 동일합니다. * 왼발 누르기:, -1은 연속을 의미하고 매개변수가 0이거나 없는 것은 1회와 같습니다. * 오른발 누르기:, -1은 연속을 의미하고 매개변수가 0이거나 없는 경우 1회와 동일합니다. * * 다리 흔들기:, -1은 연속을 의미하고 매개변수가 0이거나 없는 경우 1회와 같습니다. * 왼쪽 다리 흔들기:, -1은 연속을 의미하며, 0 또는 매개변수가 없는 경우 1회와 동일합니다. * 오른쪽 다리 흔들기:, -1은 연속을 의미하며, 매개변수가 0이거나 없는 것은 1회와 같습니다. * * 또한 명령 실행이 완료되면 코드가 직렬로 응답 문자열을 * 보냅니다. * * 명령이 정상적으로 종료된 경우 응답 문자열은 * 매개변수가 없는 명령 코드입니다. 예 진행이 완료되면 "" 응답을 보냅니다. * * 명령이 로 중단된 경우 현재 애니메이션이 중간에 중지되었을 수 있습니다. * 이 경우 로봇이 이상한 중간 포즈를 취할 수 있고 finishAnim이 재생되지 않았을 수 있습니다. 사용자에게 이러한 일이 발생했음을 알리기 위해 응답 문자열에는 * 매개변수 -1이 포함됩니다. 예를 들어 를 사용하여 걷기가 중간에 중지된 경우 응답 문자열은 * 이 되어 걷기가 중지되었지만 중간에 중지되었음을 나타냅니다. * (참고:를 사용하여 중지하면 중지하기 전에 현재 애니메이션 주기가 완료될 때까지 * 기다리게 됩니다. 따라서 이 경우 애니메이션은 중간에 중지되지 않습니다.) * * 응답이 애니메이션이 완료되면 명령 발신자는 * 응답 문자열을 찾아 로봇이 새 명령을 받을 준비가 된 시기를 결정할 수 있습니다. * 예 명령을 사용하면 3단계 *(그리고 애니메이션 종료)가 모두 완료될 때까지 응답 문자열이 전송되지 않습니다. 따라서 명령 발신자는 로봇에게 다음 작업을 수행하도록 지시하기 전에 응답 * 문자열을 기다릴 수 있습니다. */ #include #include //-------------------------------- -------------------------------------------------- // 직렬 통신 속도 - 직렬(블루투스) 카드에 대해 설정합니다.//--------------------------------------------- -------------------------------------------------- --/ 블루투스 보드와의 직렬 통신 속도.// 일부 보드의 기본값은 9600입니다. 제가 가지고 있는 보드의 기본값은 115200입니다.#define SERIAL_SPEED 115200// 이 핀에 소프트웨어 직렬 포트를 설정합니다.const int rxPin =11; // dataconst를 수신하는 데 사용되는 핀 int txPin =12; // dataSoftwareSerial을 보내는 데 사용되는 핀 softwareSerial(rxPin, txPin);//---------------------------------- ------------------------------------------------// Arduino 핀 설정 - 특정 로봇에 대해 설정합니다.//------------------------------------- ------------------------------------------ const int SERVO_LEFT_HIP =5;const int SERVO_LEFT_FOOT =2;const int SERVO_RIGHT_HIP =3;const int SERVO_RIGHT_FOOT =4;// 이 코드를 모든 4서보 바이페드에서 사용할 수 있기를 원합니다! (Bob, MobBob처럼)// 일부 빌드는 내가 MobBob을 수행한 방식과 다른 방식으로 힙 서보를 마운트하는 것을 확인했습니다. 따라서 이 설정을 사용하면 두 빌드 스타일에 대해// 코드를 구성할 수 있습니다.// MobBob 스타일의 경우 1 전면을 향한 엉덩이(관절이 앞쪽을 향함)// -1 for Bob 스타일 후면을 향한 엉덩이(관절이 뒤쪽을 향함)#define FRONT_JOINT_HIPS 1//------------------- -------------------------------------------------- -------------// Servo Max/Min/Centre Constants - 특정 로봇에 대해 설정합니다.//------------------ -------------------------------------------------- --------------const int LEFT_HIP_CENTRE =1580;const int LEFT_HIP_MIN =LEFT_HIP_CENTRE - 500;const int LEFT_HIP_MAX =LEFT_HIP_CENTRE + 500;const int LEFT_FOOT_CENTRE const int LEFT_FOOT_MAX =LEFT_FOOT_CENTRE + 500;const int RIGHT_HIP_CENTRE =1500;const int RIGHT_HIP_MIN =RIGHT_HIP_CENTRE - 500;const int RIGHT_HIP_MAX =RIGHT_HIP_CENTRE 465; const int RIGHT_FOOT_MIN =RIGHT_FOOT_CENTRE - 500; const int RIGHT_FOOT_MAX =RIGHT_FOOT_CENTRE + 500;//--------------------------------------- ------------------------------------------------// 보다 사용자 친화적인 방식으로 관절 값을 계산하는 데 도움이 되는 도우미 기능.// 서보가 다른 방식으로 설정된 경우 여기에서 기호를 조정할 수 있습니다.// 여기에서 업데이트하면 애니메이션 데이터를 수정할 필요가 없다는 의미입니다. // 서보는 다르게 설정됩니다. // (예 원래 Bob의 엉덩이 서보는 MobBob의 뒤쪽입니다.)//// (또한 왼쪽/오른쪽 엉덩이 및 왼쪽/오른쪽 발에 대해 // 다르기 때문에 각 서보에 사용할 기호를 기억하기 어렵습니다.) //------------------------------------------------ ------------------------------int LeftHipCentre() { return LEFT_HIP_CENTRE; }int LeftHipIn(int 밀리초) { 반환 LEFT_HIP_CENTRE + (FRONT_JOINT_HIPS * 밀리초); }int LeftHipOut(int 밀리초) { 반환 LEFT_HIP_CENTRE - (FRONT_JOINT_HIPS * 밀리초); }int RightHipCentre() { RIGHT_HIP_CENTRE를 반환합니다. }int RightHipIn(int 밀리초) { return RIGHT_HIP_CENTRE - (FRONT_JOINT_HIPS * 밀리초); }int RightHipOut(int 밀리초) { return RIGHT_HIP_CENTRE + (FRONT_JOINT_HIPS * 밀리초); }int LeftFootFlat() { 반환 LEFT_FOOT_CENTRE; }int LeftFootUp(int 밀리초) { 반환 LEFT_FOOT_CENTRE - 밀리초; }int LeftFootDown(int 밀리초) { LEFT_FOOT_CENTRE + 밀리초를 반환합니다. }int RightFootFlat() { RIGHT_FOOT_CENTRE를 반환합니다. }int RightFootUp(int 밀리초) { RIGHT_FOOT_CENTRE + 밀리초를 반환합니다. }int RightFootDown(int 밀리초) { RIGHT_FOOT_CENTRE 반환 - 밀리초; }//----------------------------------------------- -----------------------------------// 표준 보행 보행 및 기타 서보 애니메이션을 위한 키프레임 애니메이션 데이터// // 형식은 { , ​​, , , }// 밀리초 - 이 키프레임의 위치로 트위닝할 시간입니다. 예 500은 // 이 프레임의 시작에 있는 로봇의 위치에서 이 프레임에 지정된 위치까지 이동하는 데 500ms가 걸린다는 것을 의미합니다. // leftHipMicros - 서보 마이크로초 단위의 왼쪽 엉덩이 위치.// leftFootMicros - 서보의 왼쪽 엉덩이 위치 microsecs.// rightHipMicros - 서보 마이크로초에서 왼쪽 엉덩이의 위치.// rightFootMicros - 서보 마이크로초에서 왼쪽 엉덩이의 위치// // 서보 마이크로 값은 -1의 특수 값을 지원합니다. 이 값이 주어지면 // 이 키프레임에서 이 서보를 무시하도록 애니메이션 코드에 지시합니다. 즉, 해당 서보는 // 이 키프레임의 시작 부분에 있던 위치에 유지됩니다. //// 또한 애니메이션 데이터의 첫 번째 요소가 특별하게 배치됩니다. 메타데이터 요소입니다.// 첫 번째 요소는 { , 0, 0, 0, 0 }이며 애니메이션의// 프레임 수를 알려줍니다. 따라서 첫 번째 실제 키프레임은 animData[1]에 있고 마지막 키프레임//은 animData[]에 있습니다. (여기서 는 animData[0][0]의 값입니다.)//----------------------------------------- -------------------------------------------------- ---// 키프레임 배열에 더 쉽게 액세스할 수 있도록 하는 상수.const int TWEEN_TIME_VALUE =0;const int LEFT_HIP_VALUE =1;const int LEFT_FOOT_VALUE =2;const int RIGHT_HIP_VALUE =3;const int RIGHT_FOOT/VALUE =4 사용됨; 걷기 애니메이션에서 data.const int FOOT_DELTA =150;const int HIP_DELTA =FRONT_JOINT_HIPS * 120;// 기본 서 있는 직선 위치로 이동합니다. stopAnim().int에 의해 사용됨 standStraightAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 1, 0, 0, 0, 0 }, // 수평 피트, 피트 짝수 { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }};// 이에 앞서 로봇을 평평한 피트, 짝수 피트(예:standStraightAnim).int walkForwardAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 8, 0, 0, 0, 0 }, // 왼쪽으로 기울이기, 피트 짝수 { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 오른쪽 발 forward { 300, LeftHipIn(HIP_DELTA), LeftFootUp(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // 발을 평평하게, 오른발 앞으로 { 300, LeftHipIn(HIP_DELTA), RightFootFlatOut(),(), RightFootFlat() }, // 오른쪽으로 기울이기, 오른발 앞으로 { 300, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 발도 짝수 { 300, LeftHipCentre (), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 왼발 앞으로 { 300, LeftHipOut(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipIn(HIP_DELTA), Right , // 평평한 발, 왼발 앞으로 { 300, LeftHipOut(HIP_DELTA), LeftFootFlat(), RightHipIn(HIP_DELTA), RightFootFlat() }, // 왼쪽으로 기울이기, 왼발 앞으로 { 300, LeftHipOut(HIP_DEL TA), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }};// 이에 앞서 로봇을 Feet Flat, Feet Even(예:standStraightAnim).int walkBackwardAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 8, 0, 0, 0, 0, 0 }, // 왼쪽으로 기울이기, 피트 짝수 { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 왼발 forward { 300, LeftHipOut(HIP_DELTA), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // 발을 평평하게, 왼발 앞으로 { 300, LeftHipOut(HIP_DELTA), Right_HipFlat(), RightFootFlat(), RightFootFlat() }, // 오른쪽으로 기울이기, 왼발 앞으로 { 300, LeftHipOut(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 발 짝수 { 300, LeftHipCentre (), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 오른발 앞으로 { 300, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipOut(HIP_DELTA), Right , // 발을 평평하게, 오른발 앞으로 { 300, LeftHipIn(HIP_DELTA), LeftFootFlat(), RightHipOut(HIP_DELTA), RightFootFlat() }, // 왼쪽으로 기울이기, 오른발 앞으로 { 300, LeftHipIn(HIP_DELTA ), LeftFootUp(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootDown(FOOT_DELTA) }};// Finish 도보 애니메이션은 로봇을 walkForwardAnim/walkBackwardAnim 끝에서 다시 standStraightAnim.int로 이동합니다. walkEndAnim[][5] ={ // 메타데이터 . 첫 번째 요소는 프레임 수입니다. { 2, 0, 0, 0, 0 }, // 왼쪽으로 기울이기, 짝수 피트 { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 짝수 다리, 짝수 { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }};// 이에 앞서 로봇을 Feet Flat, Feet Even(예:standStraightAnim).int turnLeftAnim[][5] ={ / / 메타데이터. 첫 번째 요소는 프레임 수입니다. { 6, 0, 0, 0, 0 }, // 왼쪽으로 기울이기, 피트 짝수 { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 왼쪽으로 회전 엉덩이, 오른쪽 엉덩이 회전 { 300, LeftHipIn(HIP_DELTA), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // 발 평평하게, 왼쪽 엉덩이 회전, 오른쪽 엉덩이 회전 { 300, LeftHipIn(HIP_DELTA), (), RightHipIn(HIP_DELTA), RightFootFlat() }, // 오른쪽으로 기울이기, 왼쪽 엉덩이 회전, 오른쪽 엉덩이 회전 { 300, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootUp(FOOT_DELTA) // 오른쪽으로 기울이기, 피트 짝수 { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 피트 짝수, 피트 짝수 { 300, LeftHipCentre(), LeftFootFlat(), RightHipC ), RightFootFlat() }};// 이에 앞서 로봇을 Feet Flat, Feet Even(예:standStraightAnim).int turnRightAnim[][5] ={ // Metadata. 첫 번째 요소는 프레임 수입니다. { 6, 0, 0, 0, 0 }, // 오른쪽으로 기울이기, 피트 짝수 { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 왼쪽으로 회전 hip, 오른쪽 엉덩이 회전 { 300, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootUp(FOOT_DELTA) }, // 발 평평하게, 왼쪽 엉덩이 회전, 오른쪽 엉덩이 회전 { 300, LeftHipIn(HIP_FootFlat), (), RightHipIn(HIP_DELTA), RightFootFlat() }, // 왼쪽으로 기울이기, 왼쪽 엉덩이 회전, 오른쪽 엉덩이 회전 { 300, LeftHipIn(HIP_DELTA), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) // 왼쪽으로 기울이기, 피트 짝수 { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 피트 짝수 { 300, LeftHipCentre(), LeftFootFlat(), RightHipC ), RightFootFlat() }};// 머리 애니메이션을 흔듭니다. 흔들리는 head.int shakeHeadAnim[][5] ={ // Metadata. 첫 번째 요소는 프레임 수입니다. { 4, 0, 0, 0, 0 }, // 평평한 다리, 왼쪽으로 비틀기 { 150, LeftHipOut(HIP_DELTA), LeftFootFlat(), RightHipIn(HIP_DELTA), RightFootFlat() }, // 평평한 다리, 다리 짝수 { 150 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }, // 발을 평평하게, 오른쪽으로 비틀기 { 150, LeftHipIn(HIP_DELTA), LeftFootFlat(), RightHipOut(HIP_DELTA), // FeetFlat() }, flat, Feet even { 150, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() } };// 흔들리는 애니메이션. 재미있는 작업을 위해 왼쪽과 오른쪽으로 기울입니다. wobbleAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 4, 0, 0, 0, 0 }, // 왼쪽으로 기울이기, 짝수 발 { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 짝수 발, 짝수 { 300 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }, // 오른쪽으로 기울이기, 피트 짝수 { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) flat, Feet even { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() } };// 왼쪽 애니메이션을 흔들립니다. 왼쪽으로 기울이고 뒤로.int wobbleLeftAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 2, 0, 0, 0, 0 }, // 왼쪽으로 기울이기, 짝수 발 { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 짝수 발, 짝수 { 300 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() },};// 오른쪽 애니메이션을 흔들립니다. 오른쪽으로 기울이고 뒤로.int wobbleRightAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 2, 0, 0, 0, 0 }, // 오른쪽으로 기울이기, 짝수 발 { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 짝수 발, 짝수 { 300 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() } };// 발 애니메이션을 탭합니다. 두 발을 모두 탭합니다.int tapFeetAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 2, 0, 0, 0, 0 }, // 양발 들기, 발 짝수 { 500, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 발 평평하게, 발 짝수 { 500, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() },};// 왼발 탭 anim.int tapLeftFootAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 2, 0, 0, 0, 0 }, // 왼발 들기, 짝수 발 { 500, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootFlat() }, // 짝수 발, 짝수 발 { 500 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() },};// 오른발 탭 ​​anim.int tapRightFootAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 2, 0, 0, 0, 0 }, // 오른발 올리기, 짝수 발 { 500, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 짝수 발, 짝수 발 { 500 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() },};// 상하로 바운스 anim.int bounceAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 2, 0, 0, 0, 0 }, // 양발 들기, 발 짝수 { 500, LeftHipCentre(), LeftFootDown(300), RightHipCentre(), RightFootDown(300) }, // 발 평평, 발 짝수 { 500, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() },};// 다리 흔들기 Animation.int shakeLegsAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 14, 0, 0, 0, 0 }, // 왼쪽으로 기울이기, 피트 짝수 { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, {에서 오른쪽 엉덩이 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 피트 짝수 { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA)(ownTA, RightHipCent_DELTA), RightHipCent_ , // 왼쪽으로 기울이기, 오른쪽 엉덩이 밖으로 { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 피트 짝수 { 100, LeftHipCentre(), LeftFootUp()FOOT_DELTA() , RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 오른쪽 엉덩이에서 { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // 심지어 왼쪽으로 기울이기, Feet 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 발을 평평하게, 발을 짝수 { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat 틸트 리그 ht, Feet even { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // { 100에서 왼쪽 엉덩이, LeftHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA)에서 오른쪽으로 기울이기 , RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 발 짝수 { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 왼쪽 엉덩이 밖으로 { 100, LeftHip_DELTA( ), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 피트 짝수 { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) flat } Feet , Feet even { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() } };// 왼쪽 다리 흔들기 Animation.int shakeLeftLegAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 12, 0, 0, 0, 0 }, // 오른쪽으로 기울이기, 피트 짝수 { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // {에서 오른쪽으로 기울이기, 왼쪽 엉덩이 100, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 피트 짝수 { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA)(), RightHipDown(FOOT_DELTA)() , // 오른쪽으로 기울이기, 왼쪽 엉덩이 밖으로 { 100, LeftHipOut(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 발 짝수 { 100, LeftHipCentre(), LeftFootDELTA)(FOOT_DELTA) { RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 왼쪽 엉덩이에서 { 100, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, Feet 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 왼쪽 엉덩이 밖으로 { 100, LeftHipOut(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipC otUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 피트 짝수 { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // { 100에서 오른쪽으로 기울이기, 왼쪽 엉덩이에서, LeftHipIn(HIP_ , LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // 오른쪽으로 기울이기, 피트 짝수 { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) // 피트 짝수 { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() } };// 오른쪽 다리 흔들기 Animation.int shakeRightLegAnim[][5] ={ // 메타데이터. 첫 번째 요소는 프레임 수입니다. { 12, 0, 0, 0, 0 }, // 왼쪽으로 기울이기, 피트 짝수 { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, {에서 오른쪽 엉덩이 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 피트 짝수 { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA)(ownTA, RightHipCent_DELTA), RightHipCent_ , // 왼쪽으로 기울이기, 오른쪽 엉덩이 밖으로 { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 피트 짝수 { 100, LeftHipCentre(), LeftFootUp()FOOT_DELTA() , RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 오른쪽 엉덩이에서 { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // 심지어 왼쪽으로 기울이기, Feet 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 오른쪽 엉덩이 밖으로 { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightFootDELTA(FOOT_DELTA), RightFootDELTA(HIP_DELTA) (FOOT_DELTA) }, // 왼쪽으로 기울이기, 피트 짝수 { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 오른쪽 엉덩이에서 { 100, LeftHipCentre(), LeftFootCentre(), (FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // 왼쪽으로 기울이기, 피트 짝수 { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) 플랫 심지어 { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }, };//------------------------- -------------------------------------------------- -------// 서보 위치 설정/트위닝을 위한 특수 동적 애니메이션 데이터.//---------------------------- -------------------------------------------------- ----// 이것은 우리가 SetServos() 함수에 사용하는 2개의 특수 애니메이션 데이터입니다. 그것들은 // 단일 프레임을 가지고 있습니다. 그것들은 이 anim 데이터의 데이터를 변경하고 이를 재생하여 //servos.int setServosAnim1[][5] ={ // Metadata를 이동합니다. 첫 번째 요소는 프레임 수입니다. { 1, 0, 0, 0, 0 }, // 왼쪽으로 기울이기, 피트 짝수 { 0, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }};int setServosAnim2[][5] ={ / / 메타데이터. 첫 번째 요소는 프레임 수입니다. { 1, 0, 0, 0, 0 }, // Tilt left, Feet even { 0, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }};//----------------------------------------------------------------------------------// Servo Variables//----------------------------------------------------------------------------------Servo servoLeftHip;Servo servoLeftFoot;Servo servoRightHip;Servo servoRightFoot;//----------------------------------------------------------------------------------// State variables for playing animations.//----------------------------------------------------------------------------------// Milliseconds between animation updates.const int millisBetweenAnimUpdate =20;// Time when we did the last animation update.long timeAtLastAnimUpdate;// Related to currently playing anim.int (*currAnim)[5]; // Current animation we're playing.int (*finishAnim)[5]; // Animation to play when the currAnim finishes or is stopped.long timeAtStartOfFrame; // millis() at last keyframe - frame we're lerping fromint targetFrame; // Frame we are lerping toint animNumLoops; // Number of times to play the animation. -1 means loop forever.char animCompleteStr[3] ="--"; // This is a 2 character string. When the anim is complete, // we print out the status as "<" + animComplereStr + ">".// Related to anim queue. 즉. Next anim to play.bool animInProgress; // Whether an animation is playingint (*nextAnim)[5]; // This is the next animation to play once the current one is done. // i.e. It's like a queue of size 1! // If curr is non-looping, we play this at the end of the current anim. // If curr is looping, this starts at the end of the current loop, // replacing curr anim. // If nothing is playing, this starts right away. int (*nextFinishAnim)[5]; // This is the finish animation for the queued animation.int nextAnimNumLoops; // Number of times to play the animation. -1 means loop forever.char nextAnimCompleteStr[3] ="--"; // This is a 2 character string. When the anim is complete, // we print out the status as "<" + animComplereStr + ">".bool interruptInProgressAnim; // Whether to change anim immediately, interrupting the current one.// Curr servo positionsint currLeftHip;int currLeftFoot;int currRightHip;int currRightFoot;// Servo positions at start of current keyframeint startLeftHip;int startLeftFoot;int startRightHip;int startRightFoot;//-------------------------------------------------------------------------------// Parser Variables//-------------------------------------------------------------------------------// Constant delimiter tag charsconst char START_CHAR ='<';const char END_CHAR ='>';const char SEP_CHAR =',';// Constants and a variable for the parser state.const int PARSER_WAITING =0; // Waiting for '<' to start parsing.const int PARSER_COMMAND =1; // Reading the command string.const int PARSER_PARAM1 =2; // Reading param 1.const int PARSER_PARAM2 =3; // Reading param 2.const int PARSER_PARAM3 =4; // Reading param 3.const int PARSER_PARAM4 =5; // Reading param 3.const int PARSER_PARAM5 =6; // Reading param 3.const int PARSER_EXECUTE =7; // Finished parsing a command, so execute it.// Current parser state.int currParserState =PARSER_WAITING; // String for storing the command. 2 chars for the command and 1 char for '\0'.// We store the command here as we're parsing.char currCmd[3] ="--";// For tracking which letter we are in the command.int currCmdIndex;// Max command length.const int CMD_LENGTH =2;// Current param values. Store them here after we parse them.int currParam1Val;int currParam2Val;int currParam3Val;int currParam4Val;int currParam5Val;// Variable for tracking which digit we're parsing in a param.// We use this to convert the single digits back into a decimal value.int currParamIndex;// Whether the current param is negative.boolean currParamNegative;// Max parameter length. Stop parsing if it exceeds this.const int MAX_PARAM_LENGTH =6;//===============================================================================// Arduino setup() and loop().//===============================================================================void setup() { // Setup the main serial port softwareSerial.begin(SERIAL_SPEED); // Setup the Servos servoLeftHip.attach( SERVO_LEFT_HIP, LEFT_HIP_MIN, LEFT_HIP_MAX); servoLeftFoot.attach( SERVO_LEFT_FOOT, LEFT_FOOT_MIN, LEFT_FOOT_MAX); servoRightHip.attach( SERVO_RIGHT_HIP, RIGHT_HIP_MIN, RIGHT_HIP_MAX); servoRightFoot.attach(SERVO_RIGHT_FOOT, RIGHT_FOOT_MIN, RIGHT_FOOT_MAX); // Set things up for the parser. setup_Parser(); // Set things up for the animation code. setup_Animation();}void loop() { // Update the parser. loop_Parser(); // Update the animation. loop_Animation();}//===============================================================================// Related to the parser//===============================================================================// Sets up the parser stuff. Called in setup(). Should not be called elsewhere.void setup_Parser(){ // Wait for first command. currParserState =PARSER_WAITING; // Print this response to say we've booted and are ready. softwareSerial.println("");}// Loop() for the parser stuff. Called in loop(). Should not be called elsewhere.void loop_Parser(){ //--------------------------------------------------------- // PARSER // // If there is data, parse it and process it. //--------------------------------------------------------- // Read from pin serial port and write it out on USB port. if (softwareSerial.available()> 0) { char c =softwareSerial.read(); // If we're in WAITING state, look for the START_CHAR. if (currParserState ==PARSER_WAITING) { // If it's the START_CHAR, move out of this state... if (c ==START_CHAR) { // Start parsing the command. currParserState =PARSER_COMMAND; // Reset thing ready for parsing currCmdIndex =0; currCmd[0] ='-'; currCmd[1] ='-'; currParam1Val =0; currParam2Val =0; currParam3Val =0; currParam4Val =0; currParam5Val =0; } // Otherwise, stay in this state. } // In the state to look for the command. else if (currParserState ==PARSER_COMMAND) { // Else if it's a separator, parse parameter 1. But make sure it's not // empty, or else it's a parse error. if (c ==SEP_CHAR) { if (currCmdIndex ==CMD_LENGTH) { currParserState =PARSER_PARAM1; currParamIndex =0; currParamNegative =false; } else { currParserState =PARSER_WAITING; } } // Else if it's the end char, there are no parameters, so we're ready to // process. But make sure it's not empty. Otherwise, it's a parse error. else if (c ==END_CHAR) { if (currCmdIndex ==CMD_LENGTH) { currParserState =PARSER_EXECUTE; } else { currParserState =PARSER_WAITING; } } // If we've got too many letters here, we have a parse error, // so abandon and go back to PARSER_WAITING else if ( (currCmdIndex>=CMD_LENGTH) || (c <'A') || (c> 'Z') ) { currParserState =PARSER_WAITING; } // Store the current character. else { currCmd[currCmdIndex] =c; currCmdIndex++; } } // In the state to parse param 1. else if (currParserState ==PARSER_PARAM1) { // Else if it's a separator, parse parameter 1. if (c ==SEP_CHAR) { if (currParamNegative) { currParam1Val =-1 * currParam1Val; } currParserState =PARSER_PARAM2; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam1Val =-1 * currParam1Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam1Val =(currParam1Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 2. else if (currParserState ==PARSER_PARAM2) { // Else if it's a separator, parse parameter 2. if (c ==SEP_CHAR) { if (currParamNegative) { currParam2Val =-1 * currParam2Val; } currParserState =PARSER_PARAM3; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam2Val =-1 * currParam2Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam2Val =(currParam2Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 3. else if (currParserState ==PARSER_PARAM3) { // Else if it's a separator, parse parameter 2. if (c ==SEP_CHAR) { if (currParamNegative) { currParam3Val =-1 * currParam3Val; } currParserState =PARSER_PARAM4; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam3Val =-1 * currParam3Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam3Val =(currParam3Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 4. else if (currParserState ==PARSER_PARAM4) { // Else if it's a separator, parse parameter 2. if (c ==SEP_CHAR) { if (currParamNegative) { currParam4Val =-1 * currParam4Val; } currParserState =PARSER_PARAM5; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam4Val =-1 * currParam4Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam4Val =(currParam4Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 5. else if (currParserState ==PARSER_PARAM5) { // If it's the end char, there are no parameters, so we're ready to // process. if (c ==END_CHAR) { if (currParamNegative) { currParam5Val =-1 * currParam5Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam5Val =(currParam5Val * 10) + currDigitVal; currParamIndex++; } } //--------------------------------------------------------- // PARSER CODE HANDLER (Still part of Parser, but section that // processes completed commands) // // If the most recently read char completes a command, // then process the command, and clear the state to // go back to looking for a new command. // // The parsed items are stored in:// currCmd, currParam1Val, currParam2Val, currParam3Val, // currParam4Val, currParam5Val //--------------------------------------------------------- if (currParserState ==PARSER_EXECUTE) { // Ready/OK Check: if ((currCmd[0] =='O') &&(currCmd[1] =='K')) { softwareSerial.println(""); } // Set Servo: // time - time to tween to specified angles // leftHip - microsecs from centre. -ve is hip in, +ve is hip out // leftFoot - microsecs from flat. -ve is foot down, +ve is foot up // rightHip - microsecs from centre. -ve is hip in, +ve is hip out // rightFoot - microsecs from flat. -ve is foot down, +ve is foot up else if ((currCmd[0] =='S') &&(currCmd[1] =='V')) { int tweenTime =currParam1Val; if (currParam1Val <0) { tweenTime =0; } SetServos(tweenTime, currParam2Val, currParam3Val, currParam4Val, currParam5Val, "SV"); } // Stop/Reset:, Stops current anim. Also can be used to put robot into reset position. else if ((currCmd[0] =='S') &&(currCmd[1] =='T')) { StopAnim("ST"); } // Stop Immediate: else if ((currCmd[0] =='S') &&(currCmd[1] =='I')) { StopAnimImmediate("SI"); } // Forward:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='F') &&(currCmd[1] =='W')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(walkForwardAnim, walkEndAnim, numTimes, "FW"); } // Backward:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='B') &&(currCmd[1] =='W')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(walkBackwardAnim, walkEndAnim, numTimes, "BW"); } // Turn Left:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='T')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(turnLeftAnim, NULL, numTimes, "LT"); } // Turn Right:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='R') &&(currCmd[1] =='T')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(turnRightAnim, NULL, numTimes, "RT"); } // Shake Head:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='S') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeHeadAnim, NULL, numTimes, "SX"); } // Bounce:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='B') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(bounceAnim, NULL, numTimes, "BX"); } // Wobble:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='W') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(wobbleAnim, NULL, numTimes, "WX"); } // Wobble Left:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='W') &&(currCmd[1] =='Y')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(wobbleLeftAnim, NULL, numTimes, "WY"); } // Wobble Right:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='W') &&(currCmd[1] =='Z')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(wobbleRightAnim, NULL, numTimes, "WZ"); } // Tap Feet:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='T') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(tapFeetAnim, NULL, numTimes, "TX"); } // Tap Left Foot:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='T') &&(currCmd[1] =='Y')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(tapLeftFootAnim, NULL, numTimes, "TY"); } // Tap Right Foot:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='T') &&(currCmd[1] =='Z')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(tapRightFootAnim, NULL, numTimes, "TZ"); } // Shake Legs:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeLegsAnim, NULL, numTimes, "LX"); } // Shake Left Leg:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='Y')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeLeftLegAnim, NULL, numTimes, "LY"); } // Shake Right Leg:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='Z')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeRightLegAnim, NULL, numTimes, "LZ"); } //-------------------------------------------------- // Clear the state and wait for the next command! // This must be done! //-------------------------------------------------- currParserState =PARSER_WAITING; } }}//===============================================================================// Related to playing servo animations.//===============================================================================// Call this to play the given animation once. Pass in NULL if there is no finishAnim.void PlayAnim(int animToPlay[][5], int finishAnim[][5], const char *completeStr){ // Put this in the queue. PlayAnimNumTimes(animToPlay, finishAnim, 1, completeStr);}// Call this to loop the given animation. Pass in NULL if there is no finishAnim.void LoopAnim(int animToPlay[][5], int finishAnim[][5], const char *completeStr){ // Put this in the queue. PlayAnimNumTimes(animToPlay, finishAnim, -1, completeStr);}// Call this to play the given animation the specified number of times. // -1 number of times will make it loop forever.// Pass in NULL if there is no finishAnim.void PlayAnimNumTimes(int animToPlay[][5], int finishAnim[][5], int numTimes, const char *completeStr){ // Put this in the queue. nextAnim =animToPlay; nextFinishAnim =finishAnim; nextAnimNumLoops =numTimes; // Save the completeStr if (completeStr ==NULL) { nextAnimCompleteStr[0] ='-'; nextAnimCompleteStr[1] ='-'; } else { nextAnimCompleteStr[0] =completeStr[0]; nextAnimCompleteStr[1] =completeStr[1]; }}// Stop after the current animation.void StopAnim(const char *completeStr){ // Put this in the queue. PlayAnimNumTimes(standStraightAnim, NULL, 1, completeStr);}// Stop immediately and lerp robot to zero position, interrupting // any animation that is in progress.void StopAnimImmediate(const char *completeStr){ // Put this in the queue. interruptInProgressAnim =true; PlayAnimNumTimes(standStraightAnim, NULL, 1, completeStr);}// Moves servos to the specified positions. Time 0 will make it immediate. Otherwise,// it'll tween it over a specified time.// For positions, 0 means centered.// For hips, -ve is hip left, +ve is hip right// For feet, -ve is foot down, +ve is foot upvoid SetServos(int tweenTime, int leftHip, int leftFoot, int rightHip, int rightFoot, const char* completeStr){ // Save the completeStr if (completeStr ==NULL) { nextAnimCompleteStr[0] ='-'; nextAnimCompleteStr[1] ='-'; } else { nextAnimCompleteStr[0] =completeStr[0]; nextAnimCompleteStr[1] =completeStr[1]; } // Decide which tween data we use. We don't want to over-write the one that is // in progress. We have and reuse these to keep memory allocation fixed. int (*tweenServoData)[5]; if (currAnim !=setServosAnim1) { tweenServoData =setServosAnim1; } else { tweenServoData =setServosAnim2; } // Set the tween information into the animation data. tweenServoData[1][TWEEN_TIME_VALUE] =tweenTime; tweenServoData[1][LEFT_HIP_VALUE] =LeftHipIn(leftHip); tweenServoData[1][LEFT_FOOT_VALUE] =LeftFootUp(leftFoot); tweenServoData[1][RIGHT_HIP_VALUE] =RightHipIn(rightHip); tweenServoData[1][RIGHT_FOOT_VALUE] =RightFootUp(rightFoot); // Queue this tween to be played next. PlayAnim(tweenServoData, NULL, completeStr);}// Set up variables for animation. This is called in setup(). Should be not called by anywhere else.void setup_Animation(){ // Set the servos to the feet flat, feet even position. currLeftHip =LEFT_HIP_CENTRE; currLeftFoot =LEFT_FOOT_CENTRE; currRightHip =RIGHT_HIP_CENTRE; currRightFoot =RIGHT_FOOT_CENTRE; UpdateServos(); // Set the "start" positions to the current ones. So, when // we pay the next anim, we will tween from the current positions. startLeftHip =currLeftHip; startLeftFoot =currLeftFoot; startRightHip =currRightHip; startRightFoot =currRightFoot; // No animation is playing yet, and nothing in the queue yet. timeAtLastAnimUpdate =millis(); animInProgress =false; interruptInProgressAnim =false; currAnim =NULL; finishAnim =NULL; nextAnim =NULL; nextFinishAnim =NULL;}// Loop function for processing animation. This is called in every loop(). Should be be called by anywhere else.//// NOTE:The way looping animations work is that they basically add themselves back to the queue// when a cycle is done, and if there's nothing already queued up! This way, looping animations// work in a similar way to single-play animations, and fits into the queueing system.void loop_Animation(){ // Get the time at the start of this frame. long currTime =millis(); //-------------------------------------------------------------------------------------- // Decide if we want to perform the animation update. We don't execute this every frame. //-------------------------------------------------------------------------------------- if (timeAtLastAnimUpdate + millisBetweenAnimUpdate> currTime) { // Not yet time to do an anim update, so jump out. 반품; } else { // We reset the timer, and then proceed below to handle the current anim update. timeAtLastAnimUpdate =currTime; } //-------------------------------------------------------------------------------------- // Decide if we need to setup and start a new animation. We do if there's no anim // playing or we've been asked to interrupt the anim. //-------------------------------------------------------------------------------------- if ( (nextAnim !=NULL) &&(!animInProgress || interruptInProgressAnim) ) { // If this was an interrupt, we also set the "start" servo positions // to the current ones. This way, the animation system will tween from the // current positions. if (interruptInProgressAnim) { // This is the place to notify someone of an animation finishing after getting interrupted // Print the command string we just finished. -1 parameter indicates it was interrupted. softwareSerial.print("<"); softwareSerial.print(animCompleteStr); softwareSerial.println(",-1>"); // Set the "start" positions to the current ones. So, when // we pay the next anim, we will tween from the current positions. startLeftHip =currLeftHip; startLeftFoot =currLeftFoot; startRightHip =currRightHip; startRightFoot =currRightFoot; // We've handled any interrupt request, so clear the flag. interruptInProgressAnim =false; } // Store the animation we are now playing. currAnim =nextAnim; finishAnim =nextFinishAnim; animCompleteStr[0] =nextAnimCompleteStr[0]; animCompleteStr[1] =nextAnimCompleteStr[1]; nextAnim =NULL; // Queue is cleared. nextFinishAnim =NULL; nextAnimCompleteStr[0] ='-'; nextAnimCompleteStr[1] ='-'; // Record the number of times to play the animation. animNumLoops =nextAnimNumLoops; // Treat current time as start of frame for the initial lerp to the first frame. timeAtStartOfFrame =currTime; // Set the frame counters. targetFrame =1; // First frame we are lerping to. Index 0 is metadata, so skip. // An animation is now in progress animInProgress =true; } //-------------------------------------------------------------------------------------- // If we are currently playing an animation, then update the animation state and the // servo positions. //-------------------------------------------------------------------------------------- if (animInProgress) { // Determine if we need to switch to the next frame. int timeInCurrFrame =currTime - timeAtStartOfFrame; if (timeInCurrFrame> currAnim[targetFrame][TWEEN_TIME_VALUE]) { // Set the servo positions to the targetFrame's values. // We only set this if the value is> 0. -ve values means that // the current target keyframe did not alter that servos position. if (currAnim[targetFrame][LEFT_HIP_VALUE]>=0) { currLeftHip =currAnim[targetFrame][LEFT_HIP_VALUE]; } if (currAnim[targetFrame][LEFT_FOOT_VALUE]>=0) { currLeftFoot =currAnim[targetFrame][LEFT_FOOT_VALUE]; } if (currAnim[targetFrame][RIGHT_HIP_VALUE]>=0) { currRightHip =currAnim[targetFrame][RIGHT_HIP_VALUE]; } if (currAnim[targetFrame][RIGHT_FOOT_VALUE]>=0) { currRightFoot =currAnim[targetFrame][RIGHT_FOOT_VALUE]; } UpdateServos(); // These current values are now the start of frame values. startLeftHip =currLeftHip; startLeftFoot =currLeftFoot; startRightHip =currRightHip; startRightFoot =currRightFoot; // Now, we try to move to the next frame. // - If there is a next frame, set that as the new target, and proceed. // - If there's no next frame, but it's looping, we re-add this animation // to the queue. // - If there's no next frame, and this is not looping, we stop animating. // (Remember that targetFrame is 1-based since the first element of the animation // data array is metadata) // Increment targetFrame, and reset time in the current frame. targetFrame++; timeAtStartOfFrame =currTime; // If there is no next frame, we stop this current animation. // If it is looping, then we re-queue the current animation if the queue is empty. if (targetFrame> NumOfFrames(currAnim)) { // Stop the current animation. animInProgress =false; // If we're looping forever, and there's no next anim, re-queue the // animation if the queue is empty. if ((animNumLoops <0) &&(nextAnim ==NULL)) { LoopAnim(currAnim, finishAnim, animCompleteStr); } // If we're looping forever, and there is something in the queue, then // finish the animation and proceed. else if ((animNumLoops <0) &&(nextAnim !=NULL)) { if (finishAnim !=NULL) { // Switch to the finish anim. currAnim =finishAnim; finishAnim =NULL; // Record the number of times to play the animation. animNumLoops =1; // Treat current time as start of frame for the initial lerp to the first frame. timeAtStartOfFrame =currTime; // Set the frame counters. targetFrame =1; // First frame we are lerping to. Index 0 is metadata, so skip. // An animation is now in progress animInProgress =true; } else { // We've stopped, so can notify if needed. // Print the command string we just finished. softwareSerial.print("<"); softwareSerial.print(animCompleteStr); softwareSerial.println(">"); } } // If we're looping a limited number of times, and there's no next anim, // re-queue the animation if the queue is empty. else if ((animNumLoops> 1) &&(nextAnim ==NULL)) { PlayAnimNumTimes(currAnim, finishAnim, animNumLoops-1, animCompleteStr); } // In this case, numAnimLoops is 1, this is the last loop through, so // we're done. We play the finishAnim first if needed. else { // If there is a finish animation, switch to that animation. if (finishAnim !=NULL) { // Switch to the finish anim. currAnim =finishAnim; finishAnim =NULL; // Record the number of times to play the animation. animNumLoops =1; // Treat current time as start of frame for the initial lerp to the first frame. timeAtStartOfFrame =currTime; // Set the frame counters. targetFrame =1; // First frame we are lerping to. Index 0 is metadata, so skip. // An animation is now in progress animInProgress =true; } // Otherwise, we're done! We've played the finishAnim if there was one. else { // Print the command string we just finished. softwareSerial.print("<"); softwareSerial.print(animCompleteStr); softwareSerial.println(">"); } } } } // If we're still animating (i.e. the previous check didn't find that // we've finished the current animation), then proceed. if (animInProgress) { // Set the servos per data in the current frame. We only update the servos that have target // microsecond values> 0. This is to support the feature where we leave a servo at its // existing position if an animation data item is -1. float frameTimeFraction =(currTime - timeAtStartOfFrame) / ((float) currAnim[targetFrame][TWEEN_TIME_VALUE]); if (currAnim[targetFrame][LEFT_HIP_VALUE]>=0) { currLeftHip =startLeftHip + ((currAnim[targetFrame][LEFT_HIP_VALUE] - startLeftHip) * frameTimeFraction); } if (currAnim[targetFrame][LEFT_FOOT_VALUE]>=0) { currLeftFoot =startLeftFoot + ((currAnim[targetFrame][LEFT_FOOT_VALUE] - startLeftFoot) * frameTimeFraction); } if (currAnim[targetFrame][RIGHT_HIP_VALUE]>=0) { currRightHip =startRightHip + ((currAnim[targetFrame][RIGHT_HIP_VALUE] - startRightHip) * frameTimeFraction); } if (currAnim[targetFrame][RIGHT_FOOT_VALUE]>=0) { currRightFoot =startRightFoot + ((currAnim[targetFrame][RIGHT_FOOT_VALUE] - startRightFoot) * frameTimeFraction); } UpdateServos(); } }}// Move all the servo to the positions set in the curr... variables.// In the code, we update those variables and then call this to set the servos.void UpdateServos(){ servoLeftHip.writeMicroseconds(currLeftHip); servoLeftFoot.writeMicroseconds(currLeftFoot); servoRightHip.writeMicroseconds(currRightHip); servoRightFoot.writeMicroseconds(currRightFoot);}// Return the number of frames in the given animation data.// Have this helper function to avoid the "magic number" reference of animData[0][0].int NumOfFrames(int animData[][5]){ return animData[0][0];}

    회로도


    제조공정

    1. Bluetooth를 통해 제어되는 Raspberry Pi Robot
    2. DIY LUMAZOID Arduino 뮤직 비주얼라이저
    3. Arduino 디지털 주사위
    4. DIY 37 LED 룰렛 게임
    5. Arduino와 스마트폰을 사용한 DIY 전압계
    6. 음성 제어 로봇
    7. Arduino 제어 피아노 로봇:PiBot
    8. NeoMatrix Arduino Pong
    9. DIY Arduino 로봇 팔 – 손 제스처로 제어
    10. Dabble을 사용하여 제어되는 Arduino로 만든 4륜 로봇