Autonomous Driving Tutorial for VEX V5 Robots

A complete tutorial with code

Ryan Knightly
The Floating Point
5 min readJun 3, 2019

--

VEX V5 Clawbot

The 15 second autonomous period is a critical part of VEX Robotics games. You have a short amount of time to control the robot without a human driver and score as many points as possible.

During this time, it is critical that you are able to control the robot precisely. The fundamental part of this control is being able to drive the robot around the field with precision.

Driving is composed of two actions: driving (forward/backward) and turning (left/right). If you are able to these two actions autonomously with reasonable accuracy, you can move your robot practically anywhere on the field confidently. First, let’s look at driving forward and backward.

Driving (forward/backward)

Consider a simple robot with one motor on the left and one motor on the right. You can declare these two motors in robot-config.h with the following.

// Right motor reversed because it is facing the opposite direction
vex::motor LeftMotor = vex::motor(vex::PORT10, false);
vex::motor RightMotor = vex::motor(vex::PORT1, true);

Now in main.cpp we can make a function to drive the robot forward or backward by a specific distance, and then call that function from the autonomous function.

That function can be something like this:

void driveForward( float inches ) {
float inchesPerDegree = WHEEL_CIRCUMFERENCE / 360;
float degrees = inches / inchesPerDegree;
// startRotate doesn't wait for completion
// this way, the other wheel can turn at same time

LeftMotor.startRotateFor(
degrees * GEAR_RATIO, vex::rotationUnits::deg,
AUTON_DRIVE_PCT, vex::velocityUnits::pct
);
RightMotor.rotateFor(
degrees * GEAR_RATIO, vex::rotationUnits::deg,
AUTON_DRIVE_PCT, vex::velocityUnits::pct
);
}

The key here is that we need some way to convert between the linear distance moved by the robot and the rotational angle swept by the drive motor. To do that, we find the circumferential inches per degree of the wheel, by dividing the wheel’s circumference by the 360 total degrees.

From there, taking the inches needed to move divided by the inches moved per degree yields the degrees needed to move. This can be seen through dimensional analysis:

(inches) / (inches / degree)=> degrees

The last thing to consider is the gear ratio. If you directly connect the motor to the wheel, the gear ratio is 1 (1 motor rotation : 1 wheel rotation). If you have gear(s) between the motor and the wheel, you may end up creating a different gear ratio, such as 0.5, (0.5 motor rotation : 1 wheel rotation). Multiplying the degrees to rotate the wheel by this gear ratio finally gives the degrees to rotate the motor. This can be seen through dimensional analysis:

  (wheel degrees) * (gear ratio)= (wheel degrees) * (motor degrees / wheel degrees)=> motor degrees

It is also worth mentioning that this function uses a few constants that need to be declared earlier in the file, preferably near the top by convention. These declarations will look something like this:

const float WHEEL_DIAMETER = 4.125; // inches
const float WHEEL_CIRCUMFERENCE = WHEEL_DIAMETER * 3.1416;
const float GEAR_RATIO = 0.5; // 0.5 turn of motor = 1 turn of wheel
const int AUTON_DRIVE_PCT = 50; // motors at 50% power during auton

Now the autonomous function can call the driveForward function like the following:

void autonomous( void ) {
driveForward( 1.2 * 12 ); // drive 1.2 ft forwards
driveForward(-1.2 * 12 ); // drive 1.2 ft backwards
}

Turning (left/right)

In addition to being able to drive forward and backward precisely, you will also probably want to be able to rotate left and right by a specific number of degrees.

This can be done with another helper function like this:

void turn( float degrees ) {
// Note: +90 degrees is a right turn
float turningRatio = TURNING_DIAMETER / WHEEL_DIAMETER;
float wheelDegrees = turningRatio * degrees;
// Divide by two because each wheel provides half the rotation
LeftMotor.startRotateFor(
wheelDegrees * gearRatio / 2, vex::rotationUnits::deg,
AUTON_DRIVE_PCT, vex::velocityUnits::pct
);
RightMotor.rotateFor(
wheelDegrees * gearRatio / 2, vex::rotationUnits::deg,
AUTON_DRIVE_PCT, vex::velocityUnits::pct
);
}

Turning the robot is a little less straightforward than simple linear driving because we have to consider the turning radius of the robot. In this case, a four-wheeled robot has a turning diameter approximated by the diagonal distance between the wheels. This distance can simply be measured on the robot as the distance between the upper left and lower right wheels at the points where they contact the ground.

Once the turning diameter is known, the ratio a rotation of the robot as a whole and the rotation of a wheel is given by dividing the turning diameter by the wheel diameter. Then multiplying by the desired degrees of rotation from the robot gives the degrees to rotate the wheel.

To translate that to motor rotation, we do the same as we did above, multiplying by the gear ratio, and then dividing by two because we are getting rotation from both the left and right sides.

The constant turning diameter also needs to be declared like so:

// distance (in inches) from top-left wheel to bottom-right wheel
const float turningDiameter = 19.0;

From there, you can call the turn function from the autonomous function like this:

void autonomous( void ) {    
turn( 90); // right turn
turn(-90); // left turn
}

Possible Issue

The methods described only consider the rotation of the motor, gear ratios, and resulting rotation of the wheel. They do not consider possible slipping beyond that point, however. For example, if the wheels do not have proper traction on the driving surface, a full rotation of the wheel may not move the robot the full amount expected.

Possible Solutions

To fix this, you could add a simple slippage multiplier, and simply increase all motor rotations by 5% or so depending on what works.

You could also decide to use a gyroscope for turning and an accelerometer for moving linearly.

Such a solution would be a little more complicated, so you should probably try implementing a simpler system that just uses motor encoders (like that described above) before involving other sensors like gyroscopes.

Hope this helps. Good luck!

--

--