Self balancing robot

Showroom for MicroPython related hardware projects.
Target audience: Users wanting to show off their project!
User avatar
kfricke
Posts: 342
Joined: Mon May 05, 2014 9:13 am
Location: Germany

Re: Self balancing robot

Post by kfricke » Wed May 13, 2015 10:10 am

Cool build! 8-)

jeffm
Posts: 23
Joined: Tue Mar 10, 2015 9:11 am

Re: Self balancing robot

Post by jeffm » Wed May 13, 2015 6:58 pm

@pythoncoder. Very impressive indeed, it is huge compared my version - it must hit the ground with a resounding clunk when balance fails :-)

I am currently trying to reduce my wiring bird's nest by replacing breadboards with perfboard. The wifi control using an ESP8266 and mobile phone works well. I will update GitHub with this and incorporate your code suggestions when I clean it up.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Self balancing robot

Post by pythoncoder » Thu May 14, 2015 8:28 am

While I stand by my code suggestions I'm still working on the algorithm and maths: I'll comment further when I've come to a conclusion. I'm looking at solutions where the only nonlinear constraint is on motor speed but it's a work-in-progress ;)

One physical change I intend to make is to fit some kind of buffers so that it hits the ground with less of a clunk! The battery at the top is actually a six volt sealed lead acid one, so it's heavy. These are cheap and, as we've already discussed, weight high up is an asset. The remote control I have in mind is a pybard with an NRF24L01 radio. I was actually trying to figure out how to mount a little joystick when I realised I didn't need one. Why not use the Pyboard's accelerometer and just tilt the thing to achieve control? The biggest thing in the remote will be the 9V battery.

My work on the algorithm is to answer the questions of whether it can be further simplified and how best to achieve concurrency: it would be nice to have the fast control loop running as a separate process, either using cooperative multi-threading or by having it run as a timer callback. Concurrency might make it easier to enhance the robot to do other things like moving to specific locations or handling sensors.
Peter Hinch
Index to my micropython libraries.

jeffm
Posts: 23
Joined: Tue Mar 10, 2015 9:11 am

Re: Self balancing robot

Post by jeffm » Thu May 14, 2015 7:24 pm

I have now updated the code at:

https://github.com/jeffmer/micropython-upybbot

I have included the Arduino sketch that implements a UDP server on the ESP8266 for OSC messages and the template for the TouchOSC app on my iPhone used as the controller. This follows the B_ROBOT approach. The controller implements two approaches - faders or buttons - the operations should be obvious from the screendumps below:
buttons.png
buttons.png (31.98 KiB) Viewed 12604 times
sliders.png
sliders.png (35.2 KiB) Viewed 12604 times
@pythoncoder. I agree that having a concurrent implementation would be best. The issue is that to get real-time response a pre-emptive scheduler is needed rather than the cooperative scheduling that can be achieved using yield. As you suggest, the only plausible approach is to use a timer driven interrupt service routine and forego floats.

BTW. The poll routine that gets wifi messages in the revised code takes approximately 1.5ms if there is a message to process so it is within the 5ms budget.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Self balancing robot

Post by pythoncoder » Fri May 15, 2015 5:54 am

Very neat! I've yet to experiment with writing tablet or phone apps.

As for concurrency, it's worth bearing in mind that a simple loop like ours can be interrupted by a garbage collection, which on my measurements takes 3-4mS. The only way to get a truly deterministic loop time is to use a timer interrupt.

I think that, with the maths modified to take account of loop delay as I mentioned earlier, the 'bot can be made more tolerant of loop time which might open up the possibility of using cooperative scheduling. However until I get a better level of stability any experiments are limited in their usefulness. At present I'm having trouble combining a good response to the remote with good stability. It's stable insamuch as it doesn't fall over, but its does "hunt" more than I'd like (and more than yours does).

I have two project branches, one with and the other without the speed filter and I'm still unconvinced that the filtered version is any easier to optimise. But comparing different outcomes is so subjective: getting these things to run really well isn't easy ;)
Peter Hinch
Index to my micropython libraries.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Self balancing robot

Post by dhylands » Fri May 15, 2015 6:14 am

jeffm wrote:@pythoncoder. I agree that having a concurrent implementation would be best. The issue is that to get real-time response a pre-emptive scheduler is needed rather than the cooperative scheduling that can be achieved using yield. As you suggest, the only plausible approach is to use a timer driven interrupt service routine and forego floats.
I beg to differ. real-time does not require pre-emption. This seems to be common misconception. I've written lots of non pre-emptive real-time systems. And in fact, its often much more efficient to use non pre-emptive systems.

There are overheads required to using pre-emptive systems (often as much as 5%). Pre-emption can also cause performance problems in other ways, such as disrupting execution pipelines, messing up caches.

So I'm just trying to say that you do real-time perfectly well with pre-emption or without. In some circumstances, pre-emption makes real-time easier, and sometimes it doesn't.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Self balancing robot

Post by pythoncoder » Fri May 15, 2015 8:33 am

@dhylands Agreed, a truly pre-emptive system requires serious programming skills and a good set of supporting primitives. Most of the systems I've encountered have limited true pre-emption in the form of interrupt handlers, with cooperative scheduling for the bulk of the code. The potential difficulty with control systems is where the core control loop is sufficiently time critical that it has to run in true real time - in MicroPython terms as a timer callback. As I'm sure you're aware, time delays can play hell with feedback systems.

My impression is that balancing robots don't fall into that category - but I won't consider the case proven until I have a better balancing robot ;) The most challenging control system most users are likely to encounter is a multicopter: I wonder if the Pyboard is fast enough to stabilise one of those with the main loop running in a cooperative thread?
Peter Hinch
Index to my micropython libraries.

jeffm
Posts: 23
Joined: Tue Mar 10, 2015 9:11 am

Re: Self balancing robot

Post by jeffm » Fri May 15, 2015 5:54 pm

@dhylands Your comment really makes the point that the term real-time is hopelessly imprecise and I do apologise for this imprecision on my part. I was of course referring to the situation where there is a requirement to mix activities or tasks that require bounded response time to external events with tasks with potentially unbounded run times. In this situation, pre-emption is needed. I do however completely agree that cooperative scheduling is more efficient, for example in guaranteeing exclusive access to shared datastructures. I know of systems where the compiler or interpreter introduces potential rescheduling breaks - for example in loops so that maximum execution times are bounded - however this can also be inefficient.

@pythoncoder. I was wondering about the garbage collector - would it be best to disable automatic collection and explicitly call gc.collect() at the end of each balance loop?

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Self balancing robot

Post by pythoncoder » Sat May 16, 2015 6:53 am

@jeffm I think you may be on to something there. I wrote this script to test the idea:

Code: Select all

import pyb
import gc

def gcalloc():
    a = [0]*1024
    a[pyb.rng()&0x3ff] = 1
    res = a[275]
    del(a)
    return res

def test(gc_iterations = 0):
    maxtime = 0
    mintime =1000
    sigma = 0
    if gc_iterations:
        gc.disable()
    for n in range(100000):
        tim = pyb.micros()
        b = gcalloc()
        sigma += b
        t = pyb.micros() - tim
        maxtime = max(maxtime, t)
        mintime = min(mintime, t)
        if gc_iterations and not n % gc_iterations:
            gc.collect()
    print(maxtime, mintime, sigma)
    gc.enable() # For subsequent runs
The idea is to create and delete a 1024 element list every loop (doing a bit of maths just to prove it really is working) and examine the timings with different GC strategies. The outcome was as follows:
Auto GC: loop time varied from 439uS to 2011uS (when GC occurred)
GC every 20 cycles: 431 to 606uS
GC every 5 cycles: 431 to 493uS
GC every other loop: 431 to 457
So doing it each loop makes sense, potentially reducing GC time from over 1.5mS to 26uS. Remember to re-enable GC when you stop the robot if you're likely to want to run any other code on the Pyboard!
Peter Hinch
Index to my micropython libraries.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Quantifying stability

Post by pythoncoder » Sat May 16, 2015 11:02 am

One instructive test I've tried is this. Before the start of the main loop I have

Code: Select all

    fangle = 0.0                        # Angle from complementary filter
    vmax = 0
    fanglemax = 0
    count = 0
and in the loop:

Code: Select all

        count+= 1
        if count < 3000:
            vmax = max(vmax,abs(fspeed))
            fanglemax = max(fanglemax, abs(fangle))
        elif count == 3000:
            print(vmax, fanglemax)
This allows you to tweak the constants at the REPL, run the code, and check the excursions of speed and angle. It might be worth extracting the standard deviation of speed as well as the absolute maximum, but in principle it provides a way of measuring static stability rather than just sticking a wet finger in the air ;)
Peter Hinch
Index to my micropython libraries.

Post Reply