0

Line Robot - Basic programming

First program

Now, let's make Your first program. In MRMS_ESP32.ino there is function setup(). Locate line "robot = ..." and be sure that it looks like below:

void setup() {
	robot = new RobotLine(); // RobotLine, RobotSoccer, or Your custom robot
	robot->run();
}
robot (small r) will be an object of class Robot (capital R), even more, of the class RobotLine, which is a more special case of a general class Robot, as mentioned before. You could choose class RobotSoccer and You would get a soccer robot. By choosing RobotLine, You decided to get all its special features, like motor group, ability to follow a line, etc. All this data are separate from the soccer-robot code.

Now open 2 mrm-robot-line.* files located in C:\Users\[user name]\Documents\Arduino\libraries\mrm-robot. In mrm-robot-line.h there are declarations of some classes (beginning with word "class"): Your Robot line and some classes for robot's actions. In mrm-robot-line.cpp we have mostly implementations of the functions. That is the place where You should program robot's actions later.

In the same directory there are also files mrm-robot.*, which are used for a more general class Robot. We will not be using them much.

A simple function

We described action classes earlier and wrote that actions are meant to do some useful work. You will be making Your own action classes later, but first let's use an already existing action, just to get some results as soon as possible. We will use ActionLoop, an action which is declared in Robot (base class), files mrm-action.h and mrm-action.cpp, using a virtual function... All right, this is getting too complicated and it is not necessary to know to program Your robot. Remember, Robot class (for all robots) is something You needn't bother with. It can be a black box which produces some behaviour You need. In short this action will execute loop() function, which You will define, and that's what we will do now.

Therefore, find function loop() (functions are ordered alphabetically) in mrm-robot-line.cpp:


void RobotLine::loop() {
...
}

The test we tried earlier showed how lidar results look like and here You will learn how to use it in Your code. Change the code into this:

void RobotLine::loop(){
	print("%i\n\r", leftFront());
	delayMs(500);
}
Besides delayMs() function, there are 3 program lines that are front ends of some important concepts. Explanations follow.

Preprocessing

After an action is started (in this case: ActionLoop), its function (in this case: loop()) will being called continually, maybe 1000 times per second. There is some work that should be done in each run, but there are also actions that have to be done exactly once, most often in the first run. A good example is starting motors (described later). If You used monster-switch workflow control, this problem would impose some awkward solutions, like global variables. Action classes offer a more elegant solution: setup() function, which returns "true" only once, during the first run and gives You an opportunity to initialize whatever You need for that action. So, we can safely start the motors after that will will be run exactly once.

Accessing sensors

Now comes the print() line. print() is similar to Serial.print() in Arduino, but in prints to all the outputs, not just USB connected monitor. More about later. It accepts the first parameter as C-format print string and the rest of parameters are variables listed in the print format. Here, the format is just one integer ("%i") and end-line ("\n\r"). Value to be printed is leftFront() and this function returns distance as front - left lidars measures it.

Compile and run the program. As we learned, You can start an action (and its workhorse function, in this case loop()) by using a shortcut in Your mobile phone, for example. If You check ActionLoop class, You will find that its shortcut is "loo". Type "loo" in the terminal. The monitor should be displaying distances, as in the picture above.

delayMs()

This function is similar to Arduino's delay(), but, while waiting, it exchanges CAN Bus messages, blinks LED, and does other work. Always use it instead of delay().

Motors

In the following lessons the robot will start to move, so we have to prepare motor driving program part. While You can make Your program for that task, it will be easier to use the supplied one.

Find RobotLine::RobotLine() (called constructor) in mrm-robot-line.cpp and the lines we described earlier:

RobotLine::RobotLine() : Robot() {
	motorGroup = new MotorGroupDifferential(mrm_mot4x3_6can, 0, mrm_mot4x3_6can, 2, mrm_mot4x3_6can, 1, mrm_mot4x3_6can, 3);
...
This line defines motor controller for every wheel. Here we use the same one for all of them (mrm_mot4x3_6can). The 4 numbers are motor outputs of the controller.

Motor group

Without studying the details, we can say that motorGroup will be an object of class MotorGroupDifferential. To drive the robot, You can forget about this construction and just remember that go() will start the motors. It is not necessary, but stop() may also be handy. You experienced the same situation with Arduino's standard Serial object if You ever used Arduino sketches. You know what Serial.print() does and probably never bothered too much with Serial itself. In our case, we will nevertheless spend a few sentences just to get acquainted with our class. At the same time we will try to convince You a little more that object oriented programming is cool.

One way to use the motors is to start from scratch. ML-R motor controllers have defined CAN Bus interfaces where You will find commands, for example here. Use setSpeed() function and that's it. The motor is spinning. So, problem solved, why reinvent the wheel? Because the problem has not been solved. Your objective is to drive a robot, not to spin one motor, and that can be more complicated. You must coordinate a couple of motors, maybe use encoders, turn the robot (by coordinating the motors), use PID controller, drive omni-wheels, etc. Just try to move a soccer robot or try to manage maximum acceleration. Even the last issue, acceleration, cannot be solved for each motor separately, but only for the whole group. And so on. In the long run, You will spend much less time by using a ready-made class MotorGroup.

As with ActionBase, this class has a base class, MotorGroup, and derived classes: MotorGroupDifferential (tank-like motion) and MotorGroupStar (soccer-robot-like motion). Like You already did with RobotLine, You can choose the class You need in the instruction above (motorGroup = new...).

Turn the motors on

It is time to move the robot. Find again function loop() and change it into

void RobotLine::loop(){
	go(50, 90);
}
Upload the program and start it with menu command "loo" again. The robot should be moving forwards, turning to the left. First argument of the function (50) is speed of the left motors, second (90) of the right ones. Terminate the program by entering "x".