Robot courier Lesson 4.2 All coursesFor teachersStudy in the app Student Arduino Uno + L298P motor shield
Topic 4 · Lesson 4.2

Turns at the intersection and route

The robot turns from the intersection onto the desired line and follows a route consisting of several solutions.

2 × 45 min1–2 students per robotPractice

Lesson result

The robot turns left or right from the intersection to a new line, turns around on the line and follows a route of several solutions written in order in the program.

You will learn

  • Explain why turning by sensors is more reliable than turning by time.
  • Choose an exit to the center of the intersection so that after the turn the robot is on the line.
  • Write down the route as a sequence of decisions “left / right / straight”.
  • Check the route on the field and find at which solution it breaks.

Need to know

  • 4.1 Finding and counting intersections
  • 2.2 Turns and U-turns

Will need

Line sensor (analog, Trema) × 2

Why this lesson

The robot finds an intersection. Now he must collapse - to the cargo, to the delivery point or further down the road. In Lesson 2.2 the turns were timed and battery dependent. Today the robot will turn according to sensors: it will spin until it finds a new line.

Warm up · remember
  1. Why does the rotation time turn out different when the battery is low?
  2. What does passCrossing()?
  3. How does a robot understand that there is an intersection below it?
1
Lesson 1 · 45 min
Rotate by sensors
1

How to turn accurately

8 min

What should a robot do. The robot needs to go through several intersections and choose the right direction at each one. We use line sensors to detect events and a pre-recorded route plan to select an action.

What we use and how it works. Sensors answer the question “what is under the robot now,” and the route answers “what to do next.” The program reaches the intersection, makes a turn or drive straight, exits the transverse lane and moves on to the next point. An array or call sequence stores order, but does not measure the robot's position.

How to link a dimension to an action. We check the route by sections: we note which intersection was found and what action was performed. If the robot did not detect the lane or turned in the wrong direction, the next commands will be executed in a different place. Therefore, first we will check each function, then we will assemble the whole route.

Predict

The robot stands at the intersection and turns left. How can he himself understand that he has turned enough and is already looking along the new line?

  1. Discuss with a partner. Place the robot at an intersection and slowly turn it with your hands.
  2. Watch the left sensor: when will it be above the new line?
It should work

Can you explain the idea: spin until the sensor on the turning side sees a new line.

Predict

The robot stands at the intersection and turns left. How can he himself understand that he has turned enough and is already looking along the new line?

  1. Place the robot in the center of the intersection and slowly turn your hands to the left, then to the right.
  2. For each turn, write down which sensor was first above the new line and which sensor was above the old line at the beginning of the turn.
RotateSees the new line firstPasses over the old one first
Left
Right
It should work

The table is full. Can you explain the idea: spin until the sensor on the side of the turn sees a new line.

Test the "spin to new line" idea with a measurement before programming it.

  1. Place a sheet of paper with a protractor or lines at 10° intervals under the robot. Place the robot in the center of the intersection.
  2. Slowly turn your hands to the left. Write down the angle at which the left sensor for the first time sees the new line, and the angle at which the robot looks exactly along it.
  3. Repeat to the right.
  4. Are the angles the same? What then will a robot do if it stops the moment the sensor sees the line? How to fix this?
It should work

The log contains two angles for each turn and a conclusion about where the robot will stop.

2

Drive to the center of the intersection

12 min

Sensors are standing ahead wheels. When the sensors noticed the intersection, the wheels had not yet reached it. If you turn right away, the robot will end up on the side of the new line. Therefore, you first need to drive forward so that the wheels are above the center of the intersection.

  1. Place the robot so that the sensors are on the transverse line.
  2. Measure with a ruler how much longer the wheels will travel to the center of the intersection. Write it down in a journal.
  3. The time of this passage is centerMs - we will select it in the next step.
It should work

You know why you need to go to the center before turning.

Sensors are standing ahead wheels. When the sensors noticed the intersection, the wheels had not yet reached it.

Predict

The robot turned in place as soon as the sensors saw the intersection. Where will the robot end up in relation to the new line? Show on the path with your hands.

  1. Place the robot with the sensors on the transverse line and turn it 90° with your hands in place. Where were the sensors - above the new line or on the side?
  2. Measure with a ruler how much longer the wheels will travel to the center of the intersection. Write it down in a journal.
  3. The time of this passage is centerMs - you will pick it up in the next step. How will you understand that time is too short, and how will you know that it is too long? Write it down.
It should work

The forecast was checked by hand, the distance was recorded, the signs “few” and “many” for centerMs are recorded.

Sensors are standing ahead wheels, so before turning, the robot drives to the center of the intersection. Calculate the time of this departure, and do not select it at random.

  1. Measure the distance from the sensors to the wheel axle in millimeters.
  2. Take from the log of lesson 2.1 your speed in millimeters per second at close baseSpeed. There is no table - measure: travel 1 second, travel with a ruler, three times.
  3. Do the math centerMs and write down the calculation. In the next step, compare it with the one picked up on the track.
  4. Write down why time travel is a weak point and what its accuracy depends on.
It should work

In the journal - distance, speed, calculation centerMs and two reasons why the departure time may be wrong.

3

Turning at an intersection

22 min
  1. Create a sketch L42_Turn_At_Crossing. Transfer measurements kp and baseSpeed.
Turning at an intersectionL42_Turn_At_Crossing.ino.ino
const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5;const int LEFT_DIR_PIN = 7;const int LEFT_SPEED_PIN = 6;const int RIGHT_DIR_PIN = 4;const int RIGHT_SPEED_PIN = 5;const int FORWARD = HIGH;const int BACKWARD = (FORWARD == HIGH) ? LOW : HIGH; // Измерения и настройки из журнала (уроки 3.1 и 3.2).const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0;float kp = 0.5;int baseSpeed = 100; int leftLevel = 100;int rightLevel = 100; int level(int value, int white, int black) {  long result = (long)(value - black) * 100 / (white - black);  return constrain(result, 0, 100);} void readSensors() {  leftLevel = level(analogRead(LEFT_SENSOR_PIN), LEFT_WHITE, LEFT_BLACK);  rightLevel = level(analogRead(RIGHT_SENSOR_PIN), RIGHT_WHITE, RIGHT_BLACK);} void drive(int leftDir, int leftSpeed, int rightDir, int rightSpeed) {  digitalWrite(LEFT_DIR_PIN, leftDir);  digitalWrite(RIGHT_DIR_PIN, rightDir);  analogWrite(LEFT_SPEED_PIN, leftSpeed);  analogWrite(RIGHT_SPEED_PIN, rightSpeed);} void stopMotors() {  analogWrite(LEFT_SPEED_PIN, 0);  analogWrite(RIGHT_SPEED_PIN, 0);} void followLineStep() {  int error = leftLevel - rightLevel;  int correction = kp * error;  drive(FORWARD, constrain(baseSpeed + correction, 0, 255),        FORWARD, constrain(baseSpeed - correction, 0, 255));} void setupRobot() {  pinMode(LEFT_DIR_PIN, OUTPUT);  pinMode(LEFT_SPEED_PIN, OUTPUT);  pinMode(RIGHT_DIR_PIN, OUTPUT);  pinMode(RIGHT_SPEED_PIN, OUTPUT);  stopMotors();  // Без измерений робот не поедет.  if (LEFT_WHITE == LEFT_BLACK || RIGHT_WHITE == RIGHT_BLACK) {    while (true) {    }  }  delay(2000);} // Перекрёсток: оба датчика видят чёрное.bool onCrossing() {  return leftLevel < 30 && rightLevel < 30;} // Ехать по линии, пока не найдём перекрёсток.void followLineToCrossing() {  while (true) {    readSensors();    if (onCrossing()) {      return;    }    followLineStep();  }} // Проехать перекрёсток прямо, пока датчики не съедут с чёрного.void passCrossing() {  drive(FORWARD, baseSpeed, FORWARD, baseSpeed);  do {    readSensors();  } while (onCrossing());  delay(100);} int turnSpeed = 100;int centerMs = 250; // подбираем: проехать от датчиков до центра перекрёсткаint leaveMs = 200;  // время, чтобы датчик ушёл со старой линии // Выехать на центр перекрёстка: датчики стоят впереди колёс.void toCenter() {  drive(FORWARD, baseSpeed, FORWARD, baseSpeed);  delay(centerMs);  stopMotors();} // Крутиться на месте, пока датчик со стороны поворота не найдёт новую линию.void turnUntilLine(bool left) {  if (left) {    drive(BACKWARD, turnSpeed, FORWARD, turnSpeed);  } else {    drive(FORWARD, turnSpeed, BACKWARD, turnSpeed);  }  delay(leaveMs);  do {    readSensors();  } while ((left ? leftLevel : rightLevel) > 30);  stopMotors();} void turnLeftAtCrossing() {  toCenter();  turnUntilLine(true);} void turnRightAtCrossing() {  toCenter();  turnUntilLine(false);} // Развернуться на линии: крутиться, пока линия снова не окажется под датчиком.void turnAroundOnLine() {  turnUntilLine(true);} // Ехать по линии заданное время.void followLineFor(unsigned long ms) {  unsigned long start = millis();  while (millis() - start < ms) {    readSensors();    followLineStep();  }  stopMotors();} void setup() {  setupRobot();  followLineToCrossing();  turnLeftAtCrossing();  followLineFor(1500);  turnAroundOnLine();  followLineToCrossing();  stopMotors();} void loop() {}
  1. Parse the function turnUntilLine. Why in it delay(leaveMs) before searching for a line?
  2. Start on the path with an intersection. Pick up centerMs: after turning, the robot should confidently follow the new line.
It should work

The robot reaches the intersection, turns left and follows the new line.

How it works: Why leaveMs

At the beginning of the turn, the sensor is still above the old line and would immediately “find” it. Therefore the first leaveMs milliseconds the robot simply spins without looking at the sensors - so that the sensor leaves the old line. Then we look for a new one.

If it doesn't work
What do you seeWhat to do
After turning, the robot is on the side of the linePick up centerMs
The robot spins endlesslySensor does not see the line: check threshold 30 and measurements
The robot stops immediatelyEnlarge leaveMs
  1. Create a sketch L42_Turn_At_Crossing according to the template. Transfer measurements kp and baseSpeed. The line and intersection functions are already ready.
  2. Write a trip to the center yourself toCenter(), turn to line turnUntilLine() and turning left and right from the intersection. setup() below checks them out on the track.
Template: turning at an intersectionL42_Turn_At_Crossing_Gaps.ino.ino
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях.// setup() ниже — проверка твоих функций на дорожке. const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5;const int LEFT_DIR_PIN = 7;const int LEFT_SPEED_PIN = 6;const int RIGHT_DIR_PIN = 4;const int RIGHT_SPEED_PIN = 5;const int FORWARD = HIGH;const int BACKWARD = (FORWARD == HIGH) ? LOW : HIGH; // Измерения и настройки из журнала (уроки 3.1 и 3.2).const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0;float kp = 0.5;int baseSpeed = 100;int leftLevel = 100;int rightLevel = 100;int turnSpeed = 100;int centerMs = 250; // подбираем: проехать от датчиков до центра перекрёсткаint leaveMs = 200;  // время, чтобы датчик ушёл со старой линии // ===== Твоя часть: напиши сам ===== void toCenter() {  // Колёса встают над центром перекрёстка: проехать вперёд centerMs и остановиться.} void turnUntilLine(bool left) {  // Крутиться на месте (left = true — налево) до новой линии; в конце колёса стоят.  // Пока датчик ещё над старой линией, она не должна считаться новой — для этого есть leaveMs.} void turnLeftAtCrossing() {  // Повернуть с перекрёстка налево на новую линию.} void turnRightAtCrossing() {  // Повернуть с перекрёстка направо на новую линию.} // ===== Готовое из уроков 3.2–4.1 и проверка — не меняй ===== int level(int value, int white, int black) {  long result = (long)(value - black) * 100 / (white - black);  return constrain(result, 0, 100);} void readSensors() {  leftLevel = level(analogRead(LEFT_SENSOR_PIN), LEFT_WHITE, LEFT_BLACK);  rightLevel = level(analogRead(RIGHT_SENSOR_PIN), RIGHT_WHITE, RIGHT_BLACK);} void drive(int leftDir, int leftSpeed, int rightDir, int rightSpeed) {  digitalWrite(LEFT_DIR_PIN, leftDir);  digitalWrite(RIGHT_DIR_PIN, rightDir);  analogWrite(LEFT_SPEED_PIN, leftSpeed);  analogWrite(RIGHT_SPEED_PIN, rightSpeed);} void stopMotors() {  analogWrite(LEFT_SPEED_PIN, 0);  analogWrite(RIGHT_SPEED_PIN, 0);} void followLineStep() {  int error = leftLevel - rightLevel;  int correction = kp * error;  drive(FORWARD, constrain(baseSpeed + correction, 0, 255),        FORWARD, constrain(baseSpeed - correction, 0, 255));} void setupRobot() {  pinMode(LEFT_DIR_PIN, OUTPUT);  pinMode(LEFT_SPEED_PIN, OUTPUT);  pinMode(RIGHT_DIR_PIN, OUTPUT);  pinMode(RIGHT_SPEED_PIN, OUTPUT);  stopMotors();  // Без измерений робот не поедет.  if (LEFT_WHITE == LEFT_BLACK || RIGHT_WHITE == RIGHT_BLACK) {    while (true) {    }  }  delay(2000);} // Перекрёсток: оба датчика видят чёрное.bool onCrossing() {  return leftLevel < 30 && rightLevel < 30;} // Ехать по линии, пока не найдём перекрёсток.void followLineToCrossing() {  while (true) {    readSensors();    if (onCrossing()) {      return;    }    followLineStep();  }} // Проехать перекрёсток прямо, пока датчики не съедут с чёрного.void passCrossing() {  drive(FORWARD, baseSpeed, FORWARD, baseSpeed);  do {    readSensors();  } while (onCrossing());  delay(100);} // Развернуться на линии: крутиться, пока линия снова не окажется под датчиком.void turnAroundOnLine() {  turnUntilLine(true);} // Ехать по линии заданное время.void followLineFor(unsigned long ms) {  unsigned long start = millis();  while (millis() - start < ms) {    readSensors();    followLineStep();  }  stopMotors();} void setup() {  setupRobot();  followLineToCrossing();  turnLeftAtCrossing();  followLineFor(1500);  turnAroundOnLine();  followLineToCrossing();  stopMotors();} void loop() {}
Predict

What happens if you start looking for a new line right away without waiting leaveMs?

  1. Start on the path with an intersection. Pick up centerMs based on the characteristics that you wrote down in the previous step.
Hint: Spin to the line

It's like passCrossing() from Lesson 4.1: Repeat reading sensors as long as the condition is true. What condition means “there is no line under the sensor yet”?

Hint: Which sensor to look for

Turn left - the left sensor will see the new line first.

It should work

The robot reaches the intersection, turns left and follows the new line. The forecast is recorded.

If it doesn't work
What do you seeWhat to do
After turning, the robot is on the side of the linePick up centerMs
The robot spins endlesslyThe sensor does not see the line: check the search and measurement conditions
The robot stops immediatelyEnlarge leaveMs

Requirement 1. Continue with your program L41_Count and save as L42_Turn_At_Crossing. Function turnUntilLine(left) turns the robot in place to a new line:

  • first the robot spins without looking at the sensors until the sensor leaves the old line;
  • then - until the sensor on the side of the turn sees the line;
  • if the line is not found within 3 seconds, the robot stops: on the field without protection it will spin endlessly.

Requirement 2. turnLeftAtCrossing() and turnRightAtCrossing(): departure to the center according to your centerMs, then turn to the line.

Acceptance. Five turns left and right: after each, the robot leaves along a new line. Check the protection separately: lift the robot above the table while turning.

Hint: Idea (1 of 3)

Line search - cycle “read sensors while there is no line”. For protection, remember the start time of the search.

Hint: Plan (2 of 3)
  1. Turn on rotation, wait leaveMs.
  2. Remember millis().
  3. Repeat reading until there is no line and less than 3 seconds have passed.
  4. Stop the motors.
Hint: Code snippet (3 of 3)

while ((left ? leftLevel : rightLevel) > 30 && millis() - start < 3000) - read the condition in words.

It should work

The program fulfills requirements 1-2, ten turns and the defense check are recorded. Handpicked centerMs is compared with the calculation.

2
Lesson 2 · 45 min
Route from solutions
4

Reversal on line

10 min

At the end of the branch, at the cargo or delivery point, the robot needs to turn around and return to the intersection. A turn is the same turn according to the sensors, only the line is now behind.

  1. Find the call in the sketch turnAroundOnLine(). Check on the path: drive along the branch, turn around, return to the intersection.
It should work

The robot turns around, finds the same line and returns to the intersection.

At the end of the branch, the robot needs to turn around and return to the intersection. A turn is the same turn according to the sensors, only the line is now behind.

  1. Find the function in the sketch turnAroundOnLine(). Why doesn’t it have an exit to the center, like a turn at an intersection? Write down the answer.
  2. Check the path three times: drive along the branch, turn around, return to the intersection.
It should work

The robot turns around three times and returns to the intersection. The answer about going to the center is written down.

Requirement 3. turnAroundOnLine() turns the robot on the line and stops when the line is under the sensor again. The 3 second defense from requirement 1 works here too.

Acceptance. Five turns at the end of the branch: after each, the robot returns to the intersection. For each, measure how far the nose looks past the line immediately after the turn.

ReversalBack?Nose off the line, mm
1

If the nose points in the same direction every time, suggest and check one correction.

It should work

Five spreads in the table, bias correction verified.

5

Route in order

22 min

The route is solutions at intersections in order. The robot does not see what intersection it is: it simply makes the next decision.

  1. Create a sketch L42_Route. Draw a training track and path for the robot in a journal.
RouteL42_Route.ino.ino
const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5;const int LEFT_DIR_PIN = 7;const int LEFT_SPEED_PIN = 6;const int RIGHT_DIR_PIN = 4;const int RIGHT_SPEED_PIN = 5;const int FORWARD = HIGH;const int BACKWARD = (FORWARD == HIGH) ? LOW : HIGH; // Измерения и настройки из журнала (уроки 3.1 и 3.2).const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0;float kp = 0.5;int baseSpeed = 100; int leftLevel = 100;int rightLevel = 100; int level(int value, int white, int black) {  long result = (long)(value - black) * 100 / (white - black);  return constrain(result, 0, 100);} void readSensors() {  leftLevel = level(analogRead(LEFT_SENSOR_PIN), LEFT_WHITE, LEFT_BLACK);  rightLevel = level(analogRead(RIGHT_SENSOR_PIN), RIGHT_WHITE, RIGHT_BLACK);} void drive(int leftDir, int leftSpeed, int rightDir, int rightSpeed) {  digitalWrite(LEFT_DIR_PIN, leftDir);  digitalWrite(RIGHT_DIR_PIN, rightDir);  analogWrite(LEFT_SPEED_PIN, leftSpeed);  analogWrite(RIGHT_SPEED_PIN, rightSpeed);} void stopMotors() {  analogWrite(LEFT_SPEED_PIN, 0);  analogWrite(RIGHT_SPEED_PIN, 0);} void followLineStep() {  int error = leftLevel - rightLevel;  int correction = kp * error;  drive(FORWARD, constrain(baseSpeed + correction, 0, 255),        FORWARD, constrain(baseSpeed - correction, 0, 255));} void setupRobot() {  pinMode(LEFT_DIR_PIN, OUTPUT);  pinMode(LEFT_SPEED_PIN, OUTPUT);  pinMode(RIGHT_DIR_PIN, OUTPUT);  pinMode(RIGHT_SPEED_PIN, OUTPUT);  stopMotors();  // Без измерений робот не поедет.  if (LEFT_WHITE == LEFT_BLACK || RIGHT_WHITE == RIGHT_BLACK) {    while (true) {    }  }  delay(2000);} // Перекрёсток: оба датчика видят чёрное.bool onCrossing() {  return leftLevel < 30 && rightLevel < 30;} // Ехать по линии, пока не найдём перекрёсток.void followLineToCrossing() {  while (true) {    readSensors();    if (onCrossing()) {      return;    }    followLineStep();  }} // Проехать перекрёсток прямо, пока датчики не съедут с чёрного.void passCrossing() {  drive(FORWARD, baseSpeed, FORWARD, baseSpeed);  do {    readSensors();  } while (onCrossing());  delay(100);} int turnSpeed = 100;int centerMs = 250; // подбираем: проехать от датчиков до центра перекрёсткаint leaveMs = 200;  // время, чтобы датчик ушёл со старой линии // Выехать на центр перекрёстка: датчики стоят впереди колёс.void toCenter() {  drive(FORWARD, baseSpeed, FORWARD, baseSpeed);  delay(centerMs);  stopMotors();} // Крутиться на месте, пока датчик со стороны поворота не найдёт новую линию.void turnUntilLine(bool left) {  if (left) {    drive(BACKWARD, turnSpeed, FORWARD, turnSpeed);  } else {    drive(FORWARD, turnSpeed, BACKWARD, turnSpeed);  }  delay(leaveMs);  do {    readSensors();  } while ((left ? leftLevel : rightLevel) > 30);  stopMotors();} void turnLeftAtCrossing() {  toCenter();  turnUntilLine(true);} void turnRightAtCrossing() {  toCenter();  turnUntilLine(false);} // Развернуться на линии: крутиться, пока линия снова не окажется под датчиком.void turnAroundOnLine() {  turnUntilLine(true);} // Ехать по линии заданное время.void followLineFor(unsigned long ms) {  unsigned long start = millis();  while (millis() - start < ms) {    readSensors();    followLineStep();  }  stopMotors();} // Маршрут: решения на перекрёстках по порядку.void setup() {  setupRobot();   followLineToCrossing();  turnLeftAtCrossing();   // решение 1: налево   followLineToCrossing();  passCrossing();         // решение 2: прямо   followLineToCrossing();  turnRightAtCrossing();  // решение 3: направо   followLineToCrossing();  stopMotors();           // конец маршрута} void loop() {}
Predict

According to the program: left, straight, right. Draw where the robot will stop on the track.

  1. Run it. If the route breaks down, what is the solution? Why?
It should work

The robot goes through a route of three solutions and stops where you predicted.

Find the mistake · The robot turns at the wrong intersection

At the neighbors after turnLeftAtCrossing() is worth it right away turnRightAtCrossing(), without followLineToCrossing() between them. What will the robot do?

The route is solutions at intersections in order. The robot does not see what intersection it is: it simply makes the next decision.

  1. Draw a training track and path in a journal: left, straight, right.
  2. Create a sketch L42_Route according to the template and write the route in setup() itself from the ready-made functions of the lesson.
Template: routeL42_Route_Gaps.ino.ino
// Шаблон среднего уровня. Напиши код в разделе «Твоя часть» по целям в комментариях. const int LEFT_SENSOR_PIN = A4;const int RIGHT_SENSOR_PIN = A5;const int LEFT_DIR_PIN = 7;const int LEFT_SPEED_PIN = 6;const int RIGHT_DIR_PIN = 4;const int RIGHT_SPEED_PIN = 5;const int FORWARD = HIGH;const int BACKWARD = (FORWARD == HIGH) ? LOW : HIGH; // Измерения и настройки из журнала (уроки 3.1 и 3.2).const int LEFT_WHITE = 0;const int LEFT_BLACK = 0;const int RIGHT_WHITE = 0;const int RIGHT_BLACK = 0;float kp = 0.5;int baseSpeed = 100;int leftLevel = 100;int rightLevel = 100;int turnSpeed = 100;int centerMs = 250; // подбираем: проехать от датчиков до центра перекрёсткаint leaveMs = 200;  // время, чтобы датчик ушёл со старой линии // ===== Твоя часть: напиши сам ===== void setup() {  setupRobot();   // Маршрут из журнала: на каждом перекрёстке — своё решение по порядку.  // В конце остановиться на следующем перекрёстке.} // ===== Готовое из уроков 3.2–4.2 — не меняй ===== int level(int value, int white, int black) {  long result = (long)(value - black) * 100 / (white - black);  return constrain(result, 0, 100);} void readSensors() {  leftLevel = level(analogRead(LEFT_SENSOR_PIN), LEFT_WHITE, LEFT_BLACK);  rightLevel = level(analogRead(RIGHT_SENSOR_PIN), RIGHT_WHITE, RIGHT_BLACK);} void drive(int leftDir, int leftSpeed, int rightDir, int rightSpeed) {  digitalWrite(LEFT_DIR_PIN, leftDir);  digitalWrite(RIGHT_DIR_PIN, rightDir);  analogWrite(LEFT_SPEED_PIN, leftSpeed);  analogWrite(RIGHT_SPEED_PIN, rightSpeed);} void stopMotors() {  analogWrite(LEFT_SPEED_PIN, 0);  analogWrite(RIGHT_SPEED_PIN, 0);} void followLineStep() {  int error = leftLevel - rightLevel;  int correction = kp * error;  drive(FORWARD, constrain(baseSpeed + correction, 0, 255),        FORWARD, constrain(baseSpeed - correction, 0, 255));} void setupRobot() {  pinMode(LEFT_DIR_PIN, OUTPUT);  pinMode(LEFT_SPEED_PIN, OUTPUT);  pinMode(RIGHT_DIR_PIN, OUTPUT);  pinMode(RIGHT_SPEED_PIN, OUTPUT);  stopMotors();  // Без измерений робот не поедет.  if (LEFT_WHITE == LEFT_BLACK || RIGHT_WHITE == RIGHT_BLACK) {    while (true) {    }  }  delay(2000);} // Перекрёсток: оба датчика видят чёрное.bool onCrossing() {  return leftLevel < 30 && rightLevel < 30;} // Ехать по линии, пока не найдём перекрёсток.void followLineToCrossing() {  while (true) {    readSensors();    if (onCrossing()) {      return;    }    followLineStep();  }} // Проехать перекрёсток прямо, пока датчики не съедут с чёрного.void passCrossing() {  drive(FORWARD, baseSpeed, FORWARD, baseSpeed);  do {    readSensors();  } while (onCrossing());  delay(100);} // Выехать на центр перекрёстка: датчики стоят впереди колёс.void toCenter() {  drive(FORWARD, baseSpeed, FORWARD, baseSpeed);  delay(centerMs);  stopMotors();} // Крутиться на месте, пока датчик со стороны поворота не найдёт новую линию.void turnUntilLine(bool left) {  if (left) {    drive(BACKWARD, turnSpeed, FORWARD, turnSpeed);  } else {    drive(FORWARD, turnSpeed, BACKWARD, turnSpeed);  }  delay(leaveMs);  do {    readSensors();  } while ((left ? leftLevel : rightLevel) > 30);  stopMotors();} void turnLeftAtCrossing() {  toCenter();  turnUntilLine(true);} void turnRightAtCrossing() {  toCenter();  turnUntilLine(false);} // Развернуться на линии: крутиться, пока линия снова не окажется под датчиком.void turnAroundOnLine() {  turnUntilLine(true);} // Ехать по линии заданное время.void followLineFor(unsigned long ms) {  unsigned long start = millis();  while (millis() - start < ms) {    readSensors();    followLineStep();  }  stopMotors();} void loop() {}
Predict

Draw where the robot will stop on the path.

  1. Run it. If the route breaks down, what is the solution? Why?
Hint: One solution

Before each decision, the robot must reach an intersection.

It should work

The robot goes through a route of three solutions and stops where you predicted.

Find the mistake · The robot turns at the wrong intersection

At the neighbors after turnLeftAtCrossing() is worth it right away turnRightAtCrossing(), without followLineToCrossing() between them. What will the robot do?

Requirement 4. The route is planned to the program and checked in parts.

  1. Choose a path of four or more decisions on the training field that includes left, right, and straight. If the final task field is laid out in the classroom, take the path from the start to one delivery zone. Write it down in a table:
№CrossroadsSolutionWhere does the robot look after
1
  1. Write the route into the program L42_Route from its functions.
  2. If the route is broken, do not rewrite everything: run only the first solutions, then half, and find the first solution on which the robot makes a mistake. Write down how you searched.
Predict

At which solution is the route most likely to fail and why? Record before the first launch.

Hint: How to check in parts

Temporarily set stopMotors(); and return; after the required solution in setup().

It should work

Route table, program based on it, forecast and error search record. The robot goes through a route on a training track.

6

Quests by level

13 min
Basic · Rotate and rotate

Turn left and right from the intersection and go along the new line; turn around on the line and return.

Intermediate · Four solutions

Create a route on the training field with four solutions, which includes left, right and straight. Write it down in a journal, then into a program, and go through it without errors.

Advanced level Array route

Write the solutions as an array: char route[] = {'L', 'S', 'R', 'L'};. Write a function decide(d), which makes one decision, and a loop that goes through the entire array. Create a route based on the field diagram so that only the row with the array changes for the new route.

Test yourself

Test yourself

Select answers to prepare. They are not stored or evaluated on the site. Take the knowledge test in the course syllabus or discuss the answers with your instructor.

● Basic

1.Why is a turn “to the line” more reliable than a timed turn?
2.Why drive a little forward (centerMs) before turning?
3.What happens if delay(leaveMs) is removed from turnUntilLine?
4.Route: left, straight, right. What will the robot do at the second intersection?
5.char route[] = {'L', 'S', 'R'}; What is route[2] equal to?

★ Intermediate level

1.Route: left, straight, right, left. How many decisions at intersections will the robot make?
2.Why is the route first written to the log, and then to the program?
3.After the turn, the robot found itself on the side of the new line - from the direction it came from. What should I change?
4.What rotation is this line search suitable for?
do {  readSensors();} while (leftLevel > 30);
5.Why is every route solution two lines: followLineToCrossing() and an action?

★★ Advanced level

1.Which solution will be executed third?
char route[] = {'L', 'S', 'R', 'L'};
2.What needs to be changed in the array program to take a new route?
3.From the sensors to the wheel axles is 60 mm, the robot speed is 200 mm/s. What should centerMs be according to calculation?
4.The robot was lifted off the table while turning. What will happen?
unsigned long start = millis();do {  readSensors();} while ((left ? leftLevel : rightLevel) > 30 && millis() - start < 3000);stopMotors();
5.The eight-solution route breaks down. How to find a broken solution faster?

Answer questions at your level. Correct answers needed: Beginner - 4 out of 5; Average - 4 out of 5; Advanced - 4 out of 5. Answers will be checked by the teacher or course program.

Summary

The robot turns according to sensors and follows a route of solutions. For the final task, all that remains is to learn to see the load and grab it.

How to confirm the result

For each experiment, save the conditions, program version, observation and conclusion. When comparing attempts, change one reason at a time; unsuccessful attempts also remain in the log. Marking a result refers to actions that have already been completed; there is no need to repeat them for the sake of marking. If the result is not obtained, write down the discrepancy and the next hypothesis to be tested. A model, measurements of a real robot, and an explanation are different types of evidence.

Lesson vocabulary

routeSequence of decisions at intersections: left, right or straight.
intersection centerThe point where the lines intersect. To turn, the wheels must be in place above it.
arrayA list of values under one name: route[0], route[1] and so on.
millis()How many milliseconds have passed since the board was turned on. Allows you to ride for a specified time without stopping the program.