In Part 2: Robot Control 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 start, our If/Else Statement will be changed back to a simplified format like we had at the beginning of estimating the position of the arm.
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!
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 Part 2: Robot Control!
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.
There are ways to tune the motor, or ways to have the program exit once the position is reached, 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.
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 Drivetrain Encoders section.
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 Drivetrain.
For more information on the effect of motion transmission across a mechanism check out the Compound Gearing 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 Motor documentation.
In the Core Hex Motor specifications 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:
Ticks per revolution
288 ticks
Total gear reduction
2.777778
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.
Calculating counts per revolution of the 125T gear (or COUNTS_PER_GEAR_REV
)is the same formula used in Converting Encoder Ticks for our COUNTS_PER_WHEEL_REV
variable.
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.
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!