VEXcode, motor groups and drivetrain example

There have been quite a few requests for instructions on how to use motor groups and creating a drivetrain with more than 2 motors in VEXcode. I’m going to discuss these classes a little and provide a simple example.

What is a motor group ?
A motor group collects two or more motors together and allows control as if they were a single entity. Just about all motor class member functions (by that I mean spin, spinTo, setVelocity etc.) are available on the motor group class. We introduced motor groups with VEXcode preview 3, however, it has been improved since then and continues to be tweaked as we create each new iteration of the SDK that VEXcode uses. The code in this post is valid for VEXcode V1.0.1.

A motor group is created from instances of motors that already exist. It’s best to use global instances, we pass motors to the motor group constructor by reference (don’t worry about what that means) so if a motor is created as a local variable in a function, there’s a good change the motor group would have a reference to an object that may not exist anymore and the code will probably not do what you expect it to. VEXcode always creates global motor instances when you use the graphical configuration, and in general all the examples we have provided with VCS and VEXcode over that past year also use global instances.

A motor group can have a maximum of 16 motors.

What is a drivetrain ?
A drivetrain is another high level class we use to collect together motors and some other related parameters. A drivetrain needs to know which motors are used for the wheels on the left and right hand sides of the robot. A drivetrain does not handle complex drive systems such as X or H drives, however, it will work with a mecanum wheel based drive but you would have to add your own code for strafing.

A two wheel drivetrain only handles one motor on each side, this is what the VEXcode graphical configuration is capable of handling. With more than one motor on each side things get a little more complex. The drivetrain constructor needs a motor group provided for left and right wheels, the motors also need configuring correctly which depends on how the robot is constructed and if the wheels are mechanically coupled.

The drivetrain can also use the VEX yaw rate gyro to help in performing more accurate turns. If the gyro is used we use the smartdrive class rather than the drive trainclass.
(for the advanced programmers, a smartdrive is a sub class of drivetrain, it inherits all the class member functions that drivetrain provides and then adds some additional ones to handle turns using the gyro)

The drivetrain also needs to know the distance between left and right wheels (trackWidth), the distance between front and rear axles (wheelBase) and the wheel circumference (wheelTravel).

robot_diagram

An example program

Create a new project in VEXcode and use the graphical configuration to add four motors, a controller and (optionally) a gyro. The right side motors will generally have the reverse flag set on them. The graphical config would look as follows.

config_1

I’ve named the motors so they refer to the wheel positions, for example, MotorLf is the left front wheel motor.

Looking in robot-config.cpp we can see the motor and other device instances have been added.

// VEXcode device constructors
motor MotorLf = motor(PORT1, ratio18_1, false);
motor MotorLb = motor(PORT2, ratio18_1, false);
motor MotorRb = motor(PORT9, ratio18_1, true);
motor MotorRf = motor(PORT10, ratio18_1, true);
gyro GyroA = gyro(Brain.ThreeWirePort.A);
controller Controller1 = controller(primary);

They have also been added to robot-config.h so that they can be used in other source code files.

First we will create the left and right hand side motor groups.

Near the top of main.cpp, add a line of code to create the left side group

// left hand side of the robot has two motors
motor_group   leftDrive( MotorLb, MotorLf );

leftDrive is an instance of the motor_group class and we have initialized it with two motors. An alternate form of the constructor would look like this, which format you use is really personal preference.

// left hand side of the robot has two motors
motor_group   leftDrive = motor_group( MotorLb, MotorLf );

Create the right side in the same way, the beginning of main.cpp would now look like this.

partial main.cpp
// ---- START VEXCODE CONFIGURED DEVICES ----
// Robot Configuration:
// [Name]               [Type]        [Port(s)]
// MotorLf              motor         1               
// MotorLb              motor         2               
// MotorRb              motor         9               
// MotorRf              motor         10              
// GyroA                gyro          A               
// Controller1          controller                    
// ---- END VEXCODE CONFIGURED DEVICES ----

#include "vex.h"

using namespace vex;

/*----------------------------------------------------------------------------*/
// define the motor groups
// the motors should have been setup with correct the gear cartridge ratios
// and the reverse flag set as necessary so they rotate in the correct direction
// when commanded to more forwards

// left hand side of the robot has two motors
motor_group   leftDrive( MotorLb, MotorLf );

// right hand side of the robot has two motors
motor_group   rightDrive( MotorRb, MotorRf );

next we create the drivetrain, I’m going to create a smartdrive and use the gyro I added in graphical config.

smartdrive    robotDrive( leftDrive, rightDrive, GyroA, 12.56, 16, 16, distanceUnits::in );

The smartdrive I created is called “robotDrive”. I set the wheel travel at 12.56 inches (4" wheels), the track width and wheel base are set at 16 inches. A drivetrain without using the gyro would look like this.

drivetrain    robotDrive( leftDrive, rightDrive, 12.56, 16, 16, distanceUnits::in );

Now we add some code to main.cpp to control the drive. A command to rotate the drive would be.

// tell drivetrain to rotate 90 deg
robotDrive.turnFor( 90, degrees );

but you can also directly control the motors on the left and right hand sides by using spin and other methods.

leftDrive.spin( forward, leftSpeed, percent );
rightDrive.spin( forward, rightSpeed, percent );

you can access individual motors as well.

Brain.Screen.print( "  MotorLb    speed: %4.0f   position: %6.2f", MotorLb.velocity( percent ), MotorLb.position( rev ) );

The full contents of main.cpp for this little example is as follows. It’s a bit more complex than the typical examples included with VEXcode as it does start an additional task (thread) to display some of the motor properties.

main.cpp
/*----------------------------------------------------------------------------*/
/*                                                                            */
/*    Module:       main.cpp                                                  */
/*    Author:       james                                                     */
/*    Created:      Sat Nov 02 2019                                           */
/*    Description:  V5 project                                                */
/*                                                                            */
/*----------------------------------------------------------------------------*/

// ---- START VEXCODE CONFIGURED DEVICES ----
// Robot Configuration:
// [Name]               [Type]        [Port(s)]
// MotorLf              motor         1               
// MotorLb              motor         2               
// MotorRb              motor         9               
// MotorRf              motor         10              
// GyroA                gyro          A               
// Controller1          controller                    
// ---- END VEXCODE CONFIGURED DEVICES ----

#include "vex.h"

using namespace vex;

/*----------------------------------------------------------------------------*/
// define the motor groups
// the motors should have been setup with correct the gear cartridge ratios
// and the reverse flag set as necessary so they rotate in the correct direction
// when commanded to more forwards

// left hand side of the robot has two motors
motor_group   leftDrive( MotorLb, MotorLf );

// right hand side of the robot has two motors
motor_group   rightDrive( MotorRb, MotorRf );

/*----------------------------------------------------------------------------*/
// define the drivetrain
// this one is a smart drive which uses the gyro
// gyro and all motors were defined using graphical config
// we have 4 inch wheels
// drive is 16 inches wide between the wheels
// drive has a 16 inch wheelbase (between fron and back wheel axles)
//
smartdrive    robotDrive( leftDrive, rightDrive, GyroA, 12.56, 16, 16, distanceUnits::in );
// this is how the above definition would be if no gyro is used
//drivetrain    robotDrive( leftDrive, rightDrive, 12.56, 16, 16, distanceUnits::in );


/*----------------------------------------------------------------------------*/
//
// A task that just displays motor velocity and position
// The motors that are part of a motor group and/or drivetrain can still be accessed
// directly.
//
int displayTask() {
    while(1) {
      // display some useful info
      Brain.Screen.setCursor(2,1);
      Brain.Screen.print( "  MotorLb    speed: %4.0f   position: %6.2f", MotorLb.velocity( percent ), MotorLb.position( rev ) );
      Brain.Screen.newLine();
      Brain.Screen.print( "  MotorLf    speed: %4.0f   position: %6.2f", MotorLf.velocity( percent ), MotorLf.position( rev ));
      Brain.Screen.newLine();
      Brain.Screen.print( "  MotorRb    speed: %4.0f   position: %6.2f", MotorRb.velocity( percent ), MotorRb.position( rev ));
      Brain.Screen.newLine();
      Brain.Screen.print( "  MotorRf    speed: %4.0f   position: %6.2f", MotorRf.velocity( percent ), MotorRf.position( rev ));
      Brain.Screen.newLine();
      Brain.Screen.newLine();

      // motor group velocity and position is returned for the first motor in the group
      Brain.Screen.print( "  leftDrive  speed: %4.0f   position: %6.2f", leftDrive.velocity( percent ), leftDrive.position( rev ));
      Brain.Screen.newLine();
      Brain.Screen.print( "  rightDrive speed: %4.0f   position: %6.2f", rightDrive.velocity( percent ), rightDrive.position( rev ));
      Brain.Screen.newLine();
      Brain.Screen.newLine();

      // drivetrain velocity is the average of the motor velocities for left and right
      Brain.Screen.print( "  robotDrive speed: %4.0f", robotDrive.velocity( percent ) );
      Brain.Screen.newLine();

      // no need to run this loop too quickly
      wait( 20, timeUnits::msec );
    }

    return 0;
}

/*----------------------------------------------------------------------------*/
//
// main program entry and drive control
//
int main() {
    // Initializing Robot Configuration. DO NOT REMOVE!
    vexcodeInit();

    // set 10 second timeout for robot drive movements
    robotDrive.setTimeout(10, seconds);

    // set the speed used for drive turns
    robotDrive.setTurnVelocity(50, percent);

    // start the display task
    task displayTaskInstance( displayTask );

    // loop forever
    while( true ) {
      // Buitton A is used to rotate drive 90 degrees
      if( Controller1.ButtonA.pressing() ) {
        // tell drivetrain to rotate 90 deg
        robotDrive.turnFor( 90, degrees );
        // wait for it to be done
        while( robotDrive.isMoving() )
          wait( 10, timeUnits::msec );
      }
      else {
        // not rotating, then we can drive using controller

        // read percent from controller axis
        int leftSpeed  = Controller1.Axis3.position();
        int rightSpeed = Controller1.Axis2.position();
        // deadband
        if( abs(leftSpeed)  < 10 ) leftSpeed  = 0;
        if( abs(rightSpeed) < 10 ) rightSpeed = 0;

        // send to motors
        leftDrive.spin( forward, leftSpeed, percent );
        rightDrive.spin( forward, rightSpeed, percent );
      }
          
      // no need to run this loop too quickly
      wait( 20, timeUnits::msec );
    }

    return 0;
}

The VEXcode project is attached

v5code-project-ForumMotorGroup.zip
(14.5 KB)

24 Likes

How do you set a max speed for a drivetrain?

1 Like

Hi Lucky - not sure what you mean but ‘set maximum velocity’ As a rule of thumb you can set velocity either statically
leftDrive.setVelocity(100, percent )
or when moving:
leftDrive.spin( forward, 100, percent );

The V5 API website & included examples can be excellent reference - although much isn’t explained it may help w/all the different commands/syntax

2 Likes

What are the track width and the wheel base?

It was explained pretty well, including a drawing, in the original post. Perhaps read through that again.

6 Likes

I’m sorry. I didn’t realize. Thanks for pointing it out.

Do you have to have a gyro for the robot to turn exactly 90 degrees? Also can you tell the drivetrain to go forward for a certain amount of inches?

Also for driving with the controller axis’s are you required to have the dead band after the axis code?

You don’t have to have a gyro, but you may need to tune the track width and wheelbase numbers if your robot is not turning correctly. In both gyro and non-gyro applications, achieving perfect 90 deg turns is probably not going to happen, +/- 2 deg is our goal, but even that is hard.

No, you don’t have to, but many teams do to stop the robot moving if the controller joysticks don’t always return to center.

6 Likes

Alright cool thanks for the info!

would we need to calibrate the gyro ourselves or find the gyro scales? or is it done for you already?

Where can I find information on the motor group class?

Thanks,
Jim M.

Found it :slight_smile:

https://api.vexcode.cloud/v5/html/classvex_1_1motor__group.html

1 Like

What is the difference between a smart drive and a drivetrain?

Smartdrives use a gyro or inertial sensor to aid in turning, normal drivetrains don’t.

2 Likes

Thank you for your quick response

Can motor groups be assigned to the controller via the graphical robot config in VEXcode V5 text the same way a drivetrain can? Would make life awesome for beginner programmers.

We are using motor groups to move our robot forward. (We are using drivetrain for intake.)

We have 4 motors attached to the 4 wheels.
The two left wheels motors are in one motor group and the two right wheels motors are in another motor group.

The issue we are facing is that in autonomous programming we cannot find a way make the motor move a set number of rotations and at a set speed.

motor_group wheelsLeftMain(motor1,motor4);
motor_group wheelsRightMain(motor3,motor5);

We tried-
wheelsLeftMain.spin(forward,30,percent)
wheelsRightMain.spin(forward,30,percent)
wheelsLeftMain.setRotation
wheelsLeftMain.setTimeout
Here the robot moves forward but does not stop at the set rotation.

We tried-
wheelsLeftMain.spinFor
wheelsLeftMain.spinTo
wheelsLeftMain.rotateFor
wheelsLeftMain.rotateTo
Here the motors move separately instead of together.

Please let me know what I am doing wrong.

in the first example you do not have a condition that stops the motor. Setrotation merely sets the variable that represents how many rotations the motor has gone. What you are looking for is something like this

double current = motor.rotation(rev); // motor.setRotation(0,rev); also works for this scenario
wheelsLeftMain.spin(forward,30,percent);
wheelsRightMain.spin(forward,30,percent);
while(current + 1 >= motor.rotation(rev)){ // waits until motors have gone 1 revolution
    task::sleep(10);
}
motor.stop();

for the second example you are using blocking commands, meaning the code doesn’t execute anything else until the command is completed. The solution here is to add ‘false’ as your last parameter. This tells the brain to not wait for the completion of the rotations before continuing with the code.

4 Likes

In the second example, in my code
spinFor and spinTo takes only 3 parameters.
spinTo(2,rev,true)
I think it’s supposed to take 5 parameters but ours only takes 3. How do we add 2 more parameters such as -
spinTo(2,rev,30,percent,false);

We have the same issue with rotateTo, rotateFor etc.