Line follower
Follows the left edge of a dark line on a light surface, using a light sensor on input 3 pointing at the floor. A touch sensor on input 1 stops the robot.
The robot steers proportionally: the further the reading is from the target, halfway between the line and the floor, the harder it turns.
use nxt_rs::prelude::*;
/// Speed on a straight line.
const BASE: i32 = 35;
/// How hard to steer per percent of error.
const GAIN: i32 = 2;
fn main() -> nxt_rs::Result<()> {
let brick = Brick::usb_first().block()?;
let (left, right, light, stop) =
brick.take((B.motor(), C.motor(), S3.light(), S1.touch())).block()?;
// Calibrate: place the sensor over the line, then over the floor.
println!("put the sensor over the line and press enter");
std::io::stdin().read_line(&mut String::new()).ok();
let line = i32::from(light.read().block()?.get());
println!("now over the floor and press enter");
std::io::stdin().read_line(&mut String::new()).ok();
let floor = i32::from(light.read().block()?.get());
let target = (line + floor) / 2;
while !stop.read().block()? {
let error = i32::from(light.read().block()?.get()) - target;
left.run(Power::saturating(BASE + GAIN * error)).block()?;
right.run(Power::saturating(BASE - GAIN * error)).block()?;
}
brick.stop_all().block()?;
Ok(())
}
Tuning
- Increase
GAINif the robot loses the line in curves. Decrease it if the robot wobbles on straight lines. - Every loop iteration takes three round trips. Over USB that’s fast enough; over Bluetooth, use lower speeds.
Power::saturatingclamps the computed power to −100…100, so a large error can’t produce an invalid value.
Testing it
Swap Brick::usb_first() for a simulator
and feed it light readings with sim.set_sensor(InputPort::S3, raw, percent)
to check the steering logic.