今天给大家带来的是用Processing+Arduino做的一个类似雷达扫描的效果。
先看效果图
前不久看到过一个国外的人做的一个雷达扫描效果,用的是arduino + processing 做的。processing来显示雷达扫描效果以及障碍物、arduino用来控制舵机和超声波测距模块。所以就有样学样的做了一个。
需要的东西:
arduino uno板子一块
超声波测距模块一个
小型舵机一个
杜邦线若干。
固定超声波测距模块到舵机上。大家可以用超声波模块的支架或者自己动手用硬纸片和胶水粘贴的方式
舵机是用的180度的舵机。
思路就是 在arduino里 控制舵机。从0度 到180度。每次增加1度角度 如果当前角度到了180度。则从下次开始。每次角度减1
效果就是 舵机从左向右慢慢旋转、转到右侧、再从右侧慢慢向左旋转。来回的这样。就像是一个雷达在扫描前方的180度范围内的障碍物。
每次角度改变之后、同时超声波测距一次、 全局变量保存当前的角度和当前角度的前方的距离。
流程是 改变舵机角度 +-1度 -> 超声波测距 -> 像串口发送当前的角度和超声波测距得到的距离。 角度是从0~180度 距离是从 2~ 300~400 厘米(cm) 左右 这个要看具体的超声波测距模块的参数。
processing 接受到串口的数据。解析出来角度和距离。 将这个数据。放入数组中的对应角度下标值为这个角度的距离。
然后processing每次循环。都重新绘制界面。绘制当前角度的扫描扇形 取出数组中所有的数据。 再转换一下比例。再计算一下障碍物的坐标。绘制障碍物。
流程就是这样。具体的实现。可以看看程序里面。
processing的效果大概是这样的。
arduino程序方面。
首先。每次让舵机转动1度。从0~180 当到180时。再反向转回去。就是从180度再每次减少1度。
部分代码: //当前角度 int mAngleNum = 0; //当前是正向旋转还是反向旋转 char mFront = 0;
//设置舵机当前的角度 mServo.write(180 - mAngleNum);
if( mFront == 0 ) { mAngleNum ++; if( mAngleNum > 180 ) { mFront = 1; } } else { mAngleNum --; if( mAngleNum < 0 ) { mFront = 0; } }
这里。实现了从 180度开始。每次减少1度。直到0度。然后开始从0度往180度增加。每次增加1度。 然后接下来。我们就需要 在每次舵机旋转的时候。我们进行超声波测距。
//当前距离 int mDistance = 0; //超声波测距引脚 const int mTrigPin = 2; const int mEchoPin = 3; pinMode(mTrigPin, OUTPUT); // 要检测引脚上输入的脉冲宽度,需要先设置为输入状态 pinMode(mEchoPin, INPUT); // 产生一个10us的高脉冲 digitalWrite(mTrigPin, LOW); delayMicroseconds(2); digitalWrite(mTrigPin, HIGH); delayMicroseconds(10); digitalWrite(mTrigPin, LOW); mDistance = pulseIn(mEchoPin, HIGH) / 58.0; //将回波时间换算成cm 这里。我们就是测试当前的距离。然后同时每次改变角度。都测试一次当前距离。我们每次改变角度、测试距离之后。都把我们的当前角度和距离。送给processing 让processing知道我们的当前状态。然后。processing 方面。我们需要一直不停的接受 arduino传递过来的当前硬件的状态数据(就是当前的角度和当前前方的距离)我这里设计的是。只测试前方180度的1.5m范围内的障碍物。好了。我们继续。我们在processing中。一直去查找数据。每次找到数据后。都把数据进行组装以及拆分。原因就是。我们传递的数据比较多。不是单一的数据。所以我们要区分我们具体每个数据。同时还要区分。多个数据的连续和间断问题。就是如何去读取和解析一个完整的数据包(包含当前角度和当前距离的一段数据) 首先我们需要在draw()里 每次都要读取数据。当然这里每次读取的都是一个int类型的数据。其实他是一个byte或者说是一个字节。也就是8个二进制 也同样是一个C语言里的char 还同样是一个ASCII 当然关于是否是unsigned之类的就先不说了。我们只要知道read()一次 读取出来的是个8个二进制位表示出来的数据 就够了。当然他是用int类型来存放的 if ( myPort.available() > 0) { val = myPort.read(); }。我们这里没有真的用数据包的方式去处理的。而是用每次read之后。我把数据存放在181个int的数组中。因为角度是从0到180度。有181个数据.....然后。在draw() 中。每次都清空画布。然后重新绘制背景、然后绘制当前扫描效果的扇形。然后绘制当前的障碍物。画布设置:int mWidth = 600; int mHeight = 300; 设置300的原因是方便计算。我准备扫描1.5米以内的同时180度的范围。所以可以设置成 宽度 1.5m * 2 = 150cm * 2 = 300cm, 高度1.5m = 150cm 。也可以设置成这个数据的一倍。就是 600cm, 300cm 或者两倍。三倍。由于屏幕宽度高度的原因。我就设置成了600,300。然后,我们每次绘制出来画布。就开始根据当前角度。来绘制一个半透明的内部填充的扇形。我吧当前角度 放到了扇形的中间。最后。根据181个int的数组。吧181个角度的障碍物的距离。绘制到屏幕上。当然 超出100cm或者少于5cm的都没有绘制。当然大家也可以自己回去改成 150cm 10cm之类的。 下面发一下绘制障碍物的具体代码: - for( int i = 0; i < 181; i ++ )
- {
- int a, b, distance;
- distance = a = b = mDistanceArr[i];
- if( i - 1 >= 0 )
- a = mDistanceArr[ i - 1 ];
- if( i + 1 <= 180 )
- b = mDistanceArr[ i + 1 ];
- if( distance < 5 || distance > mMaxDistance ) continue;
- if( a < 5 || a > mMaxDistance ) a = distance;
- if( b < 5 || b > mMaxDistance ) b = distance;
- distance = (a + b + distance) / 3;
- if( distance < 5 || distance >= mMaxDistance ) continue;
- //distance = 60 angle = 45;
- //mWidth / 2;
- float scale = mWidthHalf / mMaxDistance;
- int tmpDistance = (int)(distance * scale);
- int px = (int)(mWidthHalf - tmpDistance * cos(i * PI/180));
- int yy = (int)(tmpDistance * sin(i * PI/180));
- int py = 300 - yy;
- println("scale:" + scale + " angle:" + i + " sin():" + sin(i * PI/180) + " distance:" + distance + " tmpDistance:" + tmpDistance + " x:" + px + " yy:" + yy + " py:" + py);
- ellipse(px, py, 30, 30);
- }
复制代码
|