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
Let’s start with one of the most basic drive train controls: tank drive. In this exercise, you’ll get your first hands-on experience with LabVIEW and how it enables you to translate controller inputs to motor outputs.
Tank drive is a direct-drive control where one joystick is used for each side of the drive train. The left joystick moves the left motor forward and backward, and the right joystick moves the right motor forward and backward. To turn, you move one joystick forward and the other in reverse. This control scheme offers the driver complete control, but tends to have a steeper learning curve.
But, this scheme is the easiest to implement in software because of its simplicity!
If you have some experience programming, try implementing this drive control now. Otherwise, read on for step-by-step instructions.
In our code, we use subsystems to organize our code by component. Since we need to control our drive base, we will start by creating a drive subsystem that controls the two drive motors.
C++/src/main/cpp/subsystems
folder and select “Create a new class/command”.
src/main/cpp/subsystems/DriveSubsystem.cpp
and src/main/include/subsystems/DriveSubsystem.h
. Click on DriveSubsystem.h
to open it.
DriveSubsystem.h
file open, we need to start with including the file that describes the motor controllers. This is known as a “header file” and ends in the .h
extension. To do this, add a new line below the existing #include
line and add
#include <ctre/Phoenix.h>
private
section of the subsystem declaration. Add the motors like this:
private:
// Components (e.g. motor controllers and sensors) should generally be
// declared private and exposed only through public methods.
TalonSRX m_leftMotor;
TalonSRX m_rightMotor;
DriveSubsystem();
. To add two ids for our two motors, we will add parameters so the declaration now looks like this:
public:
DriveSubsystem(const int leftDriveID, const int rightDriveID);
DriveSubsystem.h
should look like this:
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <frc2/command/SubsystemBase.h>
#include <ctre/Phoenix.h>
class DriveSubsystem : public frc2::SubsystemBase {
public:
DriveSubsystem(const int leftDriveID, const int rightDriveID);
/**
* Will be called periodically whenever the CommandScheduler runs.
*/
void Periodic() override;
private:
// Components (e.g. motor controllers and sensors) should generally be
// declared private and exposed only through public methods.
TalonSRX m_leftMotor;
TalonSRX m_rightMotor;
};
DriveSubsystem.cpp
. This file will have the implementation of the functions we’ve declared in the header. An easy way to switch to this file is Alt + O or you can find the file in the explorer sidebar.
DriveSubsystem::DriveSubsystem(const int leftDriveID, const int rightDriveID)
: m_leftMotor(leftDriveID)
, m_rightMotor(rightDriveID) {
}
DriveSubsystem.cpp
file should look like this:
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "subsystems/DriveSubsystem.h"
DriveSubsystem::DriveSubsystem(const int leftDriveID, const int rightDriveID)
: m_leftMotor(leftDriveID)
, m_rightMotor(rightDriveID) {
}
// This method will be called once per scheduler run
void DriveSubsystem::Periodic() {}
Now we’ve got a drive subsystem, but we don’t have a way of controlling it. Because we use the controller to control many subsystems on the robot, we need to add it to RobotContainer
instead of a subsystem.
RobotContainer.h
. A shortcut is to press Ctrl + P and type the name of the file.RobotContainer.h
, we’ll start by including the header files we need. First, we want to include our subsystem from subsystems/DriveSubsystem.h
and then we’ll include the XBox Controller library from frc/XboxController.h
. The include statements should look like this:
#include "subsystems/DriveSubsystem.h"
#include <frc/XboxController.h>
private
section of the RobotContainer
declaration.
private:
// The robot's subsystems and commands are defined here...
DriveSubsystem m_drive;
frc::XboxController m_controller;
RobotContainer.h
should look like this:
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <frc2/command/Command.h>
#include "subsystems/DriveSubsystem.h"
#include <frc/XboxController.h>
/**
* This class is where the bulk of the robot should be declared. Since
* Command-based is a "declarative" paradigm, very little robot logic should
* actually be handled in the {@link Robot} periodic methods (other than the
* scheduler calls). Instead, the structure of the robot (including subsystems,
* commands, and button mappings) should be declared here.
*/
class RobotContainer {
public:
RobotContainer();
frc2::Command* GetAutonomousCommand();
private:
// The robot's subsystems and commands are defined here...
DriveSubsystem m_drive;
frc::XboxController m_controller;
void ConfigureButtonBindings();
};
RobotContainer.cpp
to add our implementation.RobotContainer::RobotContainer()
: m_drive{2, 1}
, m_controller{0} {
// Initialize all of your commands and subsystems here
...
RobotContainer.cpp
should look like this at this point:
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "RobotContainer.h"
RobotContainer::RobotContainer()
: m_drive{2, 1}
, m_controller{0} {
// Initialize all of your commands and subsystems here
// Configure the button bindings
ConfigureButtonBindings();
}
void RobotContainer::ConfigureButtonBindings() {
// Configure your button bindings here
}
frc2::Command* RobotContainer::GetAutonomousCommand() {
// An example command will be run in autonomous
return nullptr;
}
DriveSubsystem.h
public
section after the constructor.
void TankDrive(const double leftDrive, const double rightDrive);
DriveSubsystem.h
should look like this:
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <frc2/command/SubsystemBase.h>
#include <ctre/Phoenix.h>
class DriveSubsystem : public frc2::SubsystemBase {
public:
DriveSubsystem(const int leftDriveID, const int rightDriveID);
void TankDrive(const double leftDrive, const double rightDrive);
/**
* Will be called periodically whenever the CommandScheduler runs.
*/
void Periodic() override;
private:
// Components (e.g. motor controllers and sensors) should generally be
// declared private and exposed only through public methods.
TalonSRX m_leftMotor;
TalonSRX m_rightMotor;
};
DriveSubsystem.cpp
and we’ll add the definition for DriveSubsystem::TankDrive()
.::Set
method in ControlMode::PercentOutput
mode. Make a function body that looks like this:
void DriveSubsystem::TankDrive(const double leftDrive, const double rightDrive) {
m_leftMotor.Set(ControlMode::PercentOutput, leftDrive);
m_rightMotor.Set(ControlMode::PercentOutput, rightDrive);
}
DriveSubsystem.cpp
should now look like this:
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "subsystems/DriveSubsystem.h"
DriveSubsystem::DriveSubsystem(const int leftDriveID, const int rightDriveID)
: m_leftMotor(leftDriveID)
, m_rightMotor(rightDriveID) {
}
void DriveSubsystem::TankDrive(const double leftDrive, const double rightDrive) {
m_leftMotor.Set(ControlMode::PercentOutput, leftDrive);
m_rightMotor.Set(ControlMode::PercentOutput, rightDrive);
}
// This method will be called once per scheduler run
void DriveSubsystem::Periodic() {}
TankDrive()
function. Start by opening RobotContainer.cpp
frc2/command/RunCommand.h
#include <frc2/command/RunCommand.h>
m_drive.SetDefaultCommand(frc2::RunCommand(
[this] {
m_drive.TankDrive(m_controller.GetRawAxis(static_cast<int>(frc::XboxController::Axis::kLeftY)),
m_controller.GetRawAxis(static_cast<int>(frc::XboxController::Axis::kRightY)));
},
{&m_drive}));
RobotContainer.cpp
should look like this:// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "RobotContainer.h"
#include <frc2/command/RunCommand.h>
RobotContainer::RobotContainer()
: m_drive{2, 1}
, m_controller{0} {
// Initialize all of your commands and subsystems here
m_drive.SetDefaultCommand(frc2::RunCommand(
[this] {
m_drive.TankDrive(m_controller.GetRawAxis(static_cast<int>(frc::XboxController::Axis::kLeftY)),
m_controller.GetRawAxis(static_cast<int>(frc::XboxController::Axis::kRightY)));
},
{&m_drive}));
// Configure the button bindings
ConfigureButtonBindings();
}
void RobotContainer::ConfigureButtonBindings() {
// Configure your button bindings here
}
frc2::Command* RobotContainer::GetAutonomousCommand() {
// An example command will be run in autonomous
return nullptr;
}
Excellent! Now we’re ready to test out the code!
WPILib: Deploy Robot Code
DriveSubsystem.cpp
. In the body of the constructor, we will call the SetInverted()
method of m_leftMotor
:
DriveSubsystem::DriveSubsystem(const int leftDriveID, const int rightDriveID)
: m_leftMotor(leftDriveID)
, m_rightMotor(rightDriveID) {
m_leftMotor.SetInverted(true);
}
RobotController.cpp
. To do this, we will multiply our joystick values by -1
.
m_drive.SetDefaultCommand(frc2::RunCommand(
[this] {
m_drive.TankDrive(m_controller.GetRawAxis(static_cast<int>(frc::XboxController::Axis::kLeftY)) * -1,
m_controller.GetRawAxis(static_cast<int>(frc::XboxController::Axis::kRightY)) * -1);
},
{&m_drive}));
ArgoBotDriveTraining.lvproj
from the src/ArgobotV1
folder of the training repository. This will bring up the project explorer window.Drive_Tank.vi
ArgoBotV2.lvproj
from the src/ArgobotV2
folder of the training repository. This will bring up the project explorer window.Drive_Tank.vi
ArgoBot_Main.vi
Read_Joystick
bundle to the Joystick_In
inputLeft_Motor_Out
output to the Left Motor
bundle elementRight_Motor_Out
output to the Right Motor
bundle element
Teleop.vi
JoystickDevRef
output of Joystick RefNum Registry Get to the Joystick_In
inputLeft_Motor_Out
output to the LeftSpeed
input of RobotDriveMotorsRight_Motor_Out
output to the RightSpeed
input of RobotDriveMotors
Robot Main.vi
Drive_Tank.vi
again and go to the block diagram.ArgoBot_Main.vi
Congratulations! You have a driving robot! 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.
If you are using and ArgoBot v2, there’s a better way of fixing a motor that turns the wrong direction! Every motor has an “Invert” property that is designed to allow a programmer to make the “forward” direction of a motor reverse of the default forward direction.
Before you try this new method, remove the fix from before then do the following:
Begin.vi
from the project explorerLeft Motor 1 Inv
input constant to true by clicking on the green F block
Keep in mind that future exercises will use the v1 fix instead of the v2 fix, but competition robot code will set motor direction in Begin.vi
.
Congratulations! You have a driving robot! 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.
Next you’ll be trying a new drive type: arcade drive.
<-Previous | Index | Next-> |