EV3 examples: SharpShot

Back to SharpShot | How EV3 reads it | Value reference: EV3

Warning

EV3 hardware-verified (2026-11-03, release v1.0-261101): the official firmware (EV3-G blocks, all 13 modes read with Direct Commands) and Pybricks v4 examples 1–4 passed on real hardware.

This page has 4 EV3 Python examples: turn to follow a blob, colour at a spot, PD line following with a stop at a junction, object detection (drive to a square). The same program runs on Pybricks v4 and on LEGO EV3 MicroPython v2; the library MBC_UniCam_EV3.py detects which one. EV3-G and EV3 Classroom read it with infrared sensor blocks, see How EV3 reads it.

Software How
🐍 Pybricks v4 (code.pybricks.com, EV3 needs the Pybricks firmware) "Import a file" → import the example and MBC_UniCam_EV3.py → ▶
🐍 LEGO EV3 MicroPython v2 (microSD boot card + VS Code "LEGO MINDSTORMS EV3 MicroPython" extension) put the example and MBC_UniCam_EV3.py in the same project folder → download and run

Important

  • The camera must run the EV3 firmware. On EV3 it shows up as a LEGO infrared sensor: mode 0 proximity = mode number, mode 1 seeker = 8 values, mode 2 remote = 4 values, mode 5 = device ID.
  • EV3 can read the camera's current mode: with UniCam(Port.S1) (no mode) the library reads mode 0 the first time (about 0.5 s more); the examples check the mode first and beep with a message if it is wrong.
  • Every EV3 mode switch takes about 0.5 s: in one loop use values of one EV3 mode when you can (the table below shows where each value is).

All host libraries in one download: MBC_UniCam_v1_Host_Libraries.zip

Before you start: teach colours and set the mode on the camera

  1. Teach a colour: on the live view stop on "target 1" → OK → move the cross onto the colour (AxisX → AxisY) → OK.
  2. Set the mode: left/right to MODE → OK → camera mode → OK → option → OK. "1-3" at the top right of the screen = mode number 13. Options show how many values EV3 really gets (for example "8 XY").
Mode number Camera menu What EV3 gets
11 1 Colour tracking, 1 Max 12 colour positions (8×6 grid); colours 18 in mode 1, 912 in mode 2
12 1 Colour tracking, 2 Multi 8 colour positions (mode 1)
13 1 Colour tracking, 3 Standard 4 colours: position + area % (mode 1)
14 1 Colour tracking, 4 Fine 2 colours: position, width/height (8×6 grid), count, area % (mode 1)
21 2 Spot colour, 1 Hue 12 spot hues (sent as 0100, turned back into hue codes ±2); spots 18 mode 1, 9~12 mode 2
22 2 Spot colour, 2 4 spot hues (±2) + saturation, value (tens only) (mode 1)
23 2 Spot colour, 3 2 spots: exact hue code, saturation, value (mode 1)
30 3 Colour order hues of 11 blocks from the left (±2) + total (modes 1 + 2)
41 / 42 / 43 4 Line near/mid/far offset, angle, width, junction, flags, junction distance, marker colour (mode 1); edges, far width, marker count (mode 2); no lost side on EV3
51 5 Object detection, 1 Object list code and position (8×6 grid) of 5 objects; objects 1~4 in mode 1, object 5 and the object count in mode 2
52 5 Object detection, 2 Object detail code, position, area %, angle (sent ÷2, restored ×2 by the library, ±1°) of 2 objects (all in mode 1; no count, no width/height)

Reading the numbers: the library turns the EV3 8×6 grid into pixels (X 0159 left → right, Y 0119 top → bottom, same as SPIKE), using the cell centre, ±10 px; (0, 0) = not found. For the raw EV3 grid (Y bottom = small) use grid(c). Line offsets −100~100: standing behind the robot, line on the left = negative.

Objects (modes 51 / 52): object code = colour × 10 + shape (14 = a square of target 1), 0 = none. Shapes: 0 other, 1 circle, 2 ellipse, 3 triangle, 4 square, 5 rectangle, 6 pentagon, 7 hexagon, 8 star, 9 trapezoid. Objects 1, 2… are sorted largest first; the position is the centroid (also turned into pixels ±10; raw cell via object_grid(i)). Angle 0~179: long side pointing right = 0, turning toward the bottom of the picture is positive.

MBC_UniCam_EV3.py methods

cam = UniCam(Port.S1) or UniCam(Port.S1, mode=13) (port can also be 1~4). Method names are the same as SPIKE MBC_UniCam.py.

Method Returns Modes
mode_number() camera mode number (reads mode 0 when no mode was given) all
xy(c), x(c), y(c), found(c), grid(c) position (pixels ±10), found, raw 8×6 cell 11~14
area(c) area % 13, 14
size(c), count(c) (width, height) pixels ±10, count 14
max_color() / max_point() colours / spots in this mode 1x / 2x
hue(i), sat(i), val(i), hsv(i), color(i) hue code, saturation, value, colour name 21~23
order(), total() hue codes from the left (≤11), total blocks 30
near() mid() far() angle() width() junction() flags() junction_dist() line values (mode 1) 41~43
edges(), far_width(), marker(), line_all() line values (need mode 2, +0.5 s) 41~43
max_object(), object_count() objects in this mode (51 → 5, 52 → 2), objects in view 0~8 (51, reads mode 2) 51, 52 / 51
object(i), object_found(i), object_color(i), object_shape(i) object code (0 = none), found, colour 18, shape 09 51, 52
object_xy(i), object_grid(i) centroid (pixels ±10), raw 8×6 cell (Y bottom = small) 51, 52
object_area(i), object_angle(i) area %, angle 0~179 (±1) 52
device_id(), is_unicam(), sensor_ok() mode 5: (0x4C, 3, 100), True, last read OK all
shape_name(s, zh=False), color_name(h) (module functions) shape 0~9 → 'circle' … (zh=True Chinese), hue code → colour name —
  • Values EV3 does not get (counts in modes 11~13, colours found, lost side) raise ValueError; so does a wrong mode. Messages are bilingual.
  • If the camera does not answer you get None; the examples check for None first.

Example 1: turn to follow a colour blob

What it does: the robot turns toward colour 1 while driving forward; when the area is big it only turns; with nothing seen it spins slowly to search. At the start it checks that the camera is in mode 13.

Camera mode: camera mode 1 Colour tracking, option 3 Standard (XY + area) = mode number 13; teach "target 1" first.

You need: EV3 brick, camera in port 1, left wheel motor B, right wheel C, a ball or brick of the target colour.

Download: ex01_track_turn.py + MBC_UniCam_EV3.py

Python code (Pybricks v4 / EV3 MicroPython v2)
#!/usr/bin/env pybricks-micropython
# 神攝手 × EV3 範例 1:跟著色塊轉
# SharpShot × EV3 example 1: turn to follow a colour blob
# 需要把相機選單切到 相機模式 1 顏色追蹤 ・子選項 ③ 標準(XY+面積)=模式編號 13
# Camera menu: camera mode 1 Colour tracking, option 3 Standard (XY + area) = mode number 13
# 軟體 Software:Pybricks v4(code.pybricks.com)或 樂高官方 EV3 MicroPython v2(同一支程式都能跑)
# 準備:先在相機上教「目標 1」的顏色 / teach "target 1" on the camera first
# 接線 Wiring:相機 → 1 號孔 / camera → port 1;左輪 B、右輪 C / left wheel B, right wheel C
# 同一個資料夾要有 MBC_UniCam_EV3.py / put MBC_UniCam_EV3.py in the same folder
from pybricks.hubs import EV3Brick
from pybricks.ev3devices import Motor
from pybricks.parameters import Port
from pybricks.robotics import DriveBase
from pybricks.tools import wait
from MBC_UniCam_EV3 import UniCam

ev3 = EV3Brick()
cam = UniCam(Port.S1)                            # 不填 mode:自動讀相機目前的模式 / mode read from the camera
left = Motor(Port.B)
right = Motor(Port.C)
robot = DriveBase(left, right, 56, 114)          # 輪徑 56 mm、輪距 114 mm / wheel 56 mm, axle 114 mm

if cam.mode_number() != 13:                      # EV3 讀得到相機的模式,切錯了就提醒 / EV3 can check the mode
    print('請把相機切到 模式 13(顏色追蹤 ③)/ please set the camera to mode 13')
    ev3.speaker.beep(200, 500)
    wait(3000)
    raise SystemExit

KP = 3            # 👉 可以改:偏 1 像素就多轉幾度/秒 / turn rate per pixel of error
STOP_AREA = 20    # 👉 可以改:面積 ≥ 20 % 就算很近 / area (%) that counts as "close"

while True:
    p = cam.xy(1)                                # 色 1 的 (X, Y) 像素(EV3 是 8×6 格,±10)/ pixels (±10 on EV3)
    if p is None:                                # 讀不到相機 / no camera
        robot.stop()
    elif p[0] == 0:                              # 沒看到:原地慢慢轉著找 / not seen: spin to search
        robot.drive(0, 60)
    else:
        error = p[0] - 80                        # 畫面中間 X = 80;色塊在右 → 正 / right of centre = positive
        speed = 0 if cam.area(1) >= STOP_AREA else 80
        robot.drive(speed, error * KP)           # 面積在同一個模式(模式 1),不用多等 / same EV3 mode, no wait
    wait(20)

Step by step

  1. UniCam(Port.S1): no mode → cam.mode_number() reads mode 0 for the camera's mode number; if it is not 13 the program warns and ends.
  2. cam.xy(1), cam.area(1): both in EV3 mode 1 (seeker), no mode switch.
  3. error = X − 80: right of centre is positive → positive turn = turn right.

What you should see: ball on the left → turns left; on the right → turns right; close → turns on the spot; no ball → spins to search.

Common problems

  • Beeps and ends at once? → the camera is not in mode 13: in the camera menu choose MODE → "1 Colour tracking → 3"; the screen should show 1-3.
  • Wobbles? → EV3 positions are an 8×6 grid (20 px per cell); use a smaller KP (for example 2).

Example 2: what colour is at the spot?

What it does: reads the exact hue code, saturation and value of spots S1 and S2, prints the colour; when S1 is red, green, orange or yellow the EV3 light shows it; prints one more line when both spots are the same colour.

Camera mode: camera mode 2 Spot colour, option 3 Hue + saturation + value = mode number 23 (2 spots on EV3, exact values).

You need: EV3 brick, camera in port 1, coloured bricks.

Download: ex02_spot_color.py + MBC_UniCam_EV3.py

Python code (Pybricks v4 / EV3 MicroPython v2)
#!/usr/bin/env pybricks-micropython
# 神攝手 × EV3 範例 2:座標取色判斷顏色
# SharpShot × EV3 example 2: what colour is at the spot?
# 需要把相機選單切到 相機模式 2 座標取色 ・子選項 ③ 色相+飽和+明度 =模式編號 23(EV3 給 2 點,原始值)
# Camera menu: camera mode 2 Spot colour, option 3 Hue + sat + value = mode number 23 (2 spots, exact values)
# 軟體 Software:Pybricks v4 或 樂高官方 EV3 MicroPython v2
# 準備:取樣點 S1、S2 的位置看相機螢幕上標 1、2 的十字 / spots S1, S2 are the crosses on the camera screen
# 接線 Wiring:相機 → 1 號孔 / camera → port 1
from pybricks.hubs import EV3Brick
from pybricks.parameters import Port, Color
from pybricks.tools import wait
from MBC_UniCam_EV3 import UniCam, color_name

ev3 = EV3Brick()
cam = UniCam(Port.S1, mode=23)                   # 填了 mode 就不用先讀模式 0 / giving mode skips one mode switch

NAME = {'red': '紅', 'orange': '橙', 'yellow': '黃', 'green': '綠', 'cyan': '青',
        'blue': '藍', 'purple': '紫', 'white': '白/灰', 'black': '黑'}
LIGHT = {'red': Color.RED, 'orange': Color.ORANGE, 'yellow': Color.YELLOW, 'green': Color.GREEN}
LOW_SAT = 15      # 👉 可以改:飽和度比這低就當成白/灰 / below this saturation = white / grey

while True:
    names = []
    for point in (1, 2):
        p = cam.hsv(point)                       # (色相碼, 飽和度, 明度) / (hue, sat, val)
        if p is None:
            names.append(None)
            continue
        n = color_name(p[0])
        if p[1] < LOW_SAT and n != 'black':
            n = 'white'
        names.append(n)
        print('S%d 色相碼 hue %d 飽和 sat %d 明度 val %d → %s %s' % (point, p[0], p[1], p[2], NAME[n], n))
    if names[0] in LIGHT:                        # EV3 的燈只有紅、綠、橙、黃 / EV3 light: red, green, orange, yellow
        ev3.light.on(LIGHT[names[0]])
    else:
        ev3.light.off()
    if names[0] is not None and names[0] == names[1]:
        print('兩點一樣顏色 / both spots are the same colour')
    wait(300)

Step by step

  1. UniCam(Port.S1, mode=23): with the mode given there is no mode-0 read.
  2. cam.hsv(point): EV3 mode 23 stores one hue code in two values (hundreds and remainder); the library joins them back to 0~255.
  3. color_name(): hue code → colour name, same limits as the SPIKE library.

What you should see: two lines every 0.3 s, for example S1 色相碼 hue 5 飽和 sat 80 明度 val 70 → 紅 red.

Common problems

  • More spots? → mode 21 gives 12 spot hues (±2), but spots 9~12 are in mode 2 (+0.5 s).
  • Saturation and value only in tens? → that is mode 22 (EV3 packs two numbers into one value); use mode 23 for exact values.

Example 3: PD line following + stop at a junction

What it does: PD line following that mostly uses the near offset and turns early with the far offset; stops and beeps at a cross or T junction; when none of the three scan lines sees the line it turns toward the side of the last error. It only uses EV3 mode-1 values, so there is no mode switch in the loop.

Camera mode: camera mode 4 Line, option 1 Black line = mode number 41 (white 42 and colour 43 also work; the program checks it is a line mode).

You need: EV3 brick, camera in port 1 at the front looking down and forward, left wheel B, right wheel C, a black line on white.

Download: ex03_line_follow.py + MBC_UniCam_EV3.py

Python code (Pybricks v4 / EV3 MicroPython v2)
#!/usr/bin/env pybricks-micropython
# 神攝手 × EV3 範例 3:PD 循線+路口停車
# SharpShot × EV3 example 3: PD line following + stop at a junction
# 需要把相機選單切到 相機模式 4 循線 ・子選項 ① 黑線 =模式編號 41(白線 42、色線 43 也可以)
# Camera menu: camera mode 4 Line, option 1 Black line = mode number 41 (42 / 43 also work)
# 軟體 Software:Pybricks v4 或 樂高官方 EV3 MicroPython v2
# 準備:相機裝在車頭、斜斜往前下方看線 / camera at the front, looking down and forward
# 接線 Wiring:相機 → 1 號孔;左輪 B、右輪 C / camera → port 1; left wheel B, right wheel C
# 方向:站在車後往前看,線在左=負 / standing behind the robot: line on the left = negative
# EV3 每換一次模式約 0.5 秒:這支只用模式 1 的值(近、遠、旗標、路口)/ only mode-1 values (no mode switch)
from pybricks.hubs import EV3Brick
from pybricks.ev3devices import Motor
from pybricks.parameters import Port
from pybricks.robotics import DriveBase
from pybricks.tools import wait
from MBC_UniCam_EV3 import UniCam

ev3 = EV3Brick()
cam = UniCam(Port.S1)
left = Motor(Port.B)
right = Motor(Port.C)
robot = DriveBase(left, right, 56, 114)

if cam.mode_number() not in (41, 42, 43):        # 相機沒切到循線就提醒 / camera not in line mode
    print('請把相機切到 循線(模式 41/42/43)/ please set the camera to line mode')
    ev3.speaker.beep(200, 500)
    wait(3000)
    raise SystemExit

SPEED = 120       # 👉 可以改:前進速度 mm/s / forward speed
KP = 1.5          # 👉 可以改:比例 / proportional gain
KD = 4.0          # 👉 可以改:微分 / derivative gain
last_error = 0

while True:
    junction = cam.junction()                    # 0 無 1 十字 2 T 3 左 4 右 5 盡頭 / junction type
    if junction is None:                         # 讀不到相機:停車 / no camera: stop
        robot.stop()
        wait(50)
        continue
    if junction in (1, 2):                       # 十字或 T 字路口:停車嗶一聲 / cross or T: stop
        robot.stop()
        ev3.speaker.beep()
        break
    if cam.flags() == 0:                         # 近中遠都沒看到線:往上一次偏的那邊找 / line lost
        robot.drive(0, -120 if last_error < 0 else 120)
        wait(10)
        continue
    error = 0.7 * cam.near() + 0.3 * cam.far()   # 近處為主、遠處提早轉彎 / mostly near, a bit of far
    turn = KP * error + KD * (error - last_error)
    last_error = error
    robot.drive(SPEED, turn)                     # 線在左(負)→ 往左轉 / line on the left → turn left
    wait(10)

Step by step

  1. cam.junction(): junction type (0 none 1 cross 2 T 3 left 4 right 5 end); EV3 keeps it in one value with the line flags (junction × 10 + flags), the library splits it.
  2. cam.flags(): near 1 + mid 2 + far 4; 0 = no line seen.
  3. error = 0.7 × near + 0.3 × far: line on the left is negative → turn left; KD makes the turns smoother.

What you should see: the robot follows the line and turns early before curves; at the cross junction it stops, beeps and the program ends.

Common problems

  • Slow and jerky? → edges(), far_width(), marker() and line_all() switch to mode 2 (0.5 s each time); do not use them in the line loop.
  • Wobbles a lot? → smaller KP, bigger KD, or a slower SPEED.

Example 4: drive to a square and show its angle

What it does: looks for a "square of target 1" among objects 1 and 2 (the two largest), turns to it and drives forward while the EV3 screen shows its angle; stops and beeps when the area is big enough (close); spins slowly to search when it sees none. It first checks that the camera is in mode 52.

Camera mode: camera mode 5 Object detection, option 2 Object detail = mode number 52; teach the colour of "target 1" first (for example a blue block).

You need: EV3 brick, camera in port 1, left wheel B, right wheel C, a square block or card in the target colour.

Download: ex04_object.py + MBC_UniCam_EV3.py

Python code (Pybricks v4 / EV3 MicroPython v2)
#!/usr/bin/env pybricks-micropython
# 神攝手 × EV3 範例 4:跟著正方形走、螢幕顯示它轉了幾度
# SharpShot × EV3 example 4: drive to a square and show its angle on the screen
# 需要把相機選單切到 相機模式 5 物體偵測 ・子選項 ② 物體詳細=模式編號 52
# Camera menu: camera mode 5 Object detection, option 2 Object detail = mode number 52
# 軟體 Software:Pybricks v4(code.pybricks.com)或 樂高官方 EV3 MicroPython v2(同一支程式都能跑)
# 準備:在相機上教「目標 1」的顏色(例如藍色方塊)/ teach "target 1" on the camera (e.g. a blue block)
# 接線 Wiring:相機 → 1 號孔 / camera → port 1;左輪 B、右輪 C / left wheel B, right wheel C
# 同一個資料夾要有 MBC_UniCam_EV3.py / put MBC_UniCam_EV3.py in the same folder
#
# 物體代碼=色號×10+形狀:目標 1 的正方形=1×10+4=14。EV3 模式 52 給 2 個物體(面積大的是物體 1),
# 代碼、位置、面積、角度都在 EV3 模式 1,不用換模式(不會多等 0.5 秒)。
# Object code = colour × 10 + shape: target 1 square = 14. EV3 mode 52 gives 2 objects (object 1 = largest);
# code, position, area and angle are all in EV3 mode 1, so there is no 0.5 s mode switch.
from pybricks.hubs import EV3Brick
from pybricks.ev3devices import Motor
from pybricks.parameters import Port
from pybricks.robotics import DriveBase
from pybricks.tools import wait
from MBC_UniCam_EV3 import UniCam, shape_name

ev3 = EV3Brick()
cam = UniCam(Port.S1)                            # 不填 mode:自動讀相機目前的模式 / mode read from the camera
left = Motor(Port.B)
right = Motor(Port.C)
robot = DriveBase(left, right, 56, 114)          # 輪徑 56 mm、輪距 114 mm / wheel 56 mm, axle 114 mm

if cam.mode_number() != 52:                      # EV3 讀得到相機的模式,切錯了就提醒 / EV3 can check the mode
    print('請把相機切到 模式 52(物體偵測 ② 物體詳細)/ please set the camera to mode 52')
    ev3.speaker.beep(200, 500)
    wait(3000)
    raise SystemExit

WANT = 1 * 10 + 4   # 👉 可以改:目標 1 × 10 + 形狀 4(正方);三角改成 13、圓改成 11 / triangle 13, circle 11
KP = 3              # 👉 可以改:偏 1 像素就多轉幾度/秒 / turn rate per pixel of error
STOP_AREA = 25      # 👉 可以改:面積 ≥ 25 % 就算到了 / area (%) that counts as "arrived"

print('要找 looking for: 目標 target', WANT // 10, shape_name(WANT % 10, True), shape_name(WANT % 10))
while True:
    c1 = cam.object(1)                           # 物體 1(最大)的代碼 / code of object 1 (largest)
    if c1 is None:                               # 讀不到相機 / no camera
        robot.stop()
        wait(100)
        continue
    if c1 == WANT:                               # 物體 1、2 裡哪一個是要找的(0=都不是)/ which one we want
        i = 1
    elif cam.object(2) == WANT:
        i = 2
    else:
        i = 0
    if i == 0:                                 # 沒看到:原地慢慢轉著找 / not seen: spin to search
        robot.drive(0, 60)
        wait(20)
        continue
    x, y = cam.object_xy(i)                      # 像素(EV3 是 8×6 格中心,±10)/ pixels (±10 on EV3)
    angle = cam.object_angle(i)                  # 0~179 度(EV3 精度 ±1)/ degrees (±1 on EV3)
    ev3.screen.clear()
    ev3.screen.print(shape_name(WANT % 10), angle)
    if cam.object_area(i) >= STOP_AREA:          # 到了:停車、嗶一聲 / arrived: stop and beep
        robot.stop()
        ev3.speaker.beep()
        break
    robot.drive(80, (x - 80) * KP)               # 畫面中間 X = 80;在右 → 正 → 右轉 / right of centre = positive
    wait(20)

Step by step

  1. WANT = 1 × 10 + 4: object code = colour × 10 + shape, so a square of target 1 is 14 (triangle 13, circle 11).
  2. cam.object(1), cam.object(2): EV3 mode 52 gives the 2 largest objects; code, position, area and angle are all in EV3 mode 1, so the loop never switches modes.
  3. cam.object_angle(i): an EV3 value only goes up to 100, so the camera sends "(angle + 1) ÷ 2" and the library multiplies by 2 again: 0~179, ±1°.
  4. cam.object_area(i) ≥ STOP_AREA: a big area means close, so stop.

What you should see: with the block on the left the robot turns left, on the right it turns right; the screen shows the shape and angle, like square 30 (turn the block and the number changes); close to it the robot stops and beeps.

Common problems

  • Beeps and ends right away? → the camera is not in mode 52: MODE → "5 Object detection → 2"; the top right of the camera screen must show 5-2.
  • Square not found? → look at the "colour shape" label next to each object on the camera screen; if it reads rectangle (5) or other (0), set Target 1 shape to Square or adjust ShapeTol in the SET menu.
  • Need 5 objects or the object count? → use mode 51 (object_count(); object 5 is in EV3 mode 2, +0.5 s).

Please note

  • What was verified on hardware (2026-11-03): on the official EV3 firmware all 13 camera modes were read value by value, negative values arrive as-is, and 5 re-plugs were each recognised as an infrared sensor in about 3 s; the EV3-G infrared Proximity / Beacon / Remote blocks read the camera, and the beacon heading is not clamped to ±25 (e.g. 54 and −36 come through unchanged); Pybricks v4 examples 1–4 passed. For ev3dev / EV3 MicroPython v2 we only check the mode table, with no hardware run: the camera's mode table (names, value counts, format, range, units) matches a real LEGO infrared sensor item by item, so the driver handles it as an infrared sensor.
  • KP/KD and area limits are suggestions; print the numbers first, then tune.

Next

Email for a quoteBuying Guide