群組機器人跳妖怪舞
專案起源
此專案源起自魔人從陰間回到 DM&P 本部後迎來的尾牙聚餐,
「什麼!?我才剛回來就這麼不巧的輪到我們部門表演?」
正當魔人準備落跑時,被老大逮了個正著,
「既然你回來了,那我們來搞個機器人跳舞的尾牙表演吧!」
隨後魔人就被指派為此專案的負責人。
功能說明
群組機器人跳妖怪舞,這是一個利用 ROS 系統來建立起所有機器人溝通與同步的系統,並利用 86ME 來編輯機器人跳舞動作。
在此專案中使用了機械蠍子史加納、兩台人型機器、七台 86小六足以及安裝好 ROS 系統的筆記型電腦。
並利用 ESP8266 來讓所有機器人可以通過網路來同步動作。
準備材料
- 安裝好 ROS 系統的電腦
- 數台機器人
- 與機器人數量相符的 86Duino Zero/One
- 與機器人數量相符的 ESP8266
硬體架設
為了機器人美觀與牢固性,此專案利用 86Duino 的 CrossBar 的特異功能,
即切換 UART Port 到對應的腳位上,使 ESP8266 能直接插在 SPI PORT 上,
首先介紹一般 ESP8266 的接法及使用:
一般我們可以利用杜邦線將 ESP8266 與 86Duino 連接,但這樣連接的話既不美觀也不牢固。
如果各位想和魔人一樣使用此特異功能,那必須先做出一個 ESP8266 轉 SPI 插槽的轉接座。
材料為:
1. 兩顆母座 2×5 ROHS
2. 一條 cable
3. 多一條 cable 線的用意是要讓 ESP8266 接到 3.3v 的電源。
對照過後本魔認為將 SPIDI 改 TX、SPICS 改 RX 會比較好施工。
由於只需要 TX、RX、GND,所以轉接頭只需焊 3Pins,而 3.3v 直接焊 cable 線。
完成後建議在中間灌入熱熔膠,固定並防止短路。
環境設定
ROS 的安裝與建設可以參考 ROS 官方網站。
程式解說
首先來看整體的控制架構(HOST端):
使用 irrKlang 函式庫來播放音樂,
將音樂分段後將字串指令傳送給機器人端,
當然,指令若是分得越細就可以使同步的頻率提高。
long cmd_time[act] = { 0, 1920, 5760, /* ...... */ 138240 }; char* cmd[act] = { "HOME", "DANCE01", "DANCE02", /* ...... */ "END" };
而在主程式中,使用了一個迴圈來等待命令的輸入。
int main(int argc, char **argv) { /* ...... */ while (ros::ok()) { if((str[0] = getch()) != 0){ if(s != NULL) s->drop(); if(engine != NULL){ engine->drop(); engine = irrklang::createIrrKlangDevice(); } switch(str[0]){ case '1': s = engine->play2D("monster.wav", false, false, true); msg.data = "DANCEALL"; chatter_pub.publish(msg); break; /* ...... */ } } /* ...... */ ros::spinOnce(); } return 0; }
在此可以看到我設定了許多不同的按鍵,用來測試不同的動作,而在按下數字 2 時將會進入同步播放的模式。
case '2': s = engine->play2D("monster.wav", false, false, true); msg.data = cmd[0]; chatter_pub.publish(msg); printf("Track time: %lu(ms)\t Command: %s\n",track_time,cmd[0]); RTmode=1; break;
而 RTmode 開啟後會使迴圈不斷進入同步狀態,迴圈內將會判斷音樂時間軸以決定要送出相對的動作指令給機器人們。
if(RTmode!= -1){ track_time = s->getPlayPosition(); for(int i = RTmode; i < act; i++) if(track_time > cmd_time[i]-deviation && track_time < cmd_time[i]+deviation){ msg.data = cmd[i]; chatter_pub.publish(msg); printf("Track time: %lu(ms)\t Command: %s\n",track_time,cmd[i]); RTmode++; } if(RTmode >= act) RTmode = -1; }
機器人端則是使用 86ME 來編排每段的動作,
在 Github 中可以找到此專案在 86ME 上的設定及動作檔案。
編輯完動作檔後選擇 ESP8266 來接收 ROSHOST 的指令,
完成後選擇 All in One 匯出程式,
但匯出後還必須修改程式碼,將 CrossBar 功能加入程式中。
在 86Duino 的 io 函式庫中有一個 io_outpb 函式,
io_outpb 它是可以直接存取硬體 IO 的 function,
這 function 是非常低階的控制,所以並不建議任意的去存取它,
而這個功能就是 CrossBar,是 86Duino 的特異功能!
直接存取硬體 IO 的話就可以將你要的腳位切換成你要的功能,
所以首先要先去了解 86Duino 的 IO Port,
可以在官方找到 86Duino CPU 的 Datasheet,
我們可以看到文中提到 CrossBar Config Registers 內有寫各功能的編號,
找出我們所需要的就是將 COM1 的 TX、RX 更換為 SPI 的 CS、DI,
並記得要將 TX、RX 功能關閉。
000001b COM1-TXD1 000010b COM1-RXD1 001001b SPI-CS1 001100b SPI-DI
而 COM1 的 TX、RX 及 SPI 的 CS、DI 的 address 分別為:
0x0A12 COM1 TX 0x0A13 COM1 RX 0x0A20 SPICS 0x0A22 SPIDI
如此一來我們就可以寫出接上轉接座的測試程式碼:
void setup() { io_outpb(0x0A12,0x00); //disable COM1 TX pin io_outpb(0x0A13,0x00); //disable COM1 RX pin io_outpb(0x0A20,0x02); //SPICS to RX io_outpb(0x0A22,0x01); //SPIDI to DX Serial.begin(9600); Serial1.begin(115200); } void loop() { while (Serial1.available()) { Serial.write(Serial1.read()); } while (Serial.available()) { Serial1.write(Serial.read()); } }
理解了測試程式後,我們就可以在剛剛匯出的程式中加入此功能。
void setup() { io_outpb(0x0A12,0x00); //disable COM1 TX pin io_outpb(0x0A13,0x00); //disable COM1 RX pin io_outpb(0x0A20,0x02); //SPICS to RX io_outpb(0x0A22,0x01); //SPIDI to DX nh.getHardware()->setESP8266(Serial1, 115200); nh.getHardware()->setWiFi("RoBoardGod", "00000000"); nh.initNode("10.0.0.1"); //ROS-Host IP address nh.subscribe(sub); pinMode(13, OUTPUT); //Skarner's eyes LED digitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level) Audio.begin(88200, 100); srand(time(NULL)); /* ...... */ }
成果展示
— DEMO影片 —
相關連結
[1] 本專案 Github
[2] Skarner 六足機器人 Github
[3] 86Hexapod Github
The text of the 86Duino reference is licensed under a Creative Commons Attribution-ShareAlike 3.0 License. Code samples in the reference are released into the public domain.