Posts Tagged ‘开关’

解魔方的机器人攻略26 – “大眼睛”开关

萝卜头主要功能的实现都已经介绍过了,接下来是一些优化和美化的工作。曾经有人留言说萝卜头的眼睛完全没有用,只是用来做装饰的。其实乐高积木的乐趣在于功能的拼装,每一处设计都可以有它的作用。今天就介绍一下这个大眼睛的作用。

最初的第一个版本是没有大眼睛的,那时候魔方必须一开始就放在转台上,萝卜头埋头一路转到完,然后就退出程序。这就带来了很多问题:
1. 必须一开始就放好魔方。如果没有魔方,萝卜头依然会傻乎乎的执行读颜色的操作(读出来全是黑色),然后出错退出程序
2. 有时候魔方卡住了,会掉下转台,萝卜头依然孜孜不倦的把所有步骤执行完。
3. 操作不方便,给观众表演时,需要:打乱魔方->放上转台->启动程序->解魔方->停止程序->取下魔方,实在是太不智能了。

其中第2点最让人无法接受,想起一个笑话:两个人在路边干活,一个人挖坑,另一个人填土。有人问,你们这是在干什么啊?他们说,我们平时是三个人种树,今天负责放树苗的兄弟生病没来。要不怎么说,眼睛是心灵的窗户。我特地加上了这个超声波的眼睛,主要功能包括:
1. 蓝牙连接成功后,判断有没有魔方,如果有则开始读颜色并解魔方,如果没有则进入等待状态
2. 中间过程,如果魔方掉下转台,则报告错误,并停止当前程序
3. 解好魔方以后,进入另外一个等待循环,如果有人把魔方拿开再放回来,则认为这时候的魔方又被打乱了,重新启动解魔方程序。

其实判断部分非常简单,魔方在正常位置时,大眼睛的读数应该在14cm左右。

眼睛到魔方的距离

眼睛到魔方的距离

为了避免误差,当距离读数在12~16之间时,我就认为转台上有魔方。另外,为了避免偶尔的数据跳动,我认为连续十次测量结果都相同的情况下,才是距离“稳定”的状态。是下面看看代码:

while(!Button.ESCAPE.isPressed())
{
	//Wait for the distance being in the correct range: 12~16
	int CheckStatusTimes=0;
	LCD.clear();
	boolean previousStatus = true;
	boolean currentStatus = true;
	while(CheckStatusTimes++ < 10)
	{
		int n = distance.getDistance();
		LCD.drawString("distance=" + n + "   ", 0, 0);
		currentStatus = (n>=12 && n<=16);
		if(currentStatus != previousStatus)
		{
			CheckStatusTimes = 0;
			previousStatus = currentStatus;
		}
		Thread.sleep(100);
	}
	hasCube = currentStatus;
	if(!hasCube)
	{
		//if the cube is take away, we consume it is been upset
		isChaotic = true;
	}
	if(hasCube && isChaotic)
	{
		//这里是解魔方的部分
		isChaotic = false;
	}
}

上面的代码中,hasCube表示“魔方是否在转台上”,isChaotic表示“魔方是否处于打乱状态”。
如果魔方被拿走,就认为再放回来时已经被打乱了。

在旋转的过程中,每一步操作之前,都需要这样判断一下魔方位置,只要检测到距离异常,就立刻中止程序。为了便于管理,可以定义一个hasError的全局静态变量,并把判断部分封装成一个函数。

static boolean hasError = false;

//check if the cube is still on the base
public static boolean CheckCubeReady() throws Exception
{
	//if already error, return directly to avoid play *.wav again
	if(hasError) return false;

	int d = distance.getDistance();
	int errorCount = 0;
	while((d<12 || d>16) && errorCount < 10)
	{
		errorCount++;
		Thread.sleep(20);
	}
	if(errorCount >= 10)
	{
		//The cube is break out;
		hasError = true;
		Sound.playSample(new File("Error.wav"));
	}
	return !hasError;
}

这里的逻辑和上面那段代码的逻辑稍有差别,主要是如果hasError已经是true,表示魔方已经不再转台上,那么直接返回错误,不再进行后续的判断。另外把sleep的时间从100毫秒变成20毫秒。这样改的原因是用手把魔方放上转台时,可能会使用比较多的时间(1秒),而萝卜头如果把魔方推到转台外面,这个在200毫秒内应该是足够稳定下来了。
最后把所有具体的操作,全部添加这个判断函数:

//原来:
RotatePaw();
//现在:
if(CheckCubeReady()) RotatePaw();

大家可能会注意到,在出错的判断中,有一句:

Sound.playSample(new File("Error.wav"));

这行代码执行时会有一个甜美的声音说:“出错啦~~”(其实那是我家娘子的录音)预告一下,在下一篇里给大家介绍如何让萝卜头开口说话。

Arduino开发板实验一:数字输入输出

最近有很多朋友关心小爱的进展,真是非常感谢。这段时间我主要在研究小爱的制作方案,大致的工作分为几个部分:机械设计,电子电路,嵌入式开发和人工智能几个部分。

为了确定最终的方案,我最近总在淘宝上晃悠,劲头跟美女购物狂们逛商场有一拼。前一段时间无意中看到了一个叫Arduino的东西,貌似还挺好用,暂定以这个板子为基础来开发电子电路和嵌入式的部分。我以前没有接触过嵌入式开发,如果路过的朋友有更好的方案,请一定要吱一声,谢谢 :)

昨天淘宝的Arduino板子终于到了,型号是Arduino MEGA ATmega1280-16AU AVR(完全不清楚不同型号之间有什么区别),秀一下样子:

Arduino 开发板

Arduino 开发板

今天进行了第一个小实验,跟大家分享一下。实验的目的是实现一个简单的功能,外接一个开关,通过打开或者关闭开关,来控制一个小灯的亮和灭
电子专业的老婆冷笑飘过,这个实验太简单了吧,用传统电路甚至更简单,一个开关直接切断电路即可(传说中的手电筒)。不过我通过这个小实验了解了一个重要信息:原来嵌入式开发可以直接对CPU的管脚直接进行读写,0就是低电压,1就是高电压。以前一直使用高级语言,读CPU的某个脚电压貌似非常遥远。

第一步:到Arduino官网下载Arduino开发IDE和相关驱动
下载完一看,这个软件是绿色的,并且绿的相当彻底,里面还包含了板子的驱动,双击打开就可以使用,我在Windows7,Server2008以及Vista下都用的挺好。

第二步:用USB线连接Arduino板子和电脑,发现驱动可以被自动识别,然后添加了一个USB UART的设备。查看一下这个设备的COM端口号,后面需要用到:

查看COM端口号

查看COM端口号

第三步:
双击运行arduino.exe,第一次使用前,需要设置设备端口号和板子的型号,看下面的抓图:

设置主板型号,我这个是Mega

设置主板型号,我这个是Mega

选择刚才看到的端口号

选择刚才看到的端口号

第四步:接线
为了做这个小实验,我还真拆了家里一个手电筒(真是败家)。文档里说开发板有个LED小灯接在13号管脚,据说术语叫做Pin13,所以就不需要外接小灯了。开关的两个脚一个接地(GND),另一个随便接某个管脚。考虑到13这个数字不吉利,我特地选了一个44号管脚避个邪。看下图:

接了个开关,红色灯是13号LED

接了个开关,红色灯是13号LED

第五步:写代码

int ledPin=13;
int buttonPin=44;

void setup()
{
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
}

void loop()
{
  digitalWrite(buttonPin, HIGH);
  digitalWrite(ledPin, digitalRead(buttonPin));
  delay(100);
}

经常搞windows软件开发的朋友可能有点迷糊,这个程序入口在哪里,出口在哪里?事实上这个程序是被Arduino的IDE封装了,它的规则就是先定义几个变量,然后在setup里做几个设置,最后就孜孜不倦的反复跑loop里面的程序。我猜想应该有些中断机制来执行一些特殊处理,这个以后再慢慢学。看来单片机的程序通电以后就没打算让它歇着,高级语言的程序员需要适应一下。
接下来的程序就很好解释了:
pinMode 用于设置管脚的用途,输入还是输出
digitalWrite 用于把某个管脚的电压设置成High还是LOW
digitalRead 用于读取某个管脚的电压状态,返回的也是HIGH或LOW

有一句 digitalWrite(buttonPin, HIGH); 需要解释一下。我希望44号管脚当开关打开时是1,开关闭合是是0。但是请注意第一次闭合以后,这个管脚电压降到0,它不会自动升到1。为了让下次开关打开的时候管脚电压置1,我在每次循环前都给它设置初始值1。画了一个非常丑的电路图:

电路图

电路图

第六步,编译并上传代码

用IDE中的Verify来编译代码,然后用Upload上传。选的44号辟邪管脚果然有效,代码直接编译通过上传成功(真相是确实太简单了)。结果就不给大家贴图了,没啥好看的。不过好人做到底,虽然Arduino的IDE按钮都有提示,我还是画了个说明:

Arduino 0017的菜单

Arduino 0017的菜单

总结:这虽然是个很简单的实验,但是对于一个像我这样没有接触过嵌入式的程序员来说,还是有很多的知识点。我相信有很多和我一样的人,想做机器人但是觉得无从下手,希望这些学习经验有所帮助。高手们就请优雅的飘过吧 :)