Webots Tutorial: Simple Obstacle Avoidance Robot

Introduction

Now we will begin addressing topics related to programming robot controllers. We will design a simple controller to avoid the obstacles (boxes) created in the previous tutorial.

This tutorial will introduce you to the basics of robot programming in Webots. By the end of this chapter, you should understand the link between scene tree nodes and the controller API, how to initialize and clean up the robot controller, how to initialize robot devices, how to obtain sensor values, how to command actuators, and how to write a simple feedback loop.

This tutorial only addresses the correct usage of Webots functions. The study of robot algorithms is beyond the scope of this tutorial, so it will not be elaborated here. Some basic programming knowledge is required to handle this chapter.

1. Creating a New World

Here we do not intend to create a new environment from scratch; instead, we will save the simulation environment created in the previous article as avoid_collision. We will modify our controller based on this simulation to achieve simple obstacle avoidance functionality. To better demonstrate the obstacle avoidance effect, we will add a few more boxes.

Webots Tutorial: Simple Obstacle Avoidance Robot

2. E-puck Model

Controller programming requires some information related to the e-puck model. To write the collision avoidance algorithm, we need to read the values from the 8 infrared distance sensors located around its turret and drive the two wheels based on the sensor readings. The distribution of the distance sensors is shown in the figure below.

The distance sensors are modeled by 8 DistanceSensor nodes in the robot hierarchy. These nodes are referenced by their name fields (from ps0 to ps7). We will explain how to define these nodes later.

For now, I just need guidance on how to access the DistanceSensor nodes through the relevant API in Webots (defined in webots/distance_sensor.h).

The values returned by the distance sensors are scaled between 0 and 4096 (linearly varying with distance). A value of 4096 indicates a high amount of light detected (an obstacle is close), while 0 indicates no light detected (no obstacle).

For more documentation on API functions and descriptions of each node, please refer to the Webots reference manual.

Webots Tutorial: Simple Obstacle Avoidance Robot

3. Obstacle Avoidance Controller

In this chapter, we will write a controller that enables the robot to perform simple collision avoidance behavior. Its simple operating principle is as follows: while the robot moves forward, it continuously monitors the readings from the 8 distance sensors to detect if there are obstacles. If there are, it will turn in the direction without obstacles. Similarly, we will create a new controller named avoid_collision.

Webots Tutorial: Simple Obstacle Avoidance Robot

Link the robot to our new controller.

Webots Tutorial: Simple Obstacle Avoidance Robot1. Load Header Files

At the beginning of the controller file, add header files related to the Robot, DistanceSensor, and Motor nodes to be able to use the corresponding APIs:

#include <webots/Robot.hpp>
#include <webots/DistanceSensor.hpp>
#include <webots/Motor.hpp>

After the include statements, add a macro that defines the duration of each physical step. This macro will be used as a parameter for the Robot::step function and will also be used to enable devices. This duration is specified in milliseconds and must be a multiple of the value in the WorldInfo node’s basicTimeStep field.

#define TIME_STEP 64

Finally, the namespace required for Webots classes.

using namespace webots;

Some code is automatically added when creating a new controller in Webots, and we can modify it as needed.

2. Main Function Section

The main function is where the controller program begins execution. The parameters passed to the main function are given by the Robot node’s controllerArgs field. The default template for the main function is as follows:

// entry point of the controller
int main(int argc, char **argv) {
  // create the Robot instance.
  Robot *robot = new Robot();
  // initialize devices
  // feedback loop: step simulation until receiving an exit event
  while (robot->step(TIME_STEP) != -1) {
    // read sensors outputs
    // process behavior
    // write actuators inputs
  }
  delete robot;
  return 0; //EXIT_SUCCESS
}

3. Adding Functionality

Next, we will add the functionalities we need to the main function. First, we initialize the distance sensors.

// Initialize devices
DistanceSensor *ps[8];
char psNames[8][4] = {  "ps0", "ps1", "ps2", "ps3",  "ps4", "ps5", "ps6", "ps7"}; // Enable sensors by name
for (int i = 0; i < 8; i++) {
  ps[i] = robot->getDistanceSensor(psNames[i]);
  ps[i]->enable(TIME_STEP);
}

Initialize the motors:

// Get motor devices
Motor *leftMotor = robot->getMotor("left wheel motor");
Motor *rightMotor = robot->getMotor("right wheel motor"); // Set motors to velocity control mode
leftMotor->setPosition(INFINITY);
rightMotor->setPosition(INFINITY);
leftMotor->setVelocity(0.0);
rightMotor->setVelocity(0.0);

In the while main loop, after the comment // read sensors outputs, add the following code to read the distance sensor values:

// read sensors outputs
double psValues[8];
for (int i = 0; i < 8 ; i++)
  psValues[i] = ps[i]->getValue();

In the main loop, after // process behavior, add the following code to detect if a collision occurs (i.e., if the values returned by the distance sensors exceed a threshold):

// detect obstacles
bool right_obstacle =  psValues[0] > 80.0 ||  psValues[1] > 80.0 ||  psValues[2] > 80.0;
bool left_obstacle =  psValues[5] > 80.0 ||  psValues[6] > 80.0 ||  psValues[7] > 80.0;

Finally, drive the wheels based on the obstacle information as follows:

#define MAX_SPEED 6.28...
// initialize motor speeds at 50% of MAX_SPEED.
double leftSpeed  = 0.5 * MAX_SPEED;
double rightSpeed = 0.5 * MAX_SPEED;
// modify speeds according to obstacles
if (left_obstacle) {
  // turn right
  leftSpeed  = 0.5 * MAX_SPEED;
  rightSpeed = -0.5 * MAX_SPEED;
} else if (right_obstacle) {
  // turn left
  leftSpeed  = -0.5 * MAX_SPEED;
  rightSpeed = 0.5 * MAX_SPEED;
}
// write actuators inputs
leftMotor->setVelocity(leftSpeed);
rightMotor->setVelocity(rightSpeed);

After adding the relevant functionalities, we compile the code.

4. Complete Code

#include <webots/Robot.hpp>
#include <webots/DistanceSensor.hpp>
#include <webots/Motor.hpp> // time in [ms] of a simulation step
#define TIME_STEP 64
#define MAX_SPEED 6.28 // All the webots classes are defined in the "webots" namespace
using namespace webots; // entry point of the controller
int main(int argc, char **argv) {
  // create the Robot instance.
  Robot *robot = new Robot();   // initialize devices
  DistanceSensor *ps[8];
  char psNames[8][4] = {    "ps0", "ps1", "ps2", "ps3",    "ps4", "ps5", "ps6", "ps7"  };   for (int i = 0; i < 8; i++) {
    ps[i] = robot->getDistanceSensor(psNames[i]);
    ps[i]->enable(TIME_STEP);
  }   Motor *leftMotor = robot->getMotor("left wheel motor");
  Motor *rightMotor = robot->getMotor("right wheel motor");
  leftMotor->setPosition(INFINITY);
  rightMotor->setPosition(INFINITY);
  leftMotor->setVelocity(0.0);
  rightMotor->setVelocity(0.0);   // feedback loop: step simulation until an exit event is received  while (robot->step(TIME_STEP) != -1) {
    // read sensors outputs
    double psValues[8];
    for (int i = 0; i < 8 ; i++)
      psValues[i] = ps[i]->getValue();     // detect obstacles
    bool right_obstacle =      psValues[0] > 80.0 ||      psValues[1] > 80.0 ||      psValues[2] > 80.0;
    bool left_obstacle =      psValues[5] > 80.0 ||      psValues[6] > 80.0 ||      psValues[7] > 80.0;     // initialize motor speeds at 50% of MAX_SPEED.
    double leftSpeed  = 0.7 * MAX_SPEED;
    double rightSpeed = 0.7 * MAX_SPEED;
    // modify speeds according to obstacles
    if (left_obstacle) {
      // turn right
      leftSpeed  = 0.7 * MAX_SPEED;
      rightSpeed = -0.7 * MAX_SPEED;
    }
    else if (right_obstacle) {
      // turn left
      leftSpeed  = -0.7 * MAX_SPEED;
      rightSpeed = 0.7 * MAX_SPEED;
    }
    // write actuators inputs
    leftMotor->setVelocity(leftSpeed);
    rightMotor->setVelocity(rightSpeed);
  }   delete robot;
  return 0; //EXIT_SUCCESS

After saving the world and resetting the simulation, we can see the following running effect.

Webots Tutorial: Simple Obstacle Avoidance Robot

Introduction to Quadruped Robot Control and Simulation The course will use CoppeliaSim (V-rep) as the physical simulation engine and Matlab as the programming language to implement basic motion simulation of a 12-degree-of-freedom quadruped robot.

Webots Tutorial: Simple Obstacle Avoidance Robot

(Scan the QR code to view course details)

Leave a Comment