RoboDoz3r

Python code and building instructions for the LEGO MINDSTORMS EV3 Official Fan Creations (31313).

RoboDoz3r
Image credit: LEGO

This robot bulldozer can be controlled using the IR Beacon or it can drive on it’s own, avoiding obstacles while clearing and pushing things with its bulldozer bucket.


This program requires LEGO® MINDSTORMS® EV3 MicroPython v2.0 downloadable at https://education.lego.com/en-us/support/mindstorms-ev3/python-for-ev3.

Control RoboDoz3r as follows:

  • RoboDoz3r starts with the User-Controlled Mode. You can switch between the User-Controlled Mode and the Autonomous Mode by pressing the Touch Sensor.

  • User-Controlled Mode:

    • Drive RoboDoz3r around according to instructions from Channel 1 of the IR Remote Control:
      • 2 Top/Up Buttons together: drive forward
      • 2 Bottom/Down Buttons together: drive backward
      • Top-Left/Red-Up and Bottom-Right/Blue-Down together: turn left on the spot
      • Top-Right/Blue-Up and Bottom-Left/Red-Down together: turn right on the spot
      • Top-Left/Red-Up: turn left forward
      • Top-Right/Blue-Up: turn right forward
      • Bottom-Left/Red-Down: turn left backward
      • Bottom-Right/Blue-Down: turn right backward
    • Use Channel 4 of the IR Remote Control to make RoboDoz3r raise the shovel by pressing either Up button, or lower the shovel by pressing either Down button
  • Autonomous Mode: RoboDoz3r drives around on his own to clean up small things but avoids big obstacles by reversing and turning

The code for the RoboDoz3r class is in robodoz3r.py as follows:

from pybricks.hubs import EV3Brick
from pybricks.ev3devices import Motor, TouchSensor
from pybricks.parameters import Button, Direction, Port

from rc_tank_util import RemoteControlledTank


class RoboDoz3r(RemoteControlledTank):
    WHEEL_DIAMETER = 24   # milimeters
    AXLE_TRACK = 100      # milimeters

    def __init__(
            self,
            left_motor_port: Port = Port.C, right_motor_port: Port = Port.B,
            shovel_motor_port: Port = Port.A,
            touch_sensor_port: Port = Port.S1,
            ir_sensor_port: Port = Port.S4,
            tank_drive_ir_beacon_channel: int = 1,
            shovel_control_ir_beacon_channel: int = 4):
        super().__init__(
            wheel_diameter=self.WHEEL_DIAMETER, axle_track=self.AXLE_TRACK,
            left_motor_port=left_motor_port, right_motor_port=right_motor_port,
            polarity='inversed',
            ir_sensor_port=ir_sensor_port,
            ir_beacon_channel=tank_drive_ir_beacon_channel)

        self.ev3_brick = EV3Brick()

        self.shovel_motor = Motor(port=shovel_motor_port,
                                  positive_direction=Direction.CLOCKWISE)

        self.touch_sensor = TouchSensor(port=touch_sensor_port)

        self.shovel_control_ir_beacon_channel = \
            shovel_control_ir_beacon_channel

    def raise_or_lower_shovel_by_ir_beacon(self):
        """
        If the channel 4 is selected on the IR remote
        then you can control raising and lowering the shovel on the RoboDoz3r.
        - Raise the shovel by either Up button
        - Raise the shovel by either Down button
        """
        ir_beacon_button_pressed = \
            set(self.ir_sensor.buttons(
                    channel=self.shovel_control_ir_beacon_channel))

        # raise the shovel
        if ir_beacon_button_pressed.intersection(
                {Button.LEFT_UP, Button.RIGHT_UP}):
            self.shovel_motor.run(speed=100)

        # lower the shovel
        elif ir_beacon_button_pressed.intersection(
                {Button.LEFT_DOWN, Button.RIGHT_DOWN}):
            self.shovel_motor.run(speed=-100)

        else:
            self.shovel_motor.hold()

RoboDoz3r uses a remote-controlled tank driving utility whose code is in rc_tank_util.py as follows:

from pybricks.ev3devices import Motor, InfraredSensor
from pybricks.robotics import DriveBase
from pybricks.parameters import Button, Direction, Port


class RemoteControlledTank:
    """
    This reusable mixin provides the capability of driving a robot
    with a Driving Base by the IR beacon
    """
    def __init__(
            self,
            wheel_diameter: float, axle_track: float,   # both in milimeters
            left_motor_port: Port = Port.B, right_motor_port: Port = Port.C,
            polarity: str = 'normal',
            ir_sensor_port: Port = Port.S4, ir_beacon_channel: int = 1):
        if polarity == 'normal':
            left_motor = Motor(port=left_motor_port,
                               positive_direction=Direction.CLOCKWISE)
            right_motor = Motor(port=right_motor_port,
                                positive_direction=Direction.CLOCKWISE)
        else:
            left_motor = Motor(port=left_motor_port,
                               positive_direction=Direction.COUNTERCLOCKWISE)
            right_motor = Motor(port=right_motor_port,
                                positive_direction=Direction.COUNTERCLOCKWISE)
        self.driver = DriveBase(left_motor=left_motor,
                                right_motor=right_motor,
                                wheel_diameter=wheel_diameter,
                                axle_track=axle_track)

        self.ir_sensor = InfraredSensor(port=ir_sensor_port)
        self.ir_beacon_channel = ir_beacon_channel

    def drive_by_ir_beacon(
            self,
            speed: float = 1000,    # mm/s
            turn_rate: float = 90   # rotational speed deg/s
            ):
        ir_beacon_button_pressed = \
            set(self.ir_sensor.buttons(channel=self.ir_beacon_channel))

        # forward
        if ir_beacon_button_pressed == {Button.LEFT_UP, Button.RIGHT_UP}:
            self.driver.drive(
                speed=speed,
                turn_rate=0)

        # backward
        elif ir_beacon_button_pressed == {Button.LEFT_DOWN, Button.RIGHT_DOWN}:
            self.driver.drive(
                speed=-speed,
                turn_rate=0)

        # turn left on the spot
        elif ir_beacon_button_pressed == {Button.LEFT_UP, Button.RIGHT_DOWN}:
            self.driver.drive(
                speed=0,
                turn_rate=-turn_rate)

        # turn right on the spot
        elif ir_beacon_button_pressed == {Button.RIGHT_UP, Button.LEFT_DOWN}:
            self.driver.drive(
                speed=0,
                turn_rate=turn_rate)

        # turn left forward
        elif ir_beacon_button_pressed == {Button.LEFT_UP}:
            self.driver.drive(
                speed=speed,
                turn_rate=-turn_rate)

        # turn right forward
        elif ir_beacon_button_pressed == {Button.RIGHT_UP}:
            self.driver.drive(
                speed=speed,
                turn_rate=turn_rate)

        # turn left backward
        elif ir_beacon_button_pressed == {Button.LEFT_DOWN}:
            self.driver.drive(
                speed=-speed,
                turn_rate=turn_rate)

        # turn right backward
        elif ir_beacon_button_pressed == {Button.RIGHT_DOWN}:
            self.driver.drive(
                speed=-speed,
                turn_rate=-turn_rate)

        # otherwise stop
        else:
            self.driver.stop()

The code for the main program is in main.py as follows:

#!/usr/bin/env pybricks-micropython


from pybricks.media.ev3dev import SoundFile
from pybricks.tools import wait

from time import time

from robodoz3r import RoboDoz3r


robodoz3r = RoboDoz3r()

robodoz3r.ev3_brick.screen.print('ROBODOZ3R')

robodoz3r.ev3_brick.speaker.play_file(SoundFile.MOTOR_START)

motor_idle_start_time = time()
while time() - motor_idle_start_time <= 2:
    robodoz3r.ev3_brick.speaker.play_file(SoundFile.MOTOR_IDLE)

while True:
    while not robodoz3r.touch_sensor.pressed():
        robodoz3r.raise_or_lower_shovel_by_ir_beacon()
        robodoz3r.drive_by_ir_beacon(speed=1000)
        wait(10)

    robodoz3r.ev3_brick.speaker.play_file(SoundFile.AIRBRAKE)

    while not robodoz3r.touch_sensor.pressed():
        if robodoz3r.ir_sensor.distance() < 50:
            robodoz3r.driver.stop()

            wait(1000)

            robodoz3r.driver.straight(distance=-100)

            robodoz3r.driver.turn(angle=90)

        else:
            robodoz3r.driver.drive(
                speed=500,
                turn_rate=0)

        wait(10)

    robodoz3r.ev3_brick.speaker.play_file(SoundFile.AIRBRAKE)


This project was submitted by The Lương-Phạm Family.