[英]Arduino mega queue
我編寫了這個簡單的代碼,該代碼從Sharp紅外傳感器讀取一個長度,最后以串行形式顯示以厘米(單位)為單位的平均米。
當為Arduino Mega開發板編寫此代碼時,Arduino啟動一個閃爍的LED(引腳13),並且該程序不執行任何操作。 此代碼中的錯誤在哪里?
#include <QueueList.h>
const int ANALOG_SHARP = 0; //Set pin data from sharp.
QueueList <float> queuea;
float cm;
float qu1;
float qu2;
float qu3;
float qu4;
float qu5;
void setup() {
Serial.begin(9600);
}
void loop() {
cm = read_gp2d12_range(ANALOG_SHARP); //Convert to cm (unit).
queuea.push(cm); //Add item to queue, when I add only this line Arduino crash.
if ( 5 <= queuea.peek()) {
Serial.println(average());
}
}
float read_gp2d12_range(byte pin) { //Function converting to cm (unit).
int tmp;
tmp = analogRead(pin);
if (tmp < 3)
return -1; // Invalid value.
return (6787.0 /((float)tmp - 3.0)) - 4.0;
}
float average() { //Calculate average length
qu1 += queuea.pop();
qu2 += queuea.pop();
qu3 += queuea.pop();
qu4 += queuea.pop();
qu5 += queuea.pop();
float aver = ((qu1+qu2+qu3+qu4+qu5)/5);
return aver;
}
我同意vhallac列出的peek()
-> count()
錯誤。 但我還要指出,除非有很強的理由,否則您應該考慮以2的冪進行平均。
原因是在微控制器上,除法很慢。 通過對2(2、4、8、16等)的冪進行平均,您可以簡單地計算出總和,然后對其進行位移位。
計算2的平均值: (v1 + v2) >> 1
計算4的平均值: (v1 + v2 + v3 + v4) >> 2
要計算n個值的平均值(其中n是2的冪),只需將總和右移[log2(n)]。
只要您的sum變量的數據類型足夠大且不會溢出,這將變得更加容易和快捷。
注意 :一般而言,這不適用於浮點數。 實際上,微控制器並未針對浮點數進行優化。 您應該考慮從int(我假設您正在讀取ADC的數據)轉換為在平均值之后而不是之前的末尾浮動。
通過將int轉換為float,然后對float進行平均,與將int轉換為float相比,與平均int相比,您損失的精度更高。
其他:
您正在使用+=
運算符而不初始化變量( qu1
, qu2
等)-如果您要使用+=
,則將它們初始化是一個好習慣,但是看起來=
可以正常工作。
對於浮點數,我將average
函數編寫為:
float average(QueueList<float> & q, int n)
{
float sum = 0;
for(int i=0; i<n; i++)
{
sum += q.pop();
}
return (sum / (float) n);
}
並稱之為: average(queuea, 5);
您可以使用它來平均任意數量的傳感器讀數,然后使用相同的代碼來以后平均一個完全不同的QueueList中的浮動。 在需要調整讀數的情況下,將讀數的數量作為平均值傳遞將非常有用。
TL; DR:
這是我應該做的:
#include <QueueList.h>
const int ANALOG_SHARP=0; // set pin data from sharp
const int AvgPower = 2; // 1 for 2 readings, 2 for 4 readings, 3 for 8, etc.
const int AvgCount = pow(2,AvgPow);
QueueList <int> SensorReadings;
void setup(){
Serial.begin(9600);
}
void loop()
{
int reading = analogRead(ANALOG_SHARP);
SensorReadings.push(reading);
if(SensorReadings.count() > AvgCount)
{
int avg = average2(SensorReadings, AvgPower);
Serial.println(gpd12_to_cm(avg));
}
}
float gp2d12_to_cm(int reading)
{
if(reading <= 3){ return -1; }
return((6787.0 /((float)reading - 3.0)) - 4.0);
}
int average2(QueueList<int> & q, int AvgPower)
{
int AvgCount = pow(2, AvgPower);
long sum = 0;
for(int i=0; i<AvgCount; i++)
{
sum += q.pop();
}
return (sum >> AvgPower);
}
您正在使用queuea.peek()
獲取計數。 這只會返回隊列中的最后一個元素。 您應該改為使用queuea.count()
。
您也可以考慮將條件tmp < 3
更改為tmp <= 3
。 如果tmp
為3,則除以零。
吉德沃茲(Jedwards)取得了很大的進步,但是,我的第一個問題是,為什么要使用隊列列表而不是整數數組。
例如,我將執行以下操作:
int average(int analog_reading)
{
#define NUM_OF_AVG 5
static int readings[NUM_OF_AVG];
static int next_position;
static int sum;
if (++next_position >= NUM_OF_AVG)
{
next_position=0;
}
reading[next_position]=analog_reading;
for(int i=0; i<NUM_OF_AVG; i++)
{
sum += reading[i];
}
average = sum/NUM_OF_AVG
}
現在,我每次讀取都會計算一個新的滾動平均值,它消除了與嵌入式設備中的動態內存分配(內存碎片,無可用內存,內存泄漏)有關的所有問題。
我欣賞並理解使用移位除以2,4或8的方法,但是出於兩個原因,我會遠離該技術。
我認為,源代碼的可讀性和可維護性更為重要,因此,除非您可以測試和驗證划分是一個瓶頸,否則通過轉移而不是使用划分來節省一點時間。
其次,我相信大多數當前的優化編譯器都將盡可能改變,我知道GCC會這樣做。
我將把重構留給下一個家伙。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.