Skip to main content

Lesson 9 · Object Composition

Module 2 · Line Tracking

Lesson 9 · Object Composition

60 minClasses using classesLineTrack
👩‍🏫

Teacher mode is on. Toggle it off (bottom-right) to preview the student view.

Your LineSensor handles sensing. But the robot also has to drive. In this lesson you'll build a second class, LineTrack, that contains a LineSensor and uses it to follow the line and turn at intersections. One class holding another is called object composition — and it's how real programs are built.

Learning Objectives

By the end of this lesson you will be able to:

  • Explain object composition — one class holding another ("has-a")
  • Build a LineTrack class that stores a LineSensor and a drivetrain
  • Write methods that coordinate sensing and driving
  • Package line-following and turning into clean, reusable methods

"Has-a": composition

A car has an engine. The car uses the engine to move, but the engine doesn't know about the steering wheel — each part has its own job. Code works the same way: LineTrack will have a LineSensor (for sensing) and have a drivetrain (for driving), and coordinate them.

from XRPLib.differential_drive import DifferentialDrive
import time

class LineTrack:
def __init__(self):
self.sensor = LineSensor() # has-a LineSensor
self.drivetrain = DifferentialDrive.get_default_differential_drive() # has-a drivetrain
self.base_effort = 0.4
self.kp = 0.5

When you create a LineTrack, it builds its own LineSensor inside __init__ — you don't make one separately. Now LineTrack can ask its sensor for data any time with self.sensor.get_error().

Knowledge Check

What does 'object composition' mean?

Following the line, cleanly

The core method is the Lesson 5–7 control loop, now tucked inside LineTrack and using its own sensor and settings:

def track_until_cross(self):
while not self.sensor.is_at_cross():
error = self.sensor.get_error()
correction = error * self.kp
self.drivetrain.arcade(self.base_effort, -correction)
self.drivetrain.stop()

while not self.sensor.is_at_cross(): keeps following until both sensors hit the cross — then it stops. All the messy sensor math is hidden behind self.sensor.get_error() and self.sensor.is_at_cross().

Turning to find the line again

At a cross, the robot spins until a sensor finds the next line. The trick is a brief pause first, so it clears the intersection before it starts looking:

def turn_right(self):
self.drivetrain.set_effort(0.3, -0.3) # spin clockwise
time.sleep(0.5) # drive past the cross first
while self.sensor.is_off_line():
pass # keep spinning until a line appears
self.drivetrain.stop()

turn_left() is identical with the motor efforts swapped. Two new bits to notice:

  • time.sleep(0.5) gives the robot time to physically rotate off the cross — without it, the sensor is still on the intersection and the turn ends instantly.
  • pass means "do nothing." The while loop's job here is just to keep checking the condition while the motors (already set above) keep spinning.
Knowledge Check

Why is there a time.sleep(0.5) at the start of turn_right(), before the while loop?

Knowledge Check

What does the `pass` inside the while loop do?

Activity · Three lines to drive a course

With both classes done, the main program is astonishingly short:

board = Board.get_default_board()
tracker = LineTrack()

board.wait_for_button()

tracker.track_until_cross()
tracker.turn_right()
tracker.track_until_cross()
print("Done!")

All the sensor reading and motor control is hidden inside the classes. Compare that to the wall of code this was back in Lesson 7 — that's the payoff of good class design.

  1. Build both classes and run the follow → turn → follow test on the circle-with-cross.
  2. Challenge: add a turn_around() method that calls turn_right() twice.
Knowledge Check

Why can the main program be just a few method calls now?

Real-world connections

Composition is the default way large systems are assembled:

Cars

Systems of systems

A car has an engine, a transmission, a brake system — each its own module, coordinated together.

Phones

App components

An app screen has a camera object, a network object, a database object working in concert.

Robots

Skill stacks

Complex robots stack modules — perception, planning, motion — each a class the others call.

Wrap-up

  • What is composition, in one phrase? ("Has-a" — one object holds another.)
  • What two objects does LineTrack hold? (A LineSensor and a drivetrain.)
  • Why the time.sleep() in the turn methods? (To clear the cross before looking for the next line.)

Resources