1 post / 0 new

In this post/thread/series of posts I'm going to talk about the winning code from this season's final. And I'll be guided by what the response is as to going into more detail over further posts. So if there are any questions please ask away.

Here's a link to the final race, which I'll use to refer to some specific behaviours as the explanations progress:

In the final Pico HulkenBorg and SerGPIO Pirez were both using identical code, which will make it easier to explain some quirky behaviours - the code is far from perfect. This wasn't the case through the whole season - PicoHB mostly used the same (reliable) code from the end of last season throughout this season, while SerGPIO was (mostly) being used for testing experimental code. The code submitted for the final was a minimalist version of the SerGPIO code which had been developed through the season, so it is still quite fresh.

The first thing I should point out is that this code will not run using the 2016 FPi SD card image. I hope that a new SD image will be released prior to the next season, but in case it isn't you should ask to be allowed to use the later release 2017 FPi SD card image. The main reason for the upgrade is the python-picamera module v1.11 not working using the methods I've chosen for the camera. I don't know why they didn't work with that version, but I've had two failed starts on the track trying to use it. The 2017 sd image contains picamera v1.13. The code also requires OpenCV and NumPy installed as per the standard sd card instructions. The code may run on the standard sd image if the newer picamera module is included as a subfolder of /home/pi/formulapi/ (but I haven't tested it running like that).

There were only 3 files included in the entry for the final:,, and Any other files in the directory are not mine and should be ignored or deleted, with the exception of which is required to talk to the motor controller. (I think that ThunderBorg can also be installed without requiring the file using the Piborg installation script at is launched via the default rc.local file which automatically runs on boot. All does is to launch a shell script,, which continues to reload until it exits cleanly (ie in case of an error the program always reloads automatically), and then after a clean exit it reboots the system. I hope you have as much fun with that as I have - it can be annoying. This way of loading the race code was introduced following the PicoHB python crash early in the B-final of last season (youtube link), which I strongly suspect was due to a "Segmentation fault" python crash (seen in testing at home, no other explanation), which seems to be a .

The code is based on an answer to this question on stackoverflow:
Note that as it is provided, will never exit cleanly, I mean why would you ever want it to exit at all? (I will come back to this…) is the race code file, and it can be run independently without automatically restarting/rebooting using something like 'sudo python /home/pi/formulapi/'. It's made up of a series of simple functions, some of which setup and launch the program using parallel processes, the later functions are scripts for the processes to run. At the end of the file is the command 'if __name__ == "__main__": MainLoop()' - this starts everything off from back at the top - explanation link here:

For now I’ll just list the settings in use and briefly describe the functions. If anyone wants me to go into greater depth on how any of the functions work or how I developed them then I can go into lots of detail on each if you want me to, but I don’t want to bombard the forum so this will be it at least for today. I also want to talk about how to run the software in Windows (most of my development has been done on windows, I only really use an Raspberry Pi for a final check before uploading), how to use live image displays to show the calculations from the code, how to write to logfiles*, how to save images to files*, how to run the program using pre-saved images to test the calculations, and how to run the program against the simulator, each of which has previously been included but was removed for the final. (* writing to files seems to have caused problems with the camera using my methods, but still...)

List of Settings ( a global python dictionary used by all the functions that come after):
‘framerate' : the framerate of the camera, also used in other ways
’raw_res' : the resolution of the raw image from the camera
'pro_res' : the resolution of the image as it is processed
'stk_res' : the resolution of the image to check for being stuck

'eone_k','eone_i','dil8_k','dil8_i','etwo_k','etwo_i' : erode kernel size and iterations used to create overlaps in the track line analysis.

'horiz_cor' : the level of the horizon if upright, % from top of image
'horiz_flp' : the level of the horizon if flipped
'horiz_hgt' : % height of the image to consider for the horizon target
'horiz_ord' : the order of the equation for the horizon target curve

'fov_deg' : the widthways field of view of the camera (spec link)
'track_override_deg' : value to compare to track angle before override
'track_ord' : the order of the equation for the track line analysis

'flipcrop' : compare this top and bottom % of image for flip detection
'flipratio' : multiplier for colour vs white values for flip detection
'stuck_thr' : percentage average difference to detect lack of motion

'trk_x_cor','trk_x_flp' : used to transform the track triangle view
'trk_y_cor','trk_y_flp' : not used, top of track transform == horizon

'steer_gain' : multiplier to control the amount of effect on steering
'min_speed' : minimum speed of motors to ensure not stopping on track
'spdcont_full' : amount of horizon clear to drive forward at full speed
'spdcont_stop' : amount of horizon clear to stop (lower means reverse)

Thresholds (another python dictionary) : defines upper and lower thresholds for the Binariser.

List of Functions:
MainLoop : allocates shared variables, creates and starts all of the processes, waits.
MemorySetup : called by MainLoop, creates multiprocessing Manager() and Event() objects
ImageInpt : creates a camera object and captures images to memory on an infinite loop.
StartWait : checks for reboot file or waits for lights, sets the start state, creates a reboot file.
Binariser : converts the raw image to 6 binary layers - but recognising blue is still a problem
StckCheck : compares the last second’s worth of images to define if we’re moving or stuck
SpunCheck : compare any red and green track regions to check we’re heading the right way
FlipCheck : compare sky vs track using white and colour percentage, to detect being flipped
Horiz_Tgt : find angle to least-obstruction in the distance to drive towards, also sets speed
Track_Tgt : transposes and analyses the track detecting edges to calculate a track angle
MotorsMgr: sets up motors and LEDs, sets motors depending on states and target angles

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <pre>
  • Syntax highlight code surrounded by the <pre class="brush: lang">...</pre> tags, where lang is one of the following language brushes: bash, cpp, perl, python.
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Comment Images
Files must be less than 10 MB.
Allowed file types: png gif jpg jpeg.
Comment Attachments
Files must be less than 10 MB.
Allowed file types: txt pdf nfo doc docx rtf jpg png gif bmp zip tar gz csv xls.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.