1

シャープの赤外線センサーから長さを読み取るこの単純なコードを作成しました。最後に、平均メートルをcm(単位)でシリアル単位で表示します。

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;
}
4

3 に答える 3

4

私はvhallacによってリストされたpeek()->エラーに同意します。count()ただし、他に強いケースがない限り、2の累乗で平均化することを検討する必要があることも指摘しておきます。

その理由は、マイクロコントローラーでは分割が遅いためです。2の累乗(2、4、8、16など)で平均化することにより、単純に合計を計算してからビットシフトすることができます。

2の平均を計算するには:(v1 + v2) >> 1

4の平均を計算するには:(v1 + v2 + v3 + v4) >> 2

n個の値の平均を計算するには(nは2の累乗)、合計を[log2(n)]だけ右にビットシフトします。

合計変数のデータ型が十分に大きく、オーバーフローしない限り、これははるかに簡単で高速です。

:これは一般的にフロートでは機能しません。実際、マイクロコントローラーはフロート用に最適化されていません。int(ADCが読み取っていると想定しているもの)から、平均化前ではなく、平均化後の最後にfloatに変換することを検討する必要があります。

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);
}
于 2011-09-03T20:33:09.663 に答える
1

queuea.peek()カウントを取得するために使用しています。これにより、キューの最後の要素のみが返されます。queuea.count()代わりに使用する必要があります。

tmp < 3また、条件をに変更することを検討してくださいtmp <= 3。が3の場合tmp、ゼロで除算します。

于 2011-09-03T19:39:53.847 に答える
0

大幅な改善がありましたが、最初の質問は、int配列の代わりにqueuelistを使用する理由です。

例として、私は次のことを行います。

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で除算するためのシフトの使用に感謝し、理解していますが、2つの理由でその手法を避けたいと思います。

分割がボトルネックであることをテストして確認できない限り、ソースコードの可読性と保守性は、分割ではなくシフトで少し時間を節約するよりも重要だと思います。

第二に、私は、現在のほとんどの最適化コンパイラーが可能であればシフトを行うと信じています、私はGCCがそうすることを知っています。

次の人のためにforループのリファクタリングは残しておきます。

于 2011-09-05T02:11:25.437 に答える