BB2B

The BB2B example is inspired by the vehicles invented by Valentino Breitenberg. He created a number of conceptual vehicles which show life-like behaviour. By connecting sensors and motors together in a specific way, different behaviours can be observed.

All code for this is also found in the Roboid Control for Arduino repository as the BB2B example. The first configuration is found in the SwitchesConfiguration.h, the second in the UltraSonicConfiguration.h. Both configurations share the same behaviour as specified in the BB2B.ino code.

Our goal with this example is to make a robot which moves around in a space autonomously, avoiding objects. 

BB2B with infrared sensors, DRV8833 motor controller and an Arduino UNO
BB2B with infrared sensors, DRV8833 motor controller and an Arduino UNO

How it works

Each wheel will either rotate forward or backward with a fixed speed. When the robot is not colliding, both wheels will rotate forward and thus the robot will move forward.

As both switches are placed at the front of the robot, they will be able to detect whether the robot collides with something. Depending on which switch is closed, the robot will behave as follows:

  • Left swich is closed only: the robot will turn right
  • Right switch is closed only: the robot will turn left
  • Both switches are closed: the robot will drive backward

All of this is achieved by letting the left switch control the right motor and the right switch control the left motor. When the switch is open, the motor rotates forward, when it is closed, the motor rotates backward.

You will see that with this setup, the robot will be able to move around the space quite well. 

The code

The example contains a number of different configurations:

  • SwitchesConfiguration.h
  • UltraSonicConfiguration.h

Each configuration implements the same behaviour, but uses different hardware. In this document we will start with the Switches Configuration and come back to the other configurations at the end.As is common with Roboid Control, the code is split up in a number of sections:

  • Hardware setup
  • Roboid setup
  • Behaviour

Hardware setup

First of all, we use an Arduino Uno to control the roboid. You can change this in the Platform Project Configuration, but keep in mind that when you change the board, you probably also need to change the pins used to connect to the drivers and sensors.

BB2B with Arduino Uno, infrared obstacle detectors and a TB6612 motor controller
BB2B with Arduino Uno, infrared obstacle detectors and a TB6612 motor controller
DRV8833

In the Switches Configuration we use a DRV8833 as the motor controller (the picture above shows an TB6612, but it is very similar, see below). This controller has the benefit that the board is small, quite efficient and requires only 4 control wires from the arduino while still providing the possibility to control the motor speed and direction.

In the code, the DRV8833 is setup as follows:

#include "DRV8833.h"
DRV8833 drv8833 = DRV8833(6,  // pinAIn1, Orange wire
                          5,  // pinAIn2, Yellow wire
                          9,  // pinBIn1, Blue wire
                          10  // pinBIn2, Green wire
);

So we only need to specify which 4 pins are used to control the motor controller. When choosing the pin, keep in mind that the pins should support PWM signals.

IR proximity sensors

In the Switches Configuration we use IR proximity sensors to detect if the robot is close to a wall. Instead of these sensors, you could use microswitches instead as well. In that case, the robot will need to collide with a wall to change its direction while with the IR sensors, the robot can change its direction when it is nearing a wall.

Note however, that these sensors are sensitive to lighting conditions and the structure and color of the wall and may not give the best results. In general, Time-of-Flight or Ultrasonic sensors give better results. For the use of these latter sensors, see the end of this article.

The IR sensors are basically switches: when an object comes close, a digital pin is pulled low. Therefore we configure these sensors as digital inputs:

#include "Switch.h"
DigitalInput sensorLeft = DigitalInput(8  // Out, Purple wire
);
DigitalInput sensorRight = DigitalInput(2 // Out, Purple wire
);
Resulting code for hardware setup
#include "DRV8833.h"
DRV8833 drv8833 = DRV8833(6, // pinAIn1, Orange wire
5, // pinAIn2, Yellow wire
9, // pinBIn1, Blue wire
10 // pinBIn2, Green wire
); #include "Switch.h"
DigitalInput sensorLeft = DigitalInput(8  // Out, Purple wire
);
DigitalInput sensorRight = DigitalInput(2 // Out, Purple wire
);

Roboid setup

Now we should configure the roboid, placing the sensors and motors. 

sensorLeft.position.angle = -35;
sensorRight.position.angle = 35;
Sensor* sensors[]
= {&sensorLeft,
&sensorRight}; Perception* perception = new Perception(sensors, 2);

For the sensors we only specify the orientation, their position is not important here. We specify their horizontal angle relative to the forward direction: as the angles are specified in a clockwise manner, we have -35 degrees for the left sensor and 35 degrees for the right. 

These sensors are then used to setup the perception of the roboid. The perception module is taking care of detecting objects in the environment of the robot.

The motors are used to set up the propulsion of the robot, which takes care of moving the robot around:

DifferentialDrive* propulsion = 
new DifferentialDrive(
&drv8833.motorA, &drv8833.motorB);

As we use a differential drive, we create a new DifferentialDrive instance with the two motors connected to the A and B ports of the DRV8833 motor controller: the left motor is connected to port A and the right to port B. Of couse, this setup should match to the connection on the real robot.

Now we can create the Roboid and we are done:

Roboid* roboid = new Roboid(perception, propulsion);

Resulting code for Roboid setup

Roboid* RoboidSetup() {
sensorLeft.position.angle = -35;
sensorRight.position.angle = 35;
Sensor* sensors[] = {&sensorLeft,
&sensorRight};
Perception* perception = new Perception(sensors, 2);

DifferentialDrive* propulsion =
new DifferentialDrive(
&drv8833.motorA, &drv8833.motorB);

  Roboid* roboid = new Roboid(perception, propulsion); return roboid; }

Roboid Behaviour

The behaviour of the robot is specified in the loop function.

We first read the status of the sensors. We do this with perception. This abstracttion level makes it possible to change the sensors for something completely different, like a camera or lidar, while keeping the behaviour the same. So we specify that for object detection on the left, we look -30 degrees forward, while objects on the right can be detected 30 degrees from forward:

bool obstacleLeft = roboid->perception->ObjectNearby(-30);
bool obstacleRight = roboid->perception->ObjectNearby(30);
By default, this will make the roboid look for objects between -40 and -20 degrees. As our sensor is oriented -35 degrees to the left, it will notice objects deteced by this sensor. The default range of 10 degrees can be changed using a second parameter. For example, you can use a range of 20 degrees like this:
bool obstacleLeft = roboid->perception->ObjectNearby(-30, 20);
Depending on the fact of an object is detected left and/or right, the motors will be controlled. The left motor rotates forward (value = 1.0) when no object is detected on the right side and rotates backward (value = -1.0) when it does detect an object there. The same mechanism is used for the right motor and left sensor:
float leftMotorSpeed = obstacleRight ? -1.0F : 1.0F;
float rightMotorSpeed = obstacleLeft ? -1.0F : 1.0F;
Now we can set the motor speeds using the differential drive:
DifferentialDrive* diffDrive = (DifferentialDrive*)&roboid->propulsion;
diffDrive->SetTargetSpeeds(leftMotorSpeed, rightMotorSpeed);

Finally, we have to call the Update function of the roboid. This will update the perception and propulsion to make everything work. This function should be given a timestamp in milliseconds, so we use the millis() function for that.

We add a delay of 100 milliseconds to archieve an update rate of 10 Hz (10 updates per second):

roboid->Update(millis());
delay(100);
Resulting code for Roboid Behaviour
void loop() {
bool obstacleLeft = roboid->perception->ObjectNearby(-30); bool obstacleRight = roboid->perception->ObjectNearby(30); float leftMotorSpeed = obstacleRight ? -1.0F : 1.0F; float rightMotorSpeed = obstacleLeft ? -1.0F : 1.0F; DifferentialDrive* diffDrive = (DifferentialDrive*)&roboid->propulsion; diffDrive->SetTargetSpeeds(leftMotorSpeed, rightMotorSpeed);

roboid->Update(millis());
delay(100); }
The complete code for this setup can be found in the SwitchesConfiguration.h for the Setup and the BB2B.ino for the behaviour in the BB2B example.

Alternative configurations

BB2B with ultrasonic sensors, TB6612 motor controller and a ESP32 microcontroller
BB2B with ultrasonic sensors, TB6612 motor controller and a ESP32 microcontroller

Ultrasonic distance sensors & TB6612 motor controller

To use ultrasonic HC-SR04 sensors, only two changes are necessary. First we need to replace the DigitalInput sensors by UltrasonicSensors:

#include "UltrasonicSensor.h"
UltrasonicSensor sensorLeft = UltrasonicSensor(11, // pinTrigger, Brown wire
                                             8   // pinEcho, Purple wire
);
UltrasonicSensor sensorRight = UltrasonicSensor(4, // pinTrigger, Brown wire
                                              7  // pinEcho, Purple wire
);
Then we have to specify at which distance the motors need to reverse. This can be done by setting the triggerDistance of each ultrasonic sensor. The default distance is 1 meter, but here we want to set it to 30 centimeters:
sensorLeft.position.angle = -30;
sensorLeft.triggerDistance = 0.3F;
sensorRight.position.angle = 30;
sensorRight.triggerDistance = 0.3F;

Placement sensors[] = {&sensorLeft, &sensorRight};
Perception *perception = new Perception(sensors, 2);

Next, we replace the DRV883 with a TB6612 motor controller. As this has all the pins of the DRV8833 and we don't want to control the speed of the motors, we can use the same setup.

#include "TB6612.h"
TB6612 tb6612 = TB6612(6, // pinAIn1, Orange wire
5, // pinAIn2, Yellow wire
9, // pinBIn1, Blue wire
10, // pinBIn2, Green wire

The only thing we need to ensure that the PWMA and PWMB pins are pulled high to ensure that the motors are running at full speed:

BB2B with Arduino Uno, ultrasonic sensors and a TB6612 motor controller
BB2B with Arduino Uno, ultrasonic sensors and a TB6612 motor controller