You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug] EV3 color sensor enables all LEDs when using reflection() #2840
Describe the bug
On the default OS and on pybricks 2.0 (which uses ev3dev), the color sensor enables just the red LED when using the reflection() method. Currently it seems to enable all LEDs. For ambient lighting and color sensing the LEDs are correctly enabled (just blue for ambient, all for color).
To reproduce
Steps to reproduce the behavior:
Connect the color sensor to the EV3 and either create a small program calling sensor.reflection() or just go to the port view page and cycle through the color sensor modes.
Expected behavior
Should be just red for consistency unless there is a good reason
I'd prefer to keep it consistent with the original and the Pybricks 2.0 firmware, especially for my current project (an optical disc reader where the disc is printed paper with black and white squares). It also seems that the three LEDs create a wider "light cone" which isn't ideal for my use case. I don't know how much control there is over the hardware, but if it's possible then maybe there could be a parameter or method that sets different LED modes.
I also think that red performs the best in a lot of cases. Red light seems to be the best at detecting contrast change (like my project or a line following robot) and it also uses the least amount of voltage which means better battery life. I also need to get readings as fast as possible, so averaging probably isn't ideal.
Good feedback, thanks. Is this based on expected results or did you find measurable accuracy differences?
I don't know how much control there is over the hardware, but if it's possible then maybe there could be a parameter or method that sets different LED modes.
It goes through all three rapidly and gives us the reflected intensity for all three separately.
So the good news is that we can optionally let users pass the color they want the value for, instead of taking the average.
sensor.reflection(Color.RED)
What do you think? Visually, you would still see all three, but the measurement would truly be red only, so with the narrower cone.
I also need to get readings as fast as possible, so averaging probably isn't ideal.
I just had a look, and in the mode we use we get samples at about 500 Hz, which is very fast, so this aspect probably isn't holding you back.
If you want to collect some hard facts on this, here's an idea. The existing rgb method gives you the red, green, and blue values.
The only downside is that they are not white-balanced. So you can measure them once upfront against a white surface, and linearly scale each value to be equal for white.
Then you can compare just the red value to the average of all three, and see if it makes a measurable difference in your creation. If it does, we can consider adding this option to the reflection method as above 🙂
Is this based on expected results or did you find measurable accuracy differences?
It's pretty hard for me to compare it to Pybricks 2.0 because it could barely keep up in general. For my project I can control the disc read speed and the interval between sensor samples. The sweet spot seems to be around 10ms between samples. If I go above that then there are not enough samples per bit for accurate readings, and I can't slow down the read speed any further because then the motor gets jumpy at the edges of the disc.
The Pybricks 2.0 code could just about handle it (at least some of the time) but if I did anything like print to the EV3 screen then it couldn't keep up. The new version is way better! I even tried decreasing the sample interval to 4ms and it still worked!
It does seem slightly better than the old version accuracy-wise but I think that's mainly because it just runs better and I can lower the sample interval.
I also took this image of the light area increase:
This image shows the sensor reading the paper disc from below. You can see some black bits through the paper around the sensor (the big dark circle in the middle is the disc center). It isn't really clear in this image, but the "upper half" of the red LED is perfectly on the black track. I noticed that only the upper half actually matters in my setup which allowed me to halve the bit size so I can fit more on one disc. The thing is that the blue and green LED are both at slightly different angles, so the green one shines slightly above the current track and the blue one slightly below it.
If there is one thing I have learned while working on this, then it's that precision matters. It has to be very precise. Reading like this is seemingly still possible, but the point is that it just doesn't make much sense. If I had just the red LED then it would be simpler, more efficient, and likely more accurate. I will try to do some more tests tomorrow and see how much it really matters, but in general I always love simplicity and for this project it seems simpler to just sample using a constant red LED instead of multiple cycling LEDs even if it then only uses the red reading.
For me an ideal API might be something like this:
sensor.reflection(LightMode.RED) # Uses only red LEDsensor.reflection(LightMode.BLUE) # Uses only blue LEDsensor.reflection(LightMode.GREEN) # Uses only green LEDsensor.reflection(LightMode.CYCLE) # Uses all and averages
This way I could also directly compare accuracy easily. The same could be added for the ambient() method though that may make less sense. Or it could simply be this:
sensor.reflection(color_cycle=False) # Sets the color cycle to false to only use the red LED
In general my ideal setup would be a single constant red LED with a possible sample interval of 8 to 12ms. I don't know how hard this is to implement but it seems simple to just tell it to keep only the red LED on and directly get the reading instead of cycling and averaging. After all, the ambient method can already just tell it to enable only blue.
I just had a look, and in the mode we use we get samples at about 500 Hz
When cycling through color LEDs, is this per color sample, or do you mean the combined full sample? And would this be different when just using a single LED? Like, if this is per LED and it cycles then it would be about 6ms per full sample but a single red LED would only need 2ms. That would be nice in case I do want to read samples even faster, which would also allow me to spin the disc faster. And it would just be nicer in general because it's not wasting time and resources sampling stuff that may just make it less accurate...
Sorry for this wall of text.
The sensor does all that internally, and reports back the combined set of values.
Staying in this mode is actually what makes it simpler for all users, because there isn't any delay in switching modes. We get reflection and color all at the same time.
And since we get all samples in about 2ms in one go, there's no real gain from switching to the plain color mode.
You can still get the original readings with the LUMPDevice class in the iodevices module by the way. You'll want to use mode 0.
Okay, so if it uses full color, it takes the same time as just the red LED (2 ms) but returns 3 readings instead of 1? If so, then I see that it makes sense to keep the same color mode enabled all the time to avoid delays from switching modes. But why doesn't ambient() use the color mode too and just use the blue sample? This way there are 0 mode switches.
I also didn't know about the LUMPDevice class (skipped it when reading the docs because I assumed it was only for third-party sensors for some reason)! It's very helpful indeed, thanks for pointing that out!
Also did some tests for my project:
Bit
Red
Color
White
~81
~73
Black
~9
~11
As you can see, the red readings are indeed better (especially for the white bits).
I think your initial idea for the API actually makes sense now that I understand more about how this works. Ideally I should be able to specify which of the 3 channels I want, and if I enable multiple then it will average them. This way everyone has full control without any mode switches.
For now I will use lump_device.read(0).
Activity
laurensvalk commentedon Oct 3, 2026
We're currently still working on adjusting the full scale, but we do intend to use the average of red, green, and blue for better consistency.
Would you prefer to base it off of just one LED?
SubatomicPlanets commentedon Oct 3, 2026
I'd prefer to keep it consistent with the original and the Pybricks 2.0 firmware, especially for my current project (an optical disc reader where the disc is printed paper with black and white squares). It also seems that the three LEDs create a wider "light cone" which isn't ideal for my use case. I don't know how much control there is over the hardware, but if it's possible then maybe there could be a parameter or method that sets different LED modes.
I also think that red performs the best in a lot of cases. Red light seems to be the best at detecting contrast change (like my project or a line following robot) and it also uses the least amount of voltage which means better battery life. I also need to get readings as fast as possible, so averaging probably isn't ideal.
laurensvalk commentedon Oct 3, 2026
Good feedback, thanks. Is this based on expected results or did you find measurable accuracy differences?
It goes through all three rapidly and gives us the reflected intensity for all three separately.
So the good news is that we can optionally let users pass the color they want the value for, instead of taking the average.
What do you think? Visually, you would still see all three, but the measurement would truly be red only, so with the narrower cone.
I just had a look, and in the mode we use we get samples at about
500Hz, which is very fast, so this aspect probably isn't holding you back.laurensvalk commentedon Oct 3, 2026
If you want to collect some hard facts on this, here's an idea. The existing
rgbmethod gives you the red, green, and blue values.The only downside is that they are not white-balanced. So you can measure them once upfront against a white surface, and linearly scale each value to be equal for white.
Then you can compare just the red value to the average of all three, and see if it makes a measurable difference in your creation. If it does, we can consider adding this option to the reflection method as above 🙂
SubatomicPlanets commentedon Oct 3, 2026
It's pretty hard for me to compare it to Pybricks 2.0 because it could barely keep up in general. For my project I can control the disc read speed and the interval between sensor samples. The sweet spot seems to be around 10ms between samples. If I go above that then there are not enough samples per bit for accurate readings, and I can't slow down the read speed any further because then the motor gets jumpy at the edges of the disc.
The Pybricks 2.0 code could just about handle it (at least some of the time) but if I did anything like print to the EV3 screen then it couldn't keep up. The new version is way better! I even tried decreasing the sample interval to 4ms and it still worked!
It does seem slightly better than the old version accuracy-wise but I think that's mainly because it just runs better and I can lower the sample interval.
I also took this image of the light area increase:
This image shows the sensor reading the paper disc from below. You can see some black bits through the paper around the sensor (the big dark circle in the middle is the disc center). It isn't really clear in this image, but the "upper half" of the red LED is perfectly on the black track. I noticed that only the upper half actually matters in my setup which allowed me to halve the bit size so I can fit more on one disc. The thing is that the blue and green LED are both at slightly different angles, so the green one shines slightly above the current track and the blue one slightly below it.
If there is one thing I have learned while working on this, then it's that precision matters. It has to be very precise. Reading like this is seemingly still possible, but the point is that it just doesn't make much sense. If I had just the red LED then it would be simpler, more efficient, and likely more accurate. I will try to do some more tests tomorrow and see how much it really matters, but in general I always love simplicity and for this project it seems simpler to just sample using a constant red LED instead of multiple cycling LEDs even if it then only uses the red reading.
For me an ideal API might be something like this:
This way I could also directly compare accuracy easily. The same could be added for the ambient() method though that may make less sense. Or it could simply be this:
In general my ideal setup would be a single constant red LED with a possible sample interval of 8 to 12ms. I don't know how hard this is to implement but it seems simple to just tell it to keep only the red LED on and directly get the reading instead of cycling and averaging. After all, the ambient method can already just tell it to enable only blue.
When cycling through color LEDs, is this per color sample, or do you mean the combined full sample? And would this be different when just using a single LED? Like, if this is per LED and it cycles then it would be about 6ms per full sample but a single red LED would only need 2ms. That would be nice in case I do want to read samples even faster, which would also allow me to spin the disc faster. And it would just be nicer in general because it's not wasting time and resources sampling stuff that may just make it less accurate...
Sorry for this wall of text.
laurensvalk commentedon Oct 3, 2026
The sensor does all that internally, and reports back the combined set of values.
Staying in this mode is actually what makes it simpler for all users, because there isn't any delay in switching modes. We get reflection and color all at the same time.
And since we get all samples in about 2ms in one go, there's no real gain from switching to the plain color mode.
You can still get the original readings with the LUMPDevice class in the iodevices module by the way. You'll want to use mode 0.
SubatomicPlanets commentedon Oct 4, 2026
Okay, so if it uses full color, it takes the same time as just the red LED (2 ms) but returns 3 readings instead of 1? If so, then I see that it makes sense to keep the same color mode enabled all the time to avoid delays from switching modes. But why doesn't ambient() use the color mode too and just use the blue sample? This way there are 0 mode switches.
I also didn't know about the LUMPDevice class (skipped it when reading the docs because I assumed it was only for third-party sensors for some reason)! It's very helpful indeed, thanks for pointing that out!
Also did some tests for my project:
As you can see, the red readings are indeed better (especially for the white bits).
I think your initial idea for the API actually makes sense now that I understand more about how this works. Ideally I should be able to specify which of the 3 channels I want, and if I enable multiple then it will average them. This way everyone has full control without any mode switches.
For now I will use
lump_device.read(0).