Lesson 9 · Object Composition
Lesson 9 · Object Composition
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
LineTrackclass that stores aLineSensorand 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().
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.passmeans "do nothing." Thewhileloop's job here is just to keep checking the condition while the motors (already set above) keep spinning.
Why is there a time.sleep(0.5) at the start of turn_right(), before the while loop?
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.
- Build both classes and run the follow → turn → follow test on the circle-with-cross.
- Challenge: add a
turn_around()method that callsturn_right()twice.
Why can the main program be just a few method calls now?
Real-world connections
Composition is the default way large systems are assembled:
Systems of systems
A car has an engine, a transmission, a brake system — each its own module, coordinated together.
App components
An app screen has a camera object, a network object, a database object working in concert.
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
LineTrackhold? (ALineSensorand a drivetrain.) - Why the
time.sleep()in the turn methods? (To clear the cross before looking for the next line.)