Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Encoders are a form of sensor built into some motors that help provide feedback to our robot. There are different kinds of encoders, but we're going to be focus on what's called a quadrature encoder for this tutorial. These encoders are able to count the number of revolutions of the motor also sometimes called "ticks".
While quadrature encoders aren't able to tell exact positions, they can still be used to help the motor move to a specified point. We'll go over how to do so later in this tutorial, but this works by specifying first an origin point in our code then a point the motor should move away.
Let's take a closer look at the different modes we can set our encoder to with our motor. The mode is often established during the initialization process of our code, meaning the motors are ready to go throughout our program, but it is possible to change this for specific use cases as it executes.
Which mode we need to use largely depends on the intended function of the motor and any attached mechanism. For example, we may not need our encoders active on the drivetrain motors when they're being controlled by a joystick's input. On the other hand, we may have a flywheel design that requires a specific speed our encoders can help us achieve.
When using encoders, it is strongly recommended to first use STOP_AND_RESET_ENCODER during initialization. This will allow you to know what position the motor is starting in, however you will want to plan for a repeatable start up configuration each time!
A motor can be switched to "STOP_AND_RESET_ENCODER" while a code is executing as well. This is often set up using a button on the gamepad to allow the driver to reset the encoder in the event of a motor misbehaving, such as after the robot's been caught on an obstacle.
Below is a snippet of code that demonstrates how to reset the encoder in OnBot Java. You can skip the first line if you already have retrieved the motor object from hardwareMap:
Use this mode when you don’t want the Control Hub to attempt to use the encoders to maintain a constant speed. You can still access the encoder values, but your actual motor speed will vary more based on external factors such as battery life and friction. In this mode, you provide a power level in the -1 to 1 range, where -1 is full speed backwards, 0 is stopped, and 1 is full speed forwards. Reducing the power reduces both torque and speed.
The RUN_WITHOUT_ENCODER motor mode is very straightforward, you simply set a power throughout the program using the "Power" block or, such as for a drivetrain, to be set by the joysticks.
The power level is set in Java by calling setPower()
on a DcMotor or DcMotorEx object, as shown in this snippet. You can skip the first line if you already have retrieved the motor object from hardwareMap.
In this mode, the Control Hub will use the encoder to take an active role in managing the motor’s speed. Rather than directly applying a percentage of the available power, RUN_USING_ENCODER mode targets a specific velocity (speed). This allows the motor to account for friction, battery voltage, and other factors. You can still provide a power level in RUN_USING_ENCODER mode, but this is not recommended, as it will limit your target speed significantly.
Setting a velocity from RUN_WITHOUT_ENCODER mode will automatically switch the motor to RUN_USING_ENCODER mode. You should pick a velocity that the motor will be capable of reaching even with a full load and a low battery.
The velocity is set in Java by calling setVelocity()
on a DcMotorEx object, as is shown in this snippet. You can skip the first line if you have already retrieved the motor object as a DcMotorEx from hardwareMap.
In this mode, the Control Hub will target a specific position, rather than a specific velocity. You can still choose to set a velocity, but it is only used as the maximum velocity. The motor will continue to hold its position even after it has reached its target.
If the motor is unable to reach the determined position the motor will continue to run attempting to reach or maintain that position, which can lead to the motor stalling and overheating.
To use RUN_TO_POSITION mode, you need to do the following things in this order:
Set a target position (measured in ticks)
Switch to RUN_TO_POSITION mode
Set the maximum velocity (if not determined by a gamepad input)
Remember it is recommended to always reset the encoder during initialization, however you will need to make sure your robot has been reset physically to the initialization position. For example, an arm may need to be brought back to the start up configuration like for the beginning of a FTC match.
The motor will continue to hold its position even after it has reached its target, unless you set the velocity or power to zero, or switch to a different motor mode.
For now our goal will be to have the motors move forward for 3 seconds. To accomplish this we need to edit our main While Loop so that it triggers when the OpMode is active AND the ElapsedTime timer is less than or equal to 3 seconds.
Let's take a look first at how our timer appears in OnBot Java when checking for less than or equal to 3 seconds:
When using our default template, our program will continue running until we press stop on the Driver Hub. However, in this situation we want our program to end when our timer is up. This means we need to modify our while loop to add a second condition!
Alongside our check for opModeIsActive()
we will add our check for if runtime is under 3 seconds:
&&
is a logical operator in Java. This symbol is the Java equivalent of "and."
Think back to in Part 1 we learned that ||
means OR in OnBot Java!
Now the While Loop will now activate when both conditions of the AND block are true.
Let's give our OpMode a try and test the following scenarios:
What happens when hitting play quickly after the initialization button is pressed?
What happens when hitting play 2 seconds after the initialization button is pressed?
What happens when hitting play 10 seconds after the initialization button is pressed?
Not being able to pause between initialization and pressing Play is probably not ideal in most situations. It certainly makes tracking how far the robot will travel more challenging, the opposite of what we'd like ElapsedTime to help us do.
To keep this from happening the timer should be reset once the OpMode is active. Let's add the line runtime.reset();
between the waitForStart();
command and the while loop.
Since this is before our loop our robot will complete it once when Play is pressed. Then will complete the check for our While Loop.
Test your program again with this change!
Now let's explore what happens when we change our time limit to different amounts. You can adjust your time limit by changing the 3 seconds within (runtime.seconds() <= 3.0)
to different numbers.
Consider marking different goals on the floor with tape to practice determining how much time the robot needs to reach it.
In previous parts, we've looked at adding telemetry as a way for the robot to communicate with us. In this situation, it would be helpful for the robot to be able to tell us how much time it has counted so we can make adjustments to our program!
For our key let's call it "Number of Seconds in Phase 1" for now. This will be useful for distinguishing where in our program our robot is during the next section.
When using our gamepad, we can actively communicate with our robot while our program runs. Our robot waits for our input and acts accordingly until a different command is issued. However, this may not always be the case, such as during the autonomous period of a FTC competition.
Our robot is smart enough to navigate some on its own, however we need to help it along to know what to look for. Eventually, you could work up to your robot being able to navigate using a camera and machine learning or its IMU to sense direction, but for now let's start with one of the built in features of the SDK: ElapsedTime
What do you think of when you think of a timer? A stopwatch? Your phone? Maybe the timer on a microwave or oven? Timers commonly consist of two main categories: count up and count down. You can think about the differences of these two by a comparison like keeping track of how fast a runner did a 100m dash vs. needing to know how much longer our food should cook.
ElapsedTime is a count up timer. Registering the amount of time elapsed from the start of a set event, like the starting of a stopwatch. In this situation, it is the amount of time passed from when the timer is created or reset within the code.
For more information on the ElapsedTime object outside this tutorial check out the FTC Java Docs!
This program uses the same configuration file created at the start of Hello Robot. Some hardware may not be used during this section.
We've tackled the basics. We have a robot able to drive around. What could be next?
Right now our robot is largely dependent on inputs from us as the driver from the gamepad. We've helped it learn to sense a little bit using the touch sensor, but there is still more we can do.
During Part 3 we will be learning how to help our robot navigate the world around it autonomously in different ways. To start we will look at how to use a timer for the robot to keep track of how long it should do something. From there, we will move on to using the built in encoders of the HD Hex and Core Hex Motors.
Encoders are a form of sensor that help collect data for the motor. Some encoders count the number of completed rotations. Others are able to track the exact position of a motor, similar to a servo. The use of encoders brings the need for more math and complex programming, however it will allow your robot to navigate more efficiently.
Start by creating a new OpMode called HelloRobot_ElapsedTime using the BlankLinearOpMode sample similar to what we used in Part 1.
Selecting the features discussed above will allow you to start with the following code. Remember that if "Setup Code for Configured Hardware" is selected the OpMode will try to generate a hardwareMap based on the active configuration. This example uses the same Hello Robot config file we originally created!
To prepare to use ElapsedTime, a variable and an instance of ElapsedTime
needs to be created. To do this the following line is needed:
In this case we are named our variable runtime.
The above line performs two actions:
A ElapsedTime variable called runtime is created. Once it is created and defined as an ElapsedTime variable, it can hold the relevant time information and data.
The other part of the line, runtime = new ElapsedTime();
, creates an instance of the ElapsedTime timer object and assigns it to our new runtime variable.
Add this line to the OpMode with the other private variables:
In OnBot Java the private keyword is an access modifier that, in this situation, means this variable can only be used within the class it was defined. Our class for this project is our "public class HelloRobot_ElapsedTime".
Next we can go ahead and add the basic movement for our motors. For this example, we will set both motors to a power of 1:
Lastly, we need to make sure our right motor's direction is reversed during initialization:
Right now our robot should move forward 3 seconds then stop. What if we wanted our robot to do something else after those 3 seconds? How do we request our program to continue?
To save some time we can copy and paste our entire loop and timer reset below our existing code to make adjustments to!
Notice our second loop also has a call for telemetry data, however the name is the same! Let's edit it to be "Number of Seconds in Phase 2". Keep the names in mind if you duplicate additional loops.
When copying and pasting code within OnBot Java be sure to double check you still have the correct number of brackets! You may see a line highlighted in red in the case of a bracket missing or if there are too many:
Give your program a test to see what happens. Think about the following while testing:
How long does the robot move?
Could you tell when the robot switched between Phase 1 and 2?
What happens if we change the power in the second loop?
Having multiple loops with different amounts of time can give us a lot of power to help our robot navigate an area. For now let's have our robot complete it's first movement forward for 3 seconds, then reverse back to the start.
This simply requires changing our power in the second loop to -1 !
Often times, like in the program created during , we use setPower();
to set the drivetrain motors to a set power or power based on a joystick's inputs. The combined power going to both motors help to determine the direction the robot moves or turns.
However, in RUN_TO_POSITION
mode the encoder counts are used instead to dictate directionality of the motor.
Since speed an directionality impacts how a robot turns, target position and velocity need to be edited to get the robot to turn. Consider the following code:
The rightTarget
has been changed to be a negative target position. Assuming that the encoder starts at zero due to STOP_AND_RESET_ENCODER
this causes the robot to turn to the right.
Notice the velocity is the same for both motors. If you try running this code, you can see that the robot pivots along its center of rotation.
To get a wider turn, try changing the velocity so that the right motor is running at a lower velocity than the left motor. Adjust the velocity and target position as needed to get the turn you need.
Velocity is a closed loop control within the that uses the encoder counts to determine the approximate power/speed the motors need to go in order to meet the set velocity.
To set a velocity, its important to understand the maximum velocity in RPM your motor is capable of. For the Class Bot V2 the motors are capable of a maximum RPM of 300. With a drivetrain, you are likely to get better control by setting velocity lower than the maximum. In this case, lets set the velocity to 175 RPM!
Since RPM is the amount of revolutions per minute, a conversion needs to be made from RPM to ticks per second (TPS). To do this, divide the RPM by 60 to get the amount of rotations per second.
Rotations per second can then be multiplied by COUNTS_PER_WHEEL_REV
, to get the amount of ticks per second.
Create a new double variable called TPS
. AddTPS
the to the OpMode under where rightTarget
is defined:
Exchange the setPower();
functions for setVelocity();
Add TPS
as the parameter for setVelocity();
With the velocity set, let's give our program a test run after building!
With the current state of the code you are likely to get errors similar to the ones pictured below:
This is because the setVelocity();
function is a function of theDcMotorEx
Interface. The DcMotorEx
Interface is an extension of the DcMotor
Interface, that provides enhanced motor functionality, such as access to closed loop control functions.
To use setVelocity();
the motor variables need to be changed to DcMotorEx
as seen below:
Since DcMotorEx
is an extension of DcMotor
, DcMotor
specific functions can be used by variables defined as DcMotorEx
.
In the previous section, the basic structure needed to use RUN_TO_POSITION
was created. The placement of leftmotor.setTargetPosition(1000);
and rightmotor.setTargetPosition(1000);
within the code, set the target position to 1000 ticks.
But how far is a tick and how can we use them to help our robot navigate an area? We could attempt to estimate the distance the robot moves per tick or we can convert the amount of ticks per revolution of the encoder into a unit like millimeters or inches! For instance, if you work through the conversion process and find out that a drivetrain takes 700 ticks to move an inch, this can be used to find the total number of ticks need to move the robot 24 inches.
Reminder that the basis for this guide is the . The REV DUO Build System is a metric system. Since part of the conversion process references the diameter of the wheels, this section will convert to ticks per mm.
This process will take a bit of math to achieve so let's break it down.
When using encoders built into motors, converting from ticks per revolution to ticks per unit of measure moved requires the following information:
The amount of ticks per revolution of the encoder shaft is dependent on the motor and encoder. Manufacturers of motors with built-in encoders will have information on the amount of ticks per revolution.
For HD Hex Motors the encoder counts 28 ticks per revolution of the motor shaft.
Visit the manufacturers website for your motor or encoders for more information on encoder counts. For HD Hex Motors or Core Hex Motors visit our documentation.
For the Class Bot V2 there are two UltraPlanetary Cartridges, 4:1 and 5:1, and an additional gear reduction from the UltraPlanetary Output to the wheels, 72T:45T ratio.
Using the compound gearing formula for the Class Bot V2 the total gear reduction is:
Unlike the the spur gears used to transfer motion to the wheels, the UltraPlanetary Gearbox Cartridges are planetary gear systems. To make calculations easier the gear ratios for the Cartridges are already reduced.
The Class Bot V2 uses the 90mm Traction Wheels. 90mm is the diameter of the wheel. To get the appropriate circumference use the following formula
You can calculate this by hand, but for the purpose of this guide, this can be calculated within the code.
Due to wear and manufacturing tolerances, the diameter of some wheels may be nominally different. For the most accurate results consider measuring your wheel to confirm that the diameter is accurate.
To summarize, for the Class Bot V2 the following information is true:
Each of these pieces of information will be used to find the number of encoder ticks (or counts) per mm that the wheel moves. Rather than worry about calculating this information by hand, these values can be added to the code as constant variables. To do this create three variables:
COUNTS_PER_MOTOR_REV
DRIVE_GEAR_REDUCTION
WHEEL_CIRCUMFERENCE_MM
The common naming convention for constant variables is known as CONSTANT_CASE, where the variable name is in all caps and words are separated by and underscore.
To ensure variables are referenceable they are set as static final double
variables. Static allows references to the variables anywhere within the class. Final dictates that these variables are constant and unchanged elsewhere within the code.
Since these variables are not integers they are classified as double variables.
Now that these three variables have been defined, we can use them to calculate two other variables: the amount of encoder counts per rotation of the wheel and the number of counts per mm that the wheel moves.
To calculate counts per wheel revolution multiple COUNTS_PER_MOTOR_REV
by DRIVE_GEAR_REDUCTION
Use the following formula:
Where:
Once COUNTS_PER_WHEEL_REV
is calculated, use it to calculate the counts per mm that the wheel moves. To do this divide the COUNTS_PER_WHEEL_REV
by the WHEEL_CIRCUMFERENCE_MM
. Use the following formula.
Where,
COUNTS_PER_WHEEL_REV
will be created as a separate variable fromCOUNTS_PER_MM
as it is used in calculating a target velocity.
Moving the motors to a specific position, using the encoders, removes any potential inaccuracies or inconsistencies from using Elapsed Time. The focus of this section is to move the robot to a target position using encoders.
For this tutorial, our OpMode is named HelloRobot_Encoder!
The OpMode structure below is simplified and only includes the necessary components needed to create the encoder based code.
In our example, we are addingrightmotor.setDirection(DcMotor.Direction.REVERSE);
to the code as seen below:
The first step is setting target position. To do so, add the lines leftmotor.setTargetPosition(1000);
and rightmotor.setTargetPosition(1000);
to the OpMode after the waitForStart();
command.
If we want our robot to travel a specific distance we will need to do a bit of math beforehand to calculate the TargetPosition. But for now let's start simple by setting the target position to 1000 ticks.
The next step is to set both motors to the RUN_TO_POSITION
mode. Add the lines leftmotor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
and rightmotor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
to your code, beneath the setTargetPosition
code lines.
Order matters! The TargetPosition block must come before RUN_TO_POSITION mode is set or it will result in an error.
Add the lines to set the power of both motors to 80% of duty cycle.
Build your OpMode and give it a test. What happens once you press play? What happens if you stop the program then start it again?
For our demo code we will want to request our motors reset their encoders during the initialization process of the program.
Right before waitForStart();
we can add leftmotor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER);
and rightmotor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER);
to our OpMode.
Let's say we want our program to run only for however long it takes for the motors to reach designated position. Or maybe we intend for the robot to do something else after reaching the destination. For this we will need to edit our whileLoop block!
Recall that, within a linear OpMode, a whileLoop must always have the opModeIsActive()
Boolean as a condition. This condition ensures that the whileLoop will terminate when the stop button is pressed.
To the whileLoop let's add the leftmotor.isBusy()
and righmotor.isBusy()
functions. This will check if the left motor and right motor are busy running to a target position. Once either motor reaches the target position the program will stop.
Build your OpMode and give it a try!
As soon as the motors hit the desired position the program will end instead of continuously run in the event they do not perfectly hit the position.
Right now the whileLoop is waiting for either motor to reach the target. There may be occasions when you want to wait for both motors to reach their target position. In this case the following loop can be used:
while (opModeIsActive() && (leftmotor.isBusy() || rightmotor.isBusy()))
For this tutorial, our OpMode is named HelloRobot_ArmEncoder!
Let's start by creating a simple program for moving our robot's arm. The one below will look very similar to the code created during !
Within the while loop add the lines telemetry.addData("Arm Test", arm.getCurrentPosition());
and telemetry.update();
Build the OpMode and run it.
Use the gamepad commands to move the arm to the 90 degree position. Once you have the arm properly positioned read the telemetry off the Driver Hub to determine the encoder count relative to the position of the arm.
Remember that the encoder position is set to 0 each time the Control Hub is turned on! This means that if your arm is in a position other than the starting position when the Control Hub is turned on, that position becomes zero instead of the starting position.
To add the RUN_TO_POSITION
code, the if/else
statement must first have the following three lines of code need to be added:
When DpadUp
is pressed, the arm should move to the the 90 degree position. WhenDpadDown
is pressed the arm should move back to the starting position. To do this set the firstarm.setTargetPosition(0);
equal to the number of ticks it took your arm to get to 90 degrees, for this example we will use 83 ticks.
Since we want DpadDown
to return the arm to the starting position, keeping the arm.setTargetPosition(0);
set to 0 will allow us to accomplish this. Set both arm.setPower(0);
equal to 0.5.
Despite our power being a positive value for both directions, the arm will move up or down based on the set position!
If you try running this code you may notice that the arm oscillates around the 90 degree position. When this behavior is present you should also notice the telemetry output for the encoder counts fluctuating.
Recall RUN_TO_POSITION
is a Closed Loop Control, which means that if the arm does not perfectly reach the target position, the motor will continue to fluctuate until it does. When motors continue to oscillate and never quite reach the target position this may be a sign that the factors determining tolerances and other aspects of the closed loop are not tuned to this particular motor or mechanism.
Now that we have an estimate for our target position let's see if we can refine it to be more precise using similar methods to what we covered during the .
Recall, that ticks per revolution of the encoder shaft is different than the ticks per revolution of the shaft that is controlling a mechanism, such as what we determined on our .
For more information on the effect of motion transmission across a mechanism check out the section.
The amount of ticks per revolution of the encoder shaft is dependent on the motor and encoder. Manufacturers of motors with built-in encoders will have information on the amount of ticks per revolution.
Visit the manufacturers website for your motor or encoders for more information on encoder counts. For HD Hex Motors or Core Hex Motors visit the documentation.
In the there are two different Encoder Counts per Revolution numbers:
At the motor - 4 counts/revolution
At the output - 288 counts/revolution
At the motor is the number of encoder counts on the shaft that encoder is on. This number is equivalent to the 28 counts per revolution we used for the HD Hex Motor.
The 288 counts "at the output" accounts for the change in resolution after the motion is transmitted from the motor to the built in 72:1 gearbox.
Lets use the 288 as ticks per revolution so that we do not have to account for the gearbox in our total gear reduction variable.
Since we built the the gear reduction from the motor gearbox into the ticks per revolution the main focus of this section is calculating the gear reduction of the arm joint.
The motor shaft drives a 45 tooth gear that transmits motion to a 125 tooth gear. The total gear ratio is 125T:45T. To calculate the gear reduction for this gear train, we can simply divide 125 by 45.
To summarize, for the Class Bot V2 the following information is true:
Now that we have this information let's create two constant variables:
COUNTS_PER_MOTOR_REV
GEAR_REDUCTION
Add the COUNTS_PER_MOTOR_REV
and GEAR_REDUCTION
variables to the OpMode beneath where the hardware variables are created.
Now that these two variables have been defined, we can use them to calculate two other variables: the amount of encoder counts per rotation of the 125T driven gear and the number of counts per degree moved.
So to get this variable we can multiply COUNTS_PER_MOTOR_REV
by GEAR_REDUCTION
.
To calculate the number of counts per degree or moved or COUNTS_PER_DEGREE
divide the COUNTS_PER_GEAR_REV
variable by 360.
Add both these variables to the OpMode:
We need to create one more non-constant variable that will act as our position. This will be called armPosition
.
Add this variable to the if(gamepad1.dpad_up)
section of the Target Position if/else
statement.
To get to the 90 degree position, the arm needs to move roughly 45 degrees, therefore set arm position equal to COUNTS_PER_DEGREE
times 45.
Change the target position to armPosition
.
We could change what armPosition
is equal to in the gamepad1.dpad_down
portion of the if/else if
statement such as:
In this case we would consistently redefine armPosition
to match the needs of whatever positions we want to create.
Since our only two positions at the moment are our starting position and our 90 degree position it isn't necessary. However, it is a good practice to create a variable in situations like this. If we want to add another position later, we can easily edit the variable to fit our needs.
In the idea of creating a limit switch was introduced using a physical sensor, like a touch sensor. We can make use of our motor's built-in encoder to do something similar. While a physical sensor would be described as a hard limit, using the built-in encoder is called a soft limit.
To set the soft limits we will build off the program created in the last sections (HelloRobot_ArmEncoder)!
To start, we need to create our upper and lower limits. Create two new variables one called minPosition
and one called maxPosition
to be added to initialization.
Add both of these to the in the initialization section of the OpMode above the waitForStart()
; command.
For now we want the minPosition
set as our starting position and the maxPosition
set to our 90 degree position. Set minPosition
equal to 0 and set maxPosition
equal to COUNTS_PER_DEGREE
times 45.
Remember you need to make a data type conversion!
To set the limit we need to edit our if/else
statement to include our limits:
If up on the Dpad is pressed and the position of the arm is less than the maxPosition
, then the arm will move to the maxPosition
.
If down on the Dpad is pressed and the position of the arm is greater than the minPosition
then the arm will move towards the minPosition
.
The current code configuration will stop the motor at any point that the conditions to power the motor are not met. Depending to factors like the weight of the mechanism and any load that it is bearing, when the motor stops the arm may drop below the maxPosition
. Take time to test out the code and confirm that it behaves in the way you expect it to.
One of the benefits of having a soft limit is being able to exceed that limit.
Remember that the encoders zero tick position is determined by the position of the arm when the Control Hub powers on! So if we aren't careful to reset the arm before powering on our robot this will effect the arm's range of motion. For instance, if we have to reset the Control Hub while the arm is in the 90 degree position, the 90 degree position will become equal to 0 encoder ticks.
As a back up, we can create an override for the range of motion. There are a few different ways an override can be created, but in our case we are going to use the "A" button and touch sensor to help reset our range.
Start by editing the if/else if
statement to add another else if
condition. Use the line gamepad1.a
as the condition. Add a the line arm.setPower(-0.5);
as the action item.
Now that we have this change in place, when the "A" button is pressed the arm will move toward the starting position.
When the arm reaches and presses the touch sensor we want to STOP_AND_RESET_ENCODER
.
We can create another if
statement that focuses on performing this stop and reset when the Touch Sensor is pressed.
So, if the Touch Sensor returns true
(or is pressed) the motor run mode STOP_AND_RESET_ENCODER
will be activated causing the motor encoder to reset to 0 ticks.
Now that this code is done, try testing it!
Now that you have created the constant variables needed to calculate the amount of ticks per mm moved, you can use this to set a target distance. For instance, if you would like to have the robot move forward two feet, converting from feet to millimeters and multiplying by the COUNTS_PER_MM
will give you the amount of counts (or ticks) needed to reach that distance!
Create two more variables called leftTarget
andrightTarget
.
These variables can be fluctuated and edited in your code to tell the motors what positions to go to, rather than place them with the constant variables, create these variables within the OpMode but above the waitForStart();
command.
The setTargetPosition();
function takes in a integer (or int) data type as its parameter, rather than a double. Since both the leftTarget
and rightTarget
will be used to set the target position, create both variables as int variables.
Right now the main distance factor is COUNTS_PER_MM
, however you may want to go a distance that is in the imperial system, such as 2 feet (or 24 inches). The target distance in this case will need to be converted to mm.
To convert from feet to millimeters use the following formula:
If you convert 2 feet to millimeters, it comes out the be 609.6 millimeters. For the purpose of this guide, lets go ahead an round this to be 610 millimeters.
Next, multiply 610 millimeters by the COUNTS_PER_MM
variable to get the number of ticks needed to move the robot 2 feet. Since the intent is to have the robot move in a straight line, set both the leftTarget
and rightTarget
, to be equal to 610 * COUNTS_PER_MM
As previously mentioned the setTargetPosition();
function requires that its parameter must be an integer data type. The leftTarget
and rightTarget
variables have been set to be integers, however theCOUNTS_PER_MM
variable is a double. Since these are two different data types, a conversion of data types needs to be done.
In this case the COUNTS_PER_MM
needs to be converted to an integer. This is as simple as adding the line (int) in front of the double variable. However, you need to be cautious of potential rounding errors. Since COUNTS_PER_MM
is part of an equation it is recommended that you convert to an integer AFTER the result of the equation is found.
Lastly, edit the setTargetPosition();
lines so that both motors are set to the appropriate target position. To do this add the leftTarget
and rightTarget
variables to their respective motor:
This section is written with the Class Bot V2 in mind, but can be followed with appropriate adjustments, such as mechanism angles, on other robot designs!
We've covered using encoders for a drivetrain, but what about for a different mechanism, such as an arm? Unlike the drivetrain, the arm does not follow a linear path. This means rather than converting to a linear distance it makes more sense to convert the encoder ticks into an angle measured in degrees!
In the image below two potential positions are showcased for the Class Bot arm. One of the positions (blue) is the position where the arm meets the limit of the touch sensor. Due to the limit, this position will be our default starting position.
From the Class Bot build guide, it is known that the Extrusion supporting the battery sits a 45 degree angle. Since the arm is roughly parallel to these extrusion when it is in the starting position, we can estimate that the default angle of the arm is roughly 45 degrees.
The goal of this tutorial is to determine the amount of encoder ticks it will take to move the arm from its starting position to a position around 90 degrees.
There are a few different ways this can be accomplished. For example, an estimation can be done by moving the arm to the desired position and recording the telemetry feedback from the Driver Station. Alternatively, we can do the math calculations to find the amount of encoder ticks that occur per degree moved.
Follow through this tutorial to walk through both options and determine which is the best for your team!
Since ticks per revolution of the encoder shaft is before any gear reduction calculating the total gear reduction is needed. This includes the gearbox and any addition reduction from motion transmission components. To find the total gear reduction use the .
The UltraPlanetary Cartridges use the nominal gear ratio as a descriptor. The actual gear ratios can be found in the .
We'll add the to the initialization section of the OpMode:
= COUNTS_PER_MOTOR_REV
= DRIVE_GEAR_REDUCTION
= COUNTS_PER_WHEEL_REV
= COUNTS_PER_MOTOR_REV
= DRIVE_GEAR_REDUCTION
= WHEEL_CIRCUMFERENCE_MM
= COUNTS_PER_WHEEL_REV
= COUNTS_PER_MM
Before diving in too far, recall that for certain , like the , one of the motors needs to be reversed as the motors are mirrored.
As introduced in , using RUN_TO_POSITION
mode requires a three step process.
As mentioned, normally there would be more math involved to help determine how fast the motors should move to reach the desired position. But for testing purposes, we are going to start by keeping it simple! Since the setPower
function was covered in and will communicate to the system what relative speed (or in this case duty cycle) is needed to get to the target, this can be used in the place of setVelocity
for now.
There are ways to tune the motor, or ways to have the , but for now we want to focus on working with the arm and expanding on how limits and positions work with regards to the mechanism.
Calculating counts per revolution of the 125T gear (or COUNTS_PER_GEAR_REV
)is the same formula used in for our COUNTS_PER_WHEEL_REV
variable.
To start, our If/Else Statement will be changed back to a simplified format like we had at the beginning of .
Ticks per revolution
28 ticks
Total gear reduction
30.21
Circumference of the wheel
Ticks per revolution
288 ticks
Total gear reduction
2.777778