Here you'll find some tutorials to write drive code for the ArgoBot (or any robot) using LabVIEW or C++
This project is maintained by FRC1756-Argos
Seems like you’ve mastered driving with joysticks. What about all those buttons on the controller? Now, we’ll try a different approach to driving: button drive. This exercise will skip over some of the more detailed steps covered in the tank drive and arcade drive tutorials, so you may want to refer to those tutorials if you get stuck.
Cheezy drive is an adaptation of drive code developed by FRC team 254 The Cheesy Poofs. It is based off the simple arcade drive code we wrote earlier, but with some notable improvements.
In arcade drive, you may have noticed that the radius of your turning is different when you drive fast vs driving slow. This limitation means that when driving full speed, you cannot perform tight turns.
Cheezy drive uses some more advanced control logic to enable constant-radius turning. This means that if you keep the right joystick stationary, the robot’s turning radius should be constant regardless of the robot speed.
As you’ll see, this has several advantages and disadvantages, but it’s still fairly simple to implement.
To make a working Cheezy drive, we need to implement the following (as described on Chief Delphi):
skim( v ):
// gain determines how much to skim off the top
if (v > 1.0):
return -((v - 1.0) * gain)
else if (v < -1.0):
return -((v + 1.0) * gain)
return 0;
t_left = throttle + turn
t_right = throttle - turn
left = t_left + skim(t_right)
right = t_right + skim(t_left)
That might seem pretty complicated, but we can simplify this if we think about what we want to do.
Here’s a simpler design which accomplishes the same thing:
turnPower = turn * |throttle|
left = throttle + turnPower
right = throttle - turnPower
Don’t worry if you’re unsure how this works. For now, we will implement the design to evaluate it’s advantages and disadvantages. How it works may become easier to understand as we go along.
If you have any ideas on how to do this, try it on your own before following the step-by-step instructions.
DriveSubsystem.h
to declare a new drive function
void CheezyDrive(const bool arcadeDrive, const double forwardSpeed, const double turnSpeed);
You may notice that we’ve added a boolean input named arcadeDrive
. We’ll use this as a button input so it’s easy to switch between Cheezy Drive and Arcade Drive without re-deploying the code.
DriveSubsystem.cpp
void DriveSubsystem::CheezyDrive(const bool arcadeDrive, const double forwardSpeed, const double turnSpeed) {
}
turnPower
. We’ll need to make a new variable for this:
auto turnPower = turnSpeed * std::abs(forwardSpeed);
Notice how we used auto
instead of double
like we’ve been using for other parameters and variables? This indicates that we’re allowing the compiler to detect what type of data goes into the variable. This is especially useful when we have longer type names.
If you hover your mouse over the variable name, VSCode will show you what the deduced type is.
void DriveSubsystem::CheezyDrive(const bool arcadeDrive, const double forwardSpeed, const double turnSpeed) {
auto turnPower = turnSpeed * std::abs(forwardSpeed);
m_leftDrive.Set(ControlMode::PercentOutput, forwardSpeed + turnPower);
m_rightDrive.Set(ControlMode::PercentOutput, forwardSpeed - turnPower);
}
arcadeDrive
parameter, did you? The only difference between Cheezy Drive and Arcade Drive is the value of turnPower
. In Arcade Drive, turnPower = turnSpeed
. The Cheezy Drive Value is what we’ve just implemented.
We could use an if
statement like button drive
auto turnPower = turnSpeed * std::abs(forwardSpeed);
if(arcadeDrive) {
turnPower = turnSpeed;
}
but there’s another way we can do this. The ternary operator:
auto turnPower = arcadeDrive ? turnSpeed : turnSpeed * std::abs(forwardSpeed);
The ternary operator consists of a ?
and a :
. Like we just saw, the condition we would put in an if
statement goes before the ?
, the true
case goes between the ?
and :
, and the false
case goes after the :
.
The ternary operator is best for cases when you have two simple statements because otherwise it can be harder to read than an if
/ else
block.
void DriveSubsystem::CheezyDrive(const bool arcadeDrive, const double forwardSpeed, const double turnSpeed) {
auto turnPower = arcadeDrive ? turnSpeed : turnSpeed * std::abs(forwardSpeed);
m_leftDrive.Set(ControlMode::PercentOutput, forwardSpeed + turnPower);
m_rightDrive.Set(ControlMode::PercentOutput, forwardSpeed - turnPower);
}
RobotContainer.cpp
with CheezyDrive()
. We’ll use the same joystick values as arcade drive and use RB to enable arcade drive mode.
m_drive.SetDefaultCommand(frc2::RunCommand(
[this] {
m_drive.CheezyDrive(m_controller.GetRawButton(static_cast<int>(frc::XboxController::Button::kBumperRight)),
m_controller.GetRawAxis(static_cast<int>(frc::XboxController::Axis::kLeftY)) * -1,
m_controller.GetRawAxis(static_cast<int>(frc::XboxController::Axis::kRightX)));
},
{&m_drive}
));
Just like before, we’ll start by opening the drive VI and unbundling the joystick values we want.
Drive_Cheezy.vi
from the project explorerJoystick_In
controlJoystick.RB
, Joystick.Y_Axis_Left
, and Joystick.X_Axis_Right
When you are complete, your unbundle should be identical to arcade drive except with an additional boolean (green) output.
left
and right
both require turnPower
, so let’s calculate that first.
Now that we’ve got a new drive control VI, let’s try driving with it.
ArgoBot_Main.vi
Drive_Button
block and select “Replace”>”All Palettes”>”Select A VI…”Drive_Cheezy.vi
Try driving it a little to see what you do or don’t like. You’ll want to improve on the design in future exercises.
What did you like more or less about this drive style? Is there anything that makes it hard to drive? How does changing the gain change how it drives?
Try switching between Arcade and Cheezy drive by holding the right bumper on the controller. Which do you like better? Can you think of the advantages/disadvantages of the different approaches?
Congratulations! Now you have four different drive styles under your belt! But are any of them ready for competition? These are the building blocks of more advanced controls, and we’ll explore some refinements and advanced features next. Next up: drive code refinements
<-Previous | Index | Next-> |