1rst Test
Object-Oriented Robot Management Documentation
Access the code files by clicking here.
💡 About
The main objective of this robotics test is to demonstrate our skills in object-oriented programming through the design of a flexible and extensible robot management system. This project illustrates the application of fundamental OOP principles: encapsulation, inheritance, and polymorphism.
Object-oriented programming allows for the creation of modular and maintainable systems.
-- Tekbot Robotics Team
Flexibility and Extensibility are ensured by a well-defined hierarchical architecture, combined with a clear separation of responsibilities between hardware components and robot actions.
The system uses an abstract Robot class as a common base, allowing the implementation of specialized subclasses for different types of actions (Forward, Backward, TurnLeft, TurnRight, Stop, ObstacleAvoidance).
Encapsulation is demonstrated by the use of private attributes and properties, while polymorphism is illustrated by the redefinition of the move() method in each subclass.
⭐ Main Features
- Robust object-oriented architecture
- Modular component system
- Integrated simulation for testing
- Error management and data validation
🏛 Architecture
Class Diagram

Explanation: Class Diagram
This diagram details the static software structure of our robot.
The Robot class acts as the parent class and central orchestrator. It owns instances of physical components through composition.
The component classes (Motor, Ultrasonic, ServoMotor) model the hardware. Each has clear responsibilities:
Motor: Manages the speed and direction of a motor.
Ultrasonic: Measures distance (distance()).
ServoMotor: Controls the orientation of a servomotor (write_angle()).
The behavioral subclasses (Forward, TurnRight, etc.) inherit from Robot and represent the different possible states or actions. Their main role is to implement a polymorphic version of the move() method, which translates a state (like "move forward") into concrete commands for the motors.
Use Case Diagram

Explanation: Use Case Diagram
This diagram presents the functional interactions between actors and the robotic system.
Actors: User: A human operator who initiates high-level movement commands (Forward, Stop). Environment: Represents external conditions, such as obstacles, detected by the robot. Use Cases: The main actions are Forward, Backward, TurnLeft, TurnRight, and Stop. The <Include> relationship is crucial: it means that the ObstacleAvoidance use case is systematically included in all movement actions. This ensures that the robot is constantly checking its environment to avoid collisions, making the system more robust and autonomous.
Sequence Diagram

Explanation: Sequence Diagram
This diagram illustrates the dynamic behavior of the system for a specific scenario: obstacle avoidance.
Scenario: The user presses a button to start the robot, which then moves forward.
Process:
Initialization: The User activates the Robot, which enters the Forward state and calls its move() method.
Detection loop: The robot enters a loop where it continuously requests the distance from its Ultrasonic sensor.
Decision making (alt):
If the distance is greater than 20, the robot continues to move forward (Forward.move()).
If the distance is less than 20, the robot stops (Stop.move()). It then uses its ServoMotor to look left and right, measuring the distance on each side.
Depending on the available space, it decides to enter the TurnLeft or TurnRight state and executes the corresponding movement.
End: The sequence ends when the user stops the robot.
This diagram is fundamental as it shows how objects from our different classes collaborate over time to accomplish a complex and autonomous task.
🔎 Implementation
Robot Class (Abstract Base)
Robot Class (Abstract Base)
The Robot class serves as the abstract base for all types of robots. It defines the common interface and manages the basic components.
from abc import ABC, abstractmethod
from typing import List
from component import Motor, ServoMotor, Ultrasonic
class Robot(ABC):
def __init__(
self,
motors_ids: List[str],
servo_id: str,
ultrasonic_id: str
):
"""Initialize the robot with motors, servo, and ultrasonic sensor."""
self._motors = [Motor(id) for id in motors_ids]
self._servo = ServoMotor(servo_id)
self._ultrasonic = Ultrasonic(ultrasonic_id)
@property
def servo(self) -> ServoMotor:
"""Get the servo motor."""
return self._servo
@servo.setter
def servo(self, servo: ServoMotor):
"""Set the servo motor."""
if not isinstance(servo, ServoMotor):
raise ValueError("Servo must be an instance of ServoMotor")
self._servo = servo
@property
def ultrasonic(self) -> Ultrasonic:
"""Get the ultrasonic sensor."""
return self._ultrasonic
@ultrasonic.setter
def ultrasonic(self, ultrasonic: Ultrasonic):
"""Set the ultrasonic sensor."""
if not isinstance(ultrasonic, Ultrasonic):
raise ValueError("Ultrasonic must be an instance of Ultrasonic")
self._ultrasonic = ultrasonic
@abstractmethod
def move(self, speed: int = 0):
"""Abstract method to move the robot."""
pass
Main features:
- Abstract class using the
ABCmodule - Encapsulation with protected attributes (
_) - Abstract method
move()to be implemented in subclasses - Composition with hardware components (motors, servo, ultrasonic)
Hardware Components
Hardware Components
The system uses three main types of components, each with appropriate encapsulation:
Motor Class
class Motor:
def __init__(self, identifier: str):
self._identifier = identifier
self._speed = 0
self._direction = "stop"
def move_forward(self, speed: int):
"""Move the motor forward with the specified speed."""
if not 0 <= speed <= 1023:
raise ValueError("Speed must be between 0 and 1023")
self._speed = speed
self._direction = "forward"
print(f"[SIMULATION MOTOR {self._identifier}] Moving forward with speed {speed}")
def move_backward(self, speed: int):
"""Move the motor backward with the specified speed."""
if not 0 <= speed <= 1023:
raise ValueError("Speed must be between 0 and 1023")
self._speed = speed
self._direction = "backward"
print(f"[SIMULATION MOTOR {self._identifier}] Moving backward with speed {speed}")
def stop(self):
"""Stop the motor."""
self._speed = 0
self._direction = "stop"
print(f"[SIMULATION MOTOR {self._identifier}] Stopped")
@property
def speed(self) -> int:
"""Get the current speed of the motor."""
return self._speed
@speed.setter
def speed(self, speed: int):
"""Set the speed of the motor."""
if not 0 <= speed <= 1023:
raise ValueError("Speed must be between 0 and 1023")
self._speed = speed
@property
def direction(self) -> str:
"""Get the current direction of the motor."""
return self._direction
@direction.setter
def direction(self, value: str) -> None:
"""Set the direction of the motor."""
valid_directions = ["forward", "backward", "stop"]
if value not in valid_directions:
raise ValueError(f"Direction must be one of {valid_directions}")
self._direction = value
ServoMotor Class
class ServoMotor:
def __init__(self, identifier: str):
self._identifier = identifier
self._angle = 0
def write_angle(self, degrees: Optional[float] = None, radians: Optional[float] = None):
"""Set the servo motor angle."""
if degrees is None and radians is None:
raise ValueError("An angle in degrees must be provided")
if degrees is None:
import math
degrees = math.degrees(radians)
self._angle = degrees % 360
print(f"[SIMULATION SERVO {self._identifier}] Angle set to {self._angle} degrees")
def stop(self):
"""Stop the servo motor."""
print(f"[SIMULATION SERVO {self._identifier}] Stopped")
@property
def angle(self) -> float:
"""Get the current angle of the servo motor."""
return self._angle
@angle.setter
def angle(self, degrees: float):
"""Set the angle of the motor."""
self._angle = degrees % 360
Ultrasonic Class
class Ultrasonic:
def __init__(self, identifier: str):
self._identifier = identifier
def distance(self) -> float:
"""Measure the simulated distance."""
distance = random.uniform(5, 100)
print(f"[SIMULATION ULTRASONIC {self._identifier}] Measured distance: {distance:.2f} cm")
return distance
Specialized Actions
Specialized Actions
Each action inherits from the Robot class and implements its own version of the move() method, demonstrating polymorphism.
Forward
The code below defines the Forward class, responsible for moving the robot straight ahead. It inherits from the abstract Robot class and individually controls the left and right motors. This action is integrated as the basic movement state, used in many navigation scenarios.
class Forward(Robot):
def __init__(
self,
left_motors_ids: List[str],
right_motors_ids: List[str],
servo_id: str,
ultrasonic_id: str
):
"""Initialize the Forward movement with left and right motors."""
super().__init__(left_motors_ids + right_motors_ids, servo_id, ultrasonic_id)
self.__left_motors = self._motors[:len(left_motors_ids)]
self.__right_motors = self._motors[len(left_motors_ids):]
@property
def left_motors(self) -> List[Motor]:
"""Get the left motors."""
return self.__left_motors
@left_motors.setter
def left_motors(self, motors: List[Motor]):
"""Set the left motors."""
if not motors or not all(isinstance(m, Motor) for m in motors):
raise ValueError("Left motors must be a non-empty list of Motor instances")
self.__left_motors = motors
@property
def right_motors(self) -> List[Motor]:
"""Get the right motors."""
return self.__right_motors
@right_motors.setter
def right_motors(self, motors: List[Motor]):
"""Set the right motors."""
if not motors or not all(isinstance(m, Motor) for m in motors):
raise ValueError("Right motors must be a non-empty list of Motor instances")
self.__right_motors = motors
@property
def servo(self) -> ServoMotor:
"""Get the servo motor."""
return self._servo
@property
def ultrasonic(self) -> Ultrasonic:
"""Get the ultrasonic sensor."""
return self._ultrasonic
def move(self, speed: int = 512):
"""Move forward in a straight line."""
if not 0 <= speed <= 1023:
raise ValueError("Speed must be between 0 and 1023")
for motor in self.__left_motors + self.__right_motors:
motor.move_forward(speed)
TurnLeft
The TurnLeft class allows the robot to make a left turn by reducing the speed of the left motors and increasing the speed of the right ones. It inherits from Robot and illustrates the system's polymorphism, with each specialized action adapting the move() method for specific behavior.
class TurnLeft(Robot):
def __init__(
self,
left_motors_ids: List[str],
right_motors_ids: List[str],
servo_id: str,
ultrasonic_id: str
):
"""Initialize the TurnLeft movement with left and right motors."""
super().__init__(left_motors_ids + right_motors_ids, servo_id, ultrasonic_id)
self.__left_motors = self._motors[:len(left_motors_ids)]
self.__right_motors = self._motors[len(left_motors_ids):]
@property
def left_motors(self) -> List[Motor]:
"""Get the left motors."""
return self.__left_motors
@left_motors.setter
def left_motors(self, motors: List[Motor]):
"""Set the left motors."""
if not motors or not all(isinstance(m, Motor) for m in motors):
raise ValueError("Left motors must be a non-empty list of Motor instances")
self.__left_motors = motors
@property
def right_motors(self) -> List[Motor]:
"""Get the right motors."""
return self.__right_motors
@right_motors.setter
def right_motors(self, motors: List[Motor]):
"""Set the right motors."""
if not motors or not all(isinstance(m, Motor) for m in motors):
raise ValueError("Right motors must be a non-empty list of Motor instances")
self.__right_motors = motors
@property
def servo(self) -> ServoMotor:
"""Get the servo motor."""
return self._servo
@property
def ultrasonic(self) -> Ultrasonic:
"""Get the ultrasonic sensor."""
return self._ultrasonic
def move(self, speed: int = 512):
"""Turn left."""
if not 0 <= speed <= 1023:
raise ValueError("Speed must be between 0 and 1023")
for motor in self.__left_motors:
motor.move_forward(speed // 3)
for motor in self.__right_motors:
motor.move_forward(speed)
ObstacleAvoidance
The ObstacleAvoidance class implements obstacle avoidance logic. It uses the ultrasonic sensor to measure distances and the servo motor to orient the sensor. Based on the measurements, it decides to turn or stop, ensuring the robot's safety and autonomy in its environment.
class ObstacleAvoidance(Robot):
def __init__(
self,
left_motors_ids: List[str],
right_motors_ids: List[str],
servo_id: str,
ultrasonic_id: str
):
"""Initialize the ObstacleAvoidance movement with left and right motors."""
super().__init__(left_motors_ids + right_motors_ids, servo_id, ultrasonic_id)
self.__left_motors = self._motors[:len(left_motors_ids)]
self.__right_motors = self._motors[len(left_motors_ids):]
@property
def left_motors(self) -> List[Motor]:
"""Get the left motors."""
return self.__left_motors
@left_motors.setter
def left_motors(self, motors: List[Motor]):
"""Set the left motors."""
if not motors or not all(isinstance(m, Motor) for m in motors):
raise ValueError("Left motors must be a non-empty list of Motor instances")
self.__left_motors = motors
@property
def right_motors(self) -> List[Motor]:
"""Get the right motors."""
return self.__right_motors
@right_motors.setter
def right_motors(self, motors: List[Motor]):
"""Set the right motors."""
if not motors or not all(isinstance(m, Motor) for m in motors):
raise ValueError("Right motors must be a non-empty list of Motor instances")
self.__right_motors = motors
@property
def servo(self) -> ServoMotor:
"""Get the servo motor."""
return self._servo
@property
def ultrasonic(self) -> Ultrasonic:
"""Get the ultrasonic sensor."""
return self._ultrasonic
def avoid_obstacles(self, speed: int = 512, threshold_distance: float = 20.0):
"""Implement obstacle avoidance logic."""
while True:
try:
distance = self.ultrasonic.distance()
print(f"Measured distance: {distance:.2f} cm")
if distance < threshold_distance:
print("Obstacle detected!")
for motor in self._motors:
motor.stop()
self.servo.write_angle(90)
time.sleep(0.5)
left_distance = self.ultrasonic.distance()
print(f"Left distance: {left_distance:.2f} cm")
self.servo.write_angle(-90)
time.sleep(0.5)
right_distance = self.ultrasonic.distance()
print(f"Right distance: {right_distance:.2f} cm")
self.servo.write_angle(0)
time.sleep(0.5)
if left_distance > right_distance and left_distance > threshold_distance:
print("Turning left")
for motor in self.__left_motors:
motor.move_forward(speed // 3)
for motor in self.__right_motors:
motor.move_forward(speed)
time.sleep(1)
elif right_distance > threshold_distance:
print("Turning right")
for motor in self.__left_motors:
motor.move_forward(speed)
for motor in self.__right_motors:
motor.move_forward(speed // 3)
time.sleep(1)
else:
print("No clear path, stopping")
for motor in self._motors:
motor.stop()
break
else:
print("Path clear, moving forward")
for motor in self.__left_motors + self.__right_motors:
motor.move_forward(speed)
time.sleep(0.1)
except Exception as e:
print(f"Error: {e}")
for motor in self._motors:
motor.stop()
break
def move(self, speed: int = 512):
"""Start obstacle avoidance."""
self.avoid_obstacles(speed, threshold_distance=20.0)
Simulation System
Simulation System
For testing without physical hardware, the system includes the following function:
import time
from action import Forward, Backward, TurnLeft, TurnRight, Stop, ObstacleAvoidance
from typing import List
def main():
"""Run simulation for all robot movements."""
left_motors_ids = ["motor_g1", "motor_g2"]
right_motors_ids = ["motor_d1", "motor_d2"]
servo_id = "servo_1"
ultrasonic_id = "ultrasonic_1"
print("=== Testing Forward ===")
robot_forward = Forward(left_motors_ids, right_motors_ids, servo_id, ultrasonic_id)
robot_forward.move(speed=512)
time.sleep(1)
print("\n=== Testing Backward ===")
robot_backward = Backward(left_motors_ids, right_motors_ids, servo_id, ultrasonic_id)
robot_backward.move(speed=512)
time.sleep(1)
print("\n=== Testing TurnLeft ===")
robot_left = TurnLeft(left_motors_ids, right_motors_ids, servo_id, ultrasonic_id)
robot_left.move(speed=512)
time.sleep(1)
print("\n=== Testing TurnRight ===")
robot_right = TurnRight(left_motors_ids, right_motors_ids, servo_id, ultrasonic_id)
robot_right.move(speed=512)
time.sleep(1)
print("\n=== Testing Stop ===")
robot_stop = Stop(left_motors_ids, right_motors_ids, servo_id, ultrasonic_id)
robot_stop.move()
print("\n=== Testing ObstacleAvoidance ===")
robot_obstacle = ObstacleAvoidance(left_motors_ids, right_motors_ids, servo_id, ultrasonic_id)
robot_obstacle.move(speed=512)
time.sleep(5)
if __name__ == "__main__":
main()
Demonstrated OOP Concepts
Demonstrated OOP Concepts
1. Encapsulation
- Private attributes: Use of double underscore (
__) for private attributes - Properties: Use of
@propertyand@setterto control access - Validation: Value control in setters
- Protected methods: Use of single underscore (
_) for internal methods
2. Inheritance
- Abstract class:
Robotas an abstract base class withABC - Multiple inheritance: All actions inherit from
Robot - Parent constructor call: Use of
super().__init__() - Code reuse: Sharing of common attributes and methods
3. Polymorphism
- Abstract method:
move()defined in the base class - Redefinition: Each subclass implements
move()differently - Uniform interface: All robots can be used in the same way
- Specialized behaviors: Each action has its own logic
4. Composition
- Object aggregation: Robot composes motors, servo, and ultrasonic
- Separation of responsibilities: Each component has its specific function
- Modularity: Facilitates maintenance and extension
💻 Execution
Development Mode
🧰 Tech Stack
Main Language
- Python 3.8+ - Main programming language
- MicroPython - For microcontroller execution
Python Modules
- abc - Abstract classes
- machine - MicroPython hardware interface
- time - Time management
- typing - Type annotations
- unittest - Unit testing
- random - Data simulation
Architecture
- Object-Oriented Programming - Main paradigm
- Strategy Pattern - Different robot actions
- Composition Pattern - Component assembly
- Simulation - Testing without hardware
🧪 Validation
Success Criteria
✅ Robot class correctly designed with relevant attributes
✅ Encapsulation with getters/setters and appropriate visibility levels
✅ Well-structured inheritance with at least six functional subclasses
✅ Polymorphism demonstrated by redefining the move() method
✅ Complete UML documentation with class, use case, and sequence diagrams
✅ Clean code and well-commented with modular architecture
Demonstration of OOP Concepts
- Encapsulation: Private (
__), protected (_) attributes and properties - Inheritance: Abstract
Robotclass and specialized subclasses - Polymorphism:
move()method redefined in each subclass - Abstraction: Common interface for all robot types
- Composition: Assembly of hardware components in the robot
IT Team Member:
- CHATIGRE Larissa
- COMLAN Ifè Léonce
- AGBODJA Marzoukath
Test: Test 1 - Tekbot Robotics Challenges
Language: Python 3.8+ / MicroPython
Date: 2025