9

我目前已经设法让我的 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占空比,并且是线性的。因此,通过采用最小占空比(例如0)和最大占空比(例如,为了便于数学计算,这是10)并将其分成相等的块,您将线性控制强度,这不会给出令人满意的结果。

你需要做的是以指数方式设置你的强度。假设你的最大强度是255。您可以通过将您的强度视为提高某个数字的力量来生成此结果。在我们的例子中,鉴于我们正在处理喜欢二进制的计算机,所以 2 的幂是方便的。所以,

2^0 =1
2^8=256

所以我们可以有8个强度级别。实际上,请注意 out minimum 现在没有完全关闭(1不是0),我们的最大值超出了范围(256不是255)。所以我们修改公式为

output = 2 ^ intensity - 1

或者在代码中

int output = 1<<intensity - 1;

这会产生从 0 到 255 的强度级别从08(含)的值,因此我们实际上得到了九个强度级别。如果您想要更平滑的过渡(即更多级别的强度),并且仍然使用对数强度,您将需要浮点数学。

如果您将这种计算强度的方法应用于每个通道(R、G、B),那么您的感知将与您的代码所说的一致。


至于如何在各种颜色之间平滑过渡,答案取决于您希望如何导航颜色空间。最简单的做法是将色彩空间想象成一个三角形,以 R、G 和 B 作为顶点:

在此处输入图像描述

那么问题是如何导航这个三角形:你可以沿着边走,从 R 到 G,再到 B。这样你就永远不会看到白色(所有通道完全打开)或“黑色”(所有通道完全关闭)。你可以把你的色彩空间想象成一个六边形,带有额外的紫色 (R+B)、黄色 (G+B) 和棕色 (R+G) 颜色,并且还可以在周边导航(同样,没有白色或黑色)。有多少种方法可以在这些内部导航,以及我们可能想到的其他数字,就有多少种褪色的可能性。

当我构建这样的渐变程序时,我喜欢的颜色空间和遍历如下:将每个通道想象成一个二进制位,所以现在你有三个(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 书籍和网络上的代码中通常也缺少的是,可以在 Arduino IDE 中编写 C++ 类。因此,我将展示一个使用 C++ 类在颜色之间渐变的示例。

应该解决的一个问题是应该对哪些引脚进行模拟写入,因为并非所有引脚都能够进行脉冲宽度调制 ( PWM )。在 Arduino 设备上,支持 PWM 的引脚用波浪号“~”表示。Arduino UNO 有数字引脚 ~3、~5、~6、~9、~10 和 ~11。大多数 Arduino 将这些引脚用于 PWM,但请检查您的设备以确保。您可以通过将 LED 打开 1 毫秒和 1 毫秒来在常规数字引脚上创建 PWM,这模拟了 LED 上 50% 的功率。或者将其打开 3 毫秒和 1 毫秒,这模拟了 75% 的功率。

为了使 LED 变暗,您必须减少/增加 PWM 值并稍等片刻。您将不得不等待一会儿,否则 arduino 会每秒尝试使 LED 褪色/变暗数千次,而您将看不到褪色效果,尽管它可能在那里。因此,您正在寻找一种方法来逐渐减少/增加第二个参数以analogWrite( )用于三个 LED;有关更详尽的解释,请参见Arduino Cookbook的第 7 章。无论如何,这本书对于 Arduino 粉丝来说都是一本好书!

所以我修改了 OP 的代码以包含一个“rgb_color”类,它或多或少只是一个红色、绿色和蓝色值的容器。但更重要的是推子类。构造推子实例时,正确的引脚应分别位于构造函数中的红色、绿色和蓝色。推子包含一个成员函数void fade( const rgb_color& const rgb_color&),它将在输入和输出颜色之间进行渐变。默认情况下,该函数从输入颜色到输出颜色需要 10 毫秒的 256 步。(请注意,由于整数除法,这并不意味着每一步都是 1/256 th,但从感知上讲你不会注意到它)。

/*
 * 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 来移动光。

如果你愿意,就拿这个图:

在此处输入图像描述

我们将为色调附加一个 0-360 的值,因为色调具有 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 回答
1

我想提供一个对用户更友好的答案,以帮助理解它的工作原理。

在下面的示例中,我使用的是共阳极 RGB LED。


然而,在我的项目中:要将颜色设置为 RGB LED,我通过硬件串行发送一个字符串。

命令示例: RGB000255000

这个作为字符串的命令分为 4 个部分,每个部分 3 个字符。使用上面的命令示例:

  • "RGB" : 过滤命令执行的位置。
  • “000”:第二个 3 个字符代表红色值。
  • “255”:第三个 3 个字符代表绿色值。
  • “000”:第 4 个 3 个字符代表蓝色值。

结果:这将在您的 LED 上输出绿色。


请参阅下面的代码:

// Set your LED Pins.
const int rPin = 9;
const int gPin = 10;
const int bPin = 11;

// Set the variables that will assign a value to each Color Pin.
int rVal = 255;
int gVal = 255;
int bVal = 255;

// Fade Red Pin (In / Out).
void FadeRed(int red)
{
  // When Red Value on Red Pin is Inferior to the New Value: Fade In.
  if (rVal < red)
  {
    // Fade Out.
    for (int r = rVal; r <= red; r++)
    {
      // Set the Variable and Pin values.
      rVal = r;
      analogWrite(rPin, rVal);

      // Delay Slighlty (Synchronously). For Asynchronous Delay; you may try using "millis".
      delay(6);
    } 
  }

  // When Red Value on Red Pin is Superior to the New Value: Fade Out.
  else
  { 
    for (int r = rVal; r >= red; r--)
    {
      rVal = r;
      analogWrite(rPin, rVal);
      delay(6); 
    }
  }
}

// Fade Green Pin (In / Out).
void FadeGreen(int green)
{
  if (gVal < green)
  {
    for (int g = gVal; g <= green; g++)
    {
      gVal = g;
      analogWrite(gPin, gVal);
      delay(6);
    }
  }
  
  else
  { 
    for (int g = gVal; g >= green; g--)
    { 
      gVal = g;
      analogWrite(gPin, gVal);
      delay(6);
    }
  }
}

// Fade Blue Pin (In / Out).
void FadeBlue(int blue)
{
  if (bVal < blue)
  {
    for (int b = bVal; b <= blue; b++)
    {
      bVal = b;
      delay(6);
      analogWrite(bPin, b);
    }
  }
  
  else
  {
    for (int b = bVal; b >= blue; b--)
    {
      bVal = b;
      delay(6);
      analogWrite(bPin, b);
    }
  }
}

void FadeColor(int red, int green, int blue)
{
    // Debug Only.
    Serial.println("\n[+] Received Values");
    Serial.println(red);
    Serial.println(green);
    Serial.println(blue);

    // Code.
    FadeRed(red);
    FadeGreen(green);
    FadeBlue(blue);

    // Debug Only.
    Serial.println("\n[+] Pin Values \n");
    Serial.println(rVal);
    Serial.println(gVal);
    Serial.println(bVal);
}

/* Set RGB LED Color According to String Value. (i.e: RGB000000000) */
void SetColor(String color)
{  
  // Retrieve the New Color from String.
  /* Split a String : Start Position; End Position */
  String red = color.substring(3, 6);   /* Get the 1st 3 Characters Corresponding to RED   */
  String green = color.substring(6, 9); /* Get the 2nd 3 Characters Corresponding to GREEN */
  String blue = color.substring(9, 12); /* Get the Last 3 Characters Corresponding to BLUE */

  int r = atoi(red.c_str());
  int g = atoi(green.c_str());
  int b = atoi(blue.c_str());

  int redVal = 255 - r;
  int grnVal = 255 - g;
  int bluVal = 255 - b;
  
  FadeColor(redVal, grnVal, bluVal);
}


void setup()
{  
  pinMode(rPin, OUTPUT);
  pinMode(gPin, OUTPUT);
  pinMode(bPin, OUTPUT);

  pinMode(rPin, HIGH);
  pinMode(gPin, HIGH);
  pinMode(bPin, HIGH);

  analogWrite(rPin, rVal);
  analogWrite(gPin, gVal);
  analogWrite(bPin, bVal);
}
于 2021-01-08T14:24:30.350 回答
0

Here's a fast linear fade between two RGB values stored in uint32_t as 0x00RRGGBB as is used in many addressable RGB pixel strips such as in NeoPixel (and is inspired by some of the code in the NeoPixel Arduino library).

It doesn't take colour space into consideration but still looks nice and smooth in practice.

uint32_t fadeColor(uint32_t const x, uint32_t const y, uint8_t const fade)
{
  // boundary cases don't work with bitwise stuff below
  if (fade == 0)
  {
    return x;
  }
  else if (fade == 255)
  {
    return y;
  }

  uint16_t const invFadeMod = (255 - fade) + 1;
  uint16_t const fadeMod = fade + 1;
  // overflows below to give right result in significant byte
  uint8_t const xx[3] // r g b
  {
    static_cast<uint8_t>((uint8_t(x >> 16) * invFadeMod) >> 8),
    static_cast<uint8_t>((uint8_t(x >> 8) * invFadeMod) >> 8),
    static_cast<uint8_t>((uint8_t(x >> 0) * invFadeMod) >> 8),
  };
  uint8_t const yy[3] // r g b
  {
    static_cast<uint8_t>((uint8_t(y >> 16) * fadeMod) >> 8),
    static_cast<uint8_t>((uint8_t(y >> 8)* fadeMod) >> 8),
    static_cast<uint8_t>((uint8_t(y >> 0)* fadeMod) >> 8),
  };
  return ((uint32_t)(xx[0] + yy[0]) << 16) | ((uint32_t)(xx[1] + yy[1]) <<  8) | (xx[2] + yy[2]);
}
于 2015-12-08T16:29:15.107 回答