LST Robotics Logo

Learning • Sensors • Tech

LST Robotics

Lessons
← Back to C++ Coding Track

C++ Coding Track · Lesson 17

Autonomous State Machines

In this example, we are going to introduce autonomous state machines. A state machine is a structured way of breaking an autonomous routine into separate steps. Instead of writing one long block of code, we divide the robot’s behaviour into clear states, where each state has one task and one condition for moving to the next state. This is useful because autonomous routines often require the robot to do more than one thing. For example, the robot may need to:  move forward  turn to an angle  move again  stop If all of this is written in one large section of code, it becomes difficult to read, test, and debug. A state machine solves this problem by making the code more organized and easier to understand.

In this example, the robot will: 1. start in a setup state 2. drive forward to a target distance 3. turn to a target angle using the navX 4. stop This is a good introduction to autonomous logic because it shows how the robot can complete one action before moving on to the next.

Constant.h

#pragma once
#define _USE_MATH_DEFINES
#include <math.h>
// Defines a namespace to hold constant values
namespace constant
{
 // Identifier for the Titan module
 static constexpr int TITAN_ID = 42;
 // Motor channels
 static constexpr int M0 = 0;
 static constexpr int M1 = 1;
 static constexpr int M2 = 2;
 static constexpr int M3 = 3;
 // Frequency value used in communication
 static constexpr int frequency = 15600;
 // Encoder channels
 static constexpr int M0_VMX[2] = {0,1};
 static constexpr int M1_VMX[2] = {2,3};
 static constexpr int M2_VMX[2] = {4,5};
 static constexpr int M3_VMX[2] = {6,7};
}

Robot.h

#pragma once
#include <frc/TimedRobot.h>
#include <frc2/command/Command.h>


#include "RobotContainer.h"
#include <frc/Encoder.h>
#include "studica/TitanQuad.h"
#include "Constants.h"
#include "AHRS.h"
#include <frc/SPI.h>
class Robot : public frc::TimedRobot {
public:
 void RobotInit() override;
 void RobotPeriodic() override;
 void DisabledInit() override;
 void DisabledPeriodic() override;
 void AutonomousInit() override;
 void AutonomousPeriodic() override;
 void TeleopInit() override;
 void TeleopPeriodic() override;
 void TestPeriodic() override;
private:
 studica::TitanQuad m0{constant::TITAN_ID, constant::frequency, constant::M0};
 studica::TitanQuad m1{constant::TITAN_ID, constant::frequency, constant::M1};
 studica::TitanQuad m2{constant::TITAN_ID, constant::frequency, constant::M2};
 studica::TitanQuad m3{constant::TITAN_ID, constant::frequency, constant::M3};
 frc::Encoder m0Encoder{constant::M0_VMX[0], constant::M0_VMX[1], false, frc::Encoder::k4X};
 frc::Encoder m1Encoder{constant::M1_VMX[0], constant::M1_VMX[1], false, frc::Encoder::k4X};
 frc::Encoder m2Encoder{constant::M2_VMX[0], constant::M2_VMX[1], false, frc::Encoder::k4X};
 frc::Encoder m3Encoder{constant::M3_VMX[0], constant::M3_VMX[1], false, frc::Encoder::k4X};
 AHRS navx{frc::SPI::Port::kMXP};
 double driveTargetDistance = 20.0;
 double turnTargetAngle = 180.0;
 enum AutoState
 {
 START,
 DRIVE_FORWARD,
 TURN,
 STOP
 };
 AutoState currentState = START;
};

Robot.cpp

#include "Robot.h"
#include <frc/smartdashboard/SmartDashboard.h>
#include <cmath>
void Robot::RobotInit()
{
 m0Encoder.SetDistancePerPulse(0.01);
 m1Encoder.SetDistancePerPulse(0.01);
 m2Encoder.SetDistancePerPulse(0.01);
 m3Encoder.SetDistancePerPulse(0.01);
}
void Robot::RobotPeriodic()
{
 double averageDistance =
 (std::fabs(m0Encoder.GetDistance()) +
 std::fabs(m1Encoder.GetDistance()) +
 std::fabs(m2Encoder.GetDistance()) +
 std::fabs(m3Encoder.GetDistance())) / 4.0;
 frc::SmartDashboard::PutNumber("m0Encoder", m0Encoder.GetDistance());
 frc::SmartDashboard::PutNumber("m1Encoder", m1Encoder.GetDistance());
 frc::SmartDashboard::PutNumber("m2Encoder", m2Encoder.GetDistance());
 frc::SmartDashboard::PutNumber("m3Encoder", m3Encoder.GetDistance());
 frc::SmartDashboard::PutNumber("Average Encoder Distance", averageDistance);
 frc::SmartDashboard::PutNumber("navX Yaw", navx.GetYaw());
 frc::SmartDashboard::PutNumber("Auto State", currentState);
}
void Robot::DisabledInit()
{
 m0.Set(0.0);
 m1.Set(0.0);
 m2.Set(0.0);
 m3.Set(0.0);
}
void Robot::DisabledPeriodic() {}

void Robot::AutonomousInit()
{
 m0Encoder.Reset();
 m1Encoder.Reset();
 m2Encoder.Reset();
 m3Encoder.Reset();
 navx.Reset();
 currentState = START;
}
void Robot::AutonomousPeriodic()
{
 double averageDistance =
 (std::fabs(m0Encoder.GetDistance()) +
 std::fabs(m1Encoder.GetDistance()) +
 std::fabs(m2Encoder.GetDistance()) +
 std::fabs(m3Encoder.GetDistance())) / 4.0;
 double currentAngle = navx.GetYaw();
 double angleError = turnTargetAngle - currentAngle;
 // Wrap angle so the robot takes the shortest turn
 while (angleError > 180.0) angleError -= 360.0;
 while (angleError < -180.0) angleError += 360.0;
 switch (currentState)
 {
 case START:
 m0Encoder.Reset();
 m1Encoder.Reset();
 m2Encoder.Reset();
 m3Encoder.Reset();
 currentState = DRIVE_FORWARD;
 break;
 case DRIVE_FORWARD:
 if (averageDistance < driveTargetDistance)
 {
 m0.Set(0.5);
 m1.Set(0.5);
 m2.Set(-0.5);
 m3.Set(-0.5);
 }
 else
 {
 m0.Set(0.0);
 m1.Set(0.0);
 m2.Set(0.0);
 m3.Set(0.0);
 currentState = TURN;
 }
 break;
 case TURN:
 if (std::fabs(angleError) > 2.0)
 {
 double turnSpeed = 0.35;
 if (angleError > 0)
 {
 m0.Set(turnSpeed);
 m1.Set(turnSpeed);
 m2.Set(turnSpeed);
 m3.Set(turnSpeed);
 }
 else
 {
 m0.Set(-turnSpeed);
 m1.Set(-turnSpeed);
 m2.Set(-turnSpeed);
 m3.Set(-turnSpeed);
 }
 }
 else
 {
 m0.Set(0.0);
 m1.Set(0.0);
 m2.Set(0.0);
 m3.Set(0.0);
 currentState = STOP;
 }
 break;
 case STOP:
 m0.Set(0.0);
 m1.Set(0.0);
 m2.Set(0.0);
 m3.Set(0.0);
 break;
 }
}
void Robot::TeleopInit()
{
 m0.Set(0.0);
 m1.Set(0.0);
 m2.Set(0.0);
 m3.Set(0.0);
}
void Robot::TeleopPeriodic() {}
void Robot::TestPeriodic() {}
#ifndef RUNNING_FRC_TESTS
int main() { return frc::StartRobot<Robot>(); }
#endif

Try adding another state after the turn so that the robot moves forward again before stopping.

← RGB Color Detection Using a USB Camera (OpenCV) Autonomous State Machine with Heading Hold and PID Turning →