Showing posts with label software. Show all posts
Showing posts with label software. Show all posts

Wednesday, November 29, 2023

Return of the Christmas Tree Project (ch 5): More LEDs!

It's been two years since I started my Christmas tree project. Last year, I didn't work on the project at all, but this year, we're back in full force. And I bought more led lights to put in my tree. In fact, we'll be doing five times as many LEDs as before!

Because it had been so long and because I had moved since I worked on it last, the first thing was to get the project up and running again. And the first thing to work on was my Raspberry Pi. Unfortunately, the sd card in the Pi that I was using before had gotten corrupted.

I spent quite a bit of time looking for my card reader, but all it did was confirm that the sd card did not work at all. Luckily having found the card reader also made it very easy to get a new sd card ready. The next thing was to find the code. It wasn't on my desktop, so it had to be on my private git server. However, that server failed a while ago, so I had to track down the storage I used in that server. This took some effort, but I found it eventually. And then it turned out that the code wasn't on there either...

The code had apparently only been on the sd card, so I would have to write it again. The amount and complexity of the code was such that that was feasible enough, but it was still a bummer. I took a look at the code from the project that had been the inspiration for this one to look at what libraries had been used. Looking at that repository made me remember that I had shared my own code with the world as well. And that was where the code was! You see, I only kept my private projects on my private git server. My public projects were all on GitHub. And indeed, the lost code was right there on GitHub.

It seems that I did have some code that was only on the sd card. In particular, I seem to be missing the three dimensional effects that I wrote. One was a rainbow effect and that should be easy enough to recreate, especially because the one effect that I still have does have much of the general structure I'd want to use. The other effect was an effect that never really worked that well, and isn't really that much of a loss.

With the pi running again and the code being back in my possession, I could get to working on the thing again. I was unsure if I would be able to run all the LEDs from one power source, so that was the first thing to test. I connected all my new LEDs and set them to white light at 25% brightness.

Several interpretations of white...

To be fair, all lights turned on and all of them being white is the worst case. But, 25% brightness is roughly 75% below the worst case. And the results were pretty bad. However, this looked like the power adapter was handling it just fine. Instead, it looked like voltage drop to me, which is not related to the adapter but to the lights and the wires between them.

So, I rigged up a quick test and saw that indeed, things looked a lot better if I connected the power source to each string of lights separately, even if it was still the same single adapter. The light strings do have extra wires that help you do this, but the bare wires aren't too easy to use. So, I decided to take some of my Dupont cables, cut them in half and splice the halves onto those wires to make them much easier to use. Using these tools and this skill set was a lot of fun, which surprised me because I had forgotten how much I liked doing this.

A bit of solder
Some tape
And some more tape

I added a breadboard to map from one input coming from my micro-usb breakout boar to multiple wires going to the different strings. This actually made for a surprisingly good setup.

Now, that's what I'd call white

I also worked a bit on my code. I took some of my old one-dimensional code and cleaned it up. I also wrote a simple rainbow effect. It's not as good as an effect that includes a tree and coordinates of the LEDs, of course, but it's still quite nice. And I did use some of the lessons I remembered from making the rainbow effect for in the tree two years ago.

The next step will be to try to cram all these LEDs into my small tree. (I'm at half the number of LEDs Matt Parker used, but my tree is also a lot smaller.) And then I can start using the most essential and complex code I wrote last year to map all the LEDs. I'm happy I won't have to rewrite that code!

Saturday, December 18, 2021

Christmas Tree Project Step 4: Improving the Mapping

Last time, I got the first 3D mapping of the LED coordinates. I wasn't entirely happy with the results, though, so it was time to improve them. So, I started by working on code that I could use to verify the results of the scan. The idea was to turn a few LEDs on at a time, so you can visually verify that those LEDs are in the right order on one of the axes. However, while the y axis is completely obvious, the x and z axes aren't visible in the real world, so that was the first thing I did: create a gradient from red to blue on one of the axes so you could identify the axis.

The x axis

From there, I turned the lights green one by one according to their order on that axis. My original idea was to turn just the light after it and the light before it on, or perhaps a few, but I ended up just turning everything on, with the ones that were before the green LED being red, and the ones after it blue. Then, you could press either space if the LED appeared to have the right coordinate when it came to that axis, or x if it didn't. The software would then remember the incorrect coordinates and write them to a file when done.

Verifying the green LED

A modified version of the mapper can now also be used to scan only the incorrect coordinates. In that case, it will read which LEDs need to be rescanned from the file. It then scans every such LED from all four directions, but only updates the coordinate components of the axes marked as incorrect. With those changes made, I got back to the physical tree and got working on my new coordinates.

I decided not to use my previous mapping results, but start over instead. I had marked the position and orientation of the tree and the camera stand hadn't moved, but the camera wasn't in it in the exact same way. The way the webcam was mounted on the stand was a bit improvised and the webcam was borrowed from my work setup, so it hadn't stayed in the stand and the result was that I didn't put it back in the exact same way.

Improvised does not mean bad!

So, I took a few pictures with the new setup to get the tree properly in frame again and then started scanning. I alternated between judging the coordinates and rescanning them several times until I ended up with something that I was happy with.

Deciding what LED was wrong and what wasn't always obvious. When two LEDs are in the incorrect order, this method just isn't great for determining if the first one or the second one was wrong. When there are more LEDs involved, it gets even more complicated. I did develop a bit of a feeling for it, I just chose for rescanning more than necessary in a number of cases and I restarted the verifying process without saving the results a number of times. I could probably improve things by allowing you to go back in the LED order as well, so I will probably do that if I end up mapping another tree.

I was also important to learn that the result just wasn't going to be perfect. Slight changes in perspective can have big results, and we're already combining several perspectives into one coordinate. Whenever two LEDs were a close call, it was best to just go with whatever result the scan had. The mapping wouldn't be perfect, but it was never going to be perfect and as long as it's close enough, it'll look good, especially considering that the viewer won't have a fixed perspective either.

Finally, there was one LED left that just didn't get a good scan for its Z coordinate. I checked the photos and saw that it was indeed on, but just wasn't visible from either of the two opposite sides. I decided to just set the coordinate manually, so I lit it up as well as some LEDs around it. I identified which were its closest neighbors and gave it a coordinate between them, which I manually put into the output file.

With the new mapping, the next thing to do was of course to run the test effect again. I think the actual changes were relatively small, but the whole effect looks a lot more smooth to me...

(Well, I promise it does in person, at least.)

As before, all the code I wrote for this project can be found on GitHub.

Is the this the end of this project, then? Not just yet. The basic setup is done now, but I do still need to run effects on the thing. I am planning to both write some of my own and run the ones submitted to project that serves as inspiration for this one...

Tuesday, December 14, 2021

Christmas Tree Project Step 3: Mapping

Spoilers of what I pulled off

With the hardware gathered and the lights working, the next step was to put the lights in the tree. I took a bag of zip ties that I think I bought with the intention of using the for cable management in my computer (I bought way too many, though). For the most part, this was pretty uneventful, but there were a few things that I would do different if I'd do this again. The first is that I would plan it more carefully and use more zip ties and attach in more places. The second is that I dislike the colored wires between the lights, so I think I would sleeve them in something green to make them stand out less.

The colored wires do stand out...

I also wanted wifi on my raspberry pi, so I wouldn't have to run a network cable to it. As I realized last time, the device itself doesn't have wifi hardware, so I went looking for wifi adapters. I found two. Both are old and were probably cheap, but they were worth a shot. The first adapter didn't get recognized by the Raspberry Pi, but the second one worked well and after some messing with the configuration, I was able to freely move the device anywhere I had power.

Getting wifi running

The next part was the real challenge: mapping the LEDs to three dimensional coordinates. I did actually send an email to Matt Parker to see if he could share the code he used for this, but unfortunately, he didn't have it in a usable form. Not wanting to wait, I started working on my own.

Getting the camera to take a picture was both easy and hard at the same time. Basically, copying some code from the internet and saving an image wasn't that hard. However, there were basically three libraries to choose from, and none of them was exactly what I wanted and I definitely didn't like that you have to install non-python dependencies for each of them. In the end, I went with pygame, which uses sdl2 under the hood.

My complete mapping setup

There were also several issues with the camera that I had to solve once I started using it more extensively. The first was that the first photo would often have different brightness at the top than at the bottom, with a clear dividing line about a fifth from the top. However, this didn't show up at first, as it starts happening when you run the program more than once. I solved this by taking an image from the camera on startup, discarding that and then working only with the images taken later on.

The next problem was something I started noticing when I was working on the mapping: the photos taken were often from well before I gave the command, sometimes by ten or more seconds. I got around this by re-initializing the camera every time I wanted to take a picture, as then the picture would always end up being current. To prevent the previous brightness issue, though, I had to discard an image each time I did this.

With the camera working, the next step was to start identifying the LEDs and their location. The idea was simple: turn on a LED, take a picture, identify the brightest pixel in the picture and repeat this for the next LED. I also added a step of drawing some lines on the picture, so I could see the result.

Pretty orange lines

The first result wasn't bad for some of the LEDs, but just going with the brightest pixel didn't quite work for others. So, I changed my code to take the average of several bright pixels. My first implementation was quite bad and allowed a gradual increase in brightness to shift the average towards the top of the image. But even after fixing that, the results weren't to my satisfaction at all. So, I decided to take a different approach. Instead of looking for the brightest pixels, I just look with pixels that are above a hardcoded brightness.

For the most part, that gave pretty good results. However, when a lot of light was being reflected, the reflection of the LED would sometimes throw off the calculation. The way I solved this was by lowering the brightness of the LEDs. Originally, I had assumed that full brightness was a good idea, but in the end about 20% brightness gave better results because there were far fewer reflections that were bright enough to be taken into account.

Next, it was time to do multiple scans and combine the results. Doing multiple scans was straight forward: just put the code in a function and all that function four times, with a bit in between where tell the user to rotate the tree. For combining, I gave each mapping result a score, based on the brightness and on how many pixels were involved. Then, I took the y coordinate from the highest score on any of the four scans, the x coordinate from the best among the front and back scans and the z coordinate from the best of among the left and right scans. And those results I printed to a file.

In order to check the results, I quickly threw together some code that read the file (this was basically a copy-paste job from Matt's code) and then changed the color of each LED in order of either the x, y or z portion of the coordinate. And I must say, I'm not at all displeased with the result. It's not perfect, but it's quite good, especially if you consider this was based on the first time I did a full scan of the tree.

There are still LEDs that pretty clearly have inaccurate coordinates. So, that's what I'll have to do for next time: build the tools to see what LEDs are wrong and correct those...

Oh, also: my code is now on GitHub.