群组机器人跳妖怪舞





专案起源

此专案源起自魔人从阴间回到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.