|
|
|
|
@@ -4,13 +4,12 @@
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <concepts>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
|
|
#include <gcem.hpp>
|
|
|
|
|
|
|
|
|
|
#include "wpi/math/system/DCMotor.hpp"
|
|
|
|
|
#include "wpi/math/system/LinearSystem.hpp"
|
|
|
|
|
#include "wpi/math/system/plant/DCMotor.hpp"
|
|
|
|
|
#include "wpi/units/acceleration.hpp"
|
|
|
|
|
#include "wpi/units/angular_acceleration.hpp"
|
|
|
|
|
#include "wpi/units/angular_velocity.hpp"
|
|
|
|
|
@@ -22,9 +21,9 @@
|
|
|
|
|
|
|
|
|
|
namespace wpi::math {
|
|
|
|
|
/**
|
|
|
|
|
* Linear system ID utility functions.
|
|
|
|
|
* Linear system factories.
|
|
|
|
|
*/
|
|
|
|
|
class WPILIB_DLLEXPORT LinearSystemId {
|
|
|
|
|
class WPILIB_DLLEXPORT Models {
|
|
|
|
|
public:
|
|
|
|
|
template <typename Distance>
|
|
|
|
|
using Velocity_t = wpi::units::unit_t<wpi::units::compound_unit<
|
|
|
|
|
@@ -37,17 +36,80 @@ class WPILIB_DLLEXPORT LinearSystemId {
|
|
|
|
|
wpi::units::inverse<wpi::units::seconds>>>;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a state-space model of the elevator system. The states of the system
|
|
|
|
|
* are [position, velocity]ᵀ, inputs are [voltage], and outputs are [position,
|
|
|
|
|
* velocity]ᵀ.
|
|
|
|
|
* Creates a flywheel state-space model from physical constants.
|
|
|
|
|
*
|
|
|
|
|
* The states are [angular velocity], the inputs are [voltage], and the
|
|
|
|
|
* outputs are [angular velocity].
|
|
|
|
|
*
|
|
|
|
|
* @param motor The motor (or gearbox) attached to the flywheel.
|
|
|
|
|
* @param J The moment of inertia J of the flywheel.
|
|
|
|
|
* @param gearing Gear ratio from motor to flywheel (greater than 1 is a
|
|
|
|
|
* reduction).
|
|
|
|
|
* @throws std::domain_error if J <= 0 or gearing <= 0.
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<1, 1, 1> FlywheelFromPhysicalConstants(
|
|
|
|
|
DCMotor motor, wpi::units::kilogram_square_meter_t J, double gearing) {
|
|
|
|
|
if (J <= 0_kg_sq_m) {
|
|
|
|
|
throw std::domain_error("J must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (gearing <= 0.0) {
|
|
|
|
|
throw std::domain_error("gearing must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Matrixd<1, 1> A{
|
|
|
|
|
{(-gcem::pow(gearing, 2) * motor.Kt / (motor.Kv * motor.R * J))
|
|
|
|
|
.value()}};
|
|
|
|
|
Matrixd<1, 1> B{{(gearing * motor.Kt / (motor.R * J)).value()}};
|
|
|
|
|
Matrixd<1, 1> C{{1.0}};
|
|
|
|
|
Matrixd<1, 1> D{{0.0}};
|
|
|
|
|
|
|
|
|
|
return LinearSystem<1, 1, 1>(A, B, C, D);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a flywheel state-space model from SysId constants kᵥ (V/(rad/s))
|
|
|
|
|
* and kₐ (V/(rad/s²)) from the feedforward model u = kᵥv + kₐa.
|
|
|
|
|
*
|
|
|
|
|
* The states are [velocity], the inputs are [voltage], and the outputs are
|
|
|
|
|
* [velocity].
|
|
|
|
|
*
|
|
|
|
|
* @param kV The velocity gain, in V/(rad/s).
|
|
|
|
|
* @param kA The acceleration gain, in V/(rad/s²).
|
|
|
|
|
* @throws std::domain_error if kV < 0 or kA <= 0.
|
|
|
|
|
* @see <a
|
|
|
|
|
* href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<1, 1, 1> FlywheelFromSysId(
|
|
|
|
|
decltype(1_V / 1_rad_per_s) kV, decltype(1_V / 1_rad_per_s_sq) kA) {
|
|
|
|
|
if (kV < decltype(kV){0}) {
|
|
|
|
|
throw std::domain_error("Kv must be greater than or equal to zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kA <= decltype(kA){0}) {
|
|
|
|
|
throw std::domain_error("Ka must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Matrixd<1, 1> A{{-kV.value() / kA.value()}};
|
|
|
|
|
Matrixd<1, 1> B{{1.0 / kA.value()}};
|
|
|
|
|
Matrixd<1, 1> C{{1.0}};
|
|
|
|
|
Matrixd<1, 1> D{{0.0}};
|
|
|
|
|
|
|
|
|
|
return LinearSystem<1, 1, 1>(A, B, C, D);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates an elevator state-space model from physical constants.
|
|
|
|
|
*
|
|
|
|
|
* The states are [position, velocity], the inputs are [voltage], and the
|
|
|
|
|
* outputs are [position, velocity].
|
|
|
|
|
*
|
|
|
|
|
* @param motor The motor (or gearbox) attached to the carriage.
|
|
|
|
|
* @param mass The mass of the elevator carriage, in kilograms.
|
|
|
|
|
* @param radius The radius of the elevator's driving drum, in meters.
|
|
|
|
|
* @param gearing Gear ratio from motor to carriage.
|
|
|
|
|
* @param gearing Gear ratio from motor to carriage (greater than 1 is a
|
|
|
|
|
* reduction).
|
|
|
|
|
* @throws std::domain_error if mass <= 0, radius <= 0, or gearing <= 0.
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<2, 1, 2> ElevatorSystem(
|
|
|
|
|
static constexpr LinearSystem<2, 1, 2> ElevatorFromPhysicalConstants(
|
|
|
|
|
DCMotor motor, wpi::units::kilogram_t mass, wpi::units::meter_t radius,
|
|
|
|
|
double gearing) {
|
|
|
|
|
if (mass <= 0_kg) {
|
|
|
|
|
@@ -74,105 +136,21 @@ class WPILIB_DLLEXPORT LinearSystemId {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a state-space model of a single-jointed arm system.The states of the
|
|
|
|
|
* system are [angle, angular velocity]ᵀ, inputs are [voltage], and outputs
|
|
|
|
|
* are [angle, angular velocity]ᵀ.
|
|
|
|
|
* Creates an elevator state-space model from SysId constants kᵥ (V/(m/s)) and
|
|
|
|
|
* kₐ (V/(m/s²)) from the feedforward model u = kᵥv + kₐa.
|
|
|
|
|
*
|
|
|
|
|
* @param motor The motor (or gearbox) attached to the arm.
|
|
|
|
|
* @param J The moment of inertia J of the arm.
|
|
|
|
|
* @param gearing Gear ratio from motor to arm.
|
|
|
|
|
* @throws std::domain_error if J <= 0 or gearing <= 0.
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<2, 1, 2> SingleJointedArmSystem(
|
|
|
|
|
DCMotor motor, wpi::units::kilogram_square_meter_t J, double gearing) {
|
|
|
|
|
if (J <= 0_kg_sq_m) {
|
|
|
|
|
throw std::domain_error("J must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (gearing <= 0.0) {
|
|
|
|
|
throw std::domain_error("gearing must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Matrixd<2, 2> A{
|
|
|
|
|
{0.0, 1.0},
|
|
|
|
|
{0.0, (-gcem::pow(gearing, 2) * motor.Kt / (motor.Kv * motor.R * J))
|
|
|
|
|
.value()}};
|
|
|
|
|
Matrixd<2, 1> B{{0.0}, {(gearing * motor.Kt / (motor.R * J)).value()}};
|
|
|
|
|
Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
|
|
|
|
|
Matrixd<2, 1> D{{0.0}, {0.0}};
|
|
|
|
|
|
|
|
|
|
return LinearSystem<2, 1, 2>(A, B, C, D);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a state-space model for a 1 DOF velocity system from its kV
|
|
|
|
|
* (volts/(unit/sec)) and kA (volts/(unit/sec²)). These constants can be
|
|
|
|
|
* found using SysId. The states of the system are [velocity], inputs are
|
|
|
|
|
* [voltage], and outputs are [velocity].
|
|
|
|
|
* The states are [position, velocity], the inputs are [voltage], and the
|
|
|
|
|
* outputs are [position, velocity].
|
|
|
|
|
*
|
|
|
|
|
* You MUST use an SI unit (i.e. meters or radians) for the Distance template
|
|
|
|
|
* argument. You may still use non-SI units (such as feet or inches) for the
|
|
|
|
|
* actual method arguments; they will automatically be converted to SI
|
|
|
|
|
* internally.
|
|
|
|
|
*
|
|
|
|
|
* The parameters provided by the user are from this feedforward model:
|
|
|
|
|
*
|
|
|
|
|
* u = K_v v + K_a a
|
|
|
|
|
*
|
|
|
|
|
* @param kV The velocity gain, in volts/(unit/sec).
|
|
|
|
|
* @param kA The acceleration gain, in volts/(unit/sec²).
|
|
|
|
|
* @throws std::domain_error if kV < 0 or kA <= 0.
|
|
|
|
|
* @see <a
|
|
|
|
|
* href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
|
|
|
|
|
*/
|
|
|
|
|
template <typename Distance>
|
|
|
|
|
requires std::same_as<wpi::units::meter, Distance> ||
|
|
|
|
|
std::same_as<wpi::units::radian, Distance>
|
|
|
|
|
static constexpr LinearSystem<1, 1, 1> IdentifyVelocitySystem(
|
|
|
|
|
decltype(1_V / Velocity_t<Distance>(1)) kV,
|
|
|
|
|
decltype(1_V / Acceleration_t<Distance>(1)) kA) {
|
|
|
|
|
if (kV < decltype(kV){0}) {
|
|
|
|
|
throw std::domain_error("Kv must be greater than or equal to zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kA <= decltype(kA){0}) {
|
|
|
|
|
throw std::domain_error("Ka must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Matrixd<1, 1> A{{-kV.value() / kA.value()}};
|
|
|
|
|
Matrixd<1, 1> B{{1.0 / kA.value()}};
|
|
|
|
|
Matrixd<1, 1> C{{1.0}};
|
|
|
|
|
Matrixd<1, 1> D{{0.0}};
|
|
|
|
|
|
|
|
|
|
return LinearSystem<1, 1, 1>(A, B, C, D);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a state-space model for a 1 DOF position system from its kV
|
|
|
|
|
* (volts/(unit/sec)) and kA (volts/(unit/sec²)). These constants can be
|
|
|
|
|
* found using SysId. the states of the system are [position, velocity]ᵀ,
|
|
|
|
|
* inputs are [voltage], and outputs are [position, velocity]ᵀ.
|
|
|
|
|
*
|
|
|
|
|
* You MUST use an SI unit (i.e. meters or radians) for the Distance template
|
|
|
|
|
* argument. You may still use non-SI units (such as feet or inches) for the
|
|
|
|
|
* actual method arguments; they will automatically be converted to SI
|
|
|
|
|
* internally.
|
|
|
|
|
*
|
|
|
|
|
* The parameters provided by the user are from this feedforward model:
|
|
|
|
|
*
|
|
|
|
|
* u = K_v v + K_a a
|
|
|
|
|
*
|
|
|
|
|
* @param kV The velocity gain, in volts/(unit/sec).
|
|
|
|
|
* @param kA The acceleration gain, in volts/(unit/sec²).
|
|
|
|
|
* @param kV The velocity gain, in V/(m/s).
|
|
|
|
|
* @param kA The acceleration gain, in V/(m/s²).
|
|
|
|
|
*
|
|
|
|
|
* @throws std::domain_error if kV < 0 or kA <= 0.
|
|
|
|
|
* @see <a
|
|
|
|
|
* href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
|
|
|
|
|
*/
|
|
|
|
|
template <typename Distance>
|
|
|
|
|
requires std::same_as<wpi::units::meter, Distance> ||
|
|
|
|
|
std::same_as<wpi::units::radian, Distance>
|
|
|
|
|
static constexpr LinearSystem<2, 1, 2> IdentifyPositionSystem(
|
|
|
|
|
decltype(1_V / Velocity_t<Distance>(1)) kV,
|
|
|
|
|
decltype(1_V / Acceleration_t<Distance>(1)) kA) {
|
|
|
|
|
static constexpr LinearSystem<2, 1, 2> ElevatorFromSysId(
|
|
|
|
|
decltype(1_V / 1_mps) kV, decltype(1_V / 1_mps_sq) kA) {
|
|
|
|
|
if (kV < decltype(kV){0}) {
|
|
|
|
|
throw std::domain_error("Kv must be greater than or equal to zero.");
|
|
|
|
|
}
|
|
|
|
|
@@ -189,167 +167,18 @@ class WPILIB_DLLEXPORT LinearSystemId {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Identify a differential drive drivetrain given the drivetrain's kV and kA
|
|
|
|
|
* in both linear {(volts/(meter/sec), (volts/(meter/sec²))} and angular
|
|
|
|
|
* {(volts/(radian/sec), (volts/(radian/sec²))} cases. These constants can be
|
|
|
|
|
* found using SysId.
|
|
|
|
|
* Create a single-jointed arm state-space model from physical constants.
|
|
|
|
|
*
|
|
|
|
|
* States: [[left velocity], [right velocity]]<br>
|
|
|
|
|
* Inputs: [[left voltage], [right voltage]]<br>
|
|
|
|
|
* Outputs: [[left velocity], [right velocity]]
|
|
|
|
|
* The states are [angle, angular velocity], the inputs are [voltage], and the
|
|
|
|
|
* outputs are [angle, angular velocity].
|
|
|
|
|
*
|
|
|
|
|
* @param kVLinear The linear velocity gain in volts per (meters per second).
|
|
|
|
|
* @param kALinear The linear acceleration gain in volts per (meters per
|
|
|
|
|
* second squared).
|
|
|
|
|
* @param kVAngular The angular velocity gain in volts per (meters per
|
|
|
|
|
* second).
|
|
|
|
|
* @param kAAngular The angular acceleration gain in volts per (meters per
|
|
|
|
|
* second squared).
|
|
|
|
|
* @throws domain_error if kVLinear <= 0, kALinear <= 0, kVAngular <= 0,
|
|
|
|
|
* or kAAngular <= 0.
|
|
|
|
|
* @see <a
|
|
|
|
|
* href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<2, 2, 2> IdentifyDrivetrainSystem(
|
|
|
|
|
decltype(1_V / 1_mps) kVLinear, decltype(1_V / 1_mps_sq) kALinear,
|
|
|
|
|
decltype(1_V / 1_mps) kVAngular, decltype(1_V / 1_mps_sq) kAAngular) {
|
|
|
|
|
if (kVLinear <= decltype(kVLinear){0}) {
|
|
|
|
|
throw std::domain_error("Kv,linear must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kALinear <= decltype(kALinear){0}) {
|
|
|
|
|
throw std::domain_error("Ka,linear must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kVAngular <= decltype(kVAngular){0}) {
|
|
|
|
|
throw std::domain_error("Kv,angular must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kAAngular <= decltype(kAAngular){0}) {
|
|
|
|
|
throw std::domain_error("Ka,angular must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double A1 = -(kVLinear.value() / kALinear.value() +
|
|
|
|
|
kVAngular.value() / kAAngular.value());
|
|
|
|
|
double A2 = -(kVLinear.value() / kALinear.value() -
|
|
|
|
|
kVAngular.value() / kAAngular.value());
|
|
|
|
|
double B1 = 1.0 / kALinear.value() + 1.0 / kAAngular.value();
|
|
|
|
|
double B2 = 1.0 / kALinear.value() - 1.0 / kAAngular.value();
|
|
|
|
|
|
|
|
|
|
A1 /= 2.0;
|
|
|
|
|
A2 /= 2.0;
|
|
|
|
|
B1 /= 2.0;
|
|
|
|
|
B2 /= 2.0;
|
|
|
|
|
|
|
|
|
|
Matrixd<2, 2> A{{A1, A2}, {A2, A1}};
|
|
|
|
|
Matrixd<2, 2> B{{B1, B2}, {B2, B1}};
|
|
|
|
|
Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
|
|
|
|
|
Matrixd<2, 2> D{{0.0, 0.0}, {0.0, 0.0}};
|
|
|
|
|
|
|
|
|
|
return LinearSystem<2, 2, 2>(A, B, C, D);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Identify a differential drive drivetrain given the drivetrain's kV and kA
|
|
|
|
|
* in both linear {(volts/(meter/sec)), (volts/(meter/sec²))} and angular
|
|
|
|
|
* {(volts/(radian/sec)), (volts/(radian/sec²))} cases. This can be found
|
|
|
|
|
* using SysId.
|
|
|
|
|
*
|
|
|
|
|
* States: [[left velocity], [right velocity]]<br>
|
|
|
|
|
* Inputs: [[left voltage], [right voltage]]<br>
|
|
|
|
|
* Outputs: [[left velocity], [right velocity]]
|
|
|
|
|
*
|
|
|
|
|
* @param kVLinear The linear velocity gain in volts per (meters per
|
|
|
|
|
* second).
|
|
|
|
|
* @param kALinear The linear acceleration gain in volts per (meters per
|
|
|
|
|
* second squared).
|
|
|
|
|
* @param kVAngular The angular velocity gain in volts per (radians per
|
|
|
|
|
* second).
|
|
|
|
|
* @param kAAngular The angular acceleration gain in volts per (radians per
|
|
|
|
|
* second squared).
|
|
|
|
|
* @param trackwidth The distance between the differential drive's left and
|
|
|
|
|
* right wheels, in meters.
|
|
|
|
|
* @throws domain_error if kVLinear <= 0, kALinear <= 0, kVAngular <= 0,
|
|
|
|
|
* kAAngular <= 0, or trackwidth <= 0.
|
|
|
|
|
* @see <a
|
|
|
|
|
* href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<2, 2, 2> IdentifyDrivetrainSystem(
|
|
|
|
|
decltype(1_V / 1_mps) kVLinear, decltype(1_V / 1_mps_sq) kALinear,
|
|
|
|
|
decltype(1_V / 1_rad_per_s) kVAngular,
|
|
|
|
|
decltype(1_V / 1_rad_per_s_sq) kAAngular,
|
|
|
|
|
wpi::units::meter_t trackwidth) {
|
|
|
|
|
if (kVLinear <= decltype(kVLinear){0}) {
|
|
|
|
|
throw std::domain_error("Kv,linear must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kALinear <= decltype(kALinear){0}) {
|
|
|
|
|
throw std::domain_error("Ka,linear must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kVAngular <= decltype(kVAngular){0}) {
|
|
|
|
|
throw std::domain_error("Kv,angular must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kAAngular <= decltype(kAAngular){0}) {
|
|
|
|
|
throw std::domain_error("Ka,angular must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (trackwidth <= 0_m) {
|
|
|
|
|
throw std::domain_error("r must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We want to find a factor to include in Kv,angular that will convert
|
|
|
|
|
// `u = Kv,angular omega` to `u = Kv,angular v`.
|
|
|
|
|
//
|
|
|
|
|
// v = omega r
|
|
|
|
|
// omega = v/r
|
|
|
|
|
// omega = 1/r v
|
|
|
|
|
// omega = 1/(trackwidth/2) v
|
|
|
|
|
// omega = 2/trackwidth v
|
|
|
|
|
//
|
|
|
|
|
// So multiplying by 2/trackwidth converts the angular gains from V/(rad/s)
|
|
|
|
|
// to V/(m/s).
|
|
|
|
|
return IdentifyDrivetrainSystem(kVLinear, kALinear,
|
|
|
|
|
kVAngular * 2.0 / trackwidth * 1_rad,
|
|
|
|
|
kAAngular * 2.0 / trackwidth * 1_rad);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a state-space model of a flywheel system, the states of the system
|
|
|
|
|
* are [angular velocity], inputs are [voltage], and outputs are [angular
|
|
|
|
|
* velocity].
|
|
|
|
|
*
|
|
|
|
|
* @param motor The motor (or gearbox) attached to the flywheel.
|
|
|
|
|
* @param J The moment of inertia J of the flywheel.
|
|
|
|
|
* @param gearing Gear ratio from motor to flywheel.
|
|
|
|
|
* @param motor The motor (or gearbox) attached to the arm.
|
|
|
|
|
* @param J The moment of inertia J of the arm.
|
|
|
|
|
* @param gearing Gear ratio from motor to arm (greater than 1 is a
|
|
|
|
|
* reduction).
|
|
|
|
|
* @throws std::domain_error if J <= 0 or gearing <= 0.
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<1, 1, 1> FlywheelSystem(
|
|
|
|
|
DCMotor motor, wpi::units::kilogram_square_meter_t J, double gearing) {
|
|
|
|
|
if (J <= 0_kg_sq_m) {
|
|
|
|
|
throw std::domain_error("J must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (gearing <= 0.0) {
|
|
|
|
|
throw std::domain_error("gearing must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Matrixd<1, 1> A{
|
|
|
|
|
{(-gcem::pow(gearing, 2) * motor.Kt / (motor.Kv * motor.R * J))
|
|
|
|
|
.value()}};
|
|
|
|
|
Matrixd<1, 1> B{{(gearing * motor.Kt / (motor.R * J)).value()}};
|
|
|
|
|
Matrixd<1, 1> C{{1.0}};
|
|
|
|
|
Matrixd<1, 1> D{{0.0}};
|
|
|
|
|
|
|
|
|
|
return LinearSystem<1, 1, 1>(A, B, C, D);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a state-space model of a DC motor system. The states of the system
|
|
|
|
|
* are [angular position, angular velocity]ᵀ, inputs are [voltage], and
|
|
|
|
|
* outputs are [angular position, angular velocity]ᵀ.
|
|
|
|
|
*
|
|
|
|
|
* @param motor The motor (or gearbox) attached to the system.
|
|
|
|
|
* @param J the moment of inertia J of the DC motor.
|
|
|
|
|
* @param gearing Gear ratio from motor to output.
|
|
|
|
|
* @throws std::domain_error if J <= 0 or gearing <= 0.
|
|
|
|
|
* @see <a
|
|
|
|
|
* href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<2, 1, 2> DCMotorSystem(
|
|
|
|
|
static constexpr LinearSystem<2, 1, 2> SingleJointedArmFromPhysicalConstants(
|
|
|
|
|
DCMotor motor, wpi::units::kilogram_square_meter_t J, double gearing) {
|
|
|
|
|
if (J <= 0_kg_sq_m) {
|
|
|
|
|
throw std::domain_error("J must be greater than zero.");
|
|
|
|
|
@@ -370,32 +199,21 @@ class WPILIB_DLLEXPORT LinearSystemId {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a state-space model of a DC motor system from its kV
|
|
|
|
|
* (volts/(unit/sec)) and kA (volts/(unit/sec²)). These constants can be
|
|
|
|
|
* found using SysId. the states of the system are [angular position, angular
|
|
|
|
|
* velocity]ᵀ, inputs are [voltage], and outputs are [angular position,
|
|
|
|
|
* angular velocity]ᵀ.
|
|
|
|
|
* Creates a single-jointed arm state-space model from SysId constants kᵥ
|
|
|
|
|
* (V/(rad/s)) and kₐ (V/(rad/s²)) from the feedforward model u = kᵥv + kₐa.
|
|
|
|
|
*
|
|
|
|
|
* You MUST use an SI unit (i.e. meters or radians) for the Distance template
|
|
|
|
|
* argument. You may still use non-SI units (such as feet or inches) for the
|
|
|
|
|
* actual method arguments; they will automatically be converted to SI
|
|
|
|
|
* internally.
|
|
|
|
|
*
|
|
|
|
|
* The parameters provided by the user are from this feedforward model:
|
|
|
|
|
*
|
|
|
|
|
* u = K_v v + K_a a
|
|
|
|
|
* The states are [position, velocity], the inputs are [voltage], and the
|
|
|
|
|
* outputs are [position, velocity].
|
|
|
|
|
*
|
|
|
|
|
* @param kV The velocity gain, in volts/(unit/sec).
|
|
|
|
|
* @param kA The acceleration gain, in volts/(unit/sec²).
|
|
|
|
|
*
|
|
|
|
|
* @throws std::domain_error if kV < 0 or kA <= 0.
|
|
|
|
|
* @see <a
|
|
|
|
|
* href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
|
|
|
|
|
*/
|
|
|
|
|
template <typename Distance>
|
|
|
|
|
requires std::same_as<wpi::units::meter, Distance> ||
|
|
|
|
|
std::same_as<wpi::units::radian, Distance>
|
|
|
|
|
static constexpr LinearSystem<2, 1, 2> DCMotorSystem(
|
|
|
|
|
decltype(1_V / Velocity_t<Distance>(1)) kV,
|
|
|
|
|
decltype(1_V / Acceleration_t<Distance>(1)) kA) {
|
|
|
|
|
static constexpr LinearSystem<2, 1, 2> SingleJointedArmFromSysId(
|
|
|
|
|
decltype(1_V / 1_rad_per_s) kV, decltype(1_V / 1_rad_per_s_sq) kA) {
|
|
|
|
|
if (kV < decltype(kV){0}) {
|
|
|
|
|
throw std::domain_error("Kv must be greater than or equal to zero.");
|
|
|
|
|
}
|
|
|
|
|
@@ -404,7 +222,7 @@ class WPILIB_DLLEXPORT LinearSystemId {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Matrixd<2, 2> A{{0.0, 1.0}, {0.0, -kV.value() / kA.value()}};
|
|
|
|
|
Matrixd<2, 1> B{0.0, 1.0 / kA.value()};
|
|
|
|
|
Matrixd<2, 1> B{{0.0}, {1.0 / kA.value()}};
|
|
|
|
|
Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
|
|
|
|
|
Matrixd<2, 1> D{{0.0}, {0.0}};
|
|
|
|
|
|
|
|
|
|
@@ -412,21 +230,23 @@ class WPILIB_DLLEXPORT LinearSystemId {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a state-space model of differential drive drivetrain. In this model,
|
|
|
|
|
* the states are [left velocity, right velocity]ᵀ, the inputs are [left
|
|
|
|
|
* Creates a differential drive state-space model from physical constants.
|
|
|
|
|
*
|
|
|
|
|
* The states are [left velocity, right velocity], the inputs are [left
|
|
|
|
|
* voltage, right voltage], and the outputs are [left velocity, right
|
|
|
|
|
* velocity]ᵀ.
|
|
|
|
|
* velocity].
|
|
|
|
|
*
|
|
|
|
|
* @param motor The motor (or gearbox) driving the drivetrain.
|
|
|
|
|
* @param mass The mass of the robot in kilograms.
|
|
|
|
|
* @param r The radius of the wheels in meters.
|
|
|
|
|
* @param rb The radius of the base (half of the trackwidth), in meters.
|
|
|
|
|
* @param J The moment of inertia of the robot.
|
|
|
|
|
* @param gearing Gear ratio from motor to wheel.
|
|
|
|
|
* @param gearing Gear ratio from motor to wheel (greater than 1 is a
|
|
|
|
|
* reduction).
|
|
|
|
|
* @throws std::domain_error if mass <= 0, r <= 0, rb <= 0, J <= 0, or
|
|
|
|
|
* gearing <= 0.
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<2, 2, 2> DrivetrainVelocitySystem(
|
|
|
|
|
static constexpr LinearSystem<2, 2, 2> DifferentialDriveFromPhysicalConstants(
|
|
|
|
|
const DCMotor& motor, wpi::units::kilogram_t mass, wpi::units::meter_t r,
|
|
|
|
|
wpi::units::meter_t rb, wpi::units::kilogram_square_meter_t J,
|
|
|
|
|
double gearing) {
|
|
|
|
|
@@ -465,6 +285,119 @@ class WPILIB_DLLEXPORT LinearSystemId {
|
|
|
|
|
|
|
|
|
|
return LinearSystem<2, 2, 2>(A, B, C, D);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a differential drive state-space model from SysId constants kᵥ and
|
|
|
|
|
* kₐ in both linear {(V/(m/s), (V/(m/s²))} and angular {(V/(rad/s),
|
|
|
|
|
* (V/(rad/s²))} cases.
|
|
|
|
|
*
|
|
|
|
|
* The states are [left velocity, right velocity], the inputs are [left
|
|
|
|
|
* voltage, right voltage], and the outputs are [left velocity, right
|
|
|
|
|
* velocity].
|
|
|
|
|
*
|
|
|
|
|
* @param kVLinear The linear velocity gain in volts per (meters per second).
|
|
|
|
|
* @param kALinear The linear acceleration gain in volts per (meters per
|
|
|
|
|
* second squared).
|
|
|
|
|
* @param kVAngular The angular velocity gain in volts per (meters per
|
|
|
|
|
* second).
|
|
|
|
|
* @param kAAngular The angular acceleration gain in volts per (meters per
|
|
|
|
|
* second squared).
|
|
|
|
|
* @throws domain_error if kVLinear <= 0, kALinear <= 0, kVAngular <= 0,
|
|
|
|
|
* or kAAngular <= 0.
|
|
|
|
|
* @see <a
|
|
|
|
|
* href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<2, 2, 2> DifferentialDriveFromSysId(
|
|
|
|
|
decltype(1_V / 1_mps) kVLinear, decltype(1_V / 1_mps_sq) kALinear,
|
|
|
|
|
decltype(1_V / 1_mps) kVAngular, decltype(1_V / 1_mps_sq) kAAngular) {
|
|
|
|
|
if (kVLinear <= decltype(kVLinear){0}) {
|
|
|
|
|
throw std::domain_error("Kv,linear must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kALinear <= decltype(kALinear){0}) {
|
|
|
|
|
throw std::domain_error("Ka,linear must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kVAngular <= decltype(kVAngular){0}) {
|
|
|
|
|
throw std::domain_error("Kv,angular must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kAAngular <= decltype(kAAngular){0}) {
|
|
|
|
|
throw std::domain_error("Ka,angular must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double A1 = -0.5 * (kVLinear.value() / kALinear.value() +
|
|
|
|
|
kVAngular.value() / kAAngular.value());
|
|
|
|
|
double A2 = -0.5 * (kVLinear.value() / kALinear.value() -
|
|
|
|
|
kVAngular.value() / kAAngular.value());
|
|
|
|
|
double B1 = 0.5 / kALinear.value() + 0.5 / kAAngular.value();
|
|
|
|
|
double B2 = 0.5 / kALinear.value() - 0.5 / kAAngular.value();
|
|
|
|
|
|
|
|
|
|
Matrixd<2, 2> A{{A1, A2}, {A2, A1}};
|
|
|
|
|
Matrixd<2, 2> B{{B1, B2}, {B2, B1}};
|
|
|
|
|
Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
|
|
|
|
|
Matrixd<2, 2> D{{0.0, 0.0}, {0.0, 0.0}};
|
|
|
|
|
|
|
|
|
|
return LinearSystem<2, 2, 2>(A, B, C, D);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a differential drive state-space model from SysId constants kᵥ and
|
|
|
|
|
* kₐ in both linear {(V/(m/s), (V/(m/s²))} and angular {(V/(rad/s),
|
|
|
|
|
* (V/(rad/s²))} cases.
|
|
|
|
|
*
|
|
|
|
|
* The states are [left velocity, right velocity], the inputs are [left
|
|
|
|
|
* voltage, right voltage], and the outputs are [left velocity, right
|
|
|
|
|
* velocity].
|
|
|
|
|
*
|
|
|
|
|
* @param kVLinear The linear velocity gain in volts per (meters per
|
|
|
|
|
* second).
|
|
|
|
|
* @param kALinear The linear acceleration gain in volts per (meters per
|
|
|
|
|
* second squared).
|
|
|
|
|
* @param kVAngular The angular velocity gain in volts per (radians per
|
|
|
|
|
* second).
|
|
|
|
|
* @param kAAngular The angular acceleration gain in volts per (radians per
|
|
|
|
|
* second squared).
|
|
|
|
|
* @param trackwidth The distance between the differential drive's left and
|
|
|
|
|
* right wheels, in meters.
|
|
|
|
|
* @throws domain_error if kVLinear <= 0, kALinear <= 0, kVAngular <= 0,
|
|
|
|
|
* kAAngular <= 0, or trackwidth <= 0.
|
|
|
|
|
* @see <a
|
|
|
|
|
* href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
|
|
|
|
|
*/
|
|
|
|
|
static constexpr LinearSystem<2, 2, 2> DifferentialDriveFromSysId(
|
|
|
|
|
decltype(1_V / 1_mps) kVLinear, decltype(1_V / 1_mps_sq) kALinear,
|
|
|
|
|
decltype(1_V / 1_rad_per_s) kVAngular,
|
|
|
|
|
decltype(1_V / 1_rad_per_s_sq) kAAngular,
|
|
|
|
|
wpi::units::meter_t trackwidth) {
|
|
|
|
|
if (kVLinear <= decltype(kVLinear){0}) {
|
|
|
|
|
throw std::domain_error("Kv,linear must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kALinear <= decltype(kALinear){0}) {
|
|
|
|
|
throw std::domain_error("Ka,linear must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kVAngular <= decltype(kVAngular){0}) {
|
|
|
|
|
throw std::domain_error("Kv,angular must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (kAAngular <= decltype(kAAngular){0}) {
|
|
|
|
|
throw std::domain_error("Ka,angular must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
if (trackwidth <= 0_m) {
|
|
|
|
|
throw std::domain_error("r must be greater than zero.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We want to find a factor to include in Kv,angular that will convert
|
|
|
|
|
// `u = Kv,angular omega` to `u = Kv,angular v`.
|
|
|
|
|
//
|
|
|
|
|
// v = omega r
|
|
|
|
|
// omega = v/r
|
|
|
|
|
// omega = 1/r v
|
|
|
|
|
// omega = 1/(trackwidth/2) v
|
|
|
|
|
// omega = 2/trackwidth v
|
|
|
|
|
//
|
|
|
|
|
// So multiplying by 2/trackwidth converts the angular gains from V/(rad/s)
|
|
|
|
|
// to V/(m/s).
|
|
|
|
|
return DifferentialDriveFromSysId(kVLinear, kALinear,
|
|
|
|
|
kVAngular * 2.0 / trackwidth * 1_rad,
|
|
|
|
|
kAAngular * 2.0 / trackwidth * 1_rad);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace wpi::math
|