How can a robot handle being upside-down?
So we have been racing around the track and disaster strikes, we have been flipped over:
No fear, YetiBorgs can keep driving upside-down :)
In order to drive upside-down we need to do a few things:
- Rotate the camera image 180° before processing.
This is simple with OpenCV:image = cv2.flip(image, -1)
- Make the motors run backwards.
This is done by swapping the +/- sign on the speed.
For example a speed of 0.85 becomes -0.85 when flipped. - Swap the left and right side over.
We can do this easily by swapping the +/- sign on the steering.
For example a steering of -0.6 becomes +0.6 when flipped.
When we do these three things the YetiBorg can continue racing with all of the same logic it was using before.
In other words none of the rest of the code needs to know anything has changed.
If we wanted we could even drive directly at the track wall to flip back to the correct way up :)
The only problem is, how do we know we are flipped in the first place?
With most robots we have a number of different sensors that can help here, to name a few:
- Accelerometers will tell us gravity is going the wrong way
- Gyroscopes will see the flip itself when it happens
- Distance sensors measuring the floor or ceiling will get very different readings
- Light sensors on the top of the robot will suddenly see dark
In our case though we only have the camera, so how can we tell that this:
should actually be this:
What we can do is look at the variation between the top and the bottom of the image.
First we want to remove the colour differences due to the track painting.
We do this because the background can include anything, it may also be coloured.
This can be done by converting to grey-scale or by taking the largest of the three colour channels.
We have chosen to do the second method as it is slightly faster to run.
We do this using OpenCV and numpy, so lets start by loading an image we already have:
import cv2 import numpy frame = cv2.imread('frame.jpg')
We can also make a quick function to show what our image is like this:
def show(image): cv2.imshow('image', image) cv2.waitKey(0)
Each time you use the function click the X in the top corner of the image to close it and continue:
show(frame)
The conversion to a grey image takes the largest of the colour channels.
We do this by splitting the image into the three channels:
r, g, b = cv2.split(frame) show(r) show(g) show(b)
See how each image is a grey version of that colour?
What we want is the largest value of the three for each pixel:
largest = numpy.maximum(numpy.maximum(r, g), b) show(largest)
We are going to compare the top and bottom of the images, so we need some positions:
yMax = frame.shape[0] y20 = int(yMax * 0.2) # 20% y80 = int(yMax * 0.8) # 80%
Now we can get our top and bottom slices:
top = largest[0 : y20, :] bottom = largest[y80 : yMax, :] show(top) show(bottom)
What we have are two images with very different variations in the shade of grey.
The track level should be fairly even, but the background will vary a lot.
We can get a value for how much each slice varies like this:
print top.var() print bottom.var()
We saw:116.08125910773002
and959.16180939118181
Now we have these values, how do we know we have flipped?
What we do is see which value is actually larger:
if top.var() < bottom.var(): print 'We are flipped!' correct = cv2.flip(frame, -1) else: print 'We are the right way up' correct = frame
Now we know that we are flipped over we can either try and flip back, or try and drive upside-down.
Add new comment