Giter Club home page Giter Club logo

ddbot's Introduction

Differential Drive Robot

PlatformIO Registry

This library provides an interface to easily control a differential wheeled robot using an Arduino-compatible microcontroller.

The expectation is that you have an even number of motors, with one half on either side of a structure that looks like a rover or a car. You then control the direction the robot moves in by controlling which motors turn on and in which direction. For example, if the left motors spin forward and the right motors spin backwards, the robot will rotate clockwise.

Check out line-follower-std and line-follower-smooth for two examples where this library is used in a project.

Overview of the library

In general, using this library involves the following steps. Don't worry if you don't understand the specific methods yet, we'll look at them in more detail later.

  1. Create an instance of the relevant class. You can either set the pin numbers arrays property of the instances, or pass them as arguments to the constructor

    // either
    DDBot bot();
    
    // or
    uint8_t directionPins[4] = {2, 3, 4, 5};
    DDBot bot(directionPins);
  2. Initialize the pin modes and any other settings. You just have to call the function (usually in void setup()).

    void setup() {
        bot.setPinModes();
    }
  3. Now you're all set! You can now call the direction and speed methods wherever like (void loop(), void setup(), or one of your own functions).

    bot.forward();
    delay(2000);
    
    bot.forward(100);
    delay(1000);
    
    

Types of robots offered by the library

There are classes for two kinds of robots included in this library.

  • Standard Differential Drive Robot

    This is a normal differential drive robot, with motors on each side. It is what we described above: the direction is controlled by controlling which motors turn on and in which direction.

    The class for this is DDBot.

  • Forward-biased Differential Drive Robot

    The physical robot for this can be the same as a standard differential drive robot, but the control is different. In this case, the robot's motor's are always set to move forward. The direction is controlled by varying the speeds of the motors. In certain cases (like line followers), it is expected that this library leads to more less jerky motion of the robot.

    The class for this is ForwardDDBot.

Standard Differential Drive Robot

  1. The first step for using this library is to include the DDBot.h header file and create an instance of the DDBot class.

    In doing so, you can communicate to the library which pins you'll use for the motors.

    This is based on two arrays, each of type uint8_t (basically just an integer).

    • directionPins[4]

      This sets the pins for the direction of the motors. The first two pins are for the left motors, and the last two are for the right motors. The first pin in each pair is for the first motor, and the second is for the second motor. These can be any DIO pins (though it's recommended to avoid pins 0 and 1, as they are used for serial communication with your computer).

      This is a compulsory argument and you must specify this before setting the pin modes or using the direction/speed methods.

    • PWMPins[2]

      This sets the pins for the speed of the motors. The first pin is for the left motors, and the second is for the right motors. These pins must be PWM pins (i.e. pins 3, 5, 6, 9, 10, or 11 on an Arduino Uno). You can check the Arduino Reference for analogWrite() to see which pins are PWM pins on your board.

      This is an optional argument. If you don't specify this, the library provide only the direction control and ignores the speed control.

    The easiest way to set these pins is to pass them as an array to the constructor. For example, if you want to use pins 2, 3, 4, and 5 for the direction pins, and pins 10 and 11 for the speed pins, you can do the following:

    #include <DDBot.h>
    
    uint8_t directionPins[4] = {2, 3, 4, 5};
    uint8_t speedPins[2] = {10, 11};
    
    DDBot bot(directionPins, speedPins);

    You can also set the pin arrays after creating the instance. For example, if you want to use pins 2, 3, 4, and 5 for the direction pins, and pins 10 and 11 for the speed pins, you can do the following:

    #include <DDBot.h>
    
    DDBot bot();
    
    uint8_t directionPins[4] = {2, 3, 4, 5};
    uint8_t speedPins[2] = {10, 11};
    
    for (int i = 0; i < 4; i++) {
        bot.directionPins[i] = directionPins[i];
    }
    
    for (int i = 0; i < 2; i++) {
        bot.PWMPins[i] = speedPins[i];
    }
  2. Now that you've created an instance of the DDBot class, you can set the pin modes. This is done by calling the setPinModes() method. This method takes no arguments and returns nothing.

    This method must be called before you can use the direction and speed methods.

    If you have not specified the speed pins, this method will only set the direction pins to OUTPUT mode. If you have specified the speed pins, this method will set both the direction and speed pins to OUTPUT mode.

    void setup() {
        bot.setPinModes();
    }
  3. Now you're all set! You can now call the direction and speed methods wherever like (void loop(), void setup(), or one of your own functions).

    Let's look at the different methods you can call.

    • setSpeed()

      This method sets the speed of the motors. The speeds are specified as integers between 0 and 100, where 0 is stopped and 100 is full speed.

      If you pass in only one argument, it will set the speed of both motors to that value. If you pass in two arguments, it will set the speed of the left motors to the first argument, and the speed of the right motors to the second argument.

      Note: If you have not specified the speed pins, this method will do nothing.

    • writeDirections()

      This method lets you directly set which motor spins backwards and which motors spins forwards. You can use this if you want to add more complex control to your robot.

      The arguments correspond to the pins required by something like an L293D or an L298 H-bridge. See How to Mechatronics' tutorial for more information.

      Here is how this function is declared, so you can see the parameters:

      void writeDirections(bool leftForward, bool leftBackward, bool rightForward, bool rightBackward);

      You can optionally pass in none, one, or two additional arguments for speed control. Just like the setSpeed() method, if you pass in only one argument, it will set the speed of both motors to that value. If you pass in two arguments, it will set the speed of the left motors to the first argument, and the speed of the right motors to the second argument.

    • The direction methods

      There are several methods for controlling the direction of the robot, and all of them follow the same pattern.

      You can optionally pass in none, one, or two additional arguments for speed control. Just like the setSpeed() method, if you pass in only one argument, it will set the speed of both motors to that value. If you pass in two arguments, it will set the speed of the left motors to the first argument, and the speed of the right motors to the second argument.

      The following methods are available, and their purpose can be inferred from their name.

      • forward()
      • backward()
      • left() (this has a forward bias)
      • right() (this has a forward bias)
      • clockwise() (the robot rotates in place)
      • counterclockwise() (the robot rotates in place)
    • stop()

      This method stops the robot by setting all direction pins to LOW.

      It does not affect the PWM output pins, so if you have specified the speed pins, the motors will resume spinning at the last speed you set.

Forward Differential Drive Robot

One of the key concepts behind this type of robot is that it relies on open-loop control. If you specify a change in speed or direction, it will not be set immediately. Instead, it will be set gradually over time. This is done to prevent the robot from jerking around.

So, when you call a direction method, it just sets two variables internally. You must then call another method in each iteration of your loop() function to update the actual values being written to the robot.

  1. The first step for using this library is to include the ForwardDDBot.h header file and create an instance of the ForwardDDBot class.

    Just like the DDBot class, you can pass in the direction and speed pins to the constructor. However, the ForwardDDBot class has a two additional arguments.

    These additional arguments and the PWMPins argument are both compulsory.

    • maxSpeed

      This sets the maximum speed value that will be written to the motors. This is useful if you want to limit the maximum speed of the robot. This value must be between 0 and 100.

    • adjustment

      This is a value between 0 and 1 that will be multiplied by the speed value to produce a "slow" speed value. This is used to steer the robot without setting any motor to 0 and thus not stopping the robot.

    You set the direction and speed pins just like you would with the DDBot class. A similar philosophy is used for the additional maxSpeed and adjustment arguments.

    #include <ForwardDDBot.h>
    
    uint8_t directionPins[4] = {2, 3, 4, 5};
    uint8_t speedPins[2] = {10, 11};
    
    ForwardDDBot bot(directionPins, speedPins, 100, 0.5);

    You should always call the calculateAdjustedSpeed() method after you set the adjustment or maxSpeed values, after calling the init() method.

  2. Now that you've created an instance of the ForwardDDBot class, you can set the pin modes and the initial speed and direction. This is done by calling the init() method. This method takes no arguments and returns nothing.

    This method must be called before you can use the direction and speed methods.

    This method would set the pin modes, set the "slow" speed value, and set the direction to forward. It also initializes the motors to full speed, to help with the initial acceleration.

    void setup() {
        bot.init();
    }
  3. Now you're all set! You can now call the direction and speed methods in a continuous loop.

    Though this inherits all the methods from the DDBot class, only four implement open-loop control. These are left(), right(), centre(), and stop(). They don't take any argument.

    Remember that you must call the write() method in each iteration of your loop() function to update the actual values being written to the robot.

    Here is a simple example. We'll assume that we get the user's input from some getUserCommand() function as a single character. Then depending on the character, we'll set the direction of the root.

    void loop() {
        switch (getUserCommand()) {
            'C':
                bot.centre();
                break;
    
            'L':
                bot.left();
                break;
    
            'R':
                bot.right();
                break;
    
            default:
                bot.stop();
                break;
        }
    
    
        bot.write();
    
        // Wait between loop iterations to allow the motors to smoothly change or maintain their direction.
        delay(10);
    }

ddbot's People

Contributors

eccentricorange avatar

Stargazers

 avatar  avatar

Watchers

 avatar

ddbot's Issues

Consider creating a simple `setSpeed` function.

would allow controlling both speed and direction with just two parameters.

void DDBot::setSpeed(int leftSpeed, int rightSpeed) {
        digitalWrite(directionPins[0], left > 0);
        digitalWrite(directionPins[1], left < 0);
        digitalWrite(directionPins[2], right > 0);
        digitalWrite(directionPins[3], right < 0);

        analogWrite(PWMPins[0], abs(leftSpeed));
        analogWrite(PWMPins[1], abs(rightSpeed));
}

Do not use `this`

Generally the use of the this pointer is frowned upon in C++, there is really no reason to use it. In the DDBot constructors if the input parameter name was different from the field the values were being assigned to then the this pointer would not be necessary.

DDBot::DDBot(uint8_t directionPinsIn[4]) {
    for (size_t i = 0; i < 4; i++) {
        directionPins[i] = directionPinsIn[i];
    }
}

Setting pins must use arrays

DDBot/src/DDBot.h

Lines 37 to 41 in ee01f09

// the constructors are responsible for setting the pin numbers from the
// arguments to the class properties
DDBot(); // allow the user to directly set the arrays
DDBot(uint8_t directionPinsIn[NUMBER_OF_DIRECTION_PINS], uint8_t PWMPinsIn[NUMBER_OF_PWM_PINS]);
DDBot(uint8_t directionPinsIn[NUMBER_OF_DIRECTION_PINS], uint8_t PWMPinIn = 0);

Add Serial example

Create an example that uses Serial/USB input to control the robot. For example, if the user sends an 'F', the robot should move forward. Additionally, it should be capable of transmitting the command to another robot.

Consider using physical units

Check the possibility of converting the speed parameters to physical units, such as a percentage instead of the 0-255 value enforced by the Arduino's analogWrite() function.

Incorporate CRSE Feedback

I recently submitted the code up to 5e25311 to the Code Review Stack Exchange: https://codereview.stackexchange.com/questions/284152/arduino-library-to-simplify-differential-drive-robots

This issue will tackle changes based on the two answers received up till the time of writing.

Based on Answer 1

  • #5
    This would allow controlling both speed and direction with just two parameters.

      void DDBot::setSpeed(int leftSpeed, int rightSpeed) {
          digitalWrite(directionPins[0], left > 0);
          digitalWrite(directionPins[1], left < 0);
          digitalWrite(directionPins[2], right > 0);
          digitalWrite(directionPins[3], right < 0);
    
          analogWrite(PWMPins[0], abs(leftSpeed));
          analogWrite(PWMPins[1], abs(rightSpeed));
      }
    
  • #6
    Check the possibility of converting the speed parameters to physical units, such as a percentage instead of the 0-255 value enforced by the Arduino's analogWrite() function.

  • #7
    You mention "feedback control" in your comments, however what you have implemented does not rely on feedback at all, instead it's an open-loop controller to smooth the transition from one speed to another. This will indeed help smooth the motion of the robot, but the way you implemented it is very naïve.
    At the very least, the documentation should communicate accurately that it is an open-loop system, and that it is not a closed-loop system.

  • #8
    The mathematics in the ForwardDDBot::write() method is done with 8-bit integers, and there is significant chance of overflow. So it would be better to use a float or a 16-bit integer instead.

Based on Answer 2

  • #9
    Generally the use of the this pointer is frowned upon in C++, there is really no reason to use it. In the DDBot constructors if the input parameter name was different from the field the values were being assigned to then the this pointer would not be necessary.

      DDBot::DDBot(uint8_t directionPinsIn[4]) {
          for (size_t i = 0; i < 4; i++) {
              directionPins[i] = directionPinsIn[i];
          }
      }
    
  • #10
    Some values can be defaulted, for example:

      DDBot(uint8_t directionPins[4], uint8_t PWMPins[2]);
      DDBot(uint8_t directionPins[4], uint8_t PWMPin = 0);
    
  • #11
    Some values like the number of pins (4 DIO pins, 2 PWM pins) might be better as constants, so they can be changed from one place.
    This is the concept of a magic number.

  • #12
    These are not necessary in the .cpp files.

      #ifndef DDBot_cpp
      #define DDBot_cpp
    
          ...
          
      #endif  // DDBot_cpp
    
  • #13
    Each of DDBot and ForwardDDBot should be in their own .cpp and .h files. Inheritance is a good should be handled by including the base class header file in the derived class header file.

  • #14
    This improves readability, and is a good practice.

Clarify about closed-loop control

You mention "feedback control" in your comments, however what you have implemented does not rely on feedback at all, instead it's an open-loop controller to smooth the transition from one speed to another. This will indeed help smooth the motion of the robot, but the way you implemented it is very naïve.

At the very least, the documentation should communicate accurately that it is an open-loop system, and that it is not a closed-loop system.

One class per file

Each of DDBot and ForwardDDBot should be in their own .cpp and .h files. Inheritance is a good should be handled by including the base class header file in the derived class header file.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.