mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-20 00:51:42 +00:00
[examples] ArmSimulation, ElevatorSimulation: Extract mechanism to class (#5052)
This commit is contained in:
@@ -2,150 +2,30 @@
|
||||
// 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 <numbers>
|
||||
#include "Robot.h"
|
||||
|
||||
#include <frc/Encoder.h>
|
||||
#include <frc/Joystick.h>
|
||||
#include <frc/Preferences.h>
|
||||
#include <frc/RobotController.h>
|
||||
#include <frc/TimedRobot.h>
|
||||
#include <frc/controller/PIDController.h>
|
||||
#include <frc/motorcontrol/PWMSparkMax.h>
|
||||
#include <frc/simulation/BatterySim.h>
|
||||
#include <frc/simulation/EncoderSim.h>
|
||||
#include <frc/simulation/RoboRioSim.h>
|
||||
#include <frc/simulation/SingleJointedArmSim.h>
|
||||
#include <frc/smartdashboard/Mechanism2d.h>
|
||||
#include <frc/smartdashboard/MechanismLigament2d.h>
|
||||
#include <frc/smartdashboard/MechanismRoot2d.h>
|
||||
#include <frc/smartdashboard/SmartDashboard.h>
|
||||
#include <frc/system/plant/LinearSystemId.h>
|
||||
#include <frc/util/Color.h>
|
||||
#include <frc/util/Color8Bit.h>
|
||||
#include <units/angle.h>
|
||||
#include <units/moment_of_inertia.h>
|
||||
void Robot::SimulationPeriodic() {
|
||||
m_arm.SimulationPeriodic();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a sample program to demonstrate how to use a state-space controller
|
||||
* to control an arm.
|
||||
*/
|
||||
class Robot : public frc::TimedRobot {
|
||||
static constexpr int kMotorPort = 0;
|
||||
static constexpr int kEncoderAChannel = 0;
|
||||
static constexpr int kEncoderBChannel = 1;
|
||||
static constexpr int kJoystickPort = 0;
|
||||
void Robot::TeleopInit() {
|
||||
m_arm.LoadPreferences();
|
||||
}
|
||||
|
||||
static constexpr std::string_view kArmPositionKey = "ArmPosition";
|
||||
static constexpr std::string_view kArmPKey = "ArmP";
|
||||
|
||||
// The P gain for the PID controller that drives this arm.
|
||||
double kArmKp = 50.0;
|
||||
|
||||
units::degree_t armPosition = 75.0_deg;
|
||||
|
||||
// distance per pulse = (angle per revolution) / (pulses per revolution)
|
||||
// = (2 * PI rads) / (4096 pulses)
|
||||
static constexpr double kArmEncoderDistPerPulse =
|
||||
2.0 * std::numbers::pi / 4096.0;
|
||||
|
||||
// The arm gearbox represents a gearbox containing two Vex 775pro motors.
|
||||
frc::DCMotor m_armGearbox = frc::DCMotor::Vex775Pro(2);
|
||||
|
||||
// Standard classes for controlling our arm
|
||||
frc2::PIDController m_controller{kArmKp, 0, 0};
|
||||
frc::Encoder m_encoder{kEncoderAChannel, kEncoderBChannel};
|
||||
frc::PWMSparkMax m_motor{kMotorPort};
|
||||
frc::Joystick m_joystick{kJoystickPort};
|
||||
|
||||
// Simulation classes help us simulate what's going on, including gravity.
|
||||
// This sim represents an arm with 2 775s, a 600:1 reduction, a mass of 5kg,
|
||||
// 30in overall arm length, range of motion in [-75, 255] degrees, and noise
|
||||
// with a standard deviation of 1 encoder tick.
|
||||
frc::sim::SingleJointedArmSim m_armSim{
|
||||
m_armGearbox,
|
||||
200.0,
|
||||
frc::sim::SingleJointedArmSim::EstimateMOI(30_in, 8_kg),
|
||||
30_in,
|
||||
-75_deg,
|
||||
255_deg,
|
||||
true,
|
||||
{kArmEncoderDistPerPulse}};
|
||||
frc::sim::EncoderSim m_encoderSim{m_encoder};
|
||||
|
||||
// Create a Mechanism2d display of an Arm
|
||||
frc::Mechanism2d m_mech2d{60, 60};
|
||||
frc::MechanismRoot2d* m_armBase = m_mech2d.GetRoot("ArmBase", 30, 30);
|
||||
frc::MechanismLigament2d* m_armTower =
|
||||
m_armBase->Append<frc::MechanismLigament2d>(
|
||||
"Arm Tower", 30, -90_deg, 6, frc::Color8Bit{frc::Color::kBlue});
|
||||
frc::MechanismLigament2d* m_arm = m_armBase->Append<frc::MechanismLigament2d>(
|
||||
"Arm", 30, m_armSim.GetAngle(), 6, frc::Color8Bit{frc::Color::kYellow});
|
||||
|
||||
public:
|
||||
void RobotInit() override {
|
||||
m_encoder.SetDistancePerPulse(kArmEncoderDistPerPulse);
|
||||
|
||||
// Put Mechanism 2d to SmartDashboard
|
||||
frc::SmartDashboard::PutData("Arm Sim", &m_mech2d);
|
||||
|
||||
// Set the Arm position setpoint and P constant to Preferences if the keys
|
||||
// don't already exist
|
||||
if (!frc::Preferences::ContainsKey(kArmPositionKey)) {
|
||||
frc::Preferences::SetDouble(kArmPositionKey, armPosition.value());
|
||||
}
|
||||
if (!frc::Preferences::ContainsKey(kArmPKey)) {
|
||||
frc::Preferences::SetDouble(kArmPKey, kArmKp);
|
||||
}
|
||||
void Robot::TeleopPeriodic() {
|
||||
if (m_joystick.GetTrigger()) {
|
||||
// Here, we run PID control like normal.
|
||||
m_arm.ReachSetpoint();
|
||||
} else {
|
||||
// Otherwise, we disable the motor.
|
||||
m_arm.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
void SimulationPeriodic() override {
|
||||
// In this method, we update our simulation of what our arm is doing
|
||||
// First, we set our "inputs" (voltages)
|
||||
m_armSim.SetInput(frc::Vectord<1>{m_motor.Get() *
|
||||
frc::RobotController::GetInputVoltage()});
|
||||
|
||||
// Next, we update it. The standard loop time is 20ms.
|
||||
m_armSim.Update(20_ms);
|
||||
|
||||
// Finally, we set our simulated encoder's readings and simulated battery
|
||||
// voltage
|
||||
m_encoderSim.SetDistance(m_armSim.GetAngle().value());
|
||||
// SimBattery estimates loaded battery voltages
|
||||
frc::sim::RoboRioSim::SetVInVoltage(
|
||||
frc::sim::BatterySim::Calculate({m_armSim.GetCurrentDraw()}));
|
||||
|
||||
// Update the Mechanism Arm angle based on the simulated arm angle
|
||||
m_arm->SetAngle(m_armSim.GetAngle());
|
||||
}
|
||||
|
||||
void TeleopInit() override {
|
||||
// Read Preferences for Arm setpoint and kP on entering Teleop
|
||||
armPosition = units::degree_t{
|
||||
frc::Preferences::GetDouble(kArmPositionKey, armPosition.value())};
|
||||
if (kArmKp != frc::Preferences::GetDouble(kArmPKey, kArmKp)) {
|
||||
kArmKp = frc::Preferences::GetDouble(kArmPKey, kArmKp);
|
||||
m_controller.SetP(kArmKp);
|
||||
}
|
||||
}
|
||||
|
||||
void TeleopPeriodic() override {
|
||||
if (m_joystick.GetTrigger()) {
|
||||
// Here, we run PID control like normal, with a setpoint read from
|
||||
// preferences in degrees.
|
||||
double pidOutput = m_controller.Calculate(
|
||||
m_encoder.GetDistance(), (units::radian_t{armPosition}.value()));
|
||||
m_motor.SetVoltage(units::volt_t{pidOutput});
|
||||
} else {
|
||||
// Otherwise, we disable the motor.
|
||||
m_motor.Set(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
void DisabledInit() override {
|
||||
// This just makes sure that our simulation code knows that the motor's off.
|
||||
m_motor.Set(0.0);
|
||||
}
|
||||
};
|
||||
void Robot::DisabledInit() {
|
||||
// This just makes sure that our simulation code knows that the motor's off.
|
||||
m_arm.Stop();
|
||||
}
|
||||
|
||||
#ifndef RUNNING_FRC_TESTS
|
||||
int main() {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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/Arm.h"
|
||||
|
||||
#include <frc/Preferences.h>
|
||||
#include <frc/RobotController.h>
|
||||
#include <frc/StateSpaceUtil.h>
|
||||
#include <frc/smartdashboard/SmartDashboard.h>
|
||||
|
||||
Arm::Arm() {
|
||||
m_encoder.SetDistancePerPulse(kArmEncoderDistPerPulse);
|
||||
|
||||
// Put Mechanism 2d to SmartDashboard
|
||||
frc::SmartDashboard::PutData("Arm Sim", &m_mech2d);
|
||||
|
||||
// Set the Arm position setpoint and P constant to Preferences if the keys
|
||||
// don't already exist
|
||||
frc::Preferences::InitDouble(kArmPositionKey, m_armSetpoint.value());
|
||||
frc::Preferences::InitDouble(kArmPKey, m_armKp);
|
||||
}
|
||||
|
||||
void Arm::SimulationPeriodic() {
|
||||
// In this method, we update our simulation of what our arm is doing
|
||||
// First, we set our "inputs" (voltages)
|
||||
m_armSim.SetInput(
|
||||
frc::Vectord<1>{m_motor.Get() * frc::RobotController::GetInputVoltage()});
|
||||
|
||||
// Next, we update it. The standard loop time is 20ms.
|
||||
m_armSim.Update(20_ms);
|
||||
|
||||
// Finally, we set our simulated encoder's readings and simulated battery
|
||||
// voltage
|
||||
m_encoderSim.SetDistance(m_armSim.GetAngle().value());
|
||||
// SimBattery estimates loaded battery voltages
|
||||
frc::sim::RoboRioSim::SetVInVoltage(
|
||||
frc::sim::BatterySim::Calculate({m_armSim.GetCurrentDraw()}));
|
||||
|
||||
// Update the Mechanism Arm angle based on the simulated arm angle
|
||||
m_arm->SetAngle(m_armSim.GetAngle());
|
||||
}
|
||||
|
||||
void Arm::LoadPreferences() {
|
||||
// Read Preferences for Arm setpoint and kP on entering Teleop
|
||||
m_armSetpoint = units::degree_t{
|
||||
frc::Preferences::GetDouble(kArmPositionKey, m_armSetpoint.value())};
|
||||
if (m_armKp != frc::Preferences::GetDouble(kArmPKey, m_armKp)) {
|
||||
m_armKp = frc::Preferences::GetDouble(kArmPKey, m_armKp);
|
||||
m_controller.SetP(m_armKp);
|
||||
}
|
||||
}
|
||||
|
||||
void Arm::ReachSetpoint() {
|
||||
// Here, we run PID control like normal, with a setpoint read from
|
||||
// preferences in degrees.
|
||||
double pidOutput = m_controller.Calculate(
|
||||
m_encoder.GetDistance(), (units::radian_t{m_armSetpoint}.value()));
|
||||
m_motor.SetVoltage(units::volt_t{pidOutput});
|
||||
}
|
||||
|
||||
void Arm::Stop() {
|
||||
m_motor.Set(0.0);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 <numbers>
|
||||
|
||||
#include <units/angle.h>
|
||||
#include <units/length.h>
|
||||
#include <units/mass.h>
|
||||
#include <units/time.h>
|
||||
#include <units/velocity.h>
|
||||
#include <units/voltage.h>
|
||||
|
||||
/**
|
||||
* The Constants header provides a convenient place for teams to hold robot-wide
|
||||
* numerical or bool constants. This should not be used for any other purpose.
|
||||
*
|
||||
* It is generally a good idea to place constants into subsystem- or
|
||||
* command-specific namespaces within this header, which can then be used where
|
||||
* they are needed.
|
||||
*/
|
||||
|
||||
static constexpr int kMotorPort = 0;
|
||||
static constexpr int kEncoderAChannel = 0;
|
||||
static constexpr int kEncoderBChannel = 1;
|
||||
static constexpr int kJoystickPort = 0;
|
||||
|
||||
static constexpr std::string_view kArmPositionKey = "ArmPosition";
|
||||
static constexpr std::string_view kArmPKey = "ArmP";
|
||||
|
||||
static constexpr double kDefaultArmKp = 50.0;
|
||||
static constexpr units::degree_t kDefaultArmSetpoint = 75.0_deg;
|
||||
|
||||
static constexpr units::radian_t kMinAngle = -75.0_deg;
|
||||
static constexpr units::radian_t kMaxAngle = 255.0_deg;
|
||||
|
||||
static constexpr double kArmReduction = 200.0;
|
||||
static constexpr units::kilogram_t kArmMass = 8.0_kg;
|
||||
static constexpr units::meter_t kArmLength = 30.0_in;
|
||||
|
||||
// distance per pulse = (angle per revolution) / (pulses per revolution)
|
||||
// = (2 * PI rads) / (4096 pulses)
|
||||
static constexpr double kArmEncoderDistPerPulse =
|
||||
2.0 * std::numbers::pi / 4096.0;
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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 <frc/Joystick.h>
|
||||
#include <frc/TimedRobot.h>
|
||||
|
||||
#include "subsystems/Arm.h"
|
||||
|
||||
/**
|
||||
* This is a sample program to demonstrate the use of arm simulation.
|
||||
*/
|
||||
class Robot : public frc::TimedRobot {
|
||||
public:
|
||||
void RobotInit() override {}
|
||||
void SimulationPeriodic() override;
|
||||
void TeleopInit() override;
|
||||
void TeleopPeriodic() override;
|
||||
void DisabledInit() override;
|
||||
|
||||
private:
|
||||
frc::Joystick m_joystick{kJoystickPort};
|
||||
Arm m_arm;
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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 <frc/Encoder.h>
|
||||
#include <frc/controller/ArmFeedforward.h>
|
||||
#include <frc/controller/PIDController.h>
|
||||
#include <frc/motorcontrol/PWMSparkMax.h>
|
||||
#include <frc/simulation/BatterySim.h>
|
||||
#include <frc/simulation/EncoderSim.h>
|
||||
#include <frc/simulation/PWMSim.h>
|
||||
#include <frc/simulation/RoboRioSim.h>
|
||||
#include <frc/simulation/SingleJointedArmSim.h>
|
||||
#include <frc/smartdashboard/Mechanism2d.h>
|
||||
#include <frc/smartdashboard/MechanismLigament2d.h>
|
||||
#include <frc/smartdashboard/MechanismRoot2d.h>
|
||||
#include <units/length.h>
|
||||
|
||||
#include "Constants.h"
|
||||
|
||||
class Arm {
|
||||
public:
|
||||
Arm();
|
||||
void SimulationPeriodic();
|
||||
void LoadPreferences();
|
||||
void ReachSetpoint();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
// The P gain for the PID controller that drives this arm.
|
||||
double m_armKp = kDefaultArmKp;
|
||||
units::degree_t m_armSetpoint = kDefaultArmSetpoint;
|
||||
|
||||
// The arm gearbox represents a gearbox containing two Vex 775pro motors.
|
||||
frc::DCMotor m_armGearbox = frc::DCMotor::Vex775Pro(2);
|
||||
|
||||
// Standard classes for controlling our arm
|
||||
frc2::PIDController m_controller{m_armKp, 0, 0};
|
||||
frc::Encoder m_encoder{kEncoderAChannel, kEncoderBChannel};
|
||||
frc::PWMSparkMax m_motor{kMotorPort};
|
||||
|
||||
// Simulation classes help us simulate what's going on, including gravity.
|
||||
// This sim represents an arm with 2 775s, a 600:1 reduction, a mass of 5kg,
|
||||
// 30in overall arm length, range of motion in [-75, 255] degrees, and noise
|
||||
// with a standard deviation of 1 encoder tick.
|
||||
frc::sim::SingleJointedArmSim m_armSim{
|
||||
m_armGearbox,
|
||||
kArmReduction,
|
||||
frc::sim::SingleJointedArmSim::EstimateMOI(kArmLength, kArmMass),
|
||||
kArmLength,
|
||||
kMinAngle,
|
||||
kMaxAngle,
|
||||
true,
|
||||
{kArmEncoderDistPerPulse}};
|
||||
frc::sim::EncoderSim m_encoderSim{m_encoder};
|
||||
|
||||
// Create a Mechanism2d display of an Arm
|
||||
frc::Mechanism2d m_mech2d{60, 60};
|
||||
frc::MechanismRoot2d* m_armBase = m_mech2d.GetRoot("ArmBase", 30, 30);
|
||||
frc::MechanismLigament2d* m_armTower =
|
||||
m_armBase->Append<frc::MechanismLigament2d>(
|
||||
"Arm Tower", 30, -90_deg, 6, frc::Color8Bit{frc::Color::kBlue});
|
||||
frc::MechanismLigament2d* m_arm = m_armBase->Append<frc::MechanismLigament2d>(
|
||||
"Arm", 30, m_armSim.GetAngle(), 6, frc::Color8Bit{frc::Color::kYellow});
|
||||
};
|
||||
@@ -2,152 +2,35 @@
|
||||
// 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 <numbers>
|
||||
#include "Robot.h"
|
||||
|
||||
#include <frc/Encoder.h>
|
||||
#include <frc/Joystick.h>
|
||||
#include <frc/RobotController.h>
|
||||
#include <frc/StateSpaceUtil.h>
|
||||
#include <frc/TimedRobot.h>
|
||||
#include <frc/controller/ElevatorFeedforward.h>
|
||||
#include <frc/controller/PIDController.h>
|
||||
#include <frc/controller/ProfiledPIDController.h>
|
||||
#include <frc/motorcontrol/PWMSparkMax.h>
|
||||
#include <frc/simulation/BatterySim.h>
|
||||
#include <frc/simulation/ElevatorSim.h>
|
||||
#include <frc/simulation/EncoderSim.h>
|
||||
#include <frc/simulation/RoboRioSim.h>
|
||||
#include <frc/smartdashboard/Mechanism2d.h>
|
||||
#include <frc/smartdashboard/MechanismLigament2d.h>
|
||||
#include <frc/smartdashboard/MechanismRoot2d.h>
|
||||
#include <frc/smartdashboard/SmartDashboard.h>
|
||||
#include <frc/system/plant/LinearSystemId.h>
|
||||
#include <frc/util/Color.h>
|
||||
#include <frc/util/Color8Bit.h>
|
||||
#include <units/angle.h>
|
||||
#include <units/length.h>
|
||||
#include <units/moment_of_inertia.h>
|
||||
#include "Constants.h"
|
||||
|
||||
/**
|
||||
* This is a sample program to demonstrate the use of elevator simulation.
|
||||
*/
|
||||
class Robot : public frc::TimedRobot {
|
||||
static constexpr int kMotorPort = 0;
|
||||
static constexpr int kEncoderAChannel = 0;
|
||||
static constexpr int kEncoderBChannel = 1;
|
||||
static constexpr int kJoystickPort = 0;
|
||||
void Robot::RobotPeriodic() {
|
||||
// Update the telemetry, including mechanism visualization, regardless of
|
||||
// mode.
|
||||
m_elevator.UpdateTelemetry();
|
||||
}
|
||||
|
||||
static constexpr double kElevatorKp = 5.0;
|
||||
static constexpr double kElevatorKi = 0.0;
|
||||
static constexpr double kElevatorKd = 0.0;
|
||||
void Robot::SimulationPeriodic() {
|
||||
// Update the simulation model.
|
||||
m_elevator.SimulationPeriodic();
|
||||
}
|
||||
|
||||
static constexpr units::volt_t kElevatorkS = 0.0_V;
|
||||
static constexpr units::volt_t kElevatorkG = 0.0_V;
|
||||
static constexpr auto kElevatorkV = 0.762_V / 1_mps;
|
||||
static constexpr auto kElevatorkA = 0.762_V / 1_mps_sq;
|
||||
|
||||
static constexpr double kElevatorGearing = 10.0;
|
||||
static constexpr units::meter_t kElevatorDrumRadius = 2_in;
|
||||
static constexpr units::kilogram_t kCarriageMass = 4.0_kg;
|
||||
|
||||
static constexpr units::meter_t kSetpoint = 30_in;
|
||||
static constexpr units::meter_t kMinElevatorHeight = 2_in;
|
||||
static constexpr units::meter_t kMaxElevatorHeight = 50_in;
|
||||
|
||||
// distance per pulse = (distance per revolution) / (pulses per revolution)
|
||||
// = (Pi * D) / ppr
|
||||
static constexpr double kArmEncoderDistPerPulse =
|
||||
2.0 * std::numbers::pi * kElevatorDrumRadius.value() / 4096.0;
|
||||
|
||||
// This gearbox represents a gearbox containing 4 Vex 775pro motors.
|
||||
frc::DCMotor m_elevatorGearbox = frc::DCMotor::Vex775Pro(4);
|
||||
|
||||
// Standard classes for controlling our elevator
|
||||
frc::TrapezoidProfile<units::meters>::Constraints m_constraints{2.45_mps,
|
||||
2.45_mps_sq};
|
||||
frc::ProfiledPIDController<units::meters> m_controller{
|
||||
kElevatorKp, kElevatorKi, kElevatorKd, m_constraints};
|
||||
|
||||
frc::ElevatorFeedforward m_feedforward{kElevatorkS, kElevatorkG, kElevatorkV,
|
||||
kElevatorkA};
|
||||
frc::Encoder m_encoder{kEncoderAChannel, kEncoderBChannel};
|
||||
frc::PWMSparkMax m_motor{kMotorPort};
|
||||
frc::Joystick m_joystick{kJoystickPort};
|
||||
|
||||
// Simulation classes help us simulate what's going on, including gravity.
|
||||
frc::sim::ElevatorSim m_elevatorSim{m_elevatorGearbox,
|
||||
kElevatorGearing,
|
||||
kCarriageMass,
|
||||
kElevatorDrumRadius,
|
||||
kMinElevatorHeight,
|
||||
kMaxElevatorHeight,
|
||||
true,
|
||||
{0.01}};
|
||||
frc::sim::EncoderSim m_encoderSim{m_encoder};
|
||||
|
||||
// Create a Mechanism2d display of an elevator
|
||||
frc::Mechanism2d m_mech2d{20, 50};
|
||||
frc::MechanismRoot2d* m_elevatorRoot =
|
||||
m_mech2d.GetRoot("Elevator Root", 10, 0);
|
||||
frc::MechanismLigament2d* m_elevatorMech2d =
|
||||
m_elevatorRoot->Append<frc::MechanismLigament2d>(
|
||||
"Elevator", units::inch_t{m_elevatorSim.GetPosition()}.value(),
|
||||
90_deg);
|
||||
|
||||
public:
|
||||
void RobotInit() override {
|
||||
m_encoder.SetDistancePerPulse(kArmEncoderDistPerPulse);
|
||||
|
||||
// Put Mechanism 2d to SmartDashboard
|
||||
// To view the Elevator Sim in the simulator, select Network Tables ->
|
||||
// SmartDashboard -> Elevator Sim
|
||||
frc::SmartDashboard::PutData("Elevator Sim", &m_mech2d);
|
||||
void Robot::TeleopPeriodic() {
|
||||
if (m_joystick.GetTrigger()) {
|
||||
// Here, we set the constant setpoint of 0.75 meters.
|
||||
m_elevator.ReachGoal(Constants::kSetpoint);
|
||||
} else {
|
||||
// Otherwise, we update the setpoint to 0.
|
||||
m_elevator.ReachGoal(0.0_m);
|
||||
}
|
||||
}
|
||||
|
||||
void SimulationPeriodic() override {
|
||||
// In this method, we update our simulation of what our elevator is doing
|
||||
// First, we set our "inputs" (voltages)
|
||||
m_elevatorSim.SetInput(frc::Vectord<1>{
|
||||
m_motor.Get() * frc::RobotController::GetInputVoltage()});
|
||||
|
||||
// Next, we update it. The standard loop time is 20ms.
|
||||
m_elevatorSim.Update(20_ms);
|
||||
|
||||
// Finally, we set our simulated encoder's readings and simulated battery
|
||||
// voltage
|
||||
m_encoderSim.SetDistance(m_elevatorSim.GetPosition().value());
|
||||
// SimBattery estimates loaded battery voltages
|
||||
frc::sim::RoboRioSim::SetVInVoltage(
|
||||
frc::sim::BatterySim::Calculate({m_elevatorSim.GetCurrentDraw()}));
|
||||
|
||||
// Update the Elevator length based on the simulated elevator height
|
||||
m_elevatorMech2d->SetLength(
|
||||
units::inch_t{m_elevatorSim.GetPosition()}.value());
|
||||
}
|
||||
|
||||
void TeleopPeriodic() override {
|
||||
if (m_joystick.GetTrigger()) {
|
||||
// Here, we set the constant setpoint of 30in.
|
||||
m_controller.SetGoal(kSetpoint);
|
||||
} else {
|
||||
// Otherwise, we update the setpoint to 0.
|
||||
m_controller.SetGoal(0.0_m);
|
||||
}
|
||||
// With the setpoint value we run PID control like normal
|
||||
double pidOutput =
|
||||
m_controller.Calculate(units::meter_t{m_encoder.GetDistance()});
|
||||
units::volt_t feedforwardOutput =
|
||||
m_feedforward.Calculate(m_controller.GetSetpoint().velocity);
|
||||
m_motor.SetVoltage(units::volt_t{pidOutput} + feedforwardOutput);
|
||||
}
|
||||
// To view the Elevator in the simulator, select Network Tables ->
|
||||
// SmartDashboard -> Elevator Sim
|
||||
|
||||
void DisabledInit() override {
|
||||
// This just makes sure that our simulation code knows that the motor's off.
|
||||
m_motor.Set(0.0);
|
||||
}
|
||||
};
|
||||
void Robot::DisabledInit() {
|
||||
// This just makes sure that our simulation code knows that the motor's off.
|
||||
m_elevator.Stop();
|
||||
}
|
||||
|
||||
#ifndef RUNNING_FRC_TESTS
|
||||
int main() {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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/Elevator.h"
|
||||
|
||||
#include <frc/RobotController.h>
|
||||
#include <frc/StateSpaceUtil.h>
|
||||
#include <frc/smartdashboard/SmartDashboard.h>
|
||||
|
||||
Elevator::Elevator() {
|
||||
m_encoder.SetDistancePerPulse(Constants::kArmEncoderDistPerPulse);
|
||||
|
||||
// Put Mechanism 2d to SmartDashboard
|
||||
// To view the Elevator visualization, select Network Tables -> SmartDashboard
|
||||
// -> Elevator Sim
|
||||
frc::SmartDashboard::PutData("Elevator Sim", &m_mech2d);
|
||||
}
|
||||
|
||||
void Elevator::SimulationPeriodic() {
|
||||
// In this method, we update our simulation of what our elevator is doing
|
||||
// First, we set our "inputs" (voltages)
|
||||
m_elevatorSim.SetInput(frc::Vectord<1>{
|
||||
m_motorSim.GetSpeed() * frc::RobotController::GetInputVoltage()});
|
||||
|
||||
// Next, we update it. The standard loop time is 20ms.
|
||||
m_elevatorSim.Update(20_ms);
|
||||
|
||||
// Finally, we set our simulated encoder's readings and simulated battery
|
||||
// voltage
|
||||
m_encoderSim.SetDistance(m_elevatorSim.GetPosition().value());
|
||||
// SimBattery estimates loaded battery voltages
|
||||
frc::sim::RoboRioSim::SetVInVoltage(
|
||||
frc::sim::BatterySim::Calculate({m_elevatorSim.GetCurrentDraw()}));
|
||||
}
|
||||
|
||||
void Elevator::UpdateTelemetry() {
|
||||
// Update the Elevator length based on the simulated elevator height
|
||||
m_elevatorMech2d->SetLength(m_encoder.GetDistance());
|
||||
}
|
||||
|
||||
void Elevator::ReachGoal(units::meter_t goal) {
|
||||
m_controller.SetGoal(goal);
|
||||
// With the setpoint value we run PID control like normal
|
||||
double pidOutput =
|
||||
m_controller.Calculate(units::meter_t{m_encoder.GetDistance()});
|
||||
units::volt_t feedforwardOutput =
|
||||
m_feedforward.Calculate(m_controller.GetSetpoint().velocity);
|
||||
m_motor.SetVoltage(units::volt_t{pidOutput} + feedforwardOutput);
|
||||
}
|
||||
|
||||
void Elevator::Stop() {
|
||||
m_controller.SetGoal(0.0_m);
|
||||
m_motor.Set(0.0);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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 <numbers>
|
||||
|
||||
#include <units/acceleration.h>
|
||||
#include <units/angle.h>
|
||||
#include <units/length.h>
|
||||
#include <units/mass.h>
|
||||
#include <units/time.h>
|
||||
#include <units/velocity.h>
|
||||
#include <units/voltage.h>
|
||||
|
||||
/**
|
||||
* The Constants header provides a convenient place for teams to hold robot-wide
|
||||
* numerical or bool constants. This should not be used for any other purpose.
|
||||
*
|
||||
* It is generally a good idea to place constants into subsystem- or
|
||||
* command-specific namespaces within this header, which can then be used where
|
||||
* they are needed.
|
||||
*/
|
||||
|
||||
namespace Constants {
|
||||
|
||||
static constexpr int kMotorPort = 0;
|
||||
static constexpr int kEncoderAChannel = 0;
|
||||
static constexpr int kEncoderBChannel = 1;
|
||||
static constexpr int kJoystickPort = 0;
|
||||
|
||||
static constexpr double kElevatorKp = 5.0;
|
||||
static constexpr double kElevatorKi = 0.0;
|
||||
static constexpr double kElevatorKd = 0.0;
|
||||
|
||||
static constexpr units::volt_t kElevatorkS = 0.0_V;
|
||||
static constexpr units::volt_t kElevatorkG = 0.762_V;
|
||||
static constexpr auto kElevatorkV = 0.762_V / 1_mps;
|
||||
static constexpr auto kElevatorkA = 0.0_V / 1_mps_sq;
|
||||
|
||||
static constexpr double kElevatorGearing = 10.0;
|
||||
static constexpr units::meter_t kElevatorDrumRadius = 2_in;
|
||||
static constexpr units::kilogram_t kCarriageMass = 4.0_kg;
|
||||
|
||||
static constexpr units::meter_t kSetpoint = 75_cm;
|
||||
static constexpr units::meter_t kMinElevatorHeight = 0_cm;
|
||||
static constexpr units::meter_t kMaxElevatorHeight = 1.25_m;
|
||||
|
||||
// distance per pulse = (distance per revolution) / (pulses per revolution)
|
||||
// = (Pi * D) / ppr
|
||||
static constexpr double kArmEncoderDistPerPulse =
|
||||
2.0 * std::numbers::pi * kElevatorDrumRadius.value() / 4096.0;
|
||||
|
||||
} // namespace Constants
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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 <frc/Joystick.h>
|
||||
#include <frc/TimedRobot.h>
|
||||
|
||||
#include "subsystems/Elevator.h"
|
||||
|
||||
/**
|
||||
* This is a sample program to demonstrate the use of elevator simulation.
|
||||
*/
|
||||
class Robot : public frc::TimedRobot {
|
||||
public:
|
||||
void RobotInit() override {}
|
||||
void RobotPeriodic() override;
|
||||
void SimulationPeriodic() override;
|
||||
void TeleopPeriodic() override;
|
||||
void DisabledInit() override;
|
||||
|
||||
private:
|
||||
frc::Joystick m_joystick{Constants::kJoystickPort};
|
||||
Elevator m_elevator;
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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 <frc/Encoder.h>
|
||||
#include <frc/controller/ElevatorFeedforward.h>
|
||||
#include <frc/controller/PIDController.h>
|
||||
#include <frc/controller/ProfiledPIDController.h>
|
||||
#include <frc/motorcontrol/PWMSparkMax.h>
|
||||
#include <frc/simulation/BatterySim.h>
|
||||
#include <frc/simulation/ElevatorSim.h>
|
||||
#include <frc/simulation/EncoderSim.h>
|
||||
#include <frc/simulation/PWMSim.h>
|
||||
#include <frc/simulation/RoboRioSim.h>
|
||||
#include <frc/smartdashboard/Mechanism2d.h>
|
||||
#include <frc/smartdashboard/MechanismLigament2d.h>
|
||||
#include <frc/smartdashboard/MechanismRoot2d.h>
|
||||
#include <units/length.h>
|
||||
|
||||
#include "Constants.h"
|
||||
|
||||
class Elevator {
|
||||
public:
|
||||
Elevator();
|
||||
void SimulationPeriodic();
|
||||
void UpdateTelemetry();
|
||||
void ReachGoal(units::meter_t goal);
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
// This gearbox represents a gearbox containing 4 Vex 775pro motors.
|
||||
frc::DCMotor m_elevatorGearbox = frc::DCMotor::Vex775Pro(4);
|
||||
|
||||
// Standard classes for controlling our elevator
|
||||
frc::TrapezoidProfile<units::meters>::Constraints m_constraints{2.45_mps,
|
||||
2.45_mps_sq};
|
||||
frc::ProfiledPIDController<units::meters> m_controller{
|
||||
Constants::kElevatorKp, Constants::kElevatorKi, Constants::kElevatorKd,
|
||||
m_constraints};
|
||||
|
||||
frc::ElevatorFeedforward m_feedforward{
|
||||
Constants::kElevatorkS, Constants::kElevatorkG, Constants::kElevatorkV,
|
||||
Constants::kElevatorkA};
|
||||
frc::Encoder m_encoder{Constants::kEncoderAChannel,
|
||||
Constants::kEncoderBChannel};
|
||||
frc::PWMSparkMax m_motor{Constants::kMotorPort};
|
||||
frc::sim::PWMSim m_motorSim{m_motor};
|
||||
|
||||
// Simulation classes help us simulate what's going on, including gravity.
|
||||
frc::sim::ElevatorSim m_elevatorSim{m_elevatorGearbox,
|
||||
Constants::kElevatorGearing,
|
||||
Constants::kCarriageMass,
|
||||
Constants::kElevatorDrumRadius,
|
||||
Constants::kMinElevatorHeight,
|
||||
Constants::kMaxElevatorHeight,
|
||||
true,
|
||||
{0.01}};
|
||||
frc::sim::EncoderSim m_encoderSim{m_encoder};
|
||||
|
||||
// Create a Mechanism2d display of an elevator
|
||||
frc::Mechanism2d m_mech2d{20, 50};
|
||||
frc::MechanismRoot2d* m_elevatorRoot =
|
||||
m_mech2d.GetRoot("Elevator Root", 10, 0);
|
||||
frc::MechanismLigament2d* m_elevatorMech2d =
|
||||
m_elevatorRoot->Append<frc::MechanismLigament2d>(
|
||||
"Elevator", m_elevatorSim.GetPosition().value(), 90_deg);
|
||||
};
|
||||
@@ -25,7 +25,7 @@ class Robot : public frc::TimedRobot {
|
||||
|
||||
static constexpr int kPotChannel = 1;
|
||||
static constexpr int kMotorChannel = 7;
|
||||
static constexpr int kJoystickChannel = 0;
|
||||
static constexpr int kJoystickChannel = 3;
|
||||
|
||||
// The elevator can move 1.5 meters from top to bottom
|
||||
static constexpr units::meter_t kFullHeight = 1.5_m;
|
||||
|
||||
Reference in New Issue
Block a user