Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
With the basics of controlling actuators and getting feedback from sensors is understood from Hello Robot - Test Bed, it is time to start configuring and programming our robot for Teleoperated and Autonomous control!
Before continuing it is recommended to complete, at minimum, a drivetrain. There are a few different options depending on the kit being used. For this guide the Class Bot V2 is used. Check out the build guide for full building instructions for the Class Bot V2!
The graphic below highlights the major hardware components of the Class Bot V2. These components are important to understand for the configuration process.
The Hello Robot - Configuration section focused on configuring the components in the Test Bed. In order to continue forward with the Robot Control programming sections, a new configuration file must be made for the components on the robot. It is your choice what variable names you would like to assign to your robot, but for reference this guide will use the following names for each hardware component.
Before continuing it is important to understand the mechanical behavior of different drivetrains. The two most common drivetrain categories types are Differential and Omnidirectional. The Class Bot's drivetrain is a differential drivetrain. The table below highlights the main features of these two types of drivetrains.
There are a number of different ways to control a robot teleoperated. When using the REV Control System this is done with a Driver Station Device and gamepads. There are various ways to use a controller to drive a differential drivetrain. Two of the conventional ways are Tank Drive and Arcade Drive.
For tank drive, each side of the differential drivetrain is mapped to its own joystick. Changing the position of each joystick allows the drivetrain to steer and change its heading. Sample code exists in the Robot Controller Application to control a differential drivetrain in this way.
For arcade drive, each side of the differential drivetrain is controlled by a single joystick. Changing position of the joystick changes the power applied to each side of the drivetrain allowing for a given command. Arcade drives typically have left/right movement of the joystick set to spin the robot about its axis with forward/back moving the robot forward and reverse. More information on Arcade drive are found in the Robot Navigation - Blocks and Robot Navigation - OnBot Java sections.
With the robot configured, a basic understanding of drivetrains, and teleoperated control types, we can move forward to programming the drivetrain to get the robot moving.
Section
Goals of Section
Introduces a potential robot to work with as well as the configuration file used in the following sections.
Differences between differential and omnidirectional drivetrains and their affect on teleoperated control types.
Hardware Component
Hardware Type
Name
Right Drive Motor
REV Robotics UltraPlanetary HD Hex Motor
rightmotor
Left Drive Motor
REV Robotics UltraPlanetary HD Hex Motor
leftmotor
Arm Motor
REV Robotics Core Hex Motor
arm
Claw Servo
Servo
claw
Touch Sensor
REV Touch Sensor
touch
Differential Drivetrains
Omnidirectional Drivetrains
Most common type of drivetrain
Can move in any direction due to rollers on the wheels
Moves along a central axis
Varies power to each wheel to change heading or strafe
Applies more power to one side of the drivetrain than the other to change heading
More complex programming
Can have different names depending on the number of motors, wheels, and wheel types used (4WD, 6WD, West Coast)
Requires more than 2 motors (depending on specific type and configuration)
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.
It is recommended that you review both articles before moving on with this guide.
Start by creating a basic op mode called HelloRobot_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.
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?
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.
When using encoders built into motors, converting from ticks per revolution to ticks per unit of measure moved requires the following information:
Ticks per revolution of the encoder shaft
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.
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.
Add the variables to the initialization section of the op mode.
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 multipleCOUNTS_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 theCOUNTS_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.
Create these variables in Blocks and add then to the op mode under the other constant variables.
OnceCOUNTS_PER_WHEEL_MM
is set, this completes the conversion process, and all constant variables are set.
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.
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
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.
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.
With the velocity set, this is the final thing needed to complete the objective of driving in a straight line. Consider adding telemetry and other hardware components as you begin fleshing out your full autonomous code.
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. Velocity is 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.
There are two articles in that go through the basics of 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, , focuses on the general functionality of an encoder.
For information on how op modes work please visit the section.
For more information on how to change the op mode type check out thesection.
Add the block to the op mode under the .This will change the direction of the rotation of the right motor to be the same direction as the left motor.
For more information on the directionality of motor check out the section.
Recall from that using RUN_TO_POSITION
mode requires a three step process. The first step is setting target position. To set target position, grab the block and add it to the op mode under the comment. 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.
When there are multiple of the same type of variable (such as multiple Dc Motor variables) the variable specific blocks will choose a default variable based on alphabetical order. For this example Op Mode Dc Motor blocks will default to the arm variable. Click the arrow next to the motor name to change the arm motor variable to the rightmotor variable. Use the variable drop down menu on the block to change from arm to rightmotor.
The next step is to set both motors to the RUN_TO_POSITION
mode. Place the block beneath the block.
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 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 velocity for now.
Add the block to the op mode beneath the block. Change the duty cycle (or power) of both motors to 0.8, instead of 1.
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 block.
Recall that, within a linear op mode, a while loop must always have the Boolean as a condition. This condition ensures that the while loop will terminate when the stop button is pressed.
Grab an block from the logic menu and add it to the while loop. On the left side of the block add the block. On the right side add the block.
Embed the in another block. Place the on the right side of the block. On the left side add the block.
Right now the while loop is waiting for the right and left motors to reach their respective targets. There may be occasions when you want to wait for both motors to reach their target position, in this case the can be used.
In the section, it is clarified that ell 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 . 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 section of the Using Encoders guide.
In the previous section, the basic structure needed to use RUN_TO_POSITION
was created. The placement ofwithin 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?
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.
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.
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 .
Once the variables are created and added to the op mode, use the blocks to set the variables to the respective values. For WHEEL_CIRCUMFERENCE_MM
a combination of the , , and blocks to get the circumference of the wheel. The
= 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
Again math blocks need to be used to define these variables. Lets start with the COUNTS_PER_WHEEL_REV
variable. Add a to the block. Add the and blocks to either side of the block.
Since COUNTS_PER_WHEEL_REV
has been calculated it can be used to calculate COUNTS_PER_MM
add the to the . On the left side of the add the block. On the right side of the add the .
Create two more variables called leftTarget
andrightTarget
. Add the and blocks to the op mode above the block.
Edit the so that both motors are set to the appropriate target position. To do this add the and blocks to their respective motor.
Create a new variable called TPS. Add the to the op mode under the comment block.
Add a block to the block. On the right side of the block add the . One the left side of the add the block. Add the chosen RPM to the left side of the block and 60 to the right side.
Now that the target ticks per second has been set, swap the block for a block. Add the to both motors.
In the section, the mechanism of was discussed. dictates what direction and speed a motor moves in. On a drivetrain the combined direction and speed of the motors dictates whether the robot moves in forward, backwards, or turns.
In RUN_TO_POSITION
mode the encoder counts are used instead of 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
For more information on how direction and speed impact the movement of a robot please refer to the explanation of in the section.
Ticks per revolution | 28 ticks |
Total gear reduction | 30.21 |
Circumference of the wheel |
As alluded to in the Hello Robot - Robot Control section, robot control comes in many different forms. One of the control types to consider for robots with drivetrains, is robot navigation.
Robot navigation as a concept is dependent on the type of drivetrain and the type of operation mode. For instance, the code to control a mecanum drivetrain differs from the code used to control a differential drivetrain. There is also a difference between coding for teleoperated driving, with a gamepad, or coding for autonomous, where each movement of the robot must be defined within code.
The following section goes through some of the basics of programming for a differential drivetrain, as well as how to set up a teleoperated arcade style drivetrain code. The concepts and logic highlighted in this section will be applicable the autonomous control section Elapsed Time.
Start by creating a basic op mode called DualDrive
.
Visit the Test Bed - OnBot Java section for more information on creating an op mode. The op mode below focuses on hardware mapping only the relevant drivetrain motors.
Since the focus of this section is creating a functional drivetrain in code, lets started by adding rightmotor.setPower(1);
and leftmotor.setPower(1);
to the op more while loop.
Before moving on try running the code as is and consider the following questions:
What behavior is the robot exhibiting?
What direction is the robot spinning in?
When motors run at different speeds they spin along their center pivot point. But the motors are both set to a power (or duty cycle) of 1?
DC Motors are capable of spinning in two different directions depending on the current flow: clockwise and counter clockwise. When using a positive power value the Control Hub sends current to the motor for it to spin in a clockwise direction.
With the Class Bot and current code, both motors are currently set to run in the clockwise direction. If you set the robot on blocks and run the code again though, you can see that the motors run in opposing directions. With the mirrored way the motors mount to the drivetrain, one motor is naturally the inverse of the other.
Why would the inverse motor cause the robot to spin in a circle? Both speed and direction of rotation of the wheels impact the overall direction the robot moves in. In this case, both motors were assigned to have the same power and direction but how the motors transfer motion to the wheels, causes the robot to spin instead of moving forward.
Check the Introduction to Motion section for more information on the mechanics of transferring motion and power.
In the info block above you were asked to determine which direction the robot spun in. The robot pivots in the direction of the inversed motor. For instance, when the right motor is the inversed motor the robot will pivot to the right. If the left motor is the inversed motor the robot will pivot to the left.
For the Class Bot, the robot pivots to the right, so the right motor will be reversed. Add the line rightmotor.setDirection(DcMotorSimple.Direction.REVERSE);
to the op mode under the variable declarations.
Adding the rightmotor.setDirection(DcMotorSimple.Direction.REVERSE);
code line reverses (or inverses) the direction of the right motor. Both motors now consider the same direction forward an
Recall that when the motors were running in opposing directions the robot spun in circles. This same logic will be used to control the robot using the arcade style of control mentioned in the Hello Robot - Autonomous Robot section.
To start, create two variablesand . Both variables will be doubles.
Assign as y = -gamepad1.right_stick_y;
, which is the y-axis of the right joystick.
Remember positive/negative values inputted by the gamepad's y-axis are inverse of the positive/negative values of the motor.
Assign the as the x = gamepad1.right_stick_x;
, which is the x axis of the right gamepad joystick. The x-axis of the joystick does not need to be inverted.
Setting x = gamepad1.right_stick_x;
and y = -gamepad1.right_stick_y;
assigns values from the gamepad joystick to and . As previously mentioned, the joystick gives values along a two dimension coordinate system. receives the value from the y- axis and receives the value from the x-axis. Both axis output values between -1 and 1.
To better understand consider the following table. The table shows the expected value generated from moving the joystick all the way in one direction, along the axis. For instance, when the joystick is pushed all the way in the upwards direction the coordinate values are (0,1).
The table below assumes that the the values from the gamepad have been inverted in code when assigned to the variable .
Now that you have a better understanding of how the physical movement of the gamepad affects the numerical inputs given to your Control System; its time to consider how to control the drivetrain using the joystick.
Recall, from the Programming Drivetrain Motors section, that the speed and direction of a motor plays a large part in how the drivetrain moves.
The numerical outputs for setPower
determine the speed and direction of the motors. For instance, when both motors are set to 1 they move in the forward direction at full speed (or 100% of duty cycle). Much like the gamepads, the numerical value for setPower
is in a range of -1 to 1. The absolute value of the assigned number determines percentage of duty cycle. As an example, 0.3 and -0.3 both indicate that the motor is operating at a duty cycle of 30%. The sign of the number indicates the direction the motor is rotating in. To better understand, consider the following graphic.
When a motor is assigned a setPower
value between -1 and 0, the motor will rotate in the direction it considers to be reverse. When a motor is assigned a value between 0 and 1, it will rotate forward.
In the Programming Drivetrain Motors section, it was discussed that a robot rotates when the motors are moving in opposing directions. However, this has more to do with both speed and direction. To think of it numerically, a differential drivetrain will turn to the right when the setPower
value for the right motor is less than that of the left motor. This is exhibited in the following example.
When both motors are rightmotor.setPower(1); leftmotor.setPower(1);
the robot will run at full speed in a mostly straight line. However, when the rightmotor is running in the same direction but at a lower speed, such as rightmotor.setPower(0.3); leftmotor.setPower(1);
the robot will turn or rotate to the right. This is likely to be an arching movement that is not as sharp as a full pivot. In contrast when the rightmotor
is set to full speed but in the opposite direction of the leftmotor
, the robot pivots to the right. So, mathematically the following is considered to be true:
As previously implied, gamepad1.right_stick_y
and gamepad1.right_stick_x
send values to the Control System from the game pad joystick. In contrast, the setPower
function interprets numerical information set in the code and sends the appropriate current to the motors to dictate how the motors behave.
In an arcade drive, the following joy stick inputs (directions) need to correspond with the following outputs (motor power values).
To get the outputs expressed in the table above, the gamepad values must be assigned to each motor in a meaningful way, where. Algebraic principles can be used to determine the two formulas needed to get the values. However, the formulas are provided below.
Rather than setPower(1);
both the motors can be set to the above formulas. For instance, the right motor can be set as rightmotor.setPower(y-x);
.
With this you now have a functional teleoperated arcade drive. From here you can start adding hardware mapping for the other pieces of robot hardware. Below is an outline of the expected code for the Class Bot with full hardware mapping.
As alluded to in the Hello Robot - Robot Control section, robot control comes in many different forms. One of the control types to consider for robots with drivetrains, is robot navigation.
Robot navigation as a concept is dependent on the type of drivetrain and the type of operation mode. For instance, the code to control a mecanum drivetrain differs from the code used to control a differential drivetrain. There is also a difference between coding for teleoperated driving, with a gamepad, or coding for autonomous, where each movement of the robot must be defined within code.
The following section goes through some of the basics of programming for a differential drivetrain, as well as how to set up a teleoperated arcade style drivetrain code. The concepts and logic highlighted in this section are applicable to autonomous control, including the section Elapsed Time.
For controlling the Class Bot V2 drivetrain, being able to control two motors simultaneously is important. This is done through the dual motor block within Blocks. To access the dual motor block, at the top of the Categorize Blocks section there is a drop down menu for Actuators. Selecting DcMotor will drop down the options Dual and another drop down menu Extended. Select Dual to access the dual motor blocks.
When there are multiple of the same type of variable (such as multiple Dc Motor variables) the variable specific blocks will choose a default variable based on alphabetical order. For this example Op Mode Dc Motor blocks will default to the arm variable.
Use the variable drop down menu on the block to change from arm to rightmotor.
Before moving on try running the code as is and consider the following questions:
What behavior is the robot exhibiting?
What direction is the robot spinning in?
When motors run at different speeds they spin along their center pivot point. But the motors are both set to a power (or duty cycle) of 1?
DC Motors are capable of spinning in two different directions depending on the current flow: clockwise and counter clockwise. When using a positive power value the Control Hub sends current to the motor for it to spin in a clockwise direction.
With the Class Bot and current code, both motors are currently set to run in the clockwise direction. If you set the robot on blocks and run the code again though, you can see that the motors run in opposing directions. With the mirrored way the motors mount to the drivetrain, one motor is naturally the inverse of the other.
Why would the inverse motor cause the robot to spin in a circle? Both speed and direction of rotation of the wheels impact the overall direction the robot moves in. In this case, both motors were assigned to have the same power and direction but how the motors transfer motion to the wheels, causes the robot to spin instead of moving forward.
Check the Introduction to Motion section for more information on the mechanics of transferring motion and power.
In the info block above you were asked to determine which direction the robot spun in. The robot pivots in the direction of the inversed motor. For instance, when the right motor is the inversed motor the robot will pivot to the right. If the left motor is the inversed motor the robot will pivot to the left.
Recall that when the motors were running in opposing directions the robot spun in circles. This same logic will be used to control the robot using the arcade style of control mentioned in the Hello Robot - Autonomous Robot section.
Remember positive/negative values inputted by the gamepad's y-axis are inverse of the positive/negative values of the motor.
To better understand consider the following table. The table shows the expected value generated from moving the joystick all the way in one direction, along the axis. For instance, when the joystick is pushed all the way in the upwards direction the coordinate values are (0,1).
The table below assumes that the the values from the gamepad have been inverted in code when assigned to the variable .
Much like the gamepads, the numerical value for setPower
is in a range of -1 to 1. The absolute value of the assigned number determines percentage of duty cycle. As an example, 0.3 and -0.3 both indicate that the motor is operating at a duty cycle of 30%. The sign of the number indicates the direction the motor is rotating in. To better understand, consider the following graphic.
When a motor is assigned a setPower
value between -1 and 0, the motor will rotate in the direction it considers to be reverse. When a motor is assigned a value between 0 and 1, it will rotate forward.
In the Programming Drivetrain Motors section, it was discussed that a robot rotates when the motors are moving in opposing directions. However, this has more to do with both speed and direction. To think of it numerically, a differential drivetrain will turn to the right when the setPower
value for the right motor is less than that of the left motor. This is exhibited in the following example.
In an arcade drive, the following joy stick inputs (directions) need to correspond with the following outputs (motor power values).
To get the outputs expressed in the table above, the gamepad values must be assigned to each motor in a meaningful way, where. Algebraic principles can be used to determine the two formulas needed to get the values. However, the formulas are provided below.
With this you now have a functional teleoperated arcade drive!
One way to create an autonomous code is to use a timer to define which actions should occur when. Within the SDK actions can be set to a timer by using ElapsedTime.
Timers consist of two main categories: count up and count down. In most applications a timer is considered to be a device that counts down from a specified time interval. For instance, the timer on a phone or a microwave. However, some timers, like stopwatches, count upwards from zero. These types of timers measure the amount of time that has elapsed.
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 case, it is the amount of time elapsed from when the timer is created or reset within the code.
Since this section focuses on creating an autonomous program using ElapsedTime it is important to understand where the elapsed time related blocks are located. At the top of the Categorize Blocks section there is a drop down menu for Utilities. The utilities drop down is a list of various utilities in alphabetical order. Towards the bottom of the the list select Time drop down menu. From there you can select Elapsed Time.
Start by creating a new op mode call HelloWorld_ElapsedTime
using the BasicOpMode
sample.
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.
Create a variable named runtime
.
Use tape to mark the distance from where the robot starts to where you would like it to end up. Try running the code using the following conditions:
Press the init button and immediately press play
Press the init button, wait 30 seconds and then press play
What difference in behavior did you notice?
As mentioned in previous sections, it can be beneficial to have a telemetry output when testing code. In the following example telemetry is used to output the amount of time that has passed with the timer.
The above code will allow your motor to drive straight for 3 seconds. Additional movements can be added by duplicating the while loop. Right click the while loop block and select duplicate
One way to create an autonomous code is to use a timer to define which actions should occur when. Within the SDK actions can be set to a timer by using ElapsedTime.
Timers consist of two main categories: count up and count down. In most applications a timer is considered to be a device that counts down from a specified time interval. For instance, the timer on a phone or a microwave. However, some timers, like stopwatches, count upwards from zero. These types of timers measure the amount of time that has elapsed.
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 case, it is the amount of time elapsed from when the timer is instantiated or reset within the code.
The ElapsedTime timer starts counting the amount of time elapsed from the point of its creation within a code. For instance, in this section ElapsedTime will be created (or instantiated) in the section of code that occurs when the op mode is initialized. There is no option to stop the ElapsedTime timer. Instead, the reset() function can be used within your code to reset the timer at various intervals.
One the timer has been reset, the amount of time that has elapsed can be queried by calling methods like time(), seconds(), or milliseconds(). The time given by the queried methods can be used in loops to dictate how long a specific action should take place.
Start by creating a new op mode called HelloWorld_ElapsedTime
using the BasicOpMode_Linear
sample. There are other feature you can select that may make things easier as you begin to develop your autonomous op modes. For instance, as you may recall, selecting Setup Code for Configured Hardware creates the necessary references to the hardware map. Another selection you can make is for the code to be setup as an autonomous op mode. This adds the @Autonomous
annotation that distinguishes the code as an autonomous op mode in the Driver Station Application.
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.
Selecting the features discussed above will allow you to start with the following code.
Since the focus of this section is Elapsed Time, a variable of ElapsedTime
and an instance of ElapsedTime
needs to be created. To do this the following line is needed
The above line performs two actions. A private ElapsedTime
variable called runtime
is created. Once runtime
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 the runtime variable.
Add this line to the op mode with the other private variables.
The goal for this example is to have a series of actions performed on timed intervals, like driving forward for three seconds. Another way to think about it is that the robot drives forward while the ElapsedTime
timer is less than or equal to three seconds or runtime.seconds() <= 3.0
. For this particular example the best way to achieve this goal is to use a while loop. Replace the default op mode while loop with the following loop.
It is important to know 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.
While loops run when the condition is true and stop when the condition is false. In this case, the while loop should only start if both conditions (opModeIsActive()
and runtime.seconds() <= 3.0
) are true. The while loop should terminate when theruntime.seconds() > 3
is greater than three seconds or the stop button on the driver station is pressed. To accomplish this the logical operator &&
needs to be used.
&&
is a logical operator in Java. This symbol is the Java equivalent of "and." Using this in a conditional statement requires that both statements need to be true in order for the overall condition to be true.
Recall that the ElapsedTime
timer starts when it is instantiated or reset. Since the timer is being instantiated when the runtime variable is being created, and the variable creations are happening before the waitForStart();
command is written; the timer will start when the op mode is initialized rather than when the op mode is started. This can cause issues on consistency in the robots performance, depending on the delay between initialization and start.
Consider the following scenario:
In a competition setting, teams are often required to initialize their robot prior to the start of a match. This means that a robot can sit in initialization anywhere from a few seconds to a few minutes. If an autonomous code is centered around using an ElapsedTime
timer that begins upon instantiation, the longer a robot is sitting in initialization the less likely it is to run as expected.
In order to avoid issues from a time delay between initialization and start, a timer reset can be added to the code. Add the line runtime.reset()
; between the waitForStart();
command and the while loop.
Now, within the while loop add the linesleftmotor.setPower(1);
and rightmotor.setPower(1);
to set both motors to run at full speed in the forward direction.
You now have the basic code you need to have your robot drive forward for three seconds. This should give you a basic sense of coding with ElapsedTime
. Other actions like opening and closing a claw, or lifting an arm can be coded into your autonomous program.
As advised in previous sections, it is beneficial to add telemetry to certain code to get the feedback data you want or need. For this example, the telemetry will display how many seconds have elapsed for each leg of the robots journey.
For this particular guide, the end goal is to test the accuracy of a robot driving forward from point a to point b and then driving backwards back to point a. In order to do that another section of code based off the timer needs to be written. One way to do this is to to copy the while loop that you already made and make the necessary edits like switching the direction of power to the motors.
Notice that an additional runtime.reset();
was added to the code above. The other option for a second while loop would have involved adding an additional condition to the while loop. Such as:
while(opModeIsActive() && (runtime.seconds() > 3.0) && runtime.seconds() <=6.0)
The choice to reset the timer before starting a new leg of the robots journey was made to reduce the amount of code changes that may need to be made while testing the code.
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.
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.
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.
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 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.
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.
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.
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.
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 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.
Add the block to op mode while loop.
For the Class Bot, the robot pivots to the right, so the right motor will be reversed. Add to the op mode class, under the comment block.
Adding the block changes the direction of the right motor so that both motors will run in the same direction when power is set to one.
To start, create two variables and . Add the andblocks to the while loop.
will be assigned as , which is the y-axis of the right joystick.
Assign as the , which is the x axis of the right gamepad joystick. The x-axis of the joystick does not need to be inverted.
The and block sets assign values from the gamepad joystick to and . As previously mentioned, the joystick gives values along a two dimension coordinate system. receives the value from the y- axis and receives the value from the x-axis. Both axis output values between -1 and 1.
Now that you have a better understanding of how the physical movement of the gamepad affects the numerical inputs given to your Control System; its time to consider how to control the drivetrain using the joystick. Recall, from the Programming Drivetrain Motors section, that the speed and direction of a motor plays a large part in how the drivetrain moves. The numerical outputs for determine the speed and direction of the motors. For instance, when both motors are set to 1 they move in the forward direction at full speed (or 100% of duty cycle).
When both motors are the robot will run at full speed in a mostly straight line. However, when the rightmotor is running in the same direction but at a lower speed, such as the robot will turn or rotate to the right. This is likely to be an arching movement that is not as sharp as a full pivot. In contrast when the rightmotor is set to full speed but in the opposite direction of the leftmotor, the robot pivots to the right. So, mathematically the following is considered to be true:
As previously implied, and send values to the Control System from the game pad joystick. In contrast, interprets numerical information set in the code and sends the appropriate current to the motors to dictate how the motors behave.
From the math menu grab the and blocks and add them to the respective motor in the block.
Next add the and to the formula blocks.
The ElapsedTime timer starts counting the amount of time elapsed from the point of its creation within a code. For instance, in this section ElapsedTime will be created in the section of code that occurs when the op mode is initialized. There is no option to stop the ElapsedTime timer. Instead, the block can be used within your code to reset the timer at various intervals.
Once the timer has been reset, the amount of time that has elapsed is queried by calling blocks like . The time given by the queried blocks can be used in loops to dictate how long a specific action should take place.
For information on how op modes work please visit the section.
For more information on how to change the op mode type check out thesection.
For information on creating variables in blocks please revisit the section.
Add the block to the op mode below the comment block.
In order to utilize elements of the ElapsedTime
, runtime
will act as the ElapsedTime
variable. Add the block to the block.
Before moving on to the rest of the ElapsedTime
structure lets go ahead and add the motor related blocks. Add to the op mode to the while loop.
When there are multiple of the same type of variable (such as multiple Dc Motor variables) the variable specific blocks will choose a default variable based on alphabetical order. For this example Op Mode Dc Motor blocks will default to the arm variable. Click the arrow next to the motor name to change the arm motor variable to the rightmotor variable. Use the variable drop down menu on the block to change from arm to rightmotor.
If you recall from article; the motors on the drivetrain mirror each other. The mirrored nature of the motor mounting causes the motors to rotate in opposing directions. In order to remedy this discrepancy the direction of the right motor needs to be reversed. Add the block to the op mode under the the block set.
The goal is to have the motor move forward for 3 seconds. To accomplish this the While loops needs to be edited so that it triggers when the op mode is active and the ElapsedTime timer is less than or equal to 3 seconds. Lets start by creating the less than or equal to condition. Grab the from the Logic menu.
Select the block from the Elapsed Time menu. Drop the block into the left side of the block. Use the drop down menu to change the generic to the variable.
Grab the block from the Math menu.
Add the number block to the right side of the block. Change the number block to 3.
Right now the is equal to three. Use the arrow next to the equal sign to choose the less than or equal to sign from the drop down menu.
Set this block set to the side for now. Grab an block from the Logic menu
Add the call block to the left side of the block. Add the block set to the right side of the block.
This block set will replace the block that is currently attached to the while loop. With this block set in place the while loop will now activate when both conditions of the and block are true.
It is important to know that, within a linear op mode, a while loop must always have the Boolean as a condition. This condition ensures that the while loop will terminate when the stop button is pressed.
Recall that theElapsedTime
timer starts when the timer is created, which occurs where theblock is placed. Since the timer is created prior to , the timer will start when the program is initialized.
If you tested the program you may have noticed that the robot didn't move the during the second run. Depending on how long you wait to start after initialization the timer may be close to or past 3 seconds by the time the program is played. To keep this from happening the timer should be reset once the op mode is active. Grab the call block. Use the drop down menu on the variable block to change the to .
Add the to the op mode beneath the comment and above the while loop.
Once you have duplicated the while loop you can change some of the basic information like motor power or the time interval of the loop. You will also need to add a block between the two loops.
For more information on the ElapsedTime object check out the .
For information on how op modes work please visit the section.
Now the timer is reset , lets go ahead and add the motor related code. If you recall from article; the motors on the drivetrain mirror each other. The mirrored nature of the motor mounting causes the motors to rotate in opposing directions. In order to remedy this discrepancy the direction of the right motor needs to be reversed. Add the following lines of code to the op mode above the waitForStart();
command.
There are two articles in that go through the basics of 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, , focuses on the general functionality of an encoder.
For information on how op modes work please visit the section.
For more information on how to change the op mode type check out the section.
For more information on the directionality of motor check out the section.
Recall from 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.
In the 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 section of the Using Encoders guide.
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.
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.
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 .
= 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 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.
Sections
Goals of Section
What to consider when programming drivetrain motors and how to apply this to an arcade style teleoperated control.
rightmotor.setPower = leftmotor.setPower
Forward or Reverse
rightmotor.setPower > leftmotor.setPower
Left Turn
rightmotor.setPower < leftmotor.setPower
Right Turn
Sections
Goals of Section
What to consider when programming drivetrain motors and how to apply this to an arcade style teleoperated control.
rightMotor = leftMotor
Forward or Reverse
rightMotor > leftMotor
Left Turn
rightMotor < leftMotor
Right Turn
Joystick Direction
0
1
0
-1
-1
0
1
0
Joystick Direction
(X,Y)
rightmotor
leftmotor
(0,1)
1
1
(0,-1)
-1
-1
(-1,0)
1
-1
(1,0)
-1
1
Joystick Direction
0
1
0
-1
-1
0
1
0
Joystick Direction
( , )
rightmotor
leftmotor
(0,1)
1
1
(0,-1)
-1
-1
(-1,0)
1
-1
(1,0)
-1
1
Sections | Goals of Section |
Learning the logic needed to use elapsed time for autonomous control. |
Sections | Goals of Section |
Learning the logic needed to use elapsed time for autonomous control. |
Ticks per revolution | 28 ticks |
Total gear reduction | 30.21 |
Circumference of the wheel |
Robot control comes in many different forms. Now that you have walked through programming a drivetrain, we can apply those concepts to controlling other mechanisms. Since this guide utilizes the Class Bot the focus will be on the basics of controlling it's main mechanism, a single jointed arm.
Controlling an arm requires a different thought process than the one you used to control the drivetrain. While the drivetrain uses the rotation motion of the motors to drive along a linear distance, an arm rotates along a central point, or joint. When working with an arm you will have to head caution to the physical limitations of the robot this includes load bearing, range of motion, and other forces that may apply.
In this section you will learn how to use the gamepad Dpad
controls and the installed Touch Sensor to control the arm. However, the focus of this section is using code to limit the range of motion of the arm.
Start by creating a basic op mode called HelloRobot_ArmControl
.
For more information on how to create an op mode type check out the Test Bed - Blocks section.
Save the op mode and try running the code. Consider the following questions.
What happens if you press up on the Dpad
?
What happens if you press down on the Dpad
?
Try saving and running the op mode again. Pay attention to the speed of the arm going up versus going down. Does the speed seem the same?
Working with an arm introduces different factors for consideration than what you have seen previously with drivetrains. For instance, did you notice a difference in speeds when moving the arm up or down? Unlike the drivetrain, where the effect of gravity impacts the motors consistently across either direction, gravity plays a significant role in the speed of the arm motor.
Another consideration to make is the physical limitations of your arm mechanism. Certain mechanisms may have a physical limitation, that when exceeded runs the risk of damaging the mechanism or another component of the robot. There are a few ways to limit the mechanism with sensors that will help reduce the potential of a mechanism exceeding its physical limitations. In this section we will focus on using a limit switch to limit the motion range of the arm.
This section assumes that you have a basic knowledge of limit switches form the Test Bed section and the Digital Sensors article.
As you may recall from the Test Bed section limit switches use Boolean logic to dictate when a limit has been met. Limit switches typically come in the form of digital sensors, like the Touch Sensor, as digital sensors report a Boolean on/off to the system, much like a light switch.
If you are using a Class Bot your robot should have a Touch Sensor mounted to the front of your robot chassis. You also have a Limit Switch Bumper installed. Together these items create a limit switch system. By utilizing the limit switch system you can keep your Class Bot arm from exceeding the lower physical limit, or what will be known as our starting position. Lets go ahead and start coding!
Before proceeding with code please make sure that your mechanism is interfacing with, and pressing the Touch Sensor. If you have the Class Bot this entails making sure your bumper is actively pressing the Touch Sensor when the arm comes down.
Save the op mode and run it.
What happens when the Touch Sensor is pressed?
One of the common features of a limit switch, like the Touch Sensor, is the ability to reset to its default state. If you press the Touch Sensor with your finger, you may notice that as soon as you release the pressure you are applying the Touch Sensor will return to its default "not pressed" state. However, you have to release the pressure in order to accomplish this.
Make sure that the mechanism is actually interfacing with the Touch Sensor. For the Class Bot, you may need to adjust the Touch Sensor so that the Limit Switch Bumper is interfacing with it more consistently.
The code in the info block above dictates that when the Touch Sensor is pressed the arm motor is set to zero. This would work in a mechanism where the Touch Sensor is allowed to return to its default state on its own. However, once the arm presses the Touch Sensor, the weight of the mechanism will keep the Touch Sensor from returning to its default state. The combination of the weight of the mechanism and the logic of the info block code means that once the arm meets its limit it will not be able to move again.
In the Encoder Navigation 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.
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 initial code we created at the start of the Basics of Programming an Arm, section.
For now you can move the limit switch related blocks to the side of your project.
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.
Recall from the Basic Encoder Concepts 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.
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.
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. 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 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.
To start you will need some of the same variables we used in Encoder Navigation:
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 Encoder Navigation 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 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:
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 variablesCOUNTS_PER_MOTOR_REV
and GEAR_REDUCTION
variables to the initialization section of the op mode.
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 Encoder Navigation for ourCOUNTS_PER_WHEEL_REV
variable. So to get this variable we can multipleCOUNTS_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 op mode in the initialization section of the op mode.
Finally we need to create a non-constant variable that will act as our position. Create a variable called arm 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.
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 a 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 variables one called minPosition
and one called maxPosition
. Add both of these to the initialization section of the op mode.
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 Basics of Programming an 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.
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
.
So, if the touch sensor returns false
(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!
Robot control comes in many different forms. Now that you have walked through programming a drivetrain, we can apply those concepts to controlling other mechanisms. Since this guide utilizes the Class Bot the focus will be on the basics of controlling it's main mechanism, a single jointed arm.
Controlling an arm requires a different thought process than the one you used to control the drivetrain. While the drivetrain uses the rotation motion of the motors to drive along a linear distance, an arm rotates along a central point, or joint. When working with an arm you will have to head caution to the physical limitations of the robot this includes load bearing, range of motion, and other forces that may apply.
In this section you will learn how to use the gamepad Dpad
controls and the installed Touch Sensor to control the arm. However, the focus of this section is using code to limit the range of motion of the arm.
Start by creating a basic op mode called HelloRobot_ArmControl
.
For more information on how to create an op mode check out the Test Bed - Onbot Java section.
Unlike the joystick, which sends values corresponding to the position of the joystick, the Dpad
on the gamepad inputs Boolean FALSE/TRUE
. In order to dictate how the arm moves when you press DpadUp
or DpadDown
; anif/else if
statement needs to be used. Create an if/else if
statement like the one below.
Now that the basic structure is in place, we can add the necessary blocks to dictate the direction of the arm. The best practice is to have the arm move up when DpadUp
is selected and down with DpadDown
is selected. To do this lets addarm.SetPower();
to each of the actionable parts of the if/else if
statement.
Recall that the value assigned to setPower
dictates the direction and speed of the motor. Between the motor and the gearing on the class bot the positive value will move the arm the arm upwards and a negative value will move the arm downwards.
If you are unsure which direction your motor will move create the following code and test to ensure that your motor is behaving as expected.
Starting with a lower duty cycle percentage such as the 0.2 exhibited in the code above, will allow for easier testing when making decisions for the arm. We will change to a higher duty cycle later on in this guide.
Save the op mode and try running the code. Consider the following questions.
What happens if you press up on the Dpad
?
What happens if you press down on the Dpad
?
Right now the logic of the if/else if
statement declares that when gamepad1.dpad_up
is true (has been pressed) the motor will run in the forward ( or in this case upwards) direction at 20% duty cycle. If gamepad1.dpad_down
is true the motor will run in reverse at 20% duty cycle. If you ran the code at this stage you may have noticed that even when you released theDpad
the motor continued to run in the selected direction. The current if/else if
statement tells the robot when the motor should move and in what direction, but nothing tells the motor to stop, thus the arm continues to run without limits.
To fix this edit theif/else if
statement to include and action to perform if neither gamepad conditions are true. Since we want the arm to stop moving if neither gamepad conditions are met lets use arm.setPower(0);
to stop the motor .
Try saving and running the op mode again. Pay attention to the speed of the arm going up versus going down. Does the speed seem the same?
Working with an arm introduces different factors for consideration than what you have seen previously with drivetrains. For instance, did you notice a difference in speeds when moving the arm up or down? Unlike the drivetrain, where the effect of gravity impacts the motors consistently across either direction, gravity plays a significant role in the speed of the arm motor.
Another consideration to make is the physical limitations of your arm mechanism. Certain mechanisms may have a physical limitation, that when exceeded runs the risk of damaging the mechanism or another component of the robot. There are a few ways to limit the mechanism with sensors that will help reduce the potential of a mechanism exceeding its physical limitations. In this section we will focus on using a limit switch to limit the motion range of the arm.
This section assumes that you have a basic knowledge of limit switches form the Test Bed section and the Digital Sensors article.
As you may recall from the Test Bed section limit switches use Boolean logic to dictate when a limit has been met. Limit switches typically come in the form of digital sensors, like the Touch Sensor, as digital sensors report a Boolean on/off to the system, much like a light switch.
If you are using a Class Bot your robot should have a Touch Sensor mounted to the front of your robot chassis. You also have a Limit Switch Bumper installed. Together these items create a limit switch system. By utilizing the limit switch system you can keep your Class Bot arm from exceeding the lower physical limit, or what will be known as our starting position. Lets go ahead and start coding!
Before proceeding with code please make sure that your mechanism is interfacing with, and pressing the Touch Sensor. If you have the Class Bot this entails making sure your bumper is actively pressing the Touch Sensor when the arm comes down.
In the Test Bed - Onbot Java section, you learned how to create a basic limit switch program, similar to the one below.
If you recall from the initial Limit Switch section, the Touch Sensor operates on a FALSE/TRUE
binary. When the touch sensors is not pressed touch.getState()
reports true
; when the touch sensor is pressed touch.getState()
reports false
. The logic of the code states that when touch sensor is not pressed, the motor runs at 20% duty cycle.
Rather than have the motor run at 20% of duty cycle when the Touch Sensor isn't pressed and stop when the sensor is pressed, we want to control the arm using the gamepad still. To do this we can nest the Gamepad if/else if
statement within the Limit Switch if/else
statement.
For this next portion we will be utilizing the if/else if
statement create in the Basics of Programming and Arm. From here on out this basic code logic will be refereed to as the Gamepad if/else if
. The limit switch code will be know as the Limit Switch if/else
. Both pieces of code will be referenced again.
Save the op mode and run it.
What happens when the Touch Sensor is pressed?
One of the common features of a limit switch, like the touch sensor, is the ability to reset to its default state. If you press the Touch Sensor with your finger, you may notice that as soon as you release the pressure you are applying the Touch Sensor will return to its default "not pressed" state. However, you have to release the pressure in order to accomplish this.
Make sure that the mechanism is actually interfacing with the Touch Sensor. For the Class Bot, you may need to adjust the Touch Sensor so that the Limit Switch Bumper is interfacing with it more consistently.
The code in the info block above dictates that when the Touch Sensor is pressed the arm motor is set to zero. This would work in a mechanism where the Touch Sensor is allowed to return to its default state on its own. However, once the arm presses the Touch Sensor, the weight of the mechanism will keep the Touch Sensor from returning to its default state. The combination of the weight of the mechanism and the logic of the info block code means that once the arm meets its limit it will not be able to move again.
To remedy this, an action to move the arm in the opposite direction of of the limit needs to be added to the else
statement. Since the Touch Sensor is a lower limit for the arm, the arm will need to move up (or the motor in the forward direction) to move away from the touch sensor. To do this we can create an if/else
statement similar to our gamepad Gamepad if/else if
statement. Instead of having the normal gamepad operations, when the Touch Sensor andDpadUp
are pressed the arm moves away from the Touch Sensor. Once the Touch Sensor no longer reports false the normal gamepad operations return and the arm can move in either direction again.
In the Encoder Navigation 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.
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.
Recall from the Basic Encoder Concepts 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.
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 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.
To start you will need some of the same variables we used in Encoder Navigation:
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 Encoder Navigation 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 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:
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.
Calculating counts per revolution of the 125T gear (or COUNTS_PER_GEAR_REV
)is the same formula used in Encoder Navigation for ourCOUNTS_PER_WHEEL_REV
variable. So to get this variable we can multipleCOUNTS_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 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.
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 Basics of Programming and Arm.
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. Since the Touch Sensor reports true
when its not pressed and false
when it is, we will need to use the logical not operator !
.
The not operator !
can be used in conditional binary statements when you need inverse whether something is true
of false
. For instance, an if
statement activates when something is true, but when touch.getState();
reports true
it is not pressed. In our case we want this if statement to activate when the Touch Sensor is pressed thus we need to use the not operator.
So, if the Touch Sensor returns false
(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!
Unlike the joystick, which sends values corresponding to the position of the joystick, the Dpad
on the gamepad inputs Boolean FALSE/TRUE
. In order to tell the arm to move when Dpad Up
or Dpad Down
are selected, anif/else if
statement needs to be used. Select an block. Use the settings drop down to change the block to an block. Do this by switching the out for an.
Add the block to the op mode while loop.
Add the and blocks to the condition statements of the block.
One of the purposes of using the Dpad
is to help delineate which direction the arm needs to move in. In this case, should correspond with the arm moving upwards. Add a block to the do portion of the block below the block. Change the power from 1 to 0.2.
Add another block to the do portion of the block below the block. Change the power from 1 to -0.2.
Right now the logic of the statement declares that when is true (has been pressed) the motor will run in the forward ( or in this case upwards) direction at 20% duty cycle. If is true the motor will run in reverse at 20% duty cycle. If you ran the code at this stage you may have noticed that even when you released the Dpad
the motor continued to run in the selected direction. The current statement tells the robot when the motor should move and in what direction, but nothing tells the motor to stop, thus the arm is continuing to run without limits.
To fix this edit the block to be a instead. To do this select the settings icon on the block and add an below the .
Add the the else portion of the block.
Start by grabbing the statement made in the previous section, and dragging it to the side of the blocks project.
Add a new block to the while loop. Add the block to the conditional portion of the block.
Add the block set back to the code in the do port of the block.
If you recall from the initial Limit switch section, the touch sensor operates on a FALSE/TRUE
binary. When the touch sensors is not pressed the block reports true; when the touch sensor is pressed the block reports false. At this point the logic of the code states that when touch sensor is not pressed, the gamepad commands that were previously chosen operate normally. To function as a limit switch the motor needs to stop when the touch sensor is pressed.
Try adding a block to the else portion of the block.
To remedy this, an action to move the arm in the opposite direction of of the limit needs to be added to the else statement. To do this lets use another block. Since the Touch Sensor is a lower limit for the arm, the arm will need to move up (or the motor in the forward direction) to move away from the touch sensor. Following the earlier convention for moving the arm, add the as the condition in the . In the do portion of the block add the block. Change the duty cycle from 100% to 20%. In the else portion add the block.
Add this block set to the else portion of the block set.
Within the loop add a block. Add the to the number portion of the block. Change the key string from the default "key"
to "Arm Test."
To add the RUN_TO_POSITION
code the if/else
statement must first be edited back into an block, as shown in the code below.
Now recall that in order to execute RUN_TO_POSITION
the following three blocks need to be added to both sections of the block.
When DpadUp
is pressed, the arm should move to the the 90 degree position. When DpadDown
is pressed the arm should move back to the starting position. To do this set the first 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 block set to 0 will allow us to accomplish this. Set both blocks equal to 0.5.
Recall that the target position dictates which direction the motor moves, taking over the directionality control from the block, so both blocks can be set to a positive value since they will control the speed.
Once the variables are created and added to the op mode, use the blocks to set the variables to the respective values
Add this variable to the section of the statement, as this section dictates the 90 degree position. Add the block to the block.
We could set equal to . 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.
For now we want the minPosition
set as our starting position and the maxPosition
set to our 90 degree position. Set minPosition
equal to and set maxPosition
equal to .
Start by editing the to add another condition. Use the block as the condition. Add a block to the do portion of the block.
We can create an statement that focuses on performing this stop and reset when the touch sensor is pressed. Since the touch sensor reports true
when its not pressed and false
when it is, we will need to use the block.
The not operator can be used in conditional binary statements when you need inverse whether something is true
of false
. For instance, an if
statement activates when something is true, but when the Touch Sensor reports true
it is not pressed. In our case we want this if statement to activate when the touch sensor is pressed thus we need to use the not operator.
Sections
Goals of Section
Introduction to coding an arm for teleoperated control and working with a limit switch
Using motor encoders to move an arm to a specific position, such as from 45 degrees to 90 degrees.
Working with the basics of arm control, motor encoder, and limit switches to control the range of motion for an arm.
Ticks per revolution
288 ticks
Total gear reduction
2.777778
Sections
Goals of Section
Introduction to coding an arm for teleoperated control and working with a limit switch
Using motor encoders to move an arm to a specific position, such as from 45 degrees to 90 degrees.
Working with the basics of arm control, motor encoder, and limit switches to control the range of motion for an arm.
Ticks per revolution
288 ticks
Total gear reduction
2.777778