0

Arduino tečaj: srebro

8. lesson

Ovo je dio cjelovitog tečaja Arduino-a.
  1. Štampajte dijelove koristeći ovaj Sketchup model RescueLine.zip.
  2. Sastavite robota koristeći upute za sastavljanje.
  3. Spojite kablove koristeći plan spajanja.
  4. Programirajte robota.
    1. Osnove.
    2. Praćenje crte.
    3. Naredbe.
    4. LIDARi.
    5. Prekidač, IMU, 8x8 LED.
    6. Praćenje zida.
    7. Srebro (ova stranica).
    8. 8x8 LED programiranje.
    9. Robotska ruka.
    10. Raspberry Pi.
    11. Prepoznavanje kamerom, OpenCV.
    12. Arduino i RPI surađuju.

Silver

All the ML-R reflectance sensors are designed to detect a highly reflective surface (like a silver foil or a mirror), besides bright surface (like white), and dark (like black). To achieve this, the sensors must be calibrated. Calibration of this type is included in ReflectanceSensors library, version 0.3 or higher, downloadable from https://www.github.com/PribaNosati/MRMS. Check beginning of ReflectanceSensors.h to be sure the version is appropriate. When calibrating black part, more Your robot perpendicularly to a black line on a white surface, as before.

You should adjust height of the sensors to obtain optimal results: a good difference between black and white but also between white and silver. If the white and silver values are similar, You will have to lift the sensors. However, lifting them too much may have a negative influence on black measuring. A solution may be to use a separate sensor to ready only silver. Find the sensor that measures the lowest values and lift it as much as needed.

Again, enter the orange changes:

#include <Commands.h> //ML-R library You use to issue commands to Your robot.
#include <Displays.h> //Libraray for 8x8 3-color display
#include <IMUBoschBNO055.h> //IMU
#include <Motors.h> //Library Motors will be used. It contain motor-control functions.
#include <PID.h> //Library that implements a very simple proportional–integral–derivative controller. Check Wikipedia.
#include <ReflectanceSensors.h>
#include <Switches.h>
#include <VL53L0Xs.h> //STM VL53L0X is the name of LIDAR chip.

Commands commands(&Serial2); // Object for commands is created.
Displays displays;
IMUBoschBNO055 imu(&Serial2);
Motors motors(true, &Serial2); //motors is an C++ object of class Motors. If You do not understand this, never mind. Just alwasys use motors with this line.
PID pidLine(20, 15, 0.01, &Serial2); //PID object: pidLine. After studying Wikipedia, alter the 3 parameters.
PID pidWallAngle(10, 1, 0, &Serial2); //Angle error. 10 and 1 are arbitrary parameters: find the correct values for Your robot.
PID pidWallDistance(10, 1, 0, &Serial2); // Distance to wall error. 10 and 1 are arbitrary.
ReflectanceSensors reflectanceSensors(0, 0.25, &Serial2);
Switches switches;
VL53L0Xs lidars(&Serial2); //LIDARs

void setup() {
  Serial.begin(115200);    // Serial (USB) communication to our computer is established. You call function begin of the object Serial with 1 argument: 115200.
  Serial2.begin(9600);     // Start serial communication with Bluetooth adapter.
  Wire.begin();            //Start I2C bus.
  delay(1000);             // Do nothing for 1000 ms (1 sec.) - wait for the USB connection.
  Serial.println("Start"); // Display "Start" on PC's screen. Function println() prints the argument ("Start") and jumps to a new line.
  
  //The following 2 lines will create 2 commands. First parameter is shortcut that invokes the command. The second 
  //is a function pointer. Again, if that term doesn't ring a bell, it will be no problem. Just write here the name of the function You 
  //want to invoke, for example, after typing "lin" in your mobile phone. The third parameter is an explanation.
  commands.add("dis", displayTest, "Display test");
  commands.add("imu", imuTest, "IMU test");
  commands.add("cal", calibrate, "Calibrate black");
  commands.add("caa", calibrateAll, "Calibrate all");
  commands.add("lid", lidarsTest, "Test LIDARs");
  commands.add("lin", line, "Follow line", 33); //Pressing the switch (connected to pin 33) will execute this command.
  commands.add("swi", switchesTest, "Switches test");
  commands.add("wal", wall, "Follow left wall");
  
  //Display 8x8
  displays.add(0x70);

  //IMU, false as parameter sets its I2C address to 0x28 instead of 0x29.
  imu.add(false);
  
  //LIDARs. Each add command uses 2 parameters: digital pin number, and a chosen I2C (hexadecimal) address.
  //Prefix "0x" precedes hexadecimal number. As every LIDAR uses the same I2C address, 0x29, there must be a way to resolve the conflicts, and there is. During boot phase the library enables all the LIDARs, one by one, using 0x29, and changes their addresses into the ones indicated as the second parameter.
  lidars.add(13, 0x22);    
  lidars.add(17, 0x24);
  lidars.add(29, 0x23); 
  lidars.add(31, 0x26);
  lidars.add(30, 0x25); 
  lidars.add(12, 0x27);
  lidars.begin();

  //In the following 4 lines we will add 4 motors. First 2 parameters, numbers (like 3, 5), are pins that are connected
  //to motor controller. Third parameter (true or false) is true when it is a left motor, otherwise false. There is an optional fourth parameter in the
  //first 2 lines and there can be even more of them. Optional (default) parameters do not have to be mentioned.
  motors.add(3, 5, true, true);
  motors.add(23, 4, false);
  
  //10 reflectance sensors will be added. ML-R libraries always include new elements (like sensors and motors) using the function add().
  //First parameter is analog pin's name, second millimeters from the central longitudinal axis (negative numbers for left sensors).
  //The last parameter is always true and enables that sensor for the library's line following algorithm.
  reflectanceSensors.add(A11, -49.5, true); 
  reflectanceSensors.add(A10, -38.5, true);
  reflectanceSensors.add(A13, -27.5, true);
  reflectanceSensors.add(A12, -16.5, true);
  reflectanceSensors.add(A1, -5.5, true);
  reflectanceSensors.add(A14, 5.5, true);
  reflectanceSensors.add(A0, 16.5, true);
  reflectanceSensors.add(A2, 27.5, true);
  reflectanceSensors.add(A17, 38.5, true);
  reflectanceSensors.add(A15, 49.5, true);
  reflectanceSensors.eepromRead();//Reading EEPROM calibration data into RAM.
  
  //Switches
  switches.add(33);

  displays.fillScreen(0, LED_GREEN);
  displays.writeDisplay(0);
}

void loop() {
  //List all the available commands and wait for user's input.
  do {
    commands.list();
  } while (commands.prompt());
}

/**Function for reflectance sensors calibration
*/
bool calibrate() {
  reflectanceSensors.calibrate();
  return true;
}

/* Calibrate white, black and silver
@return true
*/
bool calibrateAll()
{
  reflectanceSensors.calibrate(5, ALL);
  return true;
}

/** Any key pressed?
@return - true if so.
*/
bool commandAvailable() {
  return commands.available();
}

bool displayTest() {
  displays.test(0, commandAvailable);
  return true;
}

/**The next 6 functions convert mm to cm and introduce some more meaningful names. First of the last 2 letters (L, F or R) is robot's side and the one following determines the sensor on that side. For example LB - left side of the robot (L), back (rear sensor, B). F is front, L is left, R is right, B is back (rear). 
@return - distance in cm.
*/
float distanceLB() { return lidars.distance(0) / 10.0; }
float distanceLF() { return lidars.distance(1) / 10.0; }
float distanceFL() { return lidars.distance(2) / 10.0; }
float distanceFR() { return lidars.distance(3) / 10.0; }
float distanceRF() { return lidars.distance(4) / 10.0; }
float distanceRB() { return lidars.distance(5) / 10.0; }

/*All the ML-R libraries invoke this function when something goes wrong. Here we stop the motors, display the error
message which enters the function as the only argument ("message") and then enter an endless loop. while(1) never ends as the condition
(1) is always true. The only command that is in the loop is ";" (empty command - does nothing).
@param message
*/
void error(String message){
  motors.go(0, 0);         //stop the motors to avoid harm
  Serial.println(message); //display error message
  while(1)                 //never stop
    ;                      //do nothing
}

bool imuTest() {
  imu.test(commandAvailable);
  return true;
}

/** Test LIDARs
@return - always true
*/
bool lidarsTest() {
  lidars.test(commandAvailable);
  return true;
}

/* A line-following function, can invoked by a command.
*/
bool line(){
  while (!commandAvailable()) { // There is a way to stop the line following - press a key.
    bool found, nonLineFound;
    //The following function returns position of the black line against the longitudinal centre of the robot. found and notFound are output
    //parameters. found is true if a line is found (at least one sensor black). nonLineFound not found (that at least one sensor doesn't detect black).
    float centerMm = reflectanceSensors.findLine(found, nonLineFound);
    //centerMm is in fact error in line following. It should be 0. PID algorithm uses it as an input and calculates error correction:
    float correction = pidLine.calculate(centerMm);

    const int speedAhead = 60; //Average speed of the left and right motor. You can change this number.
    if (found)
      motors.go(speedAhead + correction, speedAhead - correction);//Here we control the motors.
    else
      motors.go(50, 50); //If no line, go straight ahead.
  }
  motors.go(0, 0);
  return true;
}

bool switchesTest() {
  switches.test(commandAvailable);
  return true;
}

/** Follow left wall
@return - always true
*/
bool wall() {
  while (!commandAvailable())
    wall(true, 10); // true - left wall, 10 - follow it at 10 cm.
  motors.go(0, 0);
  return true;
}

/** Follow wall
@param isLeftWall - left wall (true) or right (false)
@param wallDistanceNeeded - wall following distance in cm.
*/
void wall(bool isLeftWall, float wallDistanceNeeded) {

  //Determine front and back distances to the wall
  float wallDistanceFront = isLeftWall ? distanceLF() : distanceRF();
  float wallDistanceRear = isLeftWall ? distanceLB() : distanceRB();
  const float betweenLIDARs = 13.5; //This number is needed to calculate distance to the wall.

  //Check Your knowledge of primary school math to prove that this calculation yields distance to the wall:
  float distanceToWallMeasured = betweenLIDARs *(wallDistanceFront + wallDistanceRear) / 2.0 /
	hypot(betweenLIDARs, wallDistanceFront - wallDistanceRear);
  float tooFar = distanceToWallMeasured - wallDistanceNeeded; //This is the error in distance - we will try to make it 0.
  float correction = pidWallDistance.calculate(tooFar) * (isLeftWall ? -1 : 1); //PID calculates the correction for position

  //This is the second error, direction (should be parallel to the wall)
  float clockwiseTooMuch = (wallDistanceFront - wallDistanceRear) * (isLeftWall ? -1 : 1); // 2nd error.
  correction += pidWallAngle.calculate(clockwiseTooMuch); //PID calculates correction for the 2nd error and we add it to the first correction.

  const int speedAhead = 60; // Average speed ahead.
  motors.go(speedAhead + correction, speedAhead - correction);//Here we control the motors, by applying the cumulative correction for 2 errors.
}

We will now interrupt the incremental building of the robot's program to cover 2 simpler programs, for 8x8 LEDs and servo motors, before continuing to lesson 10 and introducing Raspberry Pi. You can skip the 2 lessons and immediately jump to RPI.

Previous lesson
Next lesson