Close
0%
0%

Bot Matrix

Progress towards a not-killer robot

Similar projects worth following

I'm working on a robot, hopefully for PiWars 2018 (http://piwars.org/) if it gets selected.

Update: Unfortunately I wasn't selected for PiWars, so this is just a fun robot project now. I'll probably still design it to complete the PiWars challenges

This project will be a pretty haphazard collection of progress reports and other ramblings.

  • Enter The Matrix

    usedbytes10/22/2017 at 12:37 0 comments


    OK, so the reason this robot is called Bot Matrix, is because one of the central components is a 32x32 RGB LED matrix.

    These LED panels are the type used in Jumbotron screens or advertising billboards. They have a set of shift-registers and constant current drivers on-board, and use a somewhat standard interface which seems to be called HUB75.

    Sourcing the Panel

    HUB75 panels aren't too hard to find - though I did have a certain amount of trouble getting a 5mm-pitch, 32x32 pixel panel.

    Pimoroni sell P4 and P6 (4mm/6mm pitch): https://shop.pimoroni.com/products/rgb-led-matrix-panel

    Adafruit have a selection, but their 32x32 P5 is $45 USD! (Plus shipping from the US), which was too much for me: https://www.adafruit.com/category/327

    I went shopping on Aliexpress, and found plenty for around $20-$30 USD, delivered to the UK: https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20171022045534&isPremium=y&SearchText=P5+rgb+matrix

    Driving the Panel

    The panels themselves are not able to display an image - they must be continually scanned with data, and if PWM is desired/required, then that needs to be provided in the input too. This means they have pretty hefty driver requirements - and in commercial setups they seem to often use FPGAs to generate the control signals.

    Hackaday previously featured this project by Frans-Willem which drives a set of HUB75 panels from an STM32F407 - and I just so happened to have bought an STM32F407 for something else, so I set to trying to use that code to drive my panel.

    I'll forgo a detailed description of how the drive signals work - Frans-Willem already did a great job explaining it in his writeup

    Basically, there's a set of pins which choose which row of the panel to activate, a set of pins connected to the shift registers to shift data in to that row, and a set of pins to latch and display that line.

    You have to scan through each line, latching in and displaying data for that line before moving on to the next.

    If you want full colour (not just 3-bit colour) you also need PWM. Frans-Willem came up with an elegant solution whereby the appropriate bit-patterns to give PWM-ed colours is stored in memory, then directly written to the STM32's output pins using DMA - this puts full-color, 60 FPS refresh rate on a 32x32 panel within reach of an STM32F4!

    The quirks of my specific panel

    The seller I bought my panel from claimed it was 1/16th scan - this means that the rows are scanned in groups of 16 rows, with two busses. This eventually turned out not to be the case - it took me a long time to figure out what was wrong, as there was another issue causing strange output.

    Eventually, I figured out it was in-fact 1/8th scan, as evidenced by the "D" pin being tied permanently to ground, and hidden under a sticker on the board, it indeed said "P5-32x32-8S-75-13A". So once I had changed the config to expect a 1/8th scan instead of 1/16th, I started getting output that made some sense.

    There was still a problem - I was now displaying "stripes" of 8 rows of pixels, but the stripes were in the wrong order. It turns out the way the shift registers relate to the panel pixels are non-intuitive on my panel:

    commit 6c1b8b84e7d8927119d1bfaf2ba36b600a05bbaa
    Author: XXX
    Date:   Sat Sep 9 15:57:10 2017 +0100
        Add remapping
        
        My panel has a strange pixel layout. Though the physical dimensions are
        32x32, logically the electronics are laid out as 1/8 scan with 64 pixels
        per row.
        
        If the 32x32 panel was split up as below:
        
         0                              31
         +---------------+---------------+ 0
         |               A               |
         |                               |
         +---------------+---------------+ 8
         |               B               |
         |                               |
         +---------------+---------------+ 16
         |               C               |
         |                               |
         +---------------+---------------+ 24
         |               D               |
         |                               |
         +---------------+---------------+
        
        Then the physical shift registers are arranged as 64x16, with the layout
        below:
        
         0                                                              63
         +---------------+---------------+---------------+---------------+ 0
         |               B               |               A               |    BUS0
         |                               |                               |
     +---------------+---------------+---------------+---------------+...
    Read more »

  • Baby's first steps! (More tracks)

    usedbytes09/26/2017 at 21:02 0 comments

    So, having been away at XDC (https://www.x.org/wiki/Events/XDC2017/) last week, and having finalised the CAD designs for my tracks just before I left, I was itching to get back and build a new pair of track modules.

    I managed to get on to the Makespace laser and 3D printer last night and get all the parts I needed to build the track modules:

    So now it's starting to look a bit more like a thing that can drive. And it can drive, but it turns out I have quite a problem with the centre of gravity being too high and/or too far forward (better with sound for comedic effect):

    The MDF blocks sellotaped in the middle are just to add some weight - because otherwise the motors make it really really front heavy, and it falls over all the time. I haven't got any controller electronics on it yet, both motors are just plugged directly in to a DC supply.

    There's plenty of other kit to add in to the robot, which should quite significantly change the weight distribution and dynamics, so I'm not too concerned about the instability just yet - but I will bear in mind that I need to design the rest of it to get as much weight low down as possible! And 

  • Tracks!

    usedbytes09/03/2017 at 17:43 0 comments

    OK, another weekend, another subsystem. This time I've been working on a prototype for my tracks module.


    The robot will be tracked (tank, caterpillar whatever) - I bought some rubber tank tracks from Aliexpress (type D), and assumed I would sort out my own pulleys either lasering, 3D printing or milling them.

    I originally wanted to put the motor inside the circumference of the track - aligned parallel with the direction of travel - and then have a 90° linkage to transfer the motion to the pulleys. This would save space inside the fairly cramped robot, and also give a nicely centralised, very low centre of gravity:


    Unfortunately, the tracks are too short to give me space for the motors, and so instead I've had to opt for pretty much the opposite - the motors will be directly connected to the drive pulley, at perpendicular to the direction of travel - and will be pretty high up! (Magenta is the motor in this image):


    Pulleys

    The first challenge was to reverse-engineer a pulley by measuring the track. The tracks didn't come with any kind of engineering drawing or measurements, so I had to wing it by hand. One significant challenge is that the rubber obviously moves when you try to measure it... any pressure from a caliper squashes the rubber and your reading is no good.

    The tracks have a row of teeth along the centre, which ideally I want to drive. They also have castellations along the outside edges which we could also use for drive.

    My big fear is throwing a track, so I really want as much registration as possible to avoid the track falling off. The secondary concern is to avoid slippage on the wheels so that my motor encoders can be at least some use for dead-reckoning tracking.

    And so began a highly iterative process to try and derive the right dimensions for my pulley. It took me about 10 iterations on the laser cutter to find something that ran "well enough" - you'll see one gear below which has a different tooth profile - that just has the track tooth profile cut out of it - but of course the teeth bind horribly as they're wrapped around the wheel. That was the first attempt, and was rectified in the later designs.

    I started with a fully laser-cut prototype, with no motor, no bearings - just layers of laser-cut wood mounted on bamboo skewers. I don't have any photos of it, but it gave me confidence that my pulley design was close enough, and that the overall size of the track was appropriate for the length of the rubber. (I don't have any photos from that... sorry).

    Lasering was a quick way to iterate prototypes, but I decided to 3D print the pulleys for the "final" prototype.

    I got some small bearings from China on ebay. They're fine - except that they came lubricated with what appears to be thick treacle - honestly the bearings as delivered would barely turn. I had to guy and buy some acetone nail varnish remover (which cost more than the 30 bearings!), and clean them up. Now, they're pretty good.

    I drew up a pulley design in OpenSCAD, in two flavours - one without teeth for the idler wheels, and one with teeth and a hole for designed for the motor shaft for the drive pulley.

    So I printed two idlers, and one drive pully - slightly tweaking the design after each one (drive wheel was last - as it has the important drive teeth and so needed to be the most accurate).

    To avoid any overhangs, I print the wheels in two halves, and then connect the two together with some coathanger wire. The result is actually very secure, and the bearings sit in my bearing cups very nicely:


    Mounting and Structure

    With pulleys done, I modified my plywood prototype to support the 4 mm rods (for the bearings) instead of 3 mm bamboo skewers, and to support some threaded-rod spacers. My M3 Nylon spacers are on a slow boat from China, so I've improvised using some copper pipe which I had to hand, which I've cut to the same 25mm that my spacers will be.

    Finally, with the prototype constructed I could try plugging it in......

    Read more »

  • Camera, Open GL ES and distortion models

    usedbytes08/27/2017 at 17:27 1 comment

    My robot design uses a camera with a very very wide-angle lens. I bought a Pi camera module with a 175 degree wide angle lens which fits the bill (https://www.ebay.co.uk/sch/i.html?_from=R40&_trksid=p2054502.m570.l1313.TR0.TRC0.H0.X++Camera+Module+Board+1080P+5MP+175%C2%B0+Wide+Angle.TRS0&_nkw=++Camera+Module+Board+1080P+5MP+175%C2%B0+Wide+Angle&_sacat=0). It's not a Pi-foundation official one (which it seems the Pi foundation abhors so much that they put a cryto chip on the v2 module to stop people making knock offs https://www.raspberrypi.org/forums/viewtopic.php?f=43&t=149426#p982590. If there was an official 175 degree module, I'd have bought one).

    175 degrees means that there's a decent amount of distortion in the image (fish eye style), which I want to get rid of so I can use the images for something useful. So, I need to undistort them.

    OpenCV will end up in my image pipeline at some point, and it is able to correct lens distortion itself - but CPU power is really precious on the Pi (even the Pi3 - Cortex-A53 is a deliberately weak CPU as far as ARMv8 goes - it's designed for high efficiency, not performance). So, correction on the CPU doesn't look appealing - but we have got a nice OpenGL ES 2.0 GPU sat in the Pi which will be otherwise un-utilised in my robot - so let's kick that in for the lens correcion!

    Step 1. Hello Triangle

    I don't know OpenGL ES. I'd never done any GPU programming in my life. So step 1 was to find the most basic Open GL ES example I could.

    Thankfully, my friend Ciro Santilli had just what I needed - a tiny, self-contained "Hello Triangle" example in C: https://github.com/cirosantilli/cpp-cheat/blob/master/opengl/gles/triangle.c


    Fabulous! We can draw a triangle and we didn't even need to write any code yet!

    Step 2. Textured Triangle

    OK, so we can draw a triangle and fill it with a single colour. That's not much use in the real world - what we actually want to do is draw something onto the triangle. That's called texturing.

    So my next step was to add some code to import images (using libnetpbm because it's literally as simple as you can get). Once you've loaded the image data into memory somewhere, you create an OpenGL ES texture with it, hook that up to the fragment shader - and voila:


    Creating the texture:

    glGenTextures(1, &tex);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture->width, texture->height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture->pixels);
    glBindTexture(GL_TEXTURE_2D, 0);
    

     For the vertex shader, we add some extra code to emit a texture coordinate as well as a vertex coordinate:

    #version 100
    attribute vec3 position;
    varying highp vec2 v_TexCoord;
    void main() {
       gl_Position = vec4(position, 1);
       v_TexCoord = vec2(position.x + 0.5, -position.y + 0.5);
    }
    

    And in the fragment shader, we use "texture2D" to read from the texture:

    #version 100
    varying highp vec2 v_TexCoord;
    uniform sampler2D tex;
    void main()
    {
        gl_FragColor = texture2D(tex, v_TexCoord);
    }
    

    Now we're well on the way - we now know pretty much everything we need to know in order to do our lens correction.

    Next little stepping-stone is to add in some co-ordinate transformation so that it's a little easier to think about coordinates in our "scene". I added a projection matrix which changes the GL "normalised device coordinates" (which are -1 to 1) to plain normalised coordinates (0 to 1) and to flip the Y axis so that 0,0 is in the top-left (the same as literally everything else in the world except for OpenGL).

    Step 3. Drawing and mapping to a mesh

    So, a single triangle is great - but we can't use just one triangle to do lens correction - we...

    Read more »

View all 4 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates