LEO: An Indoor, Omni-directional Robot

And a cheeky video of LEO in action:

Initial Idea

Leo is an indoor robot platform that I have been planning for a while now and my fabacademy project will be the first step towards it by building the chassis for it. LEO will be a platform to build and test home-assistant and robotics related projects to keep my mechatronics skills fresh. I do not know how far it will progress under Fabacademy, but some examples of low-hanging fruit tasks it may be able to complete in the near future are climate monitoring of all the rooms in a house (and maybe talking to electric windows to control temperature) and utilising a camera to autonomously navigate and monitor the house.

vending machine sketch

For this Fab Academy project, I wish to atleast get the chassis going, with a simple body ontop, maybe with a touchscreen and some simple sensors. It will use mecanum wheels to give it mobility (and they are something I have wanted to play around with for a while), and a suspension system that will alow it to lower and raise each leg (the suspension system was largely inspired by this James Bruton robot). There have also been plenty of omnidirectional robots in fabacademy, Mufeed Mohamed made this awesome scissor lift bot that utilises mecanum wheels, and Jefferson Sandoval has made this one with omni-wheels. Part of the project is to not only make an omni-directional platform, but also give it a personality - a touch screen with a face, coupled with the ability to manipulate each suspension arm to dance around with, should give it enough to express some sort of personality.

The project is a lot less involved than it seems and getting a basic chassis going is straight forward with some 2020 extrusion, 3d printed parts, motor + drivers, servos and mecanum wheels. This project can be an infinite scope project, but a simple implementation with a raspberry pi, 3d printed shell and touch screen - all put together to make a glorified RC car is achievable for fab academy and a good starting point. If time permits, maybe some basic computer vision or LIDAR navigation could be implemented.

Chassis Design Phase and CAD

I started this project by CAD-ing out as much as I can at this phase to get an idea of how this project will look and feel. I have an indepth of this whole modelling process in my week 2 assignment. Modelling this was quite easy as it is symetrical and all 4 of the arms can be made from the same pieces. I have modelled and printed several projects already and a focus of this project was to make something extremely easy to construct and work on. The centre of this robot to hold all the arms will be composed of some big and hefty 3d printed parts connected with 2020 extrusion, which is just a fantastic profile of metal to work with.

The arms themselves consist of 2 identical printed parts that connect to a motor mounting plate. These arms will form a parallelogram that will keep the motor mounting plate always perfectly vertical and parallel. This will be important for the mecanum wheels which will need to be kept vertical straight in order to roll smoothly.

At this stage I did not know what motor, mecanum wheel or even method of connecting the two I would use, so I opted to leave this in a "mock-up" state to come back to later when I could better assess it.

Another thing I left in a "to be decided" state was the introduction of a spring into the suspension system. I intend to use elastic cord, and with the way that I have modelled everything so far, there should be enough opportunities to mount this - either by attatching it to part of the existing body or by drilling and adding some mounting points to the printed body.

And with that I had the majority of my project modelled. As the model was parametric and built on variables, I went through a process of refining the design, larger mounting plates, shorter arms, just playing around with it till I was happy with all the dimensions. After which I added some primitive joints so that I could move it around and see what it would look like in action.

Manufacturing the Body and Suspension Arms

With a nearly finished 3D model of the chassis that was designed to be highly printable, it was an easy process to get it manufactured. The only modifications required before printing was to add clearances between parts to allow them a proper fit and tolerances. To do this, I went along and added a 0.3 mm gap to anything that I wanted to slide freely, and a 0.2 mm gap for anything to fit somewhat snugly.

With these fits now modelled, I started printing the chassis. At this point I still had pieces I wasn't too sure about, so I only printed the things that:

A. I was confident would not be changed in a near-future revision
B. I could easily modify / re-drill / repurpose if needed.

I printed all of these on my slow-but-reliable Prusa Mk2 with PLA. Ideally I would like to use PETG, I think it has better mechanical properties and isn't much more difficult to print with, but I had a large stash of PLA that I needed to get rid of. If anyone is to reproduce this robot, I would reccomend PETG.

And after about a week of printing, I had the majority of my chassis printed up. The design for this assembly is extremely simple and everything is held together exactly as you it looks with 16x 130mm M6 bolts.

At this point I was quite happy with how it came out, each part of it could be easily removed and serviced with the removal of only 2 bolts, which came in handy when building the wheel assemblies.

Drivetrain Design and Assembly

This robot is quite heavy, and its final weight comes in at about 7.5 kg. With the robot also needing to be snappy and responsive, quite large motors would need to be used. Large motors are expensive, but fortnutely I had some of these Polulu 37D motors lying around that should provide plenty enough torque. The only issue is that I have 2 sets of motors. 2 that run on 24 volt with a 30:1 gearbox, and 2 that run on 12v with a 50:1 gear ratio.

I figured that this would not be an issue later on as the motors had a similar maximum power output, and some 3d printed gears could fix the mis-match in gearbox ratios. Ontop of that, these motors have encoders which we will be using with a feedback loop to regulate the motors to exact speeds.

I also finalised the wheel assemblies and opted for a shaft which a mecanum wheel would freely spin on, and a series of gears to connect the wheel to the motor shaft. The mounting plate I originally designed in illustrator, but I remodelled it here to visually see it in 3d space. I didn't properly model them as I did not have a model for the wheel, nor know the gear ratio yet, but here is what it will look like:

The outter house of this that holds the shaft was also split into 2 section. By having it split into 2 pieces, the lower section (the pinkish colour in the image above) could be attatched and hold the wheel assembly together and still allow access to the drivetrain for maintanence. This is one of the many design considerations made to help maximise the servicability and ease of assembly of this project.

At this point I went to cross an item off my "maker bucket-list" and make my own mecanum wheels. After 9 hours of design, I realised how difficult this task is, but arrived at a design that might of worked.

However, I did not want to pursue this route that could very easily unfold into a headache and instead opted to modify an existing set of cheap mecanum wheels. To do so, I drilled out, the centre of the wheel to be slightly larger than the 6mm rod it would mount to, then I designed and printed some gears that would glue to the sides of the wheel and allow it freely spin on some bearings that press-fit in.

At this point I also was not sure of size of the gear on the motor I would need, but for testing purposes, I printed one the size of the gear on the motor. Because the size of the gear might change later, I opted to create a mounting plate for the motor which would allow it to be set to any height. This is one of my subtractively manufactured parts as it was more optimal to laser cut this than 3d print.

And with that, the most complex assembly of the project comes together. This setup is far from perfect and suffers from a few issues - the mounting plate is too small to mount with 4 bolts, the m4 bolts that hold the mounting plate are easy to thread (I should use heated inserts here), and the shaft is made of wooden dowel (my bearings were much smaller than 6mm and sanding down wood was the easiest way to get a 5.75 mm rod). However, for the sake of testing it works just fine and after a little bit of abuse and use, it seems to be holding together just fine. I may not even revise this for Fabacdemy unless it gives me major issues.

Motor Driver Assembly and Milled Breakout Board

To drive the motors, I used these inexpensive 2-channel motor drivers which are great for upto 24v and 7a of power, plenty of headroom for the 37d motors. However, with this set up, each motor will need:

2x Digital Outputs to control motor direction
1x PWM Output to control speed
2x Digital Inputs to measure the encoder

This means that in total we will need 20 GPIO pins to just drive the motors of the robot. Because of this I decided to dedicate an entire microcontroller just to motor control, and the Raspberry Pi Pico is a perfect fir for this thanks to its quantity and flexibility of GPIO pins. This will likely be connected via UART to a Raspberry Pi 4 (or 5 if I can get my hands on one!) which will send command signals to this Pico to control the wheels.

As we are fitting this Pico for a specific task and we have a specific use for it and its pins, its a good opportunity to mill a PCB for it. The motor encoders need a 4 pin connector, and the motor drivers needed 4 pins per motor (when using 2 motors a board), so I broke the Pico out into a series of 4 pin connectors.

And I went ahead and milled and soldered this board, I go into depth on that process in week8. With the hindsight of finishing this project, having this breakout board has saved me far more time throughout the course of making this project, than it took to make the board.

And at this point I could go ahead and wire it all up. The headers coming from the milled board could easily be plugged into the encoders and motor driver headers in lots of 4s without having to cross any wires. The motor's encoders go into the Pico and take power from the driver, and the driver plugs into the pico for control signals and takes power from a Lipo. Here is a crude diagram of what that looks like because its messy in real life:

Mid Semester Review

At this point we had reached the mid semester review and I felt that the project was at a very nice point for this - we had a robot chassis with some wheels and a wide range of possible directions to take this. Looking ahead with the remaining time and being realistic, I think we will now be aiming to get the chassis driving around and the legs being actuated individually, as well as getting controlling the Pi wirelessly (which will be controlling the robot).

For this we need the following tasks to be done
- Finish connecting the motors to the Pico with the milled break out board
- Implement and Tune a PID controller to regulate motor speed to a targe RPM
- Finish designing the leg actuation mechanism
- Make and connect another break out board for a Pico to control the leg actuation
- Get Pi operational and accepting keyboard inputs (how we will wirelessly control)
- Get Pi controlling Picos via UART
- Make top cover to House Touch Screen and electronics
- Clean and tidy up for final presentations (securing batteries, wirelooms, etc.)

To complete these tasks I produced this reasonable Gantt chart in Canva:

At this point I also knew what the final system would look like so here is a system diagram for it all. Note that the LIDAR I wish to implelement in the future uses UART, as does many of the things I wish to add onto it, so we will be getting the 2 picos to shart the same UART perhipheral on the Pi 4. I want to free these up as much as possible as UART will be extensively used in future project expansions.

Controlling the Motors

We are now at the point where much of the systems are built and we need to start integrating so I will be applying incremental integration more and more now - a process where you get a small chunk of something working in isolation, then integrate it into the project so far, and repeat that. I started building the entire project up from this point of getting the motors going, its the seed which everything will grow. In inputs week and outputs week, I go into depth of how I got this entire wheel system going, but here is a summary of it:

First I got encoders reading the wheel speeds with interrupts, and I got the motor controllers driving the wheels - both independantly.

Then I implemented a PID controller to measure the speed of the motor and regulate it to a target RPM with the motor controller. This is how we are going to offset having 2 different motors with different voltages and max speeds. We will be limited to a maximum RPM of about 220 though (which is the slower of the 2 motors). This could be fixed by changing the gearing of the slower motors, but for now we will keep it as it should result in about a 5 kmh top speed which is plenty enough.

In this whole proccess I did encounter a few issues. Initially I was calculating the RPM in each interrupt from the encoder, everytime an encoder "clicked" I would use ticks_ms to find the time since the last click and use that to calculate speed. But at 220rpm, the encoders would be dealing with 60,000 interupts a second and this calculation was far too intensive for the pico at this rate. So instead I had to swap to just counting the clicks, and calculating the speed on a fixed period of about every 40 ms.

But after that I now had motors that could be regulated to any speed between -220 and 220 RPM. And here is a video of that in action. On the graph the blue line is the target RPM, the green is the PWM duty cycle being applied and the orange is the current RPM. As I grab the wheel, the PID makes the motor work harder to keep up with speed and you can see the green line jump up.

And there was a whole bunch of tuning required in terms of the length RPM calculation period, PID values and I was even at one stage testing it with a moving window average. In the end I came out with something that was responsive enough for what I needed, but it may need tuning in the future.

Leg / suspension arm actuation

This was one of the parts that I was dreading, the suspension arm was the most difficult part of this whole project and the one I was most unsure about. I settled on a design where some paracord would pull on one axis of the suspension and keep the robot's body up, but then a pair of servos would rotate a pulley, and the pulley would tug on the other axis of the suspension to lower it. I designed some pulleys to attatch to the servos, and set an apropriately tight set of paracord - just enough to keep the body up.

To control all 8 servos, I used another Pico and soldered up a breakout board for it with some protoboard. This made the connections extremely easy and clean, not having power and signal wires going everywhere. Each one of these servos needed about 3.5a maximum, multiply that by 8 and you have a 30 amp maximum power requirement. I searched for a suitable voltage regulator to step down the 12v or 24v lipos (the servos needed 5v-8v), but I couldn't find a viable solution. So instead I added a 5th battery to this project (12v lipo, 24v lipo, 2x 5v battery banks for the picos and Rpi, and now this 2 cell for the servos). And this worked out perfectly fine and was far more cost effective. I also used this as an opportunity to start connecting the boards via UART, and in the image below you can also see some headers that I use to connect the 2 picos to a long 3 wire lead which will connect to the Pi 4. The ground wire can be disconnected incase the setup changes and a ground loop is formed.

This was getting into the more dire stages of the project and I didn't get footage of it working at this point, but here is some footage from a later stage in the project of the leg actuating.

And I was extremely happy with this! It seemed like it was working and that we were on track to get the robot dancing. But some foreshadowing here, the servos were only JUST powerful enough to move the legs currently and we would run into some issues. In wildcard week we laser cut and sewed fabrics and I used it to take the opportunity to make these pants for the legs of the robot - to add some style but mainly to keep fingers out of the very powerful pinch points this robot had now.

Pi 4 Control

Many of the things I wish to add to this project will require processing power that isn't possible on an embedded microcontroller, so we will be adding a Raspberry Pi 4 to the project and getting it to control both the motor and leg picos via UART. So I grabbed my Pi, installed Pi OS, mounted it to the touch screen, powered it off a 5v battery bank and plugged in the UART coming from the servo pico protoboard. Here is a whole system diagram of LEO, servos in the bottom left, motors in the bottom right:

You can find the code at the full code at the bottom but here is a breakdown of how they interact. The Pi 4 is controlled via keyboard inputs using the pynput library. We set up a listener in a python script with:

with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    print("Listening for inputs")
    listener.join()

Then we handle do something based on which key was pressed with:

def on_press(key):
    global speed  # Declare speed as global to modify it inside this function
    try:
        if key.char == 'w':
            print("W key was pressed")
            send_command("m", speed, speed, speed, speed, 0, 0, 0, 0)  # Move forward
        elif key.char == 'a':
            print("A key was pressed")
            send_command("m", speed, -speed, -speed, speed, 0, 0, 0, 0)  # Move left

        etc...
        etc...

And each of these keystrokes calls the send_command function which send a UART command to the 2 picos:

def send_command(device, RL_rpm, RR_rpm, FL_rpm, FR_rpm, RLdeg, RRdeg, FLdeg, FRdeg):
    command = f"{device} {RL_rpm} {RR_rpm} {FL_rpm} {FR_rpm} {RLdeg} {RRdeg} {FLdeg} {FRdeg}"
    ser.write(command.encode())  # Encode the string to bytes
    print(f"Sent: {command}")
    time.sleep(0.045)

And then when we release the key we will send a stop all command:

def on_release(key):
    # Stop the robot if any key is released
    time.sleep(0.1)
    command = f"m 0 0 0 0 0 0 0 0"
    ser.write(command.encode())  # Encode the string to bytes
    time.sleep(0.1)
    command = f"s 0 0 0 0 0 0 0 0"
    ser.write(command.encode())  # Encode the string to bytes
    print(f"Sent: {command}")

So this will send both the Picos a message in the format of:

device RL_rpm RR_rpm FL_rpm FR_rpm RLdeg RRdeg FLdeg FRdeg

The device here is being used to signal which pico this message was meant for, m is the motor controller, s is the servo controller. The next 4 are the speeds of the motors and these are calculated on the pi. If I press "E", the Pi calculates the wheel directions and RPMs needed to rotate the robot, and send those through. And the next 4 are just a target servo angle that the servos will be set to. There are also inputs for all the number keys which incrementally set the speed of the robot from 0% to 100%, so if i pressed 5, the robot would be set to 50% speed on all its movements.

On the Pico's ends we just constantly check the UART buffer and if there is a message we use the .split() function to seperate the command, and if it was addressed to that pico, it will update it's target rpm or angle with that new message. Here it is for the motor controller, which was tacked onto the code from inputs and outputs week:

    if uart.any():
        message = uart.read().decode().strip() # remove

        try:
            # Split and convert message to individual RPM values
            device, iRLsetpointRPM, iRRsetpointRPM, iFLsetpointRPM, iFRsetpointRPM, iRLdeg, iRRdeg, iFLdeg, iFRdeg = message.split()

            if device == "m":
                RLsetpointRPM = int(iRLsetpointRPM)
                RRsetpointRPM = int(iRRsetpointRPM)
                FLsetpointRPM = int(iFLsetpointRPM)
                FRsetpointRPM = int(iFRsetpointRPM)

            else:
                print("not for me")

        except ValueError:
            print("Invalid UART message format. Expected 8 numbers.")
            print(message)

And with that, we could now press a button on a keyboard, the Pi 4 would read it, then send off a corresponding set of RPM values or angles, a pico would recieve the message, and enact that with the hardware it has. Here is a video of that in action.

Making the Top Cover and Bringing it all Together.

Right now the Pi 4 and Lipos are resting ontop of the robot and we need something to mount the screen and hide the rat's nest of electronics so Neil doesn't see my handywork. So I went back to my CAD model in onshape and designed a top cover. This cover will mount the Pi 4 in the front, and I littered it with holes on the top to allow me to mount anything I want in the future, and it has these larger holes on the side to poke through ultrasonic sensors, or anything really. And this can either just sit on the top, or be mounted to the 2020 extrusions with the side moutning holes.

And then I went through and cut it out on our trotec laser cutter following the same steps in week 3. And this all assembled very nicely. And gave me space to mount the batteries inside here. But at this time things were getting dire time-wise so this box was just hot glued together which actually turned out a lot better than I thought it would be.

And here is a final shot of what it looks like under the shell, all the wires I am holding in my hand are plugged into the upper shell, and consist of a UART cable that goes to the Pi, and 5 cables for power. These cables are excesively long to allow for easy removal of the top shell, and they hang free as the top shell is designed to be frequently removed.

And after mounting it on the 2020, in addition with those pants, this helped clean up, package and hide most of the wiring. And that was the core lot of the project finished!

Conclusion

At this point in the project it was about 10 hours before my presentation. I had a robot that could drive around like a glorified RC car, it had an onboard computer running, it could drive in all direction,had some form of leg actuation, and had been designed to be upgradable and have other projects mounted on it in the future - so I decided to call it. I made my video and my slides and submitted it.

The Good

Overall I was very happy with this project, I had done many of these skills / systems of this project in isolation and it was the first time integrating it all together into a project of this scale. Much of the focus I put into making this easy to assemble and work on really paid off - especially those breakout boards I made for the Picos. I had to rewire those sections dozens of times and those boards made it fast and very difficult to wire inccorectly. I was also happy with the gearings I chose, at full speed it moves slightly faster than walking speed, and its snappy and responsive enough to instantly go from full forward to full reverse. Having a touchscreen and an onboard computer also made developing this thing a breeze and its a strong candidate to have one on all my projects from now on. I also feel like I did system integration really well here. I have never taken incremental integration this seriously and it paid off as I didn't have the normal amount of headaches that I encounter.

The Bad

There were not too many bad things in this, moreso things that I would redo in the future. The biggest thing would be how the gears were attatched to the motor shaft. I have had many issues with those 6mm mounting hubs, the grub screws strip too easy and the motor will just spin in the gear. I ended up printing some adapters that were wayy too small and hammering them onto it which fixed it for a few running hours. The other part htat could be improved were the mounting plates, they were too small and didn't allow enough screws for mounting. The mounting for the servos on the arms were also a little dodgey, they were just hotglued on and some proper fasteners here would help.

The Ugly

The only ugly thing about this project is that the legs didn't actuate in the end. Half way through, I got them first working and it seemed like it would work. However, once the batteries and touch screen, and everything in the top enclosure were added the robot became much heavier. The suspension wasn't tight enough and it would sit on its belly and I had to tighten the paracord to stop it from drooping, and the servos weren't powerful enough to counter this tighter paracord. This was going to be my main way of adding personality to the robot and its a bit of a shame that it didn't turn out. You can see in the presentation video at the top though that it sort of moves a little bit. The way to fix this would be to either find a better way to add spring to the suspension, or to reduce the diameter of the pulleys as the servos were only rotating halfway (you could half the diameter).

But overall, I was very happy with this project, it tested a few skills that I was rusty on, introduced some new ones, and allowed me to tackle a project of this scale. And now I have a really cool robotics platform to do things with.

Bill of Materials

Here is a list of everything needed to make this project and where to find them:

Item Price (AUD) Quantity Total (AUD) Total (USD Aprox.) Link / Location
Pico W 9.9 2 19.8 13.2 Core Electronics
Pi 4 2GB 73.8 1 73.8 49.2 Core Electronics
7 inch Touch Screen 65.99 1 65.99 43.99 Amazon
USB Power Bank 10 1 10 6.66 Amazon
20kg Servo (4 pack) 45 2 90 60 Amazon
Copper Clad 3.1 2 6.2 4.13 Core Electronics
2x7A DC Motor Driver 34.95 2 69.9 46.6 Core Electronics
DC Motor w/Encoder 48.25 4 193 128.66 Core Electronics
Jumper Wires 3.95 4 15.8 10.53 Core Electronics
Silicone Wire (16AWG) 7.54 2 15.08 10.05 Core Electronics
XT60 Connectors 2.6 3 7.8 5.2 Core Electronics
12v Lipo 58.99 1 58.99 39.32 Amazon
7.4 Lipo 30.59 1 30.59 20.39 Amazon
Meccanum Wheel Kit 36.55 1 36.55 24.36 Core Electronics
Mounting Hubs (2 pack) 16.6 2 33.2 22.13 Core Electronics
2020 Extrusion (600 mm) 10.35 2 20.7 13.8 Core Electronics
130mm M6 Bolt and Nuts 15.62 1 15.62 10.41 Bunnings
PETG Filament (1kg) 26.95 3 80.85 53.9 3DFillies
6mm Dowel 4.4 1 4.4 2.93 Bunnings
10mm M4 Bolts and Nuts 1.86 5 9.3 6.2 Bunnings
25mm M4 Bolts and Nuts 2.12 4 8.48 5.65 Bunnings
3mm Acrylic Sheet 20 1 20 13.33 Bulk Acrylics
Totals: 886.05 590.7

Files

All of LEO's Code
All 2d and 3d files Milled Board KiCad Project and SVGs

License

LEO © 2024 by Jaryd Giesen is licensed under CC BY 4.0