Cannon Build Update

Cannon has been coming along very well. I have spent the majority of my free moments–those not dedicated to studying for midterms or doing projects–getting Cannon together. Team Whyachi finally finished Coercion’s new 9.2oz, hardened S7 blade and Cannon’s 3.0oz titanium beater; I expect to have them here by Monday. The goal is to have Cannon in a place where I can plug in the beater and have a finished robot (but this is combat robotics, so that’s a very lofty goal indeed).

Since I last posted, I cut out the back and drilled the twenty holes that will hold Cannon together. I am using .375″ #6-19 button head plastite screws from McMaster-Carr, which are similar to the mind-bendingly strong ones that we used on Tough Nut last year. So far, everything seems to be holding up pretty well. I did the traditional “throw your frame down the stairs” test, and nothing broke. That means it will be super-strong in the arena, right?

The next steps in the build process will be to cut out and thread the ends of the weapon shaft, fashion a pulley for the weapon motor, drill and thread holes in the pulley I already have for the weapon, and then to get wiring. If I have time before Motorama, I will also harden Cannon’s S7 weapon shaft and Lolcat’s S7 weapon shaft. Lolcat’s bent at Bot Blast, so I do not have high hopes for its toughness against all the horizontal spinners that are registered for Motorama. However, the new motors I bought for it should provide a nice speed boost.

Here’s a front view of Cannon (note that I am waiting on shorter plastite screws from McMaster so not all the screws are in it yet):

It would look so much better with a weapon...

It would look so much better with a weapon…

Breaking Ground on the Cannon

My father’s health has improved a lot and I have a three-day weekend: it’s high time I started getting the new version of Cannon built. I have spent the last few days machining away and making small changes to my drawings. With the exception of the first side rail (on which I carelessly used a 1/4″ end mill instead of the 1/8″ end mill I told my GCode generator I would use), everything has gone surprisingly smoothly. There have been no bumps in the road.

Just kidding, this is combat robotics — where the probability of fire is about equal to the probability of success. Things actually have gone relatively smoothly. The biggest issue I have had during my time standing in my machine shop was that the power went out while I was making the top. Luckily, I was able to finish cutting the piece out with an X-Acto knife. X-Acto knives always seem to save the day.

As of writing (I am taking a break from working), I have a very nice looking pile of parts. I just need to cut the back out and drill 20 holes before I can wire up Cannon and take it for a drive. Before any kind of spinning takes place, I will need to create some pulleys on my lathe and make a belt. It looks like everything will somehow get done before Motorama, which is a big relief.

Here are some pictures of the build process so far:

This pile of parts has about a month to becoming a functioning, radio-controlled killer.

This pile of parts has about a month to becoming a functioning, radio-controlled killer.

The beginnings of a 3/8"-thick side rail for Cannon.

The beginnings of a 3/8″-thick side rail for Cannon.

The mill putting finishing touches on one of Cannon's rails.

The mill putting finishing touches on one of Cannon’s rails.

A finished side rail. It needs to be cleaned up a little more and get mounting holes drilled in it, but it is more or less complete.

A finished side rail. It needs to be cleaned up a little more and get mounting holes drilled in it, but it is more or less complete.

Robot Update

I’ll admit it — I am way behind of where I normally am robot-wise at this time of year. At this time last year, I was spending the better part of my weekends toiling over the mill trying to get Coercion done and troubleshooted. This year, much of the time I would have spent building robots has been dedicated to visiting my dad in the hospital over an hour away, as he has been very sick. Still, Motorama is about a month away and it will not wait for me. It’s time to get some robots built.

I have already designed Cannon and Coercion’s new blade (with some slight changes from the original post). Cannon’s blade and Coercion’s blade will be fabricated by Team Whyachi, since I am not overwhelmingly comfortable machining neither titanium nor S7. I don’t consider our mill’s CNC reliable enough to machine a hundred dollars worth of material at once. Besides, the S7 blade needs to be heat-treated to avoid another Bot Blast debacle (the blade bending 30 degrees).

Here’s what will need to happen over the course of the next month:

  • I will need to receive Coercion’s blade and Cannon’s beater from TW.
  • I will need to CNC mill Cannon’s body.
  • I will need to create some blade hubs on the lathe.
  • I will need to fashion and harden an S7 weapon shaft for Cannon.
  • Lots of testing.
  • Ordering a few new parts.

Overall, it should be manageable. There are a few three-day weekends coming up that I can completely dedicate to “botting it,” and there is still a month until Motorama. The race is on. Let’s hope it ends in a trophy…

The good news is that I converted all my SLDPRT drawings for Cannon’s sides into DXFs, then created GCode from the DXFs today.

I use GMail to transfer the files between my dad's PC (which has the GCode generation software) and the mill-driving PC. I use Mach3 to drive the CNC.

I use GMail to transfer the files between my dad’s PC (which has the GCode generation software) and the mill-driving PC. I use Mach3 to drive the CNC.

Drawing the Mandelbrot Fractal in C#

I suppose you could say I had a deprived coding childhood; I already wrote about how I never calculated pi and now I am about to tell you that until this afternoon, I had never drawn the Mandelbrot set. I don’t know why I never took the time to do so, but as with the pi calculator, when I decided to plot the Mandelbrot set, I found a very interesting coding challenge waiting for me.

The first step in the process was to fix the notoriously slow Bitmap.SetPixel() method. I haven’t decompiled System.Drawing to look at .NET’s implementation but I assume it involves some sort of Marshal.Copy()ing of unmanaged bitmap data to a managed byte array, modifications to pixels in the managed byte array, and recopying the array to unmanaged memory. That’s well and good for setting three pixels. We’re going to render Mandelbrot sets of an arbitrary size; 2800×1600 is about 4.5 million pixels. Bitmap.SetPixel() would make this process way, way longer than necessary.

I created a class with a readable, writable bitmap as well as a method to set any of its pixels using unsafe code.

public class FastBitmap
{
    public FastBitmap(int width, int height)
    {
        this.Bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
    }
 
    public unsafe void SetPixel(int x, int y, Color color)
    {
        BitmapData data = this.Bitmap.LockBits(new Rectangle(0, 0, this.Bitmap.Width, this.Bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
        IntPtr scan0 = data.Scan0;
 
        byte* imagePointer = (byte*)scan0.ToPointer(); // Pointer to first pixel of image
        int offset = (y * data.Stride) + (3 * x); // 3x because we have 24bits/px = 3bytes/px
        byte* px = (imagePointer + offset); // pointer to the pixel we want
        px[0] = color.B; // Red component
        px[1] = color.G; // Green component
        px[2] = color.R; // Blue component
 
        this.Bitmap.UnlockBits(data); // Set the data again
    }
 
    public Bitmap Bitmap
    {
        get;
        set;
    }
}

This class isn’t very useful outside of this program; it would need some modifications to use pixel formats other than 24bpp RGB. However, it gets the job done! The SetPixel method is what makes this class useful. It locks the provided Bitmap’s pixels, then gets a pointer to where the first color byte of the first pixel is in memory (Scan0.ToPointer()). It then figures out what the offset is to the pixel we want to modify. Finally, it creates a pointer to the pixel we want. It sets the blue, green and red to the specified color’s blue, green and red, then finally unlocks the bitmap data. When the data gets unlocked, it is saved in memory.

The SetPixel method presented here does not provide tremendous performance. It does, however, let you work with a bitmap fast enough that you won’t get bored. Most Mandelbrot rendering time is in calculations, anyway.

Now that we have an image class, we can get down to the math. The Mandelbrot fractal is based on the following formula:

Where C is the starting point on the complex plane and the starting value is zero.

Where C is the starting point on the complex plane and the starting value is zero.

The formula is given a starting value, C, that lies somewhere in the complex plane. If the formula (using C) does not tend to infinity, then C is part of the Mandelbrot set. If C tends to infinity, it is part of the Mandelbrot set. As a quick programmatic note, if the absolute value of Z sub N (the value of the formula after N iterations) ever becomes greater than two, the formula will tend to infinity.

Knowing that, we can start coming up with some pseudo-code:

for x on the screen:
  for y on the screen:
    scale (x, y) to the complex plane to figure out where we are
    c = where we are on the screen
    z = 0 + 0i
    b = a bitmap
    for i = 0 to however many iterations:
      z = z^2 + c
      if |z| > 2:
        plot (x, y) on b as some color that indicates how much "infinite tendency" there is at C
        break

If you read the Wikipedia article about rendering the Mandelbrot set, you will notice that it uses two real numbers to accomplish the same thing as one complex one. C# has a complex number class, which we will use in our code. However, before we talk about the actual rendering, we need a color palette. The members of the Mandelbrot set will be rendered black (or actually, based on the flow of the pseudo-code, not rendered at all), while the rest of the set will be rendered some color based on how many iterations it takes for the absolute value of Z to eclipse two. The more iterations it takes, the whiter the pixel. The fewer iterations, the bluer the pixel. It all comes together in the following (very simple) method:

public static List<Color> GenerateColorPalette()
{
    List<Color> retVal = new List<Color>();
    for (int i = 0; i <= 255; i++)
    {
        retVal.Add(Color.FromArgb(255, i, i, 255));
    }
    return retVal;
}

Okay, we can finally translate the pseudo-code into a real Mandelbrot-rendering method:

public static Bitmap DrawMandelbrot(int width, int height, double rMin, double rMax, double iMin, double iMax)
{
    List<Color> Palette = GenerateColorPalette();
    FastBitmap img = new FastBitmap(width, height); // Bitmap to contain the set
 
    double rScale = (Math.Abs(rMin) + Math.Abs(rMax)) / width; // Amount to move each pixel in the real numbers
    double iScale = (Math.Abs(iMin) + Math.Abs(iMax)) / height; // Amount to move each pixel in the imaginary numbers
 
    for (int x = 0; x < width; x++)
    {
        for (int y = 0; y < height; y++)
        {
            Complex c = new Complex(x * rScale + rMin, y * iScale + iMin); // Scaled complex number
            Complex z = c;
            for (int i = 0; i < Palette.Count; i++) // 255 iterations with the method we already wrote
            {
                if (z.Magnitude >= 2.0)
                {
                    img.SetPixel(x, y, Palette[i]); // Set the pixel if the magnitude is greater than two
                    break; // We're done with this loop
                }
                else
                {
                    z = c + Complex.Pow(z, 2); // Z = Zlast^2 + C
                }
            }
        }
    }
 
    return img.Bitmap;
}

Some notable points about this method:

  1. The rScale and iScale variables represent the change in real or imaginary value in the complex plane of each pixel on the screen.
  2. Some good parameters to pass to this method are rMin = -2.5, rMax = 1.0, iMin = -1.0 and iMax (no copyright infringement intended!) = 1.0.
  3. This method could be significantly improved on multi-core machines by dividing up the x-values to render and running a thread on each core. However, any rendering would have to be done after all the threads finished to avoid any cross-thread bit-locking issues. Luckily, the part that takes a while is the iterating, not the rendering.

That’s all it takes to draw a pretty good-looking Mandelbrot fractal. Here’s a fairly large one that you can give to your math teacher. I promise it will make him or her proud. I’m totally giving one to my math team coach as a New Year’s present.

Very blue...

Very blue…