9

現在、選択した 8 色を LED で循環させることができました。すべてが正しく機能していますが、より自然な感じにしたい場合と、ある色から別の色に置き換えるのではなく、フェード/トランジションしたい場合を除きます。

これまでの私のコードは次のとおりです。

int redPin = 11;
int greenPin = 10;
int bluePin = 9;

void setup()
{
    pinMode(redPin, OUTPUT);
    pinMode(greenPin, OUTPUT);
    pinMode(bluePin, OUTPUT);
}

void loop()
{
    setColor(250, 105, 0);   // Yellow
    delay(1000);

    setColor(250, 40, 0);    // Orange
    delay(1000);

    setColor(255, 0, 0);     // Red
    delay(1000);

    setColor(10, 10, 255);   // Blue
    delay(1000);

    setColor(255, 0, 100);   // Pink
    delay(1000);

    setColor(200, 0, 255);   // Purple
    delay(1000);

    setColor(0, 255, 0);     // Green
    delay(1000);

    setColor(255, 255, 255); // White
    delay(1000);
}

void setColor(int red, int green, int blue)
{
    analogWrite(redPin, 255-red);
    analogWrite(greenPin, 255-green);
    analogWrite(bluePin, 255-blue);
}
4

7 に答える 7

13

このトピックについて他の回答が省略しているのは、人間の光強度の知覚が線形ではなく対数的であるという事実です。analogWrite()ルーチンは出力ピンの PWM デューティ サイクルを設定しており線形です。したがって、最小デューティ サイクル(たとえば010

代わりに行う必要があるのは、強度を指数関数的に設定することです。あなたの最大強度が であるとしましょう255。この結果は、自分の強さを数値を上げるパワーとして扱うことで生成できます。私たちの場合、バイナリが好きなコンピューターを扱っていることを考えると、2 の累乗が便利です。そう、

2^0 =1
2^8=256

8つの強度レベルを持つことができます。実際には、最小値が完全にオフになっておらず ( では1ない0)、最大値が範囲外である( では256ない) ことに注意してください255。したがって、式を次のように変更します。

output = 2 ^ intensity - 1

またはコードで

int output = 1<<intensity - 1;

0これにより、 ~ から(両端を含む)までの強度レベルに対して 0 ~ 255 の値が得られる8ため、実際には 9 レベルの強度が得られます。よりスムーズなトランジション (より多くのレベルの強度) が必要で、対数強度を使用する場合は、浮動小数点演算が必要になります。

強度を計算するこの方法を各チャネル (R、G、B) に適用すると、知覚はコードが示すべきものと一致します。


さまざまな色の間をスムーズに移行する方法に関する限り、答えは色空間をどのようにナビゲートするかによって異なります。最も簡単な方法は、色空間を R、G、B を頂点とする三角形として考えることです。

ここに画像の説明を入力

問題は、この三角形をどのようにナビゲートするかです。R から G、B の辺に沿って進むことができます。この方法では、白 (すべてのチャンネルが完全にオン) または「黒」 (すべてが完全にオフ) が表示されることはありません。色空間は、紫 (R+B)、黄 (G+B)、茶色 (R+G) の色が追加された六角形と考えることができ、境界をナビゲートすることもできます (ここでも、白または黒はありません)。これらの内部をナビゲートする方法があるのと同じくらい多くのフェージングの可能性があり、私たちが考えるかもしれない他の数字があります.

このようなフェージング プログラムを作成したとき、私が気に入った色空間とトラバーサルは次のとおりでした。各チャネルを 2 進ビットと考えれば、3 つ (R、G、B) になります。各色を、これらのチャネルの組み合わせが完全にオンになっていると考えると、合計 7 色 (黒を除くが白を含む) になります。これらの色の最初の色を取り、黒から黒にフェードしてから、次の色に進みます。そのようなことを行うコードを次に示します。

int targetColor = 1;
int nIntensity = 0;
int nDirection = 1;         // When direction is 1 we fade towards the color (fade IN)
                            // when 0 we fade towards black (fade OUT)
#define MAX_INTENSITY 8
#define MIN_INTENSITY 0
#define MAX_TARGETCOLOR 7

void loop() {
    for (;;) {

        // Update the intensity value
        if (nDirection) {
            // Direction is positive, fading towards the color
            if (++nIntensity >= MAX_INTENSITY) {
                // Maximum intensity reached
                nIntensity = MAX_INTENSITY;  // Just in case
                nDirection = 0;             // Now going to fade OUT
            } // else : nothing to do
        } else {
            if (--nIntensity <= MIN_INTENSITY) {
                nIntensity = MIN_INTENSITY; // Just in case
                // When we get back to black, find the next target color
                if (++targetColor>MAX_TARGETCOLOR) 
                    targetColor=1;          // We'll skip fading in and out of black
                nDirection = 1;             // Now going to fade IN
            } // else: nothing to do
        }

        // Compute the colors
        int colors[3];
        for (int i=0;i<3;i++) {
            // If the corresponding bit in targetColor is set, it's part of the target color
            colors[i] = (targetColor & (1<<i)) ? (1<<nIntensity) -1 : 0;
        }

        // Set the color
        setColor(colors[0], colors[1], colors[2]);

        // Wait
        delay(100);     
    }
}
于 2013-04-04T16:24:54.933 に答える
9

実際、異なる色の間でフェードすることは可能です。また、Arduino の書籍や Web 上のコードで私が通常見逃しているのは、Arduino IDE で C++ クラスを記述できることです。したがって、C++ クラスを使用して色をフェードする例を示します。

すべてのピンがパルス幅変調 ( PWM )に対応しているわけではないため、解決すべき問題はどのピンに対して analogWrite を実行するかということです。Arduino デバイスでは、PWM をサポートするピンはチルダ「~」で示されます。Arduino UNO には、~3、~5、~6、~9、~10、~11 のデジタル ピンがあります。また、ほとんどの Arduino はこれらのピンを PWM に使用しますが、デバイスを確認してください。LED を 1 ミリ秒オンにし、1 ミリ秒オンにすることで、通常のデジタル ピンで PWM を作成できます。これにより、LED の 50% の電力が模倣されます。または、3 ミリ秒オンにして 1 ミリ秒オンにすると、75% の電力が模倣されます。

LED をフェードさせるには、PWM 値を増減して少し待つ必要があります。そうしないと、arduino が 1 秒間に何千回も LED をフェード/ディミングしようとし、フェード効果が見られないため、しばらく待つ必要があります。analogWrite( )したがって、3 つの LED に対して2 番目のパラメータを徐々に減らしたり増やしたりする方法を探しています。より完全な説明については、たとえばArduino Cookbookの第 7 章を​​参照してください。とにかく、その本はArduinoファンにとっては良い読み物です!

そこで、OP のコードを調整して、多かれ少なかれ赤、緑、青の値の単なるコンテナーである「rgb_color」クラスを含めました。しかし、もっと重要なのはフェーダー クラスです。フェーダーのインスタンスが構築されるとき、適切なピンがそれぞれ赤、緑、青のコンストラクターにある必要があります。フェーダーにはvoid fade( const rgb_color& const rgb_color&)、インカラーとアウトカラーの間のフェードを行うメンバー関数が含まれています。デフォルトでは、関数は入力カラーから出力カラーまで 10 ミリ秒の 256 ステップを実行します。(整数除算のため、これは実際には各ステップが 1/256 であることを意味するわけではありませんが、知覚的には気付かないことに注意してください)。

/*
 * LedBrightness sketch
 * controls the brightness of LEDs on "analog" (PWM) output ports.
 */

class rgb_color {

  private:
    int my_r;
    int my_g;
    int my_b;
  public:
    rgb_color (int red, int green, int blue)
      :
        my_r(red),
        my_g(green),
        my_b(blue)
    {
    }

    int r() const {return my_r;}
    int b() const {return my_b;}
    int g() const {return my_g;}
};

/*instances of fader can fade between two colors*/
class fader {

  private:
    int r_pin;
    int g_pin;
    int b_pin;

  public:
    /* construct the fader for the pins to manipulate.
     * make sure these are pins that support Pulse
     * width modulation (PWM), these are the digital pins
     * denoted with a tilde(~) common are ~3, ~5, ~6, ~9, ~10 
     * and ~11 but check this on your type of arduino. 
     */ 
    fader( int red_pin, int green_pin, int blue_pin)
      :
        r_pin(red_pin),
        g_pin(green_pin),
        b_pin(blue_pin)
    {
    }

    /*fade from rgb_in to rgb_out*/
    void fade( const rgb_color& in,
               const rgb_color& out,
               unsigned n_steps = 256,  //default take 256 steps
               unsigned time    = 10)   //wait 10 ms per step
    {
      int red_diff   = out.r() - in.r();
      int green_diff = out.g() - in.g();
      int blue_diff  = out.b() - in.b();
      for ( unsigned i = 0; i < n_steps; ++i){
        /* output is the color that is actually written to the pins
         * and output nicely fades from in to out.
         */
        rgb_color output ( in.r() + i * red_diff / n_steps,
                           in.g() + i * green_diff / n_steps,
                           in.b() + i * blue_diff/ n_steps);
        /*put the analog pins to the proper output.*/
        analogWrite( r_pin, output.r() );
        analogWrite( g_pin, output.g() );
        analogWrite( b_pin, output.b() );
        delay(time);
      }
    }

};

void setup()
{
  //pins driven by analogWrite do not need to be declared as outputs
}

void loop()
{
  fader f (3, 5, 6); //note OP uses 9 10 and 11
  /*colors*/
  rgb_color yellow( 250, 105,   0 );
  rgb_color orange( 250,  40,   0 );
  rgb_color red   ( 255,   0,   0 );
  rgb_color blue  (  10,  10, 255 );
  rgb_color pink  ( 255,   0, 100 );
  rgb_color purple( 200,   0, 255 );
  rgb_color green (   0, 255,   0 );
  rgb_color white ( 255, 255, 255 );

  /*fade colors*/
  f.fade( white, yellow);
  f.fade( yellow, orange);
  f.fade( orange, red);
  f.fade( red, blue);
  f.fade( blue, pink);
  f.fade( pink, purple);
  f.fade( purple, green);
  f.fade( green, white);
}
于 2014-01-17T09:10:18.533 に答える
5

これはおそらくあなたが探しているものです。スペクトル全体で色をシフトし、円を描くように滑らかな動きで色を移行したい場合、実際に行っているのは、HSI/HSV (色相、彩度、強度/値) 色空間で HUE を使用して光をシフトすることです。

あなたがこの図をするなら取る:

ここに画像の説明を入力

色相には 360 度の色があるため、色相には 0 ~ 360 の値を付けます。彩度には 0.00 - 1.00 の値、強度/値には 0.00 -1.00 の値

これがMEGA 2560の私の回路です: ここに画像の説明を入力

このコードを実行しているビデオは次のとおりです。

<iframe width="560" height="315" src="https://www.youtube.com/embed/gGG-GndSKi0" frameborder="0" allowfullscreen></iframe>

それでは、色相値と for ループをループ関数内に渡して、その値を 360 回呼び出して虹色全体をシフトできる関数を作成しましょう。

//Define the pins we will use with our rgb led
int redPin = 9;
int greenPin = 10;
int bluePin = 11;

//define that we are using common anode leds
#define COMMON_ANODE

void setup()
{
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
}

int rgb[3];
//Arduino has no prebuilt function for hsi to rgb so we make one:
void hsi_to_rgb(float H, float S, float I) {
  int r, g, b;
  if (H > 360) {
    H = H - 360;
  }
  // Serial.println("H: "+String(H));
  H = fmod(H, 360); // cycle H around to 0-360 degrees
  H = 3.14159 * H / (float)180; // Convert to radians.
  S = S > 0 ? (S < 1 ? S : 1) : 0; // clamp S and I to interval [0,1]
  I = I > 0 ? (I < 1 ? I : 1) : 0;
  if (H < 2.09439) {
    r = 255 * I / 3 * (1 + S * cos(H) / cos(1.047196667 - H));
    g = 255 * I / 3 * (1 + S * (1 - cos(H) / cos(1.047196667 - H)));
    b = 255 * I / 3 * (1 - S);
  } else if (H < 4.188787) {
    H = H - 2.09439;
    g = 255 * I / 3 * (1 + S * cos(H) / cos(1.047196667 - H));
    b = 255 * I / 3 * (1 + S * (1 - cos(H) / cos(1.047196667 - H)));
    r = 255 * I / 3 * (1 - S);
  } else {
    H = H - 4.188787;
    b = 255 * I / 3 * (1 + S * cos(H) / cos(1.047196667 - H));
    r = 255 * I / 3 * (1 + S * (1 - cos(H) / cos(1.047196667 - H)));
    g = 255 * I / 3 * (1 - S);
  }
  rgb[0] = r;
  rgb[1] = g;
  rgb[2] = b;

}
void setColor(int red, int green, int blue)
{
  #ifdef COMMON_ANODE
    red = 255 - red;
    green = 255 - green;
    blue = 255 - blue;
  #endif
  analogWrite(redPin, red);
  analogWrite(greenPin, green);
  analogWrite(bluePin, blue);
}


///here we have our main loop and the for loop to shift color
void loop()
{
//the for loop counts to 360 and because its in our control loop it will run forever
// We will use int i to increment the actual desired color 
for (int i=0; i<=360;i++){
  hsi_to_rgb(i,1,1);
  setColor(rgb[0],rgb[1],rgb[2]);
  //Changing the delay() value in milliseconds will change how fast the
  //the light moves over the hue values 
  delay(5);
  }


}
于 2016-08-22T15:41:48.833 に答える
4

色間でフェードしたい場合は、簡単な色空間で作業し、最後に RGB に変換します。

たとえば、HSL 色空間で作業し、S と L を一定に保ち (完全に飽和した明るい色など)、円の周りで H を「フェード」します。赤から緑、青を経て赤に戻ります。RGB に変換してから、それらの値を LED ドライブに使用します。私はこの手法を「ムード ランプ」アプリに使用しました。色空間変換の他のコードは SO にあります。

于 2013-04-05T10:35:35.477 に答える
1

色に構造体を使用すると、コードを簡素化できます。

struct Color
{
    unsigned char r;
    unsigned char g;
    unsigned char b;
};

すると、簡単にフェード機能が付きます

// the old color will be modified, hence it is not given as reference
void fade(Color old, const Color& newColor)
{
    // get the direction of increment first (count up or down)
    // each of the inc_x will be either 1 or -1
    char inc_r = (newColor.r - old.r)/abs(newColor.r-old.r); // note, that the right hand side will be sign extended to int according to the standard.
    char inc_g = (newColor.g - old.g)/abs(newColor.g-old.g);
    char inc_b = (newColor.g - old.g)/abs(newColor.g-old.g);

    fadeOneColor(old.r, newColor.r, inc_r, old);
    fadeOneColor(old.g, newColor.g, inc_g, old);
    fadeOneColor(old.b, newColor.b, inc_b, old);
}

void fadeOneColor( unsigned char& col_old, 
                   const unsigned char& col_new, 
                   const char inc, 
                   Color& col)
{
    while(col_old != col_new)
    {
        col_old += inc;
        SetColor(col); 
        delay(20);
    }        
}
于 2013-04-04T07:44:59.690 に答える