Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Hello Robot Part 3: Autonomous and Encoders is currently under construction. Please stay tuned for future updates!
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.
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.
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:
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."
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.
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.
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 !
ElapsedTime | Drivetrain Encoders | Arm Encoders |
---|---|---|
Think back to in we learned that ||
means OR in OnBot Java!
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:
Coming Soon
Coming Soon
In the previous section you learned about how to use Elapsed Time to allow your robot to navigate the world around it autonomously. When starting out many of the robot actions can be accomplished by turning on a motor for a specific amount of time. Eventually, these time-based actions may not be accurate or repeatable enough. Environmental factors, such as the state of battery charge during operation and mechanisms wearing in through use, can all affect time-based actions. Fortunately, there is a way to give feedback to the robot about how it is operating by using sensors; devices that are used to collect information about the robot and the environment around it.
With Elapsed Time, in order to get the robot to move to a specific distance, you had to estimate the amount of time and the percentage of duty cycle needed to get from point a to point b. However, the REV motors come with built in encoders, which provide feedback in the form of ticks ( or counts) per revolution of the motor. The information provided by the encoders can be used to move the motor to a target position, or a target distance.
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.
There are two articles in that go through the basics of Encoders. Using Encoders goes through the basics of the different types of motor modes, as well as a few application examples of using these modes in code. In this section we will focus on using RUN_TO_POSITION
.
The other article, Encoders, focuses on the general functionality of an encoder.
It is recommended that you review both articles before moving on with this guide.
Start by creating a basic op mode calledHelloRobot_EncoderAuton
.
When creating an op mode a decision needs to be made on whether or not to set it to autonomous mode. For applications under 30 seconds, typically required for competitive game play changing the op mode type to autonomous is recommended. For applications over 30 seconds, setting the code to the autonomous op mode type will limit your autonomous code to 30 seconds of run time. If you plan on exceeding the 30 seconds built into the SDK, keeping the code as a teleoperated op mode type is recommended.
For information on how op modes work please visit the Introduction to Programming section.
For more information on how to change the op mode type check out the Creating an OpMode - OnBot Java section.
The op mode structure below is simplified and only includes the necessary components needed to create the encoder based code.
As with all drivetrain related navigation, the directionality of one of the motors needs to be reversed in order for both motors to move in the same direction. Since the Class Bot V2 is still being used add the line rightmotor.setDirection(DcMotor.Direction.REVERSE);
to the code beneath the rightmotor = hardwareMap.get(DcMotor.class, "rightmotor");
code line.
For more information on the directionality of motor check out the Basics of Programming Drivetrains section.
Recall from Using Encoders that using RUN_TO_POSITION
mode requires a three step process. The first step is setting target position. To set target position add the lines leftmotor.setTargetPosition(1000);
and rightmotor.setTargetPosition(1000);
to the op mode after the waitForStart();
command. To get a target position that equates to a target distance requires so calculations, which will be covered later. For now, set 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.
The main focus of the three step process is to set a target, tell the robot to move to that target, and at what speed (or velocity) the robot should get to that target. Normally, the recommended next step is to calculate velocity and set a target velocity based on ticks. However, this requires quite a bit of math to find the appropriate velocity. For testing purposes, its more important to make sure that the main part of the code is working before getting too deep into the creation of the code. Since the setPower
function was covered in previous sections 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.
Add the lines to set the power of both motors to 80% of duty cycle.
Now that all three RUN_TO_POSITION
steps have been added to the code the code can be tested. However, if you want to wait for the motor to reach its target position before continuing in your program, you can use a while loop that checks if the motor is busy (not yet at its target). For this program lets edit the while (opModeIsActive()){}
Recall that, within a linear op mode, a while loop must always have the opModeIsActive()
Boolean as a condition. This condition ensures that the while loop will terminate when the stop button is pressed.
Edit the while loop to include the leftmotor.isBusy()
and righmotor.isBusy()
functions. This will check if the left motor and right motor are busy running to a target position. The while loop will stop when either motor reaches the target position.
Right now the while loop is waiting for the 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()))
Save and run the op mode two times in a row. Does the robot move as expected the second time?
Try turning the Control Hub off and then back on. How does the robot move?
In the Basic Encoder Concepts section, it is clarified that all encoder ports start at 0 ticks when the Control Hub is turned on. Since you did not turn off the Control Hub in between runs, the second time you ran the op mode the motors were already at, or around, the target position. When you run a code, you want to ensure that certain variables start in a known state. For the encoder ticks, this can be achieved by setting the mode to STOP_AND_RESET_ENCODER
. Add this block to the op mode in the initialization section. Each time the op mode is initialized, the encoder ticks will be reset to zero.
For more information on the motor mode STOP_AND_RESET_ENCODERS
check out the STOP_AND_RESET_ENCODERS section of the Using Encoders guide.
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 op mode 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. Multiply 610 millimeters by theCOUNTS_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 theCOUNTS_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. The example of how to do this is exhibited below.
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.
Try running the code and observing the behavior of the robot. Consider some of the following
Is the robot moving forward by two feet?
Does the robot seem to be moving in straight line?
Is the code running without error?
Velocity is a closed loop control within the SDK that uses the encoder counts to determine the approximate power/speed the motors need to go in order to meet the set velocity. When working with encoder setting a velocity is recommended over setting a power level, as it offers a higher level of control.
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
Recall that setVelocity
is measure in ticks per second.
Create a new double variable called TPS
. Add TPS
the to the op mode under where rightTarget
is defined.
Since RPM is the amount of revolutions per minute a conversion needs to be made from RPM to ticks per second. To do this divide the RPM by 60, to get the amount of rotations per second. Rotations per second can the be multiplied by COUNTS_PER_WHEEL_REV
, to get the amount of ticks per second.
Exchange the setPower();
functions for setVelocity();
. AddTPS
as the parameter for setVelocity();
.
Try to build the code. Do you get errors?
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
. To do this both the private variable creation of the motors, and the hardware mapping need to be changed to DcMotorEx
.
Since DcMotorEx
is an extension of DcMotor
, DcMotor
specific functions can be used by variables defined as DcMotorEx
.
Once you have made these changes the basic, drive two feet code is done! The code below is the finalized version of the code. In this the other hardware components and telemetry have been added.
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. What is the distance from the starting point of the robot and the point the robot moves to after running this code?
Rather than attempt to measure, or estimate, the distance the robot moves, the encoder ticks can be converted from amount of ticks per revolution of the encoder to how many encoder ticks it takes to move the robot a unit of distance, like a millimeter or inch. Knowing the amount of ticks per a unit of measure allows you to set a specific distance. 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 Class Bot V2. 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.
For the conversion process the following information is needed:
Ticks per revolution of the encoder
Total gear reduction on the motor
Including gearboxes and motion transmission components like gears, sprockets and chain, or belts and pulleys
Circumference of the driven wheels
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 Motor documentation.
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 Compound Gearing formula.
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.
The UltraPlanetary Cartridges use the nominal gear ratio as a descriptor. The actual gear ratios can be found in the UltraPlanetary Users Manual's Cartridge Details.
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.
Add the variables to op mode class, where the hardware variables are defined. Defining the variables within the bounds of the class but outside of the op mode, allows them to be referenced in other methods of functions within the class. To ensure variables are referenceable they are set as static final double
variables. Static allows references to the variables anywhere within the class and 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.
Add the variables to op mode class, where the hardware variables are defined. Defining the variables within the bounds of the class but outside of the op mode, allows them to be referenced in other methods of functions within the class. To ensure variables are referenceable they are set as static final double
variables. Static allows references to the variables anywhere within the class and 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, they can be used 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 multipleCOUNTS_PER_MOTOR_REV
by DRIVE_GEAR_REDUCTION
Use the following formula:
Where,
Create the COUNTS_PER_WHEEL_REV
variable within the code. This will also be a static final double
variable.
Once COUNTS_PER_WHEEL_REV
is calculated, use it to calculate the counts per mm that the wheel moves. To do this divide theCOUNTS_PER_WHEEL_REV
by the WHEEL_CIRCUMFERENCE_MM
. Use the following formula.
Where,
Create the COUNTS_PER_MM
variable within the code. This will also be a static final double
variable.
COUNTS_PER_WHEEL_REV
will be created as a separate variable fromCOUNTS_PER_MM
as it is used in calculating a target velocity.
In the image below two potential positions are showcased for the ClassBot arm. One of the positions - highlighted in blue below - is the position where the arm meets the limit of the touch sensor. Due to the limit, this position will be our default or 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 section 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. An estimation can be done by moving the arm to the desired position and recording the telemetry feedback from the Driver Station. Another option is to do to the math calculations to find the amount of encoder ticks occur per degree moved. Follow through this section to walk through both options and determine which is the best for your team.
To estimate the position of the arm using telemetry and testing, lets start with the Gamepad if/else if
code.
For now you can comment out the limit switch related code.
Within the while loop add the linestelemetry.addData("Arm Test", arm.getCurrentPosition());
and telemetry.update();
Save the op mode 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 Station to determine the encoder count relative to the position of the arm.
The number given in the image above is not necessarily an accurate encoder count for the 90 degree position. To get the most accurate encoder reading for your robot make sure that your starting position reads as 0 encoder counts. To further increase accuracy consider doing several testing runs before deciding on the number of counts.
Recall that in order to execute RUN_TO_POSITION
the following three lines of cod need to be added to both sections of the Gamepad if/else if
block.
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.
Note: the code above was given a file name Target Position if/else if
this code will be referenced again.
Recall that the target position dictates which direction the motor moves, taking over the directionality control from arm.setPower();
so both blocks can be set to a positive value since they will control the speed.
If you try running this code you may notice that the arm oscillates in the 90 degree position. When this behavior is present you should also notice the telemetry output for the encoder counts fluctuating. 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. There are ways to tune the motor, 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.
In RUN_TO_POSITION
mode the encoder counts ( or setTargetPosition();
) are used instead of setPower();
to dictate directionality of the motor. If a target position value is greater than the current position of the encoder, the motor moves forward. If the target position value is less than the current position of the encoder, the motor moves backwards
Since speed an directionality impacts how a robot turns, setTargetPostion();
and setVelocity();
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. Velocity remains the same for both motors. If you try running this code, you may notice that the robot pivots along its center of rotation. To get a wider turn 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.
The following code walks through adding a turn to the program, after the robot moves forward for 2 feet. After the robot reaches the 2 foot goal, there is a call to STOP_AND_RESET_ENCODERS
this will reduce the need to calculate what position to go to after a position has been reached.
In the initial introduction to run to position, you worked through the calculations needed to convert the ticks per rotation of a motor into ticks per mm moved. Now we want to focus on how to convert ticks per rotation of the motor to ticks per degree moved. From the previous section you should have a rough estimate of the amount of ticks you need to get to the 90 degree position. The goal of this section is to work through how to get a more exact position.
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.
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 lets create two constant variables:
COUNTS_PER_MOTOR_REV
GEAR_REDUCTION
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.
Add the COUNTS_PER_MOTOR_REV
and GEAR_REDUCTION
variables to the op mode 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.
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 op mode.
Finally we need to create a non-constant variable that will act as our position. Create a variable called armPosition
above the waitForStart();
command.
Add this variable to the if(gaempad1.dpad_up)
section of the Target Position if/else if
statement, as this section dictates the 90 degree position. To get to the 90 degree position, the arm needs to move roughly 45 degrees. Set arm position equal to COUNTS_PER_DEGREE
times 45.
Recall that setTargetPosition()
requires an integer to be its parameter. When defining armPosition
remember to add 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.
Set target position to armPostion
.
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.
= 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
In the section the concept of moving the motor to a specific position based on encoder ticks was introduced. The process highlighted in Encoder Navigation focused on how to convert from encoder ticks to rotations to a linear distance. A similar procedure can be utilized to move the arm to a particular position. However, unlike the drivetrain, the arm does not follow a linear path. Rather than convert to a linear distance it makes more sense to convert the encoder ticks into an angle measured in degrees.
Recall from the section 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.
In the section, the mechanism of setPower();
was discussed. setPower();
dictates what direction and speed a motor moves in. On a drivetrain this dictates whether the robot moves in forward, reverse, or turns.
For more information on how direction and speed impact the movement of a robot please refer to the explanation of setPower();
in the section.
To start you will need some of the same variables we used in :
Recall, that ticks per revolution of the encoder shaft is different than the ticks per revolution of the shaft that is controlling a mechanism. We saw this in the section when the ticks per revolution at the motor was different from the ticks per revolution of the wheel. As motion is transmitted from a motor to a mechanism, the resolution of the encoder ticks changes.
For more information on the effect of motion transmission across a mechanism check out the section.
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:
Calculating counts per revolution of the 125T gear (or COUNTS_PER_GEAR_REV
)is the same formula used in for ourCOUNTS_PER_WHEEL_REV
variable. So to get this variable we can multipleCOUNTS_PER_MOTOR_REV
by GEAR_REDUCTION
.
Ticks per revolution
28 ticks
Total gear reduction
30.21
Circumference of the wheel
Ticks per revolution | 288 ticks |
Total gear reduction | 2.777778 |
In the previous sections you worked on some of the building blocks for restricting an arms range of motion. From those sections you should have the foundation you need to perform basic arm control. However, there are some other creative ways you can use encoder positions and limits to expand the control you have over your arm.
This section will cover two additional types of control. The first type of control we will explore is the idea of soft limits. In the Adding a Limit Switch section we discuss the concept of physical limits of a mechanism however, there may be times you need to limit the range of motion of an arm without installing a physical limit. To do this position based code can be used to create a range for the arm.
Once you have a basic idea of how to create soft limits, we will explore how to use a limit switch (like a touch sensor) to reset the range of motion. This type of control reduces the risk of getting stuck outside of your intended range of motion, which can affect the expected behavior of your robot.
To set the soft limits we will use some of the basic logic we established in previous sections, with some edited changes. Start with a Basic Op Mode
and add the constant variables from the Calculating Target Position section to the op mode.
Next we need to create our upper and lower limits. Create two new integer variables one called minPosition
and one called maxPosition
. Add both of these to the in the initialization section of the op mode 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 toCOUNTS_PER_DEGREE
times 45 .
Remember you need to make a data type conversion!
An if/else if
statement needs to be added to control the arm, for this we can use the same basic logic we use in the Estimating the Position of the Arm section.
To set the limit we need to edit our if/else if
statement so that the limits are built in. If DpadUp
is selected and the position of the arm is less than the maxPosition
then the arm will move to the maxPosition
. If DpadDown
is selected and the position of the is greater that 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. Since encoders zero tick position is determined by the position of the arm when the Control Hub powers on; if attention is not payed to what position the arm is on power up the range of motion of the arm is affected. For instance, if we have to reset the Control Hub while the arm is in the 90 degree position, the 90 degree position is equal to 0 encoder ticks. One way around this is to create an override for the range of motion.
There are a few different ways an override of sorts can be created, 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 toSTOP_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!