今天給大家帶來的是用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);
- }
復制代碼
|