2015年12月12日 星期六

ROSA 系統開發 17 ─ 四腳機器蜘蛛控制 (P&B 4 x 2 Spider)

http://4rdp.blogspot.com/2015/12/rosa-17-p-4-x-2-spider.html

(請開啟字幕觀賞)

P&B 4x2 Spider

前一陣子在台灣 Maker 界出現一隻開源機器蜘蛛,網友雷基士 (Regis) 很全能,除了寫程式外還 3D 列印、製作電路板,而我正在開發 ROSA,這作業系統可以控制 meArm,當然也可以控制機器蜘蛛,正好網友 Pizg 設計出新型機器蜘蛛機構請我測試,因此本文以他的作品當主體展示 ROSA 紅外線遙控器操控成果,還有採用 ROSA 解決方案,不需要用到數學三角函數,很適合學生入門學習。

這款機器蜘蛛姑且稱為 P&B 4 x 2 Spider,設計理念很簡單,要讓 Maker 玩家除了蜘蛛機構本體,其它所需零組件都盡可能在現有通路買的到,以方便無電子電路基礎玩家入門學習,減少製作電路障礙,清單如下:
1. Arduino UNO x 1
2. Arduino IO 擴充板 x 1
3. SG-90 伺服馬達 x 8
4. 雙孔 USB 行動電源 (Output 5V 2.1A) x 1
5. KEYES 紅外線遙控器及模組 x 1
6. 3-pin 母對母杜邦線 x 1
7. USB 轉 TTL 傳輸線 (或是自製 USB 電源線 ) x 1 
8. 超音波模組 (可略,目前當眼睛裝飾,也可以其它物件替代) x 1

好不多說,先看 ROSA 程式怎麼寫,

// (C) 2015, Bridan Wang, CC BY-NC-SA 3.0 TW
// http://4rdp.blogspot.tw/search/label/ROSA%20(Arduino)


#define LAST_SERVO   8  // for 4 x 2 spider 
#define SERVO_TABLE  13
#define zeo 0           // zero
#define lif 45          // lift leg
#define std 90          // stand
#define hon 251         // hold on
#define hof 250         // hold off
#define fwd 60          // forward
#define mid 90          // middle
#define bck 120         // backward

byte servo_pin[] = 
{ 0,  4,  5,  6,  7,  9, 10, 11, 12, 14, 15, 16, 17 };
byte servo_revse[] = 
{ 0,  1,  0,  0,  1,  1,  1,  0,  0,  1,  0,  0,  1 };
byte servo_angle[] = 
{ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 };
byte servo_set[] = 
{ 0, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90 };
byte servo_speed[] = 
{ 0,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5 };
byte servo_hold[] = 
{ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 };

const byte RUN_0[] PROGMEM = {                // spider start
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SPEED,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,   //0
_SERVO,fwd,lif,255,255,255,255,255,255,255,255,255,255,   //1 
_SERVO,mid,std,255,255,255,255,255,255,255,255,255,255,   //2
_SERVO,hon,hon,fwd,lif,255,255,255,255,255,255,255,255,   //3
_SERVO,255,255,mid,std,255,255,255,255,255,255,255,255,   //4
_SERVO,255,255,hon,hon,fwd,lif,255,255,255,255,255,255,   //5
_SERVO,255,255,255,255,mid,std,255,255,255,255,255,255,   //6
_SERVO,255,255,255,255,hon,hon,fwd,lif,255,255,255,255,   //7
_SERVO,255,255,255,255,255,255,mid,std,255,255,255,255,   //8
_SERVO,255,255,255,255,255,255,hon,hon,fwd,lif,255,255,   //9
_SERVO,255,255,255,255,255,255,255,255,mid,std,255,255,   //10
_SERVO,255,255,255,255,255,255,255,255,hon,hon,fwd,lif,   //11
_SERVO,255,255,255,255,255,255,255,255,255,255,mid,std,   //12
_GOTO, 212,                              // goto RUN_P    //13
};


const byte RUN_P[] PROGMEM = {                // spider stand 
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SERVO,255,std,255,std,255,std,255,std,mid,std,mid,std,   //0
_HOLD, 250,250,250,250,250,250,250,250,250,250,250,250,   //1
_STOP,                                                    //2
};

const byte RUN_1[] PROGMEM = {                 // spider wave
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_LOOP,   3,255,255,255,255,255,255,255,255,255,255,255,   //0
_SERVO,255,lif,255,255,255,255,255,255,255,255,255,255,   //1 
_SERVO,255,std,255,255,255,255,255,255,255,255,255,255,   //2 
_NEXT,   1,255,255,255,255,255,255,255,255,255,255,255,   //3
_RETURN,                                                  //4
};

const byte RUN_2[] PROGMEM = {                // spider hello
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SERVO,255,lif,255,255,255,255,255,255,255,255,255,255,   //0
_LOOP,   3,255,255,255,255,255,255,255,255,255,255,255,   //1
_SERVO,fwd,255,255,255,255,255,255,255,255,255,255,255,   //2 
_SERVO,mid,255,255,255,255,255,255,255,255,255,255,255,   //3 
_NEXT,   2,255,255,255,255,255,255,255,255,255,255,255,   //4 
_SERVO,255,std,255,255,255,255,255,255,255,255,255,255,   //5 
_RETURN,                                                  //6
};

const byte RUN_3[] PROGMEM = {              // spider dance-1
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SERVO,bck,std,bck,std,fwd,std,fwd,std,fwd,std,fwd,std,   //0
_HOLD, 150,255,150,255,150,255,150,255,150,255,150,255,   //1
_LOOP,   3,255,255,255,255,255,255,255,255,255,255,255,   //2
_SERVO,255, 60,255,120,255, 60,255,120,255, 60,255,120,   //3
_SERVO,255,std,255,std,255,std,255,std,255,std,255,std,   //4
_NEXT,   3,255,255,255,255,255,255,255,255,255,255,255,   //5 
_LOOP,   3,255,255,255,255,255,255,255,255,255,255,255,   //6
_SERVO,255,120,255, 60,255,120,255, 60,255,120,255, 60,   //7
_SERVO,255,std,255,std,255,std,255,std,255,std,255,std,   //8
_NEXT,   7,255,255,255,255,255,255,255,255,255,255,255,   //9
_GOTO,   0,                                               //10
};

const byte RUN_4[] PROGMEM = {              // spider dance-2
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SERVO,mid,std,mid,std,mid,std,mid,std,mid,std,mid,std,   //0
_SERVO,255,zeo,255,255,255,hon,255,255,255,255,255,255,   //1
_SERVO,255,std,255,255,255,255,255,255,255,255,255,255,   //2
_SERVO,255,hon,255,255,255,255,255,255,255,255,255,255,   //3
_SERVO,255,255,255,zeo,255,255,255,255,255,255,255,255,   //4
_SERVO,255,255,255,std,255,255,255,255,255,255,255,255,   //5
_SERVO,255,255,255,hon,180,255,255,255,mid,std,mid,std,   //6
_SERVO,255,255,255,255,255,255,255,zeo,255,255,255,255,   //7
_SERVO,255,255,255,255,mid,255,255,std,255,255,255,255,   //8
_SERVO,255,255,255,255,255,255,180,hon,255,255,255,255,   //9
_SERVO,255,255,255,255,255,zeo,255,255,255,255,255,255,   //10
_SERVO,255,255,255,255,255,std,255,255,255,255,255,255,   //11
_GOTO, 0,                                                 //12
};

const byte RUN_5[] PROGMEM = {};

const byte RUN_6[] PROGMEM = {};

const byte RUN_7[] PROGMEM = {           // spider speed slow
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SPEED,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,   //0
_RETURN,                                                  //1
};

const byte RUN_8[] PROGMEM = {         // spider speed normal
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SPEED,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,   //0
_RETURN,                                                  //1
};

const byte RUN_9[] PROGMEM = {           // spider speed fast
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SPEED, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,   //0
_RETURN,                                                  //1
};

const byte RUN_U[] PROGMEM = {    // spider forward x 6 steps
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SERVO,255,hon,255,lif,255,lif,255,hon,255,hon,255,lif,   //0
_SERVO,bck,255,255,255,255,255,bck,255,bck,255,255,255,   //1
_SERVO,hon,255,fwd,255,fwd,255,hon,255,hon,255,fwd,255,   //2
_SERVO,255,255,255,std,255,std,255,255,255,255,255,std,   //3
_SERVO,255,lif,255,hon,255,hon,255,lif,255,lif,255,hon,   //4
_SERVO,fwd,255,255,255,255,255,fwd,255,fwd,255,255,255,   //5
_SERVO,255,255,bck,255,bck,255,255,255,255,255,bck,255,   //6
_SERVO,255,std,hon,255,hon,255,255,std,255,std,hon,255,   //7
_GOTO,   0,                                               //8
};

const byte RUN_D[] PROGMEM = {   // spider backward x 6 steps
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SERVO,255,hon,255,lif,255,lif,255,hon,255,hon,255,lif,   //0
_SERVO,fwd,255,255,255,255,255,fwd,255,fwd,255,255,255,   //1
_SERVO,hon,255,bck,255,bck,255,hon,255,hon,255,bck,255,   //2
_SERVO,255,255,255,std,255,std,255,255,255,255,255,std,   //3
_SERVO,255,lif,255,hon,255,hon,255,lif,255,lif,255,hon,   //4
_SERVO,bck,255,255,255,255,255,bck,255,bck,255,255,255,   //5
_SERVO,255,255,fwd,255,fwd,255,255,255,255,255,fwd,255,   //6
_SERVO,255,std,hon,255,hon,255,255,std,255,std,hon,255,   //7
_GOTO,   0,                                               //8
};

const byte RUN_R[] PROGMEM = {           // spider turn right
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_LOOP,   4,255,255,255,255,255,255,255,255,255,255,255,   //0
_SERVO,fwd,lif,255,255,255,hon,255,255,255,255,255,255,   //1
_SERVO,255,std,255,255,255,255,255,255,255,255,255,255,   //2
_SERVO,255,hon,bck,lif,255,255,255,255,255,255,255,255,   //3
_SERVO,255,255,255,std,255,255,255,255,255,255,255,255,   //4
_SERVO,255,255,255,hon,255,255,bck,lif,255,255,255,255,   //5
_SERVO,255,255,255,255,255,255,255,std,255,255,255,255,   //6
_SERVO,255,255,255,255,fwd,lif,255,hon,255,255,255,255,   //7
_SERVO,255,255,255,255,255,std,255,255,255,255,255,255,   //8
_SERVO,bck,255,fwd,255,bck,hon,fwd,255,bck,255,fwd,255,   //9
_NEXT,   1,255,255,255,255,255,255,255,255,255,255,255,   //10 
_RETURN,                                                  //11
};

const byte RUN_L[] PROGMEM = {            // spider turn left
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_LOOP,   4,255,255,255,255,255,255,255,255,255,255,255,   //0
_SERVO,255,255,fwd,lif,255,255,255,255,255,255,255,255,   //1
_SERVO,255,255,255,std,255,255,255,255,255,255,255,255,   //2
_SERVO,bck,lif,255,hon,255,255,255,255,255,255,255,255,   //3
_SERVO,255,std,255,255,255,255,255,255,255,255,255,255,   //4
_SERVO,255,hon,255,255,bck,lif,255,255,255,255,255,255,   //5
_SERVO,255,255,255,255,255,std,255,255,255,255,255,255,   //6
_SERVO,255,255,255,255,255,hon,fwd,lif,255,255,255,255,   //7
_SERVO,255,255,255,255,255,255,255,std,255,255,255,255,   //8
_SERVO,fwd,255,bck,255,fwd,255,bck,hon,fwd,255,bck,255,   //9
_NEXT,   1,255,255,255,255,255,255,255,255,255,255,255,   //10 
_RETURN,                                                  //11
};

const byte RUN_K[] PROGMEM = {  // spider forward-2 x 6 steps
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SERVO,fwd,lif,hon,255,hon,255,hon,255,hon,255,hon,255,   //0
_SERVO,255,std,255,255,255,255,255,255,255,255,255,255,   //1
_SERVO,255,hon,fwd,lif,255,255,255,255,255,255,255,255,   //2
_SERVO,255,255,255,std,255,255,255,255,255,255,255,255,   //3
_SERVO,255,255,255,hon,fwd,lif,fwd,lif,255,255,255,255,   //4
_SERVO,255,255,255,255,255,std,255,std,255,255,255,255,   //5
_SERVO,255,255,255,255,255,hon,255,hon,fwd,lif,fwd,lif,   //6
_SERVO,255,255,255,255,255,255,255,255,255,std,255,std,   //7
_SERVO,bck,255,bck,255,bck,255,bck,255,bck,hon,bck,hon,   //8
_GOTO,   0,                                               //9
};

const byte RUN_S[] PROGMEM = {                  // spider sit
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_SERVO,255,lif,255,lif,255,lif,255,lif,255,lif,255,lif,   //0
_GOTO, 200,                              // goto RUN_N    //1
};

const byte RUN_N[] PROGMEM = {                 // spider stop
//   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12  
_HOLD,   1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,   //0 
_STOP,                                                    //1
};

const byte *ACTION[] = {
  RUN_N, // none,
  RUN_1, // 1
  RUN_2, // 2
  RUN_3, // 3
  RUN_4, // 4
  RUN_5, // 5
  RUN_6, // 6
  RUN_7, // 7
  RUN_8, // 8
  RUN_9, // 9,  
  RUN_0, // 0
  RUN_S, // *
  RUN_P, // #
  RUN_U, // up
  RUN_D, // down
  RUN_R, // right
  RUN_L, // left
  RUN_K  // ok
};

const byte IR_CODE[][18] PROGMEM = {
//  X, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, *, #,up,dn,rt,lf,ok
    0,22,25,13,12,24,94, 8,28,90,82,66,74,70,21,67,68,64,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

void(*FUNC_JUMP[])(void) = {
  FUNC_N,            // none
  FUNC_N, // FUNC_1, // 1
  FUNC_N, // FUNC_2, // 2
  FUNC_N, // FUNC_3, // 3
  FUNC_N, // FUNC_4, // 4
  FUNC_N, // FUNC_5, // 5
  FUNC_N, // FUNC_6, // 6
  FUNC_N, // FUNC_7, // 7
  FUNC_N, // FUNC_8, // 8
  FUNC_N, // FUNC_9, // 9
  FUNC_N, // FUNC_0, // 0
  FUNC_N, // FUNC_S, // *
  FUNC_N, // FUNC_P, // #
  FUNC_N, // FUNC_U, // up
  FUNC_N, // FUNC_D, // down
  FUNC_N, // FUNC_R, // right
  FUNC_N, // FUNC_L, // left
  FUNC_N, // FUNC_K  // ok
};

void SYS_TIMER(void) {
  static byte  tm_10ms = 0;
  static byte  tm_100ms = 0;
  static byte  tm_1sec = 0;

  while (millis() - now >= 2) {
    now += 2;
    tm_10ms++;
    tm_100ms++;
    PROCESS_SERVO();
  }
  if (tm_10ms >= 5) {
    tm_10ms -= 5;
    PROCESS_10ms();
  }
  if (tm_100ms >= 50) {
    tm_100ms -= 50;
    PROCESS_100ms();
    tm_1sec++;
  }
  if (tm_1sec >= 10) {
    tm_1sec -= 10;
    PROCESS_1sec();
  }
}

void PROCESS_10ms(void) {
  IR_CHECK();
}

void setup() {
  // system timer0
  now = millis();

  // pin 0,1
  // Open serial communications and wait for port to open:
  Serial.begin(BAUD);
  while (!Serial) {};  // wait for serial port to connect. 

  // pin 2,3
  ir.begin();   // open IR

  ir_code = 10;
  step_no = 0;  // spider start
}

使用了 ROSA,對機器蜘蛛的動作控制就變成動作編輯,只要更動程序內容,不再需要大幅修改程式了。 #define LAST_SERVO  8 表示使用八顆伺服馬達,雖然這隻四腳蜘蛛只用到八顆馬達,不過變數仍預留十二顆馬達記憶空間,因為下周將介紹另一隻六腳蜘蛛會用到。


將所有紅外線遙控碼對應的 FUNC_X[] 改成 FUNC_N[],也就是執行空指令,並執行 RUN_X[] 內的程序設定。
RUN_0[] 為蜘蛛一開機的預設動作,讓每個馬達停位在 90 度角,這也方便組裝者馬達定位用,_GOTO 有兩種跳法,數值 200 以內會跳到自身程序內的行號,其它會跳到外部對應的紅外線遙控碼程序,像那 212 去掉 200 等於12,對應 *ACTION[] 是 RUN_P[],它就會接下去執行 RUN_P[] 站起來的動作。
RUN_1[] 前肢抬腿三次,
RUN_2[] 揮手跟你說 Hello,
RUN_3[]、RUN_4[] 跳舞,
RUN_5[]、RUN_6[] 空白無作用,
RUN_7[] 慢速動作,
RUN_8[] 正常速度動作,
RUN_9[] 快速動作,
RUN_U[] 前進,
RUN_D[] 後退,
RUN_R[] 右轉,
RUN_L[] 左轉,
RUN_K[] 蛙泳前進,
RUN_S[] 坐下,
RUN_N[] 停止。

結尾動作放一個 _RETURN 可以當一個副程式,例如原本動作正在前進中,按下遙控器右轉鍵,原地右轉後,會自動繼續之前前進動作,目前安排蜘蛛的打招呼、調速、左右轉等動作都有 _RETURN,而跳舞及前進後退等動作有 _GOTO 0,除非被中斷停止不然會持續動作。

外 _SERVO 動作編輯也定義一些常數,zeo 零度角、 lif 抬腿、 std 站立、 hon 持續通電、 hof 停止通電、 fwd 前進、 mid 中間 90 度角、 bck 後退,數值 255 表示不更動狀態,其它數值表示特定角度。因為伺服馬達增多了,分時控制的時間修改成 2 ms,因此 SYS_TIMER()內的數值配合修正。

對機器蜘蛛製作有興趣的朋友可以與我們聯絡。

2 則留言:

  1. 我想學
    請問這樣的組合要多少錢?

    回覆刪除
    回覆
    1. 您好,

      很高興看到你的留言,因為你是前期試用戶,會給予客製化服務及價格優惠。

      我與 Pizg 的聯絡信箱是idea22much@gmail.com,請將型號、數量,姓名,電話,寄送地址,或特殊需求列明,若有 Line 帳號歡迎提供以便緊急聯繫處理,相關明細亦將回信答覆你。

      謝謝

      刪除