Mindstorms ev3 Football

posted in Python, Hardware at 08 September 2017

In my sixth form, I worked as part of a team to make a Mindstorms ev3 robot play football using ev3dev (a loadable Linux OS for the ev3) and Python.

Our Robot (sorry about the image quality, for some reason all of my images of it were missing so all I had was a video of it)

The Robot

DARWIN, our robot, consists of the following components. It has two independent motors and a caster for flexible movement, and a third motor to apply backspin to the ball so that it stays in the robot's grasp. It has a rotation sensor to detect whether it has been pushed off course and a colour sensor to look at the pitch and its lines to make sure it isn't going off the pitch. It also has an ultrasonic sensor to look for a goal and other robots, and an infrared (IR) sensor to look for the ball.

Our Strategy

This robot was designed to score goals rather than just defend, so with that in mind it has a simple plan: find the ball, grab the ball and then score a goal with it. This essentially means it has two states, which we'll get to later. While it doesn't have the ball, it uses the IR sensor to find out what direction the ball is in and moves towards it. If it can't find it at all, it just spins on the spot until it sees it.

The biggest problem we faced was that the IR sensor was unreliable at very short distances, causing it to produce very weird values suggesting that the robot should take a sharp right when the ball is right in front of it. To deal with this, the robot ignores values corresponding to very sharp turns and rather than directly acting on the value it receives keeps calculating the average of the last few readings. We also ignore the IR sensor completely once the robot has the ball, which is done by monitoring the speed of the backspin motor - if it has slowed slightly, we know the ball is likely underneath it so we have it.

To detect the goal, we use both our rotation and ultrasonic sensor. If we have the ball, are facing the right way and detect an object, we assume it is the goal and attempt to score. For collision detection, we turn left or right randomly to avoid the object, and then get back to our original path.

The Code

Because our robot essentially has a few states it can be in, Kirsty cleverly realised that our code could be structured in that way also. So, our main loop in the code is:

functions = {"moveToGoal": moveToGoal, "retreat": retreat, "shoot": shoot, "lookForBall": lookForBall, "realign": realign}
while True:
        state, ro = functions[state](ro)

Where ro is our Robot Object, which contains some key-value pairs of information. Every function returns a state and newly-modified object once done, so we have effortless switching between states.

These stats are organised as follows. Our main state is lookForBall, which in a surprising twist looks for the ball. It reads the IR sensor, keeps track of the average and turns in the relevant direction. This returns its current state unless it turns out it has now got the ball, then it moves to realign. Realign rotates the robot until it is facing the right way, then moves on to moveToGoal (or lookForBall if it loses the ball while rotating). In moveToGoal we move in a straight line and try to score (changing to the state shoot), changing to realign if we get knocked in the wrong direction. The state shoot is self-explanatory.

The final state that we've not covered is retreat, which is when it detects the white line surrounding the outside of the pitch. When it does this, it reverses, makes a quarter turn and then goes back to realign. This can be called while the robot is in moveToGoal. Illustrated, this state system looks like this:

State Diagram

Turns out if you can split a program up like this, it looks really nice.

URL: https://github.com/finnbar/rrrrobot/blob/master/ballfollow2.py