回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

【创客大侦探挑战赛】好奇心大营救行动之——逃离多多岛 简单

头像 TR-SHA 2019.09.01 834 1

故事篇

话说上回粒子姐姐带着大家去了好奇心城堡,从七彩宝盒中拿到了存有导航资料的模块。跟着模块的导航,大家很快就找到了被困已久的好奇心小朋友。于是大家开开心心地来到多多岛火车站准备坐火车回家去了。

可是刚一上车,他们几个发现又被困住了:车门关闭的一瞬间,车厢里的灯也忽然变得很暗。只看见座位前的小茶桌上放着一个遥控器、一台老式的拨盘电话机,电话机上还连着一条线,接到了旁边一个像引爆器一样的按钮开关。同时墙上有一组正在闪烁的彩色灯条仿佛在提示着什么……

正在大家都不知该如何是好时,电话铃突然响了!阿杜一诺小心翼翼地拎起电话,从听筒里传来了一个机器人说话的声音:

我的客人们,你们好啊。我是多多岛的主人——数学天才和计算机天才西加加博士。欢迎你们来到我的火车密室。你们要想乘坐的我的托马斯列车离开这里,必须要先做对两道我出的题。我听说你们当中的阿杜一诺和好奇心小朋友都是神通广大的,我倒要看看你们有没有传说中的那么厉害,哈哈哈!

首先:你们要破译转换道岔的密码。这个密码的线索藏在墙上闪烁的灯条里,灯条上的每个灯都有亮和灭两种状态,在灯的左边写着灯的名字(D0~D6),两种状态意味着什么你们应该是可以想到的。闪亮三次的灯就是密码的线索。然后你们要根据这个线索,推算出密码,再用手里的遥控器把密码发送出去,用遥控器输入密码的内容和速度如果都是正确的,灯条会亮绿色并且闪烁三下。否则你们就得退回去从头再检查密码。如果你们够聪明的话就可以在规定的时间之内完成密码的破解和发送,那就可以让道岔完成转辙。这样你们离开多多岛的铁路线就开通了。

然后,你们得想办法给火车头里的司机打个电话,让他把火车开起来,你们就能离开这里了。你们手里的这台内线电话机可以连到驾驶室。火车司机的电话号码就隐藏在灯条里。这一次灯条上每个灯所显示内容,也就是所代表的数字写在了灯的右边。你们要读出灯带显示的这一组数字。然后告诉你们一个小秘密:我西加加博士只喜欢质数,所以你们要把这一串数字里的质数挑出来,按照它们原来的顺序排列好,就是司机的电话号码了。还有,这是一台十八世纪末期制造的拨号盘式电话,它可是古董哦,你们一定要轻拿轻放,不要把它摔坏了!哦对了,我差点忘了告诉你们,这台电话机太老了,以至于它的拨号盘已经坏掉了,要想拨出电话,你们得另外想一个有效的办法才行。

好了,整个游戏规则就是这样,祝你们在多多岛密室里玩得快乐,而且要注意时间,如果在天黑之前还没有完成任务,你们可就走不了啦!哈哈哈哈!……

—— 原来这是一个伪装成火车站的密室,多多岛的主人——西加加博士给密室设置了两道关卡,只有成功地完成两次解密闯关(破译道岔解锁码和拨通火车头上的电话号码)他们才能坐着托马斯小火车顺利离开。

人物篇

A、曼德佳——指导老师; B、阿杜一诺——家长; C、好奇心——小朋友。

project-image

第一关破译道岔的解锁码

【铁道小知识】:道岔是铁路上的一种设备,它是用来为火车选择行驶路线的一种换线装置(火车上是没有方向盘的,火车要往哪条轨道开全靠道岔来决定)。火车在行驶之前必须先把沿途的道岔转动(称为“转辙”)到正确的位置并锁定,否则火车就会走错路线甚至发生脱轨事故。

【关卡说明】:托马斯小火车的前方有一组通往密室出口的道岔。道岔受到地面调度中心的程序控制,平时是被锁定的。要转动道岔到正确的位置必须事先输入正确的解锁码。道岔的解锁码是通过放在车厢里的遥控器发送到地面调度中心的。道岔的解锁密码来自于墙上灯条显示的信息。灯条以二进制数的方式用白灯显示了解锁码的内容,好奇心小朋友负责把灯条上显示的二进制数读出来,由曼德佳把二进制数转成十进制数,再由阿杜一诺用遥控器依次把密码发送出去。如果发送的密码正确并且速度合适,就能够解锁道岔,把轨道开通到逃出密室的方向。

【数学小知识】:把二进制数转成十进制数的方法是把各个位的数乘以各个位的权重(2^n)之后全部累加在一起即可。

例如1101010(二进制)=1×2^6+1×2^5+0×2^4+1×2^3+0×2^2+1×2^1+0×2^0=106(十进制)

【闯关秘诀】:根据彩色灯条上闪烁的二进制密码(D0~D6),转换成十进制数,再用遥控器发送给地面的调度中心,让轨道上的道岔转到正确位置。

第二关:破译火车司机的电话号码并拨打电话

【关卡说明】:火车司机的电话号码隐藏在灯条显示的内容中。这次灯条显示的是十进制的数字(7个灯分别对应0~6这7个数字),好奇心小朋友根据灯条闪烁依次把这一组数字记录下来,由曼德佳把其中的质数保留,其余不是质数的数字都剔除,剩下的就是司机的“电话号码”了。再由阿杜一诺用按钮把“电话号码”发送出去。

【数学小知识】:质数(也叫素数)是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。例如2、3、5、7、11、13、17、19……要注意0和1都不是质数。例如灯条闪烁的数字是6、0、3、2、1、4、5,其中的质数是325。

【电话小知识】:现在的电话一般采用双音频方式拨号,即每一个数字按钮上组合了两个不同频率的音频信号,叠加之后拨出。而以往的老式电话机则采用脉冲拨号的方式,拨号盘后面是一个旋转的凸轮,每当转过一格(就是一个数字)就会发出一个脉冲。也就是说拨“1”就是发出1个脉冲,拨“2”就是发出2个脉冲,……,拨“0”是发出10个脉冲。这些脉冲是等间隔发送的,每两个数字之间有所停顿。所以可以用一个按钮来模拟拨号脉冲。只要根据电话号码的数字来按动按钮即可实现模拟的脉冲拨号。

【闯关秘诀】:从灯条里读出数字,去除其中的非质数数字,留下质数,再通过按钮模拟拨号的方式发送脉冲信息即可拨通电话启动火车。

设计篇

通过上次比赛,我们对神经元模块有了一定的了解。神经元模块可以接受数字或模拟信号作为输入,输出为数字信号。由此这次的两个关卡设计从不同的角度出发来“玩”好这个神经元模块。

因为觉得好玩,装备争夺赛结束之后的奖品我特地要了两个神经元模块,这次正好都用上,每个关卡用一个。

第一关,采用模拟信号输入给第一个神经元模块。遥控器输入的数字在Arduino里转换成不同占空比的脉冲,也即类似模拟电压信号,送入神经元模块进行判断。根据经验,拉大输入信号的幅值范围会有利于提高神经元模块的识别率,所以在设计程序时对输入神经元模块的模拟电压进行了线性展宽处理:让数字0代表占空比0(或者说是Mind+配合Arduino能达到的最小占空比),数字9代表最大占空比255。这样就把整个输入量程都用足了,识别起来效果很好。最初设计时第一关的输入采用彩色灯条显示二进制数,再通过红外线遥控器输入十进制数的方式进行。后来调试过程中发现红外线遥控器本身发送的码是每个按键6个字符,于是在接收端再做了一次转换,把字符先转成0~9这十个数字,再转成0~255的脉冲占空比。这样就可以实现对遥控器输入的按钮数字(其实还包含了其中的间隔时长)进行训练或识别了。同时为了提示操作人员,Arduino还把转换成0~9数字的信号同时送给灯条进行显示。由于灯条只有7个灯,只能显示0~6。7~9我这次是故意调整了一下密码避开了,实际上也可以用灯条的复显来显示。不过我认为最好的方式是再加几个2812的灯,凑够十个就可以了。例如再买一条灯条串上去。

第二关,采用数字信号输入给第二个神经元模块。模拟的拨号脉冲是个0V或5V的数字脉冲,通过Arduino整形之后一方面驱动灯条给操作者一个反馈,另外一方面送入神经元模块进行匹配。这次实际上匹配的是方波脉冲的宽度和排列时序。

两道关卡的神经元模块输出都送入Arduino用于驱动彩灯条,同时也输给录音模块,播放“解码成功!”的语音提示音。

材料清单

  • Arduino UNO板 X1
  • 神经元模块 X2
  • 信号分离模块(自己改动了一下,增加了口子) X1
  • 录音模块(含喇叭) X1
  • 七彩灯条(2812) X1
  • 双H桥电机驱动模块 X1
  • 红外接收模块 X1
  • 红外遥控器 X1
  • 微动开关(按钮)模块 X1
  • 模型火车道岔(含电磁转辙机) X1
  • 模型火车(含轨道、列车、房子等) X1
  • 各种导线若干 X1

制作过程如下:

步骤1 在草稿纸上设计好总体的思路和逻辑

步骤2 把硬件平台搭建起来

底盘部分是在上次比赛作品的基础上修改而来的,其中超声波传感器没有用到但是懒得拆了,在造物例子那边新增了第二个神经元模块,在小喇叭的旁边还新增了一个按钮开关和一个红外接收模块。

为了节省硬件,两个神经元模块的输出共同输给Arduino和同一个录音模块,这样就不用准备两套录音模块了。同时,又由于手头上的PH2.0脚距的插头线和信号分离模块的数量不够,只好自己动手制作了几组专门的传输线(一头是杜邦一头是PH2.0或者两头都是PH2.0但中间信号线剪断并分出来两个杜邦头等等),还把原来一进两出的信号分离模块改成了三进两出。

改好的传输线长这样:

project-image

最后搭建出来的不带火车模型部分的控制部分硬件是这样的:

project-image

步骤3 编写并调试程序,软件平台选用Mind+1.5.6,还要进行两个神经元模块的训练和测试。

project-image

步骤4 编完的软件长这个样子:

project-image

步骤5 软件初步完成之后搭起一段小轨道开始带列车的调试

project-image

步骤6 初步测试完成之后找块大点儿的地方把轨道搭起来,再放上小场景就可以准备拍视频了

project-image

接下来让我们看看视频,看看好奇心他们是怎么成功从多多岛上密室逃脱的:

代码
#include <DFRobot_NeoPixel.h>
#include <DFString.h>
#include <IRRemote.h>

double mind_n_Level_GS_Passed;
String mind_s_IR_Code;
double mind_n_IR_Digital;
double mind_n_Temp_ID;
double mind_n_Steps;
double mind_n_Steps_Changed;

DFRobot_NeoPixel  rgb_display_4;
IRRecv remote(3);
void Level_GS();
void Scan_IR();
void IR_Decoder(String IR_String);
void Level_Standby();
void Level_Locomotive();
void Motor_Drv(double Velocity);
void Wait_for_IR();
void Step_Control(String Control_Value);
void LED_Display_Number(double LED_Value);

void Level_GS()
{
	mind_n_Level_GS_Passed = 0;
	rgb_display_4.clear();
	delay(500);
	rgb_display_4.setRangeColor(0, 0, 0xffff00);
	delay(500);
	rgb_display_4.shift(1);
	rgb_display_4.setRangeColor(0, 0, 0xffff00);
	delay(500);
	rgb_display_4.shift(1);
	rgb_display_4.setRangeColor(0, 0, 0x000000);
	delay(500);
	rgb_display_4.shift(1);
	rgb_display_4.setRangeColor(0, 0, 0xffff00);
	delay(500);
	rgb_display_4.shift(1);
	rgb_display_4.setRangeColor(0, 0, 0x000000);
	delay(500);
	rgb_display_4.shift(1);
	rgb_display_4.setRangeColor(0, 0, 0xffff00);
	delay(500);
	rgb_display_4.shift(1);
	rgb_display_4.setRangeColor(0, 0, 0x000000);
	delay(500);
	for (int count = 0; count < 3; count++) {
		rgb_display_4.setBrightness(127);
		delay(500);
		rgb_display_4.setBrightness(18);
		delay(500);
	}
	delay(1000);
	rgb_display_4.clear();
	while ((!(mind_n_Level_GS_Passed == 1))) {
		Scan_IR();
		if (mind_s_IR_Code.startsWith("FF")) {
			rgb_display_4.clear();
			IR_Decoder(mind_s_IR_Code);
			mind_n_Temp_ID = mind_n_IR_Digital;
			rgb_display_4.setRangeColor(mind_n_Temp_ID, mind_n_Temp_ID, 0xcc33cc);
			analogWrite(5, (constrain((map(mind_n_IR_Digital, 0, 6, 0, 255)), 0, 255)));
			Serial.print((String("IR_Digital= ") + String(mind_n_IR_Digital)));
			Serial.println((String(" PWM5= ") + String((map(mind_n_IR_Digital, 0, 6, 0, 255)))));
		}
		if (digitalRead(2)) {
			mind_n_Level_GS_Passed = 1;
		}
	}
	analogWrite(5, 0);
	rgb_display_4.setRangeColor(0, 6, 0x00ff00);
	for (int count = 0; count < 3; count++) {
		rgb_display_4.setBrightness(127);
		delay(500);
		rgb_display_4.setBrightness(18);
		delay(500);
	}
	digitalWrite(7, HIGH);
	delay(800);
	digitalWrite(7, LOW);
	delay(3000);
	rgb_display_4.clear();
	mind_n_Steps = mind_n_Steps + 1;
}

void Scan_IR()
{
	mind_s_IR_Code = (remote.getIrCommand());
	if ((mind_s_IR_Code.startsWith("FF") && ((String(mind_s_IR_Code).length()) == 6))) {
		Serial.println(mind_s_IR_Code);
	}
	else {
		mind_s_IR_Code = "EF";
	}
}

void IR_Decoder(String IR_String)
{
	if (((String((dfstring.substring(IR_String,0,1,0,2))).indexOf(String("EF")) != -1))) {
		mind_n_IR_Digital = -1;
	}
	if (((String(IR_String).indexOf(String("FF6897")) != -1))) {
		mind_n_IR_Digital = 0;
	}
	if (((String(IR_String).indexOf(String("FF30CF")) != -1))) {
		mind_n_IR_Digital = 1;
	}
	if (((String(IR_String).indexOf(String("FF18E7")) != -1))) {
		mind_n_IR_Digital = 2;
	}
	if (((String(IR_String).indexOf(String("FF7A85")) != -1))) {
		mind_n_IR_Digital = 3;
	}
	if (((String(IR_String).indexOf(String("FF10EF")) != -1))) {
		mind_n_IR_Digital = 4;
	}
	if (((String(IR_String).indexOf(String("FF38C7")) != -1))) {
		mind_n_IR_Digital = 5;
	}
	if (((String(IR_String).indexOf(String("FF5AA5")) != -1))) {
		mind_n_IR_Digital = 6;
	}
	if (((String(IR_String).indexOf(String("FF42BD")) != -1))) {
		mind_n_IR_Digital = 7;
	}
	if (((String(IR_String).indexOf(String("FF4AB5")) != -1))) {
		mind_n_IR_Digital = 8;
	}
	if (((String(IR_String).indexOf(String("FF52AD")) != -1))) {
		mind_n_IR_Digital = 9;
	}
}

void Level_Standby()
{
	if ((mind_n_Steps_Changed == 1)) {
		rgb_display_4.showRainbow(0, 6, 1, 360);
		mind_n_Steps_Changed = 0;
	}
}

void Level_Locomotive()
{
	rgb_display_4.clear();
	while ((!(!digitalRead(12)))) {
		mind_n_Temp_ID = 6;
		rgb_display_4.setRangeColor(mind_n_Temp_ID, mind_n_Temp_ID, 0xff99ff);
		delay(600);
		rgb_display_4.clear();
		mind_n_Temp_ID = 0;
		rgb_display_4.setRangeColor(mind_n_Temp_ID, mind_n_Temp_ID, 0xff99ff);
		delay(600);
		rgb_display_4.clear();
		mind_n_Temp_ID = 3;
		rgb_display_4.setRangeColor(mind_n_Temp_ID, mind_n_Temp_ID, 0xff99ff);
		delay(600);
		rgb_display_4.clear();
		mind_n_Temp_ID = 2;
		rgb_display_4.setRangeColor(mind_n_Temp_ID, mind_n_Temp_ID, 0xff99ff);
		delay(600);
		rgb_display_4.clear();
		mind_n_Temp_ID = 1;
		rgb_display_4.setRangeColor(mind_n_Temp_ID, mind_n_Temp_ID, 0xff99ff);
		delay(600);
		rgb_display_4.clear();
		mind_n_Temp_ID = 4;
		rgb_display_4.setRangeColor(mind_n_Temp_ID, mind_n_Temp_ID, 0xff99ff);
		delay(600);
		rgb_display_4.clear();
		mind_n_Temp_ID = 5;
		rgb_display_4.setRangeColor(mind_n_Temp_ID, mind_n_Temp_ID, 0xff99ff);
		delay(600);
		rgb_display_4.clear();
		delay(1500);
	}
	rgb_display_4.setRangeColor(0, 6, 0x0000ff);
	delay(300);
	rgb_display_4.setBrightness(0);
	delay(300);
	rgb_display_4.setBrightness(64);
	delay(300);
	rgb_display_4.setBrightness(0);
	delay(300);
	rgb_display_4.setBrightness(64);
	delay(500);
	rgb_display_4.clear();
	while ((!digitalRead(2))) {
		if (digitalRead(12)) {
			digitalWrite(9, LOW);
			rgb_display_4.clear();
		}
		else {
			digitalWrite(9, HIGH);
			rgb_display_4.setRangeColor(0, 6, 0x00ffff);
		}
	}
	rgb_display_4.setRangeColor(0, 6, 0x00ff00);
	for (int count = 0; count < 3; count++) {
		rgb_display_4.setBrightness(127);
		delay(500);
		rgb_display_4.setBrightness(18);
		delay(500);
	}
	Motor_Drv(200);
	rgb_display_4.clear();
	mind_n_Steps = mind_n_Steps + 1;
}

void Motor_Drv(double Velocity)
{
	if ((Velocity == 0)) {
		digitalWrite(10, LOW);
		digitalWrite(11, LOW);
		analogWrite(6, 0);
	}
	else {
		if ((Velocity > 0)) {
			digitalWrite(10, LOW);
			digitalWrite(11, HIGH);
		}
		else {
			digitalWrite(10, HIGH);
			digitalWrite(11, LOW);
		}
		analogWrite(6, (constrain((abs(Velocity)), 0, 255)));
	}
}

void Wait_for_IR()
{
	mind_s_IR_Code = "EF";
	while ((!(mind_s_IR_Code.startsWith("FF") && ((String(mind_s_IR_Code).length()) == 6)))) {
		mind_s_IR_Code = (remote.getIrCommand());
	}
	Serial.println(mind_s_IR_Code);
}

void Step_Control(String Control_Value)
{
	// Steps control and display
	if (((String(Control_Value).indexOf(String("FF629D")) != -1))) {
		LED_Display_Number(mind_n_Steps);
		Serial.println((String("Steps=") + String(mind_n_Steps)));
	}
	else {
		if (((String(Control_Value).indexOf(String("FFE21D")) != -1))) {
			mind_n_Steps = mind_n_Steps + 1;
			mind_n_Steps_Changed = 1;
			LED_Display_Number(mind_n_Steps);
			Serial.println((String("Steps=") + String(mind_n_Steps)));
		}
		else {
			if ((((String(Control_Value).indexOf(String("FFA25D")) != -1)) && (mind_n_Steps >= 1))) {
				mind_n_Steps = mind_n_Steps - 1;
				mind_n_Steps_Changed = 1;
				LED_Display_Number(mind_n_Steps);
				Serial.println((String("Steps=") + String(mind_n_Steps)));
			}
		}
	}
}

void LED_Display_Number(double LED_Value)
{
	rgb_display_4.clear();
	rgb_display_4.showBarGraph(0, 6, (LED_Value+1), 7);
}


void setup() {
	Serial.begin(19200);
	rgb_display_4.begin(4, 7, 11);
	remote.enableIRIn();
	remote.resume();

	LED_Display_Number(6);
	rgb_display_4.setRangeColor(0, 0, 0xffffff);
	rgb_display_4.setRangeColor(1, 1, 0xcc33cc);
	rgb_display_4.setRangeColor(2, 2, 0xff0000);
	rgb_display_4.setRangeColor(3, 3, 0xffff00);
	rgb_display_4.setRangeColor(4, 4, 0x00ff00);
	rgb_display_4.setRangeColor(5, 5, 0x00ffff);
	rgb_display_4.setRangeColor(6, 6, 0x0000ff);
	analogWrite(5, 0);
	Motor_Drv(0);
	IR_Decoder("EF");
	mind_n_Steps = 0;
	mind_n_Steps_Changed = 1;
}

void loop() {
	Scan_IR();
	Step_Control(mind_s_IR_Code);
	if ((mind_n_Steps == 0)) {
		Level_Standby();
	}
	if ((mind_n_Steps == 1)) {
		Level_GS();
	}
	if ((mind_n_Steps == 2)) {
		Level_Locomotive();
	}
	if ((mind_n_Steps > 2)) {
		if ((mind_n_Steps_Changed == 1)) {
			rgb_display_4.showRainbow(0, 6, 1, 360);
			mind_n_Steps_Changed = 0;
		}
		rgb_display_4.rotate(1);
		delay(500);
		if ((mind_n_Steps >= 4)) {
			Motor_Drv(0);
			rgb_display_4.clear();
		}
	}
}

评论

user-avatar
  • Echo1

    Echo12020.04.14

    老师您好,我太想学习您的智能垃圾分类这个作品啦,冒昧的请教您,方便加个QQ吗,我想请教您,真是非常冒昧,感谢您。

    0