Robotics

Robot Football ⚽🤖

Build a Webots football pitch, drive one robot with your keyboard, and code an AI opponent that hunts the ball and shoots for goal.

Saturday 10 October

16 blocks
0 of 15

⚽ Robot Football 🤖

Two e-puck robots. One ball. One goal each. You drive one — the computer drives the other, and it is genuinely trying to score against you.

Everything you need is on this page. Build the pitch, paste in two programs, and play a real match.

 🏟️ Steps 1-4        🤖 Steps 5-6        💻 Steps 7-8        ⚽ Steps 9-10
 The pitch    ───▶   The players   ───▶  The brains    ───▶   Kick off
 arena, ball,        two robots on       your code and        play, then
 goals               the field           the AI's             coach the AI

1 of 15

🎯 What you'll have by the end

  1. 🏟️ A football pitch with a ball that really rolls. ✅ You can shove the ball and it keeps going.
  2. 🤖 Two robots on the pitch, each with its own brain. ✅ One obeys your arrow keys, the other moves all by itself.
  3. ⚽ An AI that hunts the ball and shoots at your goal. ✅ Leave the keyboard alone and it still scores.
  4. 🎚️ The power to make it easy or really hard. ✅ You can change one number and feel the difference.

2 of 15

🏟️ Step 1 — Build the pitch

File → New → New Project Directory...

Name it robot_football, tick all the boxes (especially Add a rectangle arena), and click Finish.

Now click RectangleArena in the scene tree on the left and set:

field value
floorSize 1 1
wallHeight 0.08

✅ Done when: you see a square pitch about one metre across with low walls round the edge.


3 of 15

⚪ Step 2 — Add a ball that rolls

Press the Add button at the top of the scene tree, then choose Base nodes → Solid.

Open your new Solid and fill it in:

field what to do
translation type 0 0 0.03
children Add → Shape. Inside it: geometry → Sphere, radius 0.03
the Shape's appearance Add → PBRAppearance, baseColor 1 1 1 (white)
boundingObject Add → Sphere, radius 0.03
physics Add → Physics, then set mass to 0.05

✅ Done when: you press ▶ and the ball sits on the floor. Hold Alt and drag it — it rolls away like a real ball.

🛟 Stuck? Ball falling through the floor? You missed the boundingObject. Ball frozen in place? You missed the physics. It needs both: one to have a surface, one to have weight.

🤔 Think about it (Q1): what would happen if you gave the ball a boundingObject but no Physics node? (Answer at the bottom.)


4 of 15

🏷️ Step 3 — Give the ball a name

The AI needs to find the ball in its code. We do that by giving it a label.

Right-click your ball Solid in the scene tree and choose DEF. Type exactly this — all capitals:

BALL

✅ Done when: the scene tree shows DEF BALL Solid instead of just Solid.

⭐ Capital letters matter here. BALL works. Ball and ball do not. If the AI later says it can't find the ball, this line is the first place to look.


5 of 15

🥅 Step 4 — Paint the two goals

A football pitch needs a goal at each end. In Webots we just paint a coloured stripe on the floor — a thin, flat box lying down.

We make two stripes, one at a time. They are only paint, so neither of them gets a boundingObject and neither gets a physics node. The ball will roll straight over the top of them, which is exactly what we want.

🧭 Which goal is whose? Think of it like a real match. You stand at one end, the other team stands at the other end, and each team tries to put the ball in the goal at the far end.

  • The red stripe is at the far end from the AI robot. The AI tries to push the ball onto the red stripe. So red is the goal you have to defend.
  • The blue stripe is at the far end from your robot. You try to push the ball onto the blue stripe. So blue is the goal you are attacking.

Easy way to remember it: you score on blue, you defend red.

🔴 4a — Paint the red stripe (the goal you defend)

  1. Click anywhere in the empty white space of the scene tree on the left, so that nothing is selected.
  2. Press the Add button at the top of the scene tree.
  3. In the window that opens, choose Base nodes, then scroll down and double-click Solid.
  4. A new Solid appears at the bottom of the scene tree. Click the small arrow next to it to open it up.
  5. Click its translation field and type exactly:
-0.48 0 0.001
  1. Click its children field, press Add, choose Base nodes, and double-click Shape.
  2. Open that Shape. Click its geometry field, press Add, choose Base nodes, and double-click Box.
  3. Click the Box's size field and type exactly:
0.02 0.36 0.002
  1. Go back up to the Shape and click its appearance field. Press Add, choose Base nodes, and double-click PBRAppearance.
  2. Click its baseColor field and type exactly:
1 0 0
  1. Leave the Solid's boundingObject and physics fields empty. Do not touch them.

✅ Done when: a thin red stripe is lying flat on the floor at one end of the pitch.

🛟 Stuck? Can't see the stripe? The three numbers in translation mean left-right, forward-back, and up-down. The last one, 0.001, lifts the stripe one millimetre off the floor so it doesn't disappear into it. If you typed 0 there, change it to 0.001.

🔵 4b — Paint the blue stripe (the goal you attack)

Do exactly the same eleven steps again for the second stripe. Only two numbers are different.

Add another Solid, and this time translation is:

0.48 0 0.001

Give it a Shape → Box with size:

0.02 0.36 0.002

Give the Shape a PBRAppearance with baseColor:

0 0 1

Again, leave boundingObject and physics empty.

✅ Done when: you have a red stripe at one end of the pitch and a blue stripe at the other. The middle is empty and ready for the ball.

🛟 Stuck? Both stripes ended up in the same place? Check the very first number of translation. One must be minus -0.48 and the other must be plus 0.48. The minus sign is what puts them at opposite ends.

🤔 Think about it (Q2): both stripes are 0.02 0.36 0.002 — very thin, quite wide, and almost completely flat. Why make them so flat?


6 of 15

🤖 Step 5 — Add your robot

Press Add → PROTO nodes → robots → gctronic → e-puck → E-puck

Set these fields:

field value
translation -0.25 0 0
name player

Leave controller alone for now — we'll write it in step 7.

✅ Done when: an e-puck is standing on your half of the pitch.


7 of 15

🦾 Step 6 — Add the AI robot

Add a second E-puck the same way, then set:

field value
translation 0.25 0 0
rotation 0 0 1 3.14159
name ai
supervisor TRUE ← don't miss this one

✅ Done when: two robots face each other across the ball, and the second one has supervisor set to TRUE.

👁️ What does supervisor mean? A normal robot only knows what its own sensors tell it — it's like playing football blindfolded, feeling around with your hands.

A supervisor robot is allowed to ask the simulator directly: "where is the ball right now?" Like having a drone camera above the pitch.

Real RoboCup teams do exactly this — an overhead camera tells every robot where everything is.

🤔 Think about it (Q3): why does only the AI robot need supervisor TRUE, and not yours?


8 of 15

⌨️ Step 7 — Write YOUR robot's program

7a — Create the file

File → New → New Robot Controller...

  1. Choose Python
  2. Name it exactly player_striker
  3. Tick the box to open it in the editor

Webots creates the file for you at:

robot_football/controllers/player_striker/player_striker.py

Delete everything already in that file — we're writing our own.

✅ Done when: an empty editor tab called player_striker.py is open on your right.

7b — Paste in your program

Type or paste all of this into player_striker.py:

"""
player_striker.py  -  YOUR robot
Controls:  UP forward   DOWN reverse   LEFT/RIGHT turn   SPACE turbo
"""

from controller import Robot, Keyboard

TIME_STEP = 64
SPEED = 5.0
TURN = 2.5
TURBO = 6.28

robot = Robot()

left = robot.getDevice('left wheel motor')
right = robot.getDevice('right wheel motor')
left.setPosition(float('inf'))
right.setPosition(float('inf'))

keyboard = robot.getKeyboard()
keyboard.enable(TIME_STEP)

while robot.step(TIME_STEP) != -1:
    key = keyboard.getKey()

    if key == Keyboard.UP:
        left.setVelocity(SPEED)
        right.setVelocity(SPEED)

    elif key == Keyboard.DOWN:
        left.setVelocity(-SPEED)
        right.setVelocity(-SPEED)

    elif key == Keyboard.LEFT:
        left.setVelocity(-TURN)
        right.setVelocity(TURN)

    elif key == Keyboard.RIGHT:
        left.setVelocity(TURN)
        right.setVelocity(-TURN)

    elif key == ord(' '):
        left.setVelocity(TURBO)
        right.setVelocity(TURBO)

    else:
        left.setVelocity(0.0)
        right.setVelocity(0.0)

Save with Ctrl+S.

Now connect it: click the player robot in the scene tree → its controller field → Select... → choose player_striker.

✅ Done when: you press ▶, click inside the 3D window, and your robot moves with the arrow keys.

🛟 Stuck? Keys doing nothing? Click inside the 3D window first. If the editor has focus, the arrow keys just move your text cursor around.

🤔 Think about it (Q4): in the LEFT arrow part of your code, the left wheel gets -TURN and the right wheel gets +TURN. Why opposite signs?

🤔 Think about it (Q5): the very last else sets both wheels to 0.0. What's that for?


9 of 15

🧠 Step 8 — Write the AI's program

8a — Create the second file

File → New → New Robot Controller... again.

  1. Choose Python
  2. Name it exactly ai_striker

Webots creates it at:

robot_football/controllers/ai_striker/ai_striker.py

Clear out whatever is in there.

✅ Done when: an empty ai_striker.py tab is open.

💭 How the AI will think It does not just drive at the ball. If it did, it would shove the ball the wrong way half the time — think about what happens if it approaches from the wrong side.

Instead, every single step it works out a spot behind the ball, on the far side from the goal. It drives to that spot first, lines itself up, and only then charges straight through the ball.

That's three lines of maths, and it makes the robot look like it has a plan.

        goal ◀───────────────  ⚪ ball  ◀── 🤖 lines up HERE first
   (where the AI shoots)                    (behind the ball, on the far side)

8b — Paste in the AI program

Put all of this into ai_striker.py:

"""
ai_striker.py  -  the autonomous opponent
Needs:  supervisor TRUE on this robot, and DEF BALL on the ball.
"""

from controller import Supervisor
import math

TIME_STEP = 64

# ---- CHANGE THIS TO MAKE IT EASIER OR HARDER ----
SKILL = 0.6        # 0.3 = easy    0.6 = fair    1.0 = brutal
# -------------------------------------------------

GOAL_X = -0.5      # the goal the AI shoots at
GOAL_WIDTH = 0.18
STANDOFF = 0.10    # how far behind the ball it lines up
TURN_LIMIT = 0.25  # how straight it must be before charging

robot = Supervisor()

me = robot.getSelf()
ball = robot.getFromDef('BALL')
ball_pos = ball.getField('translation')

left = robot.getDevice('left wheel motor')
right = robot.getDevice('right wheel motor')
left.setPosition(float('inf'))
right.setPosition(float('inf'))


def wheels(l, r):
    left.setVelocity(l * SKILL)
    right.setVelocity(r * SKILL)


def turn_needed(target_x, target_y, my_x, my_y, facing):
    """How far to turn to point at the target. Answer is -pi to +pi."""
    err = math.atan2(target_y - my_y, target_x - my_x) - facing
    while err > math.pi:
        err -= 2 * math.pi
    while err < -math.pi:
        err += 2 * math.pi
    return err


def kick_off():
    """Put the ball back in the middle after a goal."""
    ball_pos.setSFVec3f([0.0, 0.0, 0.03])
    ball.resetPhysics()


score = 0

while robot.step(TIME_STEP) != -1:

    # where is everyone?
    mx, my, _ = me.getPosition()
    bx, by, _ = ball.getPosition()

    o = me.getOrientation()
    facing = math.atan2(o[3], o[0])

    # did the AI score?
    if bx < GOAL_X + 0.06 and abs(by) < GOAL_WIDTH:
        score += 1
        print("GOAL for the AI!   score:", score)
        kick_off()
        continue

    # find the spot behind the ball
    gx = bx - GOAL_X
    gy = by
    dist = math.hypot(gx, gy)
    if dist < 0.000001:
        dist = 0.000001

    target_x = bx + (gx / dist) * STANDOFF
    target_y = by + (gy / dist) * STANDOFF

    # already lined up? then go straight for the ball
    if math.hypot(target_x - mx, target_y - my) < 0.07:
        target_x, target_y = bx, by

    # turn towards it, or charge
    err = turn_needed(target_x, target_y, mx, my, facing)

    if err > TURN_LIMIT:
        wheels(-2.0, 2.0)
    elif err < -TURN_LIMIT:
        wheels(2.0, -2.0)
    else:
        wheels(6.0, 6.0)

Save with Ctrl+S.

Connect it: click the ai robot → controller field → Select... → ai_striker.

✅ Done when: you press ▶ and the AI robot starts moving by itself, curving around towards the ball.

🛟 Stuck? Error mentioning NoneType? That means getFromDef('BALL') found nothing — go back to step 3 and check the DEF name is exactly BALL in capitals.

🤔 Think about it (Q6): find the line if math.hypot(target_x - mx, target_y - my) < 0.07:. It checks whether the AI is close to its lining-up spot. What does it do once it gets there?

🤔 Think about it (Q7): what do you think kick_off() is for?


10 of 15

⚽ Step 9 — Kick off!

Press ▶, click inside the 3D window, and defend your goal.

Watch the console at the bottom — the AI prints GOAL for the AI! each time it scores, and resets the ball by itself.

First to 3 goals wins.

✅ Done when: you've played a full match against the AI.

💡 Tactic that genuinely works: don't chase the ball. Sit in front of your own goal and let the AI come to you. Save the SPACE turbo for when the ball is right in front of you.


11 of 15

🎚️ Step 10 — Coach the AI

Go back to ai_striker.py and try changing these, one at a time. Save, then press ▶ again each time.

line try what you should notice
SKILL = 0.6 0.3 slow and clumsy, easy to beat
SKILL = 0.6 1.0 very hard to stop
STANDOFF = 0.10 0.25 takes much wider approaches
TURN_LIMIT = 0.25 0.05 aims very precisely but wastes time turning

✅ Done when: you've found the SKILL setting where you win roughly half your matches.

🤔 Think about it (Q8): you set SKILL = 1.0 and the AI got much better. Looking at the wheels() function, what is SKILL actually doing?


12 of 15

🧩 Challenges

Challenge 1 — Make the AI a defender. Turn the AI into a defender instead of a striker: it should shoot at the other end, which means it spends the whole match clearing the ball away from you.

Hint: there's exactly one number that decides which goal it attacks. Expected: the AI pushes the ball towards the blue end instead of the red one.

Challenge 2 — Add a scoreboard for you. Right now only the AI's goals get counted. Add a score for you too.

Hint: the AI checks if bx < GOAL_X + 0.06. Your goal is the blue stripe at x = 0.5, so you need the mirror of that test. Expected: the console prints your goals as well as the AI's.


13 of 15

✅ What you built today

  1. Built a football pitch with a rolling ball and two goals.
  2. Labelled the ball DEF BALL so code could find it.
  3. Put two robots in one world, each with its own program.
  4. Gave one robot supervisor powers so it can see the ball.
  5. Wrote player_striker.py and drove your robot with the keyboard.
  6. Wrote ai_striker.py — a robot that lines up behind the ball and scores.
  7. Coached the AI by changing its skill, standoff and aim.

14 of 15

🔑 Answers

Q1. It would sit there like a statue — solid, but impossible to move. No physics means no weight and no movement. (With no boundingObject it would fall through the floor instead.)

Q2. So they are just paint on the floor and the ball can roll over them. A tall box would be a wall, not a goal.

Q3. Because you can already see the ball with your own eyes on the screen — you're the sensor for your robot.

Q4. Opposite directions make the robot spin on the spot instead of driving. One wheel forward, one back = a spin. (Try making both positive and see what happens!)

Q5. So the robot stops when you're not pressing anything. Without it the robot would keep rolling forever.

Q6. It switches its target from the spot to the ball itself, and charges. Line up first, then attack.

Q7. It puts the ball back in the middle after someone scores — otherwise the ball would sit in the goal forever.

Q8. It multiplies every wheel speed, so the whole robot moves faster. Its thinking never changes, only its speed.

Challenge 1 solution:

GOAL_X = 0.5

One number. From your side of the pitch it now looks like a defender constantly booting the ball away.

Challenge 2 solution: add my_score = 0 next to score = 0 near the top, then put this just below the AI's goal check inside the loop:

    if bx > 0.44 and abs(by) < GOAL_WIDTH:
        my_score += 1
        print("GOAL for you!   score:", my_score)
        kick_off()
        continue

15 of 15

🚀 Next time — give the AI eyes 👁️

Right now the AI cheats slightly: it asks the simulator where the ball is.

Next lesson we take that away and give it a camera instead. It has to find the ball by hunting for the colour white — the way a real robot has to.

💡 Did you know? There's a real world championship for this called RoboCup, running since 1997. Their official goal is a robot team that can beat the human World Cup winners by 2050. You just wrote your first robot footballer. ⚽