0

Line Robot - RCJ Rescue Line

Task

Here we will continue learning how to solve some of the problems in RCJ Rescue Line competition. We will make a program that should be following a line in the line-following area and following a wall in the wall following area (a room). The areas are separated by a reflective, aluminum tape. During line following, there can be obstacles on the line, which have to be avoided.

Line following using mrm-ref-can's firmware

MRMS reflectance sensors 8x, CAN, analog, I2C (mrm-ref-can8) and MRMS reflectance sensors 9x, CAN, analog, I2C (mrm-ref-can) are quite useful sensors for line following. By now we have not been using some of the built-in capabilities but will do so now.

Calibration in firmware

If we do the calibration in the Arduino code, we will have some tasks:

  • to develop the algorithm,
  • to store the calibration permanently,
  • to retrieve the stored data on each robot's start.
A lot of work. So why not using the calibration that is built in sensors' ARM firmware? Let's check this option. Note this command in the menu:

cal - Calibrate refl.

If we choose it, mrm-ref-can's LED will be turned off for 5 seconds and will turn on for 1 second after that. These 2 events mark begin and end of the calibration process, during which should the robot go over black line and white area (moved by hand) in the way that is expected during the run. Minimum and maximum for every of the 9 transistors will be stored in board's flash and will be retained when the power is switched off.

How can we use the calibration data? We have 2 options.

Local calibration data

The data are now in sensor and we can store them in Arduino driver library in order not to ask every time the sensor to deliver them because that would slow down the program a little. There is a command for this purpose:

mrm_ref_can->calibrationDataRequest(0, true); // Read line sensor's calibration data.
First parameter, 0, chooses sensor 0. Second, "true", means that the function will block program flow until the sensor sends the data back to Arduino MRMS ESP32: Arduino, IMU, eFuse, BT, WiFi, CAN Bus (mrm-esp32). If the choice was "false", there would be no delay, but the next program statements would not be able to use the correct calibration data. They would be ready only after exchanging appropriate CAN Bus messages later. To use a blocking call is usually a poor choice, but here it is all right, as the function is called only once, and it would not be good that the robot starts following a line without the calibration data.

If we want to use the data, they can be recalled from local Arduino library, using a command, in this example to read the data (value in the middle between extreme bright and dark) for transistor 7:

calibrationDataGet(7);
Now we can compare analog readings to the calibration data and decide if the particular transistor detects black or white. For example, function dark(receiverNumberInSensor, deviceNumber), will return a boolean value as a result of comparing value of receiverNumberInSensor to the stored calibration value for that transistor. To get the value for transistor 7, the call will be dark(7) and will return true or false. deviceNumber is optional and is 0 if not specified.

No local data

The other option is to leave the calibration data in the sensor and read digital values. Each transistor's reading will be compared to the stored calibration data and the result will be digital: dark or not. This option will bring some benefits.

  • Much less messages are needed to convey the data: 1 short instead of 3 long. That will increase frequency of data exchange 3 times. Using firmware's default setup, the messages arrive each 3 or 4 ms, yielding frequency of at least 250 readings per second. If the robot goes 20 cm/sec., it will get a fresh reading every 0.08 mm.
  • Less local program logic is needed and less local processing time.

Line center

By using digital values it is not possible to calculate exact position of the line. To overcome this, the same message carrying digital values transfers line center as an analog value. If the line is under transistor 1, the value will be 1000, under transistor 2 it will be 2000, and so on until transistor 9, where it will be 9000. If no line detected (all sensors white), the value will be 0. If the line is between adjacent transistors, the value will be interpolated. For example, being 1/3 of the way between transistors 1 and 2, the value will be 1333. We will use this value in the program below.

Program

Lets change function lineFollow() it the following way:

void RobotLine::lineFollow() {
	static float lastLineCenter = 0;

	float lineCenterNow = lineCenter();                       // Result: -50 to 50.
	if (lineCenterNow < -40 || lineCenterNow > 40) {		// No line under inner sensors, including lost line.
											// Choice depending on the line center the last time it was detected, stored in variable lastLineCenter.
		if (lastLineCenter < -15) 			// Lost line right or line far right.
			go(127, -127); 		// Rotate in place.
		else if (lastLineCenter > 15) 		// Lost line left or line far left.
			go(-127, 127); 		// Rotate in place.
		else 								// Line was around the robot's center when lost. Therefore, it was interrupted
			go(127, 127); 		// Go straight ahead.
	}
	else { 									// Follow line
									// Maximum speed of the faster motor, decrease the other one.
		go(lineCenterNow < 0 ? 127 : 127 - lineCenterNow * 3, lineCenterNow < 0 ? 127 + lineCenterNow * 3 : 127);
		lastLineCenter = lineCenterNow; 			// Remember the line.
	}
}
The comments are in the code. You can change maximum speed (127) or change how much the robot turns (by changing "* 3" part). We can start the program with menu's command:
lin - FollowLine 

Run the program

Remember, object derived from base class ActionBase initiate the work. Look at the class ActionLineFollow in mrm-robot-line.h:

/** Follow a line.
*/
class ActionLineFollow : public ActionBase {
	void perform(){ ((RobotLine*)_robot)->lineFollow(); }
public:
	ActionLineFollow(RobotLine* robot) : ActionBase(robot, "lnf", "Line follow", 1) {}
};
As we learned, this action will display menu line "lnf - Line follow". Type "lnf" in the terminal application and the program will be continually executing ActionLineFollow's function, lineFollow() - thus following a line.

Work in progress...

Expected to be finished in October.

Servo

To test servo alignment, position it at 90º (central position), using program servoWrite() function, with desired angle as the only argument:

void loop(){
	if (setup())
		servo(90);
}
Use servo to position lidars better. In the above example of obstacle avoiding, the robot will not follow a small obstacle well. Using servoWrite(120), or some other angle, You can turn sensors into a more convenient position. Try this.

Work in progress...

Expected to be finished in October.