Start the simulation. Write a program that connects to the simulation, prints Hello car! and closes the connection.
Run the program once more while the simulation is closed and look at what it prints.
The first job of every program is to connect. If sim_connect() does not return 0, the simulation is not running; the program then ends with 1 and the library prints "Start the simulation first". Before the program ends, sim_disconnect() closes the connection.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1; // 0 = connected
printf("Hello car!\n");
sim_disconnect();
return 0;
}
from otonom import *
if sim_connect() != 0: # 0 = connected
exit(1)
print("Hello car!")
sim_disconnect()
Drive the car forward for 2 seconds and stop it. The car should move up the track a little and stop without hitting a line.
drive() only writes a command and returns at once; the car keeps going until we write DRIVE_STOP. That is why we wait 2 seconds between the two commands. In 2 seconds the car moves about 64 units.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
drive(DRIVE_FORWARD); // start moving
Sleep(2000); // keep going for 2 s
drive(DRIVE_STOP); // stop
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
drive(DRIVE_FORWARD) # start moving
time.sleep(2) # keep going for 2 s
drive(DRIVE_STOP) # stop
sim_disconnect()
Drive the car forward for 2 seconds, stop it for 1 second, then drive backward for 2 seconds and stop. The car should stop close to where it started.
The commands run one after another from top to bottom. Forward and backward speeds are equal, so 2 seconds forward + 2 seconds backward brings the car back to where it started.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
drive(DRIVE_FORWARD);
Sleep(2000);
drive(DRIVE_STOP);
Sleep(1000);
drive(DRIVE_BACKWARD);
Sleep(2000);
drive(DRIVE_STOP);
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
drive(DRIVE_FORWARD)
time.sleep(2)
drive(DRIVE_STOP)
time.sleep(1)
drive(DRIVE_BACKWARD)
time.sleep(2)
drive(DRIVE_STOP)
sim_disconnect()
Open the Manual Control panel from the menu. While the car is stopped, turn the steering 30° to the left, wait 2 seconds, turn it 30° to the right, wait 2 seconds and straighten it (0°).
You should see the steering slider and the angle value in the panel change. Does the car move?
steer() only sets the steering angle; it does not run the motor. So the car stays where it is; the angle only matters while the car is moving. Positive values turn left, negative values turn right.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
steer(30); // 30 degrees left
Sleep(2000);
steer(-30); // 30 degrees right
Sleep(2000);
steer(0); // straight
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
steer(30) # 30 degrees left
time.sleep(2)
steer(-30) # 30 degrees right
time.sleep(2)
steer(0) # straight
sim_disconnect()
Turn the steering 30° to the left, then drive the car forward for 3 seconds and stop it. At the end straighten the steering.
The car should turn left on an arc and should not hit a line.
The steering angle and the drive command together decide the path of the car. With the steering at 30° left the car drives on part of a circle; in 3 seconds it turns about 45°.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
steer(30); // wheels to the left
drive(DRIVE_FORWARD);
Sleep(3000);
drive(DRIVE_STOP);
steer(0); // straighten
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
steer(30) # wheels to the left
drive(DRIVE_FORWARD)
time.sleep(3)
drive(DRIVE_STOP)
steer(0) # straighten
sim_disconnect()
Drive the car in this order:
1 second straight, 2 seconds 20° left, 2 seconds 20° right, 1 second straight, then stop.
The car should draw a path that looks like the letter S.
The motor is started once and then only the steering changes. Each Sleep sets the length of one part. The program is a list of commands written in time order; this is called a timed sequence.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
steer(0);
drive(DRIVE_FORWARD);
Sleep(1000); // straight
steer(20);
Sleep(2000); // left
steer(-20);
Sleep(2000); // right
steer(0);
Sleep(1000); // straight
drive(DRIVE_STOP);
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
steer(0)
drive(DRIVE_FORWARD)
time.sleep(1) # straight
steer(20)
time.sleep(2) # left
steer(-20)
time.sleep(2) # right
steer(0)
time.sleep(1) # straight
drive(DRIVE_STOP)
sim_disconnect()
Press Restart Sim. Read the front (90°) and right (0°) lidar values and print them:
Front: ... and Right: ...
You should see about 15 for the right side and 0 for the front. What does 0 in front mean?
For each angle the lidar gives the distance to the nearest line. The right line is 15 units away. In front there is no line within 100 units, so the value is 0: 0 does not mean "very close", it means "I see nothing".
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int front = lidar_read(90);
int right = lidar_read(0);
printf("Front: %d\n", front);
printf("Right: %d\n", right);
sim_disconnect();
return 0;
}
from otonom import *
if sim_connect() != 0:
exit(1)
front = lidar_read(90)
right = lidar_read(0)
print("Front:", front)
print("Right:", right)
sim_disconnect()
Read and print the right (0°), front (90°), left (180°) and back (270°) distances. Then add the right and left distances to find the width of the road and print it as Lane width: ....
At the start position you should see about 15, 0, 52, 0 and a width of 67.
Read the lidar distance every 10 degrees from the right of the car to its left (0° to 180°) and print one angle per line, for example 40 deg: 19.
At which angles do you see 0? Where do these angles point?
The loop variable holds the angle and grows by 10 each time. The right line is close, so small angles show small numbers; around 90°, which points forward, the road is longer than 100 units, so 0 is read.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
for (int angle = 0; angle <= 180; angle += 10)
{
printf("%d deg: %d\n", angle, lidar_read(angle));
}
sim_disconnect();
return 0;
}
from otonom import *
if sim_connect() != 0:
exit(1)
for angle in range(0, 181, 10):
print(angle, "deg:", lidar_read(angle))
sim_disconnect()
Turn the steering 25° to the left and drive the car forward. After 1 second print drive_read() and steer_read() (you should see 1 and 25).
Then stop the car, straighten the steering and print the two values again (0 and 0).
The read functions give the current state in the simulation. These values are the commands we wrote, but the simulation can change them too: for example it stops the car on a collision (Task 13). Changes made in the Manual Control panel also show up here.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
steer(25);
drive(DRIVE_FORWARD);
Sleep(1000);
printf("Drive: %d Steer: %d\n", drive_read(), steer_read());
drive(DRIVE_STOP);
steer(0);
printf("Drive: %d Steer: %d\n", drive_read(), steer_read());
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
steer(25)
drive(DRIVE_FORWARD)
time.sleep(1)
print("Drive:", drive_read(), " Steer:", steer_read())
drive(DRIVE_STOP)
steer(0)
print("Drive:", drive_read(), " Steer:", steer_read())
sim_disconnect()
Read the right (0°) and left (180°) distances. If the right is closer print Right line is closer, if the left is closer print Left line is closer, and if they are equal print Centered.
At the start the right is closer. Then drive the car a bit to the left with the Manual Control panel, stop it and run the program again.
A smaller distance means a closer line. if runs the block of the first true condition; if none is true, else runs. This comparison is the base of keeping the car in the middle of the road later.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int right = lidar_read(0);
int left = lidar_read(180);
printf("Right: %d Left: %d\n", right, left);
if (right < left)
printf("Right line is closer\n");
else if (left < right)
printf("Left line is closer\n");
else
printf("Centered\n");
sim_disconnect();
return 0;
}
from otonom import *
if sim_connect() != 0:
exit(1)
right = lidar_read(0)
left = lidar_read(180)
print("Right:", right, " Left:", left)
if right < left:
print("Right line is closer")
elif left < right:
print("Left line is closer")
else:
print("Centered")
sim_disconnect()
Drive the car straight for 9.5 seconds and stop it. Then read the front distance and print:
Nothing ahead (more than 100) if it is 0,
Line ahead is close: ... if it is less than 50,
otherwise Line ahead: ....
What would happen if the program did not check for 0 first?
First print collision_read() (it should be 0). Then drive the car straight forward and wait 12 seconds. After the wait print collision_read() and drive_read().
At about 10 seconds the car hits the outer line of the top curve. Which values do you see? Why did the car stop?
On a collision the simulation itself sets the drive command to 0 and the collision flag to 1; that is why collision_read() is 1 and drive_read() is 0. The flag goes back to 0 when the car moves again. A collision only happens when driving forward with a line closer than 25 units in the 66°–114° area in front; lines at the side do not count.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
printf("Collision: %d\n", collision_read());
drive(DRIVE_FORWARD);
Sleep(12000);
printf("Collision: %d\n", collision_read());
printf("Drive: %d\n", drive_read());
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
print("Collision:", collision_read())
drive(DRIVE_FORWARD)
time.sleep(12)
print("Collision:", collision_read())
print("Drive:", drive_read())
sim_disconnect()
Drive the car straight forward. Wait until the front distance is greater than 0 and less than 80, then stop the car and print the last front distance.
The car should stop after about 9.5 seconds without crashing. Using Task 13, explain why we use 80 and not 25.
The loop keeps going while the condition is true (nothing ahead, or the line is still far). The outer line comes first to the front right of the car (around 66°); when the 90° ray sees 80, the line on the front right is about 30 units away. If we waited for 25, the car would already have crashed.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
drive(DRIVE_FORWARD);
int front = lidar_read(90);
while (front == 0 || front >= 80) // nothing seen or still far
{
Sleep(20);
front = lidar_read(90);
}
drive(DRIVE_STOP);
printf("Stopped. Front: %d\n", front);
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
drive(DRIVE_FORWARD)
front = lidar_read(90)
while front == 0 or front >= 80: # nothing seen or still far
time.sleep(0.02)
front = lidar_read(90)
drive(DRIVE_STOP)
print("Stopped. Front:", front)
sim_disconnect()
Change Task 14: wait 100 ms in the loop and count how many times the loop runs. When the car stops print Stopped after ... s (counter × 0.1).
You should see about 9.5 seconds. Run the same program a few times (Restart Sim each time): is the result the same?
Each loop round takes about 0.1 seconds, so rounds × 0.1 gives the time. Because the readings are 100 ms apart, the car can stop a few units further each time; the result can change by ±0.1 seconds.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int steps = 0;
drive(DRIVE_FORWARD);
int front = lidar_read(90);
while (front == 0 || front >= 80)
{
Sleep(100);
steps++; // one more 0.1 s
front = lidar_read(90);
}
drive(DRIVE_STOP);
printf("Stopped after %.1f s\n", steps * 0.1);
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
steps = 0
drive(DRIVE_FORWARD)
front = lidar_read(90)
while front == 0 or front >= 80:
time.sleep(0.1)
steps += 1 # one more 0.1 s
front = lidar_read(90)
drive(DRIVE_STOP)
print(f"Stopped after {steps * 0.1:.1f} s")
sim_disconnect()
Drive the car forward and in a loop read the right (0°) and left (180°) distances:
if left is more than 5 bigger than right turn 15° left, if right is more than 5 bigger than left turn 15° right, otherwise go straight.
The loop should end when collision_read() is 1 and print Crash!. Wait 50 ms in the loop.
The car moves to the middle on the straight. But what happens in the curve? Watch where it crashes.
In Task 16 use the diagonal rays instead of 0° and 180°: front right 45°, front left 135°.
This time there are only two cases: if the front left is farther turn 20° left, otherwise 20° right.
The car should zigzag through the curves and keep going around. Stop the program with Ctrl+C and the car with the Stop button.
The 45° and 135° rays see the road in front of the car. In a curve the outer line comes closer on the front right and the inner line moves away on the front left; the car sees this and starts turning before the curve. On this track these two rays always see a line (never 0). With only two cases the steering keeps going left-right; one lap takes about 2 minutes.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
drive(DRIVE_FORWARD);
while (1) // Ctrl+C to stop
{
int right = lidar_read(45); // front right
int left = lidar_read(135); // front left
if (left > right)
steer(20);
else
steer(-20);
Sleep(50);
}
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
drive(DRIVE_FORWARD)
while True: # Ctrl+C to stop
right = lidar_read(45) # front right
left = lidar_read(135) # front left
if left > right:
steer(20)
else:
steer(-20)
time.sleep(0.05)
Add a third case to Task 17: if the difference between front left and front right is 5 or less, go straight (0°).
The car should drive on the straights without zigzag. Time one lap with a stopwatch: it should be about 72 seconds (about 2 minutes in Task 17).
Not moving the steering at all for small differences (a dead zone) stops the zigzag. In this simulation the car covers less ground while turning than while going straight, and every time the steering angle changes the car does not move in that step. So fewer steering moves mean a faster lap.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
drive(DRIVE_FORWARD);
while (1) // Ctrl+C to stop
{
int right = lidar_read(45);
int left = lidar_read(135);
if (left - right > 5)
steer(20);
else if (right - left > 5)
steer(-20);
else
steer(0); // small difference: straight
Sleep(50);
}
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
drive(DRIVE_FORWARD)
while True: # Ctrl+C to stop
right = lidar_read(45)
left = lidar_read(135)
if left - right > 5:
steer(20)
elif right - left > 5:
steer(-20)
else:
steer(0) # small difference: straight
time.sleep(0.05)
Run the driver of Task 18 for 600 rounds (about 30 seconds) instead of forever. Count which decision is made in each round: left, right, straight.
At the end stop the car and print the three numbers. In 30 seconds the car passes the straight and most of the top curve; which number is the biggest, and why?
All curves on this track turn left, so the left count is clearly the biggest. The straight count shows the time on the straight, and the right count shows small corrections. Seeing with numbers what your program does helps a lot when looking for bugs.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int lefts = 0, rights = 0, straights = 0;
drive(DRIVE_FORWARD);
for (int i = 0; i < 600; i++)
{
int right = lidar_read(45);
int left = lidar_read(135);
if (left - right > 5) { steer(20); lefts++; }
else if (right - left > 5) { steer(-20); rights++; }
else { steer(0); straights++; }
Sleep(50);
}
drive(DRIVE_STOP);
steer(0);
printf("Left: %d Right: %d Straight: %d\n", lefts, rights, straights);
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
lefts = rights = straights = 0
drive(DRIVE_FORWARD)
for i in range(600):
right = lidar_read(45)
left = lidar_read(135)
if left - right > 5:
steer(20)
lefts += 1
elif right - left > 5:
steer(-20)
rights += 1
else:
steer(0)
straights += 1
time.sleep(0.05)
drive(DRIVE_STOP)
steer(0)
print("Left:", lefts, " Right:", rights, " Straight:", straights)
sim_disconnect()
Rewrite Task 18 with two functions:
distance(angle): reads the lidar and returns 100 if it gets 0 (0 = farther than 100).
follow_lane(): gets the 45° and 135° distances with distance() and sets the steering as in Task 18.
Only follow_lane() and the wait should stay in the main loop. The car should drive as in Task 18.
Functions let us write a job once and use it everywhere by name. distance() solves the zero trap (Task 12) in one place: "nothing" acts like the farthest distance. We will reuse these two functions in the next tasks.
#include "otonom.h"
// Lidar distance; 0 (nothing within 100) is returned as 100
int distance(int angle)
{
int d = lidar_read(angle);
if (d == 0) return 100;
return d;
}
// Steer towards the side with more room
void follow_lane(void)
{
int right = distance(45);
int left = distance(135);
if (left - right > 5) steer(20);
else if (right - left > 5) steer(-20);
else steer(0);
}
int main(void)
{
if (sim_connect() != 0) return 1;
drive(DRIVE_FORWARD);
while (1) // Ctrl+C to stop
{
follow_lane();
Sleep(50);
}
}
from otonom import *
import time
def distance(angle):
"""Lidar distance; 0 (nothing within 100) is returned as 100."""
d = lidar_read(angle)
if d == 0:
return 100
return d
def follow_lane():
"""Steer towards the side with more room."""
right = distance(45)
left = distance(135)
if left - right > 5:
steer(20)
elif right - left > 5:
steer(-20)
else:
steer(0)
if sim_connect() != 0:
exit(1)
drive(DRIVE_FORWARD)
while True: # Ctrl+C to stop
follow_lane()
time.sleep(0.05)
Read all rays from the right of the car to its left (0°–180°, every 2 degrees, 91 values) into an array. Find the smallest non-zero distance in the array and its angle, print it like Closest: 0 deg, 15 and add (right) if the angle is less than 90 or (left) if it is more.
Repeat this every half second. While the program runs, drive the car with the Manual Control panel and watch how the values change.
First all readings go into the array, then the array is walked once while the smallest value and its place are kept. 0 values are skipped because 0 means "none", not "close". At the start the closest line is on the right (0°, 15 units). This program does not drive the car, it only watches, so driving by hand is safe.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int scan[91]; // scan[i] = distance at 2*i degrees
while (1) // Ctrl+C to stop
{
for (int i = 0; i < 91; i++)
scan[i] = lidar_read(2 * i);
int best = 101, best_angle = -1;
for (int i = 0; i < 91; i++)
{
if (scan[i] > 0 && scan[i] < best)
{
best = scan[i];
best_angle = 2 * i;
}
}
if (best_angle < 0)
printf("No line within 100\n");
else
printf("Closest: %d deg, %d %s\n", best_angle, best,
best_angle < 90 ? "(right)" : "(left)");
Sleep(500);
}
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
while True: # Ctrl+C to stop
scan = [] # scan[i] = distance at 2*i degrees
for i in range(91):
scan.append(lidar_read(2 * i))
best, best_angle = 101, -1
for i in range(91):
if 0 < scan[i] < best:
best = scan[i]
best_angle = 2 * i
if best_angle < 0:
print("No line within 100")
else:
side = "(right)" if best_angle < 90 else "(left)"
print(f"Closest: {best_angle} deg, {best} {side}")
time.sleep(0.5)
Instead of trusting one ray, use the average of several rays. Write a function average(from, to) that reads the angles between from and to every 2 degrees with distance() and returns their average.
In the driver of Task 20 use average(30, 60) for the front right and average(120, 150) for the front left. The car should still go around the track.
Until now the steering was only -20, 0 or +20. Now make the angle proportional to the error:
error = front left (135°) − front right (45°), angle = 0.5 × error.
Write a function limit() that keeps the angle between -60 and +60. The car should go around smoothly without zigzag.
In proportional (P) control the correction is proportional to the size of the error; 0.5 is the gain. The path is smooth, but in this simulation a lap takes longer (about 105 seconds): the angle changes a little in almost every round, and the car does not move in a step where the steering angle changes. Try changing the gain (0.5) and see the effect.
#include "otonom.h"
int distance(int angle)
{
int d = lidar_read(angle);
if (d == 0) return 100;
return d;
}
// Keep the value between -max and +max
int limit(int value, int max)
{
if (value > max) return max;
if (value < -max) return -max;
return value;
}
int main(void)
{
if (sim_connect() != 0) return 1;
drive(DRIVE_FORWARD);
while (1) // Ctrl+C to stop
{
int error = distance(135) - distance(45);
int angle = (int)(0.5 * error); // gain = 0.5
steer(limit(angle, 60));
Sleep(50);
}
}
from otonom import *
import time
def distance(angle):
d = lidar_read(angle)
if d == 0:
return 100
return d
def limit(value, max_value):
"""Keep the value between -max_value and +max_value."""
if value > max_value:
return max_value
if value < -max_value:
return -max_value
return value
if sim_connect() != 0:
exit(1)
drive(DRIVE_FORWARD)
while True: # Ctrl+C to stop
error = distance(135) - distance(45)
angle = int(0.5 * error) # gain = 0.5
steer(limit(angle, 60))
time.sleep(0.05)
In Task 23, instead of using the error directly, use the average of the last 4 errors. Keep the last 4 errors in an array of 4 elements; in every round write the new error over the oldest one.
The steering should change more calmly and the lap should get a few seconds shorter (about 95 instead of 105 seconds).
The side-ray driver of Task 16 crashed in the curve. Add a recovery behavior: if collision_read() is 1, increase the crash count, turn the steering 30° right (-30), go backward for 1 second and then continue driving forward.
The program should run for 60 seconds (1200 rounds × 50 ms) or stop at 5 crashes; at the end stop the car and print the crash count. Can the car pass the curve, even with crashes?
On a collision the simulation stops the car; even if forward is given again, the line is still closer than 25 units, so the car stops again at once. That is why it first backs up; the collision flag becomes 0 when the car moves. Backing up with the steering turned right turns the nose in the direction of the curve (left); in our test the car crashed once, backed up and then passed the top curve. The driver is still weak, but a program that can get out of an error is much more useful than one that gets stuck.
#include "otonom.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int crashes = 0;
drive(DRIVE_FORWARD);
for (int i = 0; i < 1200 && crashes < 5; i++)
{
if (collision_read() == 1)
{
crashes++;
printf("Crash %d, backing up\n", crashes);
steer(-30); // nose swings left while backing
drive(DRIVE_BACKWARD);
Sleep(1000);
drive(DRIVE_FORWARD);
}
int right = lidar_read(0);
int left = lidar_read(180);
if (left - right > 5) steer(15);
else if (right - left > 5) steer(-15);
else steer(0);
Sleep(50);
}
drive(DRIVE_STOP);
steer(0);
printf("Crashes: %d\n", crashes);
sim_disconnect();
return 0;
}
from otonom import *
import time
if sim_connect() != 0:
exit(1)
crashes = 0
drive(DRIVE_FORWARD)
for i in range(1200):
if crashes >= 5:
break
if collision_read() == 1:
crashes += 1
print("Crash", crashes, "backing up")
steer(-30) # nose swings left while backing
drive(DRIVE_BACKWARD)
time.sleep(1)
drive(DRIVE_FORWARD)
right = lidar_read(0)
left = lidar_read(180)
if left - right > 5:
steer(15)
elif right - left > 5:
steer(-15)
else:
steer(0)
time.sleep(0.05)
drive(DRIVE_STOP)
steer(0)
print("Crashes:", crashes)
sim_disconnect()
The 76° ray, which looks to the front right of the car, reads 0 on a straight (the line is farther than 100) and sees the outer line in a curve.
While running the driver of Task 20, use this ray to track whether the car is in a curve or on a straight, and print CURVE or STRAIGHT only when the state changes.
To ignore sudden single readings: print CURVE as soon as 76° > 0, but for STRAIGHT the 76° ray must read 0 for 20 readings in a row (about 1 second).
In one lap you should see two CURVE and two STRAIGHT. One extra STRAIGHT appears right after the start, because the car starts right at the end of a curve.
The program remembers a state (curve / straight) and only does something when the state changes; this is called event detection. The counter (quiet) stops a single 0 reading inside a curve from wrongly saying "straight"; this is called debouncing. We will use this detector to count laps.
#include "otonom.h"
int distance(int angle)
{
int d = lidar_read(angle);
if (d == 0) return 100;
return d;
}
void follow_lane(void)
{
int right = distance(45);
int left = distance(135);
if (left - right > 5) steer(20);
else if (right - left > 5) steer(-20);
else steer(0);
}
int main(void)
{
if (sim_connect() != 0) return 1;
int in_curve = lidar_read(76) > 0; // state at the start
int quiet = 0; // 0 readings in a row
drive(DRIVE_FORWARD);
while (1) // Ctrl+C to stop
{
follow_lane();
if (lidar_read(76) > 0)
{
quiet = 0;
if (!in_curve) { in_curve = 1; printf("CURVE\n"); }
}
else
{
quiet++;
if (in_curve && quiet >= 20) { in_curve = 0; printf("STRAIGHT\n"); }
}
Sleep(50);
}
}
from otonom import *
import time
def distance(angle):
d = lidar_read(angle)
if d == 0:
return 100
return d
def follow_lane():
right = distance(45)
left = distance(135)
if left - right > 5:
steer(20)
elif right - left > 5:
steer(-20)
else:
steer(0)
if sim_connect() != 0:
exit(1)
in_curve = lidar_read(76) > 0 # state at the start
quiet = 0 # 0 readings in a row
drive(DRIVE_FORWARD)
while True: # Ctrl+C to stop
follow_lane()
if lidar_read(76) > 0:
quiet = 0
if not in_curve:
in_curve = True
print("CURVE")
else:
quiet += 1
if in_curve and quiet >= 20:
in_curve = False
print("STRAIGHT")
time.sleep(0.05)
Using the curve detector of Task 26, drive the car one full lap and stop it close to where it started.
At the start the car is on the right straight; one lap = top curve + left straight + bottom curve. When the car leaves the second curve (the second STRAIGHT) the lap is done.
When it stops print One lap done!. The car should never crash and should stop after about 72 seconds.
Add time measurement to Task 27: when the lap ends print Lap time: ... s (GetTickCount() in C, time.time() in Python).
Move the turning angle (20) to a constant called TURN at the top of the program and measure the lap time for 20, 30, 45 and 60.
Goal: under 70 seconds without any crash. With 20° you should see about 72 seconds, with 45° about 66 seconds.
Drive 3 laps with the driver of Task 28 and print the time of each lap separately (Lap 1: ... s).
Add the recovery of Task 25 to the driver: if there is a collision, count it, back up for 1 second with -30° steering and continue. At the end print the total time and the number of crashes.
With a good driver the crash count should be 0; the recovery only works when something goes wrong (for example if someone interferes with Manual Control).
Write the final program that combines everything:
1. The program asks the user for the number of laps (1–5). If an invalid number is entered it asks again.
2. The car drives as in Task 29 and recovers from collisions.
3. Every second it prints a status line: Lap 1/3 | CURVE | 23 s | crashes 0.
4. When the requested laps are done it stops the car and prints a summary: the time of each lap, the fastest lap and the total crashes.