群組機器人跳妖怪舞






專案起源

此專案源起自魔人從陰間回到DM&P本部後迎來的尾牙聚餐,
「什麼!?我才剛回來就這麼不巧的輪到我們部門表演?」
正當魔人準備落跑時,被老大逮了個正著,
「既然你回來了,那我們來搞個機器人跳舞的尾牙表演吧!」
隨後魔人就被指派為此專案的負責人。



功能說明


群組機器人跳妖怪舞,這是一個利用ROS系統來建立起所有機器人溝通與同步的系統,並利用86ME來編及機器人跳舞動作。
在此專案中使用了機械蠍子史加納、兩台人型機器、七台86小六足以及安裝好ROS系統的筆記型電腦。
並利用ESP8266來讓所有機器人可以通過網路來同步動作。




準備材料


安裝好ROS系統的電腦
數台機器人
與機器人數量相符的86Duino Zero/One
與機器人數量相符的ESP8266



硬體架設


為了機器人美觀與牢固性,此專案利用86Duino的CrossBar的特異功能,
將UART Port切換到相對應的腳位上,使ESP8266能直接插在SPI PORT上,
首先介紹一般ESP8266的接法及使用:


一般我們可以利用杜邦線將ESP8266與86Duino連接,
但這樣連接的話既不美觀也不牢固。

如果各位想和魔人一樣使用此特異功能,那必須先做出一個ESP8266轉SPI插槽的轉接座。
材料為:
兩顆母座2×5 ROHS
一條cable
多一條cable線的用意是要讓ESP8266接到3.3v的電源。

對照過後本魔認為將SPIDI改TX、SPICS改RX會比較好施工。

由於只需要TX、RX、GND,所以轉接頭只需焊3Pins,而3.3v直接焊cable線。

完成後建議在中間灌入熱熔膠,固定並防止短路。

結束後就可以將ESP8266接在SPI插槽上了!




環境設定


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 libraries中的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影片 —




相關連結


Skarner
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.