2020-12-26 14:12:05 -08:00
|
|
|
// 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.
|
2020-09-20 09:39:52 -07:00
|
|
|
|
|
|
|
|
#include "frc/simulation/DifferentialDrivetrainSim.h"
|
|
|
|
|
|
|
|
|
|
#include <frc/system/plant/LinearSystemId.h>
|
|
|
|
|
|
2020-12-28 10:12:52 -08:00
|
|
|
#include <utility>
|
|
|
|
|
|
2021-05-24 23:36:26 -07:00
|
|
|
#include <wpi/MathExtras.h>
|
|
|
|
|
|
2020-12-28 13:03:31 -08:00
|
|
|
#include "frc/RobotController.h"
|
2021-01-06 21:40:25 -08:00
|
|
|
#include "frc/system/NumericalIntegration.h"
|
2020-09-20 09:39:52 -07:00
|
|
|
|
|
|
|
|
using namespace frc;
|
|
|
|
|
using namespace frc::sim;
|
|
|
|
|
|
|
|
|
|
DifferentialDrivetrainSim::DifferentialDrivetrainSim(
|
2020-12-28 10:12:52 -08:00
|
|
|
LinearSystem<2, 2, 2> plant, units::meter_t trackWidth, DCMotor driveMotor,
|
|
|
|
|
double gearRatio, units::meter_t wheelRadius,
|
2020-12-04 18:46:50 -08:00
|
|
|
const std::array<double, 7>& measurementStdDevs)
|
2020-12-28 10:12:52 -08:00
|
|
|
: m_plant(std::move(plant)),
|
2020-09-20 09:39:52 -07:00
|
|
|
m_rb(trackWidth / 2.0),
|
|
|
|
|
m_wheelRadius(wheelRadius),
|
|
|
|
|
m_motor(driveMotor),
|
|
|
|
|
m_originalGearing(gearRatio),
|
2020-12-04 18:46:50 -08:00
|
|
|
m_currentGearing(gearRatio),
|
|
|
|
|
m_measurementStdDevs(measurementStdDevs) {
|
2020-09-20 09:39:52 -07:00
|
|
|
m_x.setZero();
|
|
|
|
|
m_u.setZero();
|
2020-12-04 18:46:50 -08:00
|
|
|
m_y.setZero();
|
2020-09-20 09:39:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DifferentialDrivetrainSim::DifferentialDrivetrainSim(
|
|
|
|
|
frc::DCMotor driveMotor, double gearing, units::kilogram_square_meter_t J,
|
|
|
|
|
units::kilogram_t mass, units::meter_t wheelRadius,
|
2020-12-04 18:46:50 -08:00
|
|
|
units::meter_t trackWidth, const std::array<double, 7>& measurementStdDevs)
|
2020-09-20 09:39:52 -07:00
|
|
|
: DifferentialDrivetrainSim(
|
|
|
|
|
frc::LinearSystemId::DrivetrainVelocitySystem(
|
|
|
|
|
driveMotor, mass, wheelRadius, trackWidth / 2.0, J, gearing),
|
2020-12-04 18:46:50 -08:00
|
|
|
trackWidth, driveMotor, gearing, wheelRadius, measurementStdDevs) {}
|
2020-09-20 09:39:52 -07:00
|
|
|
|
2021-08-19 00:23:48 -07:00
|
|
|
Eigen::Vector<double, 2> DifferentialDrivetrainSim::ClampInput(
|
|
|
|
|
const Eigen::Vector<double, 2>& u) {
|
2021-12-30 21:30:08 -05:00
|
|
|
return frc::DesaturateInputVector<2>(u,
|
|
|
|
|
frc::RobotController::GetInputVoltage());
|
2020-12-28 13:03:31 -08:00
|
|
|
}
|
|
|
|
|
|
2020-09-20 09:39:52 -07:00
|
|
|
void DifferentialDrivetrainSim::SetInputs(units::volt_t leftVoltage,
|
|
|
|
|
units::volt_t rightVoltage) {
|
2021-10-25 08:58:12 -07:00
|
|
|
m_u << leftVoltage.value(), rightVoltage.value();
|
2020-12-28 13:03:31 -08:00
|
|
|
m_u = ClampInput(m_u);
|
2020-09-20 09:39:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DifferentialDrivetrainSim::SetGearing(double newGearing) {
|
|
|
|
|
m_currentGearing = newGearing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DifferentialDrivetrainSim::Update(units::second_t dt) {
|
2021-01-06 21:40:25 -08:00
|
|
|
m_x = RK4([this](auto& x, auto& u) { return Dynamics(x, u); }, m_x, m_u, dt);
|
2020-12-04 18:46:50 -08:00
|
|
|
m_y = m_x + frc::MakeWhiteNoiseVector<7>(m_measurementStdDevs);
|
2020-09-20 09:39:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double DifferentialDrivetrainSim::GetGearing() const {
|
|
|
|
|
return m_currentGearing;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-19 00:23:48 -07:00
|
|
|
Eigen::Vector<double, 7> DifferentialDrivetrainSim::GetOutput() const {
|
2020-12-04 18:46:50 -08:00
|
|
|
return m_y;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-19 00:23:48 -07:00
|
|
|
Eigen::Vector<double, 7> DifferentialDrivetrainSim::GetState() const {
|
2020-09-20 09:39:52 -07:00
|
|
|
return m_x;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-04 18:46:50 -08:00
|
|
|
double DifferentialDrivetrainSim::GetOutput(int output) const {
|
|
|
|
|
return m_y(output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double DifferentialDrivetrainSim::GetState(int state) const {
|
|
|
|
|
return m_x(state);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-20 09:39:52 -07:00
|
|
|
Rotation2d DifferentialDrivetrainSim::GetHeading() const {
|
2020-12-04 18:46:50 -08:00
|
|
|
return Rotation2d(units::radian_t(GetOutput(State::kHeading)));
|
2020-09-20 09:39:52 -07:00
|
|
|
}
|
|
|
|
|
|
2020-09-27 16:25:17 -04:00
|
|
|
Pose2d DifferentialDrivetrainSim::GetPose() const {
|
2020-12-04 18:46:50 -08:00
|
|
|
return Pose2d(units::meter_t(GetOutput(State::kX)),
|
|
|
|
|
units::meter_t(GetOutput(State::kY)), GetHeading());
|
2020-09-20 09:39:52 -07:00
|
|
|
}
|
|
|
|
|
|
2020-12-29 23:46:51 -05:00
|
|
|
units::ampere_t DifferentialDrivetrainSim::GetLeftCurrentDraw() const {
|
2020-09-20 09:39:52 -07:00
|
|
|
auto loadIleft =
|
2021-10-25 08:58:12 -07:00
|
|
|
m_motor.Current(
|
|
|
|
|
units::radians_per_second_t(m_x(State::kLeftVelocity) *
|
|
|
|
|
m_currentGearing / m_wheelRadius.value()),
|
|
|
|
|
units::volt_t(m_u(0))) *
|
2020-09-20 09:39:52 -07:00
|
|
|
wpi::sgn(m_u(0));
|
|
|
|
|
|
2020-12-29 23:46:51 -05:00
|
|
|
return loadIleft;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
units::ampere_t DifferentialDrivetrainSim::GetRightCurrentDraw() const {
|
2020-09-20 09:39:52 -07:00
|
|
|
auto loadIRight =
|
2021-10-25 08:58:12 -07:00
|
|
|
m_motor.Current(
|
|
|
|
|
units::radians_per_second_t(m_x(State::kRightVelocity) *
|
|
|
|
|
m_currentGearing / m_wheelRadius.value()),
|
|
|
|
|
units::volt_t(m_u(1))) *
|
2020-09-20 09:39:52 -07:00
|
|
|
wpi::sgn(m_u(1));
|
|
|
|
|
|
2020-12-29 23:46:51 -05:00
|
|
|
return loadIRight;
|
|
|
|
|
}
|
|
|
|
|
units::ampere_t DifferentialDrivetrainSim::GetCurrentDraw() const {
|
|
|
|
|
return GetLeftCurrentDraw() + GetRightCurrentDraw();
|
2020-09-20 09:39:52 -07:00
|
|
|
}
|
|
|
|
|
|
2020-09-27 13:26:47 -07:00
|
|
|
void DifferentialDrivetrainSim::SetState(
|
2021-08-19 00:23:48 -07:00
|
|
|
const Eigen::Vector<double, 7>& state) {
|
2020-09-27 13:26:47 -07:00
|
|
|
m_x = state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DifferentialDrivetrainSim::SetPose(const frc::Pose2d& pose) {
|
2021-10-25 08:58:12 -07:00
|
|
|
m_x(State::kX) = pose.X().value();
|
|
|
|
|
m_x(State::kY) = pose.Y().value();
|
|
|
|
|
m_x(State::kHeading) = pose.Rotation().Radians().value();
|
2020-11-12 01:37:14 -05:00
|
|
|
m_x(State::kLeftPosition) = 0;
|
|
|
|
|
m_x(State::kRightPosition) = 0;
|
2020-09-27 13:26:47 -07:00
|
|
|
}
|
|
|
|
|
|
2021-08-19 00:23:48 -07:00
|
|
|
Eigen::Vector<double, 7> DifferentialDrivetrainSim::Dynamics(
|
|
|
|
|
const Eigen::Vector<double, 7>& x, const Eigen::Vector<double, 2>& u) {
|
2020-09-20 09:39:52 -07:00
|
|
|
// Because G^2 can be factored out of A, we can divide by the old ratio
|
|
|
|
|
// squared and multiply by the new ratio squared to get a new drivetrain
|
|
|
|
|
// model.
|
|
|
|
|
Eigen::Matrix<double, 4, 2> B;
|
|
|
|
|
B.block<2, 2>(0, 0) = m_plant.B() * m_currentGearing * m_currentGearing /
|
|
|
|
|
m_originalGearing / m_originalGearing;
|
|
|
|
|
B.block<2, 2>(2, 0).setZero();
|
|
|
|
|
|
|
|
|
|
// Because G can be factored out of B, we can divide by the old ratio and
|
|
|
|
|
// multiply by the new ratio to get a new drivetrain model.
|
|
|
|
|
Eigen::Matrix<double, 4, 4> A;
|
|
|
|
|
A.block<2, 2>(0, 0) = m_plant.A() * m_currentGearing / m_originalGearing;
|
|
|
|
|
|
|
|
|
|
A.block<2, 2>(2, 0).setIdentity();
|
|
|
|
|
A.block<4, 2>(0, 2).setZero();
|
|
|
|
|
|
|
|
|
|
double v = (x(State::kLeftVelocity) + x(State::kRightVelocity)) / 2.0;
|
|
|
|
|
|
2021-08-19 00:23:48 -07:00
|
|
|
Eigen::Vector<double, 7> xdot;
|
2020-09-20 09:39:52 -07:00
|
|
|
xdot(0) = v * std::cos(x(State::kHeading));
|
|
|
|
|
xdot(1) = v * std::sin(x(State::kHeading));
|
|
|
|
|
xdot(2) =
|
|
|
|
|
((x(State::kRightVelocity) - x(State::kLeftVelocity)) / (2.0 * m_rb))
|
2021-10-25 08:58:12 -07:00
|
|
|
.value();
|
2020-09-20 09:39:52 -07:00
|
|
|
xdot.block<4, 1>(3, 0) = A * x.block<4, 1>(3, 0) + B * u;
|
|
|
|
|
return xdot;
|
|
|
|
|
}
|