0

Line Robot - Worst RCJ Line

Of many possibilities, this may the the worst RCJ Line program but this shouldn't bother us much. We will just show the concepts here.

Wall following

This is one of the easiest tasks. Only 1 sensor is needed. Logic is simple: if You are too close to the wall, move away. If You are too far, turn towards the wall. Let's follow right wall. The wall-following lidar should be directed at 45º to the right, if ahead of robot is 0º. According to the picture that shows lidars, this is lidar number 2.

We will be following right wall at 100 mm distance (more precisely in 45º direction to the wall). So, if distance is more than 100 mm, turn towards the wall. Otherwise away from it. This program will do the task:

void loop(){
	if (rightFront() > 100)
		go(80, 20);
	else
		go(20, 80);
}
Distance to the wall is checked. If it is more than 100 (mm), turn right (toward the wall). 80 is left motors' speed, 20 right motors'. As left rotates faster, robot will be turning toward the wall.

Start the program by entering command "loo". If 80 and 20 are not right speeds, change them. Increasing difference will result in sharper turning but may start unwanted oscillations. Use trial and error method.

This simple program will not yield smooth movements and may not be able to follow any wall. We will consider these problems later.

Line following

This task is a more complicated one. In fact, it can be quite challenging. For now, we will consider only the simplest possibilities and our simple program will be able to track these simple lines.

Prepare a white (or some other bright) surface. Stick a black stripe, at least half a meter long. Even better, make a closed loop. The objective is to program the robot to move along the stripe.

Enter command "ref" to see how the sensor measures black and white values. If You put the stripe below the sensor, the results will look like:

Values will not be the same, but You will notice that 1 or 2 adjacent numbers are smaller than the rest. Bigger reflected infrared light (white) yields higher number and smaller reflected (black) smaller numbers. Here, numbers around 300 designate the black stripe, which is under 3rd sensor. If we want the robot to follow the line, we will always force it to turn in a way that the line is in the middle of the sensor.

Find smallest values of all sensors (black stripe below) and highest (white below), when the surface is at normal distance (robot's wheels in contact with the surface). Find averages for each sensor. For example, if 360 is black and 780 white, average will be (780 + 360) / 2 = 570. These will be our test values. All the readings below them (for each sensor) we will declare as black and white above. The code will be similar to wall following example:

void loop(){
	if (brightness(0) < 300 || brightness(1) < 490 || brightness(2) < 450 || brightness(3) < 480)
		go(100, 10);
	else if (brightness(5) < 425 || brightness(6) < 410 || brightness(7) < 480 || brightness(8) < 340)
		go(10, 100);
	else
		go(80, 80);
}
Enter Your values, not the ones above. For example, compare sensor 0 against Your average. Therefore, not 300 but 380 (if Your average is 380): reading(0) < 380.

A short explanation.

  1. If any of the rightmost 4 sensors (0, 1, 2, or 3) see the black line (brightness(0) < 300 ||...), turn right (go(100, 10);).
  2. Otherwise, if any of the leftmost 4 sensors (5, 6, 7, or 8) see the black line (brightness(5) < 425 ||...), turn left (go(10, 100);).
  3. Otherwise (no line left and no right), go straight ahead (go(80, 80));

Start the program by entering command "loo". Change 10 and 100 into some other values till You get the best line tracking. You can use negative values (go backwards). Again, this is not a very good program for line following but it very simple and shows the concept.

Avoiding obstacles

It there is an obstacle ahead, when we follow a wall or a line, the robot will have to avoid it.

  • In case we are following a wall, we shall be turning in the direction away from the wall we are following, till the obstacle ahead disappears, when we will be able to restart wall following.
  • In case of line following, we will also have to turn, till the obstacle disappears, but will have more freedom in choosing on which side (any of the 2). The next phase will be the same as for the wall following: we will be following the wall (i.e. the obstacle). The only difference will be that we will be looking for a black line. As soon as we detect it, we will restart line following.
These actions are more complex than the previous ones so we will have some work to do.

A small digression. We could mount the lidars on a holder attached to a servo. The servo would allow us to use less lidars, maybe even one, if robot's speed is not a priority. Servo would sweep the lidar to scan different directions.

First, the wall.

void loop(){
	if (frontLeft() < 90)
		go(-50, 50);
	else if (rightFront() > 100)
		go(80, 20);
	else
		go(20, 80);
}
We already learned that in the first pass we have to start the sensors. The second "if", testing frontLeft() (one of the 2 lidars measuring distance ahead), executes the following line (go(-50, 50)) if and obstacle is closer than 90 mm ahead. In that case, the robot turns leftwards in place. In the successive passes, it will continue turning to the left, till the obstacle disappears. That "if" will not evaluate as true so the program will give chance the other 2 ifs to execute their commands - therefore, following the right wall. More precisely, the obstacle that is now on robot's right side. This is a common situation when the robot encounters a corner.

Now, the line. The logic is similar:

void loop(){
	if (frontLeft() < 90)         // Obstacle detected, turn left in place
		go(-50, 50);
	else if (brightness(0) < 570 || brightness(1) < 570 || brightness(2) < 570 || brightness(3) < 570)      // Line left, turn left
		go(20, 60);
	else if (brightness(5) < 570 || brightness(6) < 570 || brightness(7) < 570 || brightness(8) < 570)      // Line right, turn right
		go(60, 20);
	else if (brightness(4) < 570)     // Line in center, go straight ahead
		go(40, 40);
	else if (rightFront() > 100)   // Right wall too far, turn right
		go(80, 20);
	else                                       // Right wall too close, turn left
		go(20, 80);
}
Comments are in code. In general, this is a right logic, but some adjusting may be needed. When the obstacle is small, the robot will be turning a short time. It is possible that the line is still under line sensors after the obstacle disappears so the robot will not continue following the wall, as intended. In that case, another state will be needed, to disallow line following a certain time after the obstacle is found.

To be honest, the code above will not avoid an obstacle successfully. There are different shapes of obstacles and other problems so You will have to make a more elaborate code. The example here is just that - an example, not a complete solution, just an idea how to do it. Implementing subtle details would obscur the general logic.

Many beginners use a different approach. After detecting an obstacle, they turn left (or right) for a fixed angle, then go ahead following a fixed trajectory, till the robot encounters the line again. This method is much easier to program, but will not be so good as the previous one.

Thermal sensor

If Your kit contains a thermal sensor, You will be able to find hot objects. The following simple code will show how to stop the robot during wall following, when it detects a heated object sideways.

void loop(){
	static uint32_t startMs = 0;              // A long integer variable to record hot object's detection moment
	if (millis() - startMs < 1000)            // If the object detected in the last 1000 ms (= 1 sec.), do nothing (motors already turned off).
		;
	else if (mrm_therm_b_can->reading() > 25) {     // If a hot object detected, record time and stop motors.
		startMs = millis();
		stop();
	}
	else if (rightFront() > 100)       // Follow wall - too far, go closer
		go(80, 20);
	else                                      // Follow wall - to close, go away
		go(20, 80);
}
The objective could have been reached simpler: using Arduino delay(1000) function. This is not a good way to do it. delay() would stop all the other activities: exchanging CAN Bus messages, LED blinking, etc. In this example it is not important, but this is a bad programming pattern. Always use the one above.

Next steps

We covered all the devices in this basic kit. In the next lesson, we will assemble these pieces of code in a small program that will do tasks similar to ones found in RCJ competitions.