[wpimath] Add LTV controllers (#4094)

This adds a unicycle controller that's a drop-in replacement for Ramsete
and a differential drive controller that controls the full pose and
outputs voltages. The main benefit is LQR-like tuning knobs using a
system model.
This commit is contained in:
Tyler Veness
2022-04-30 22:54:22 -07:00
committed by GitHub
parent ebd2a303bf
commit 87bf70fa8e
11 changed files with 1373 additions and 0 deletions

View File

@@ -0,0 +1,132 @@
// 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 <wpi/SymbolExports.h>
#include <wpi/array.h>
#include <wpi/interpolating_map.h>
#include "frc/EigenCore.h"
#include "frc/geometry/Pose2d.h"
#include "frc/system/LinearSystem.h"
#include "frc/trajectory/Trajectory.h"
#include "units/length.h"
#include "units/time.h"
#include "units/velocity.h"
#include "units/voltage.h"
namespace frc {
/**
* The linear time-varying differential drive controller has a similar form to
* the LQR, but the model used to compute the controller gain is the nonlinear
* model linearized around the drivetrain's current state. We precomputed gains
* for important places in our state-space, then interpolated between them with
* a LUT to save computational resources.
*
* See section 8.7 in Controls Engineering in FRC for a derivation of the
* control law we used shown in theorem 8.7.4.
*/
class WPILIB_DLLEXPORT LTVDifferentialDriveController {
public:
/**
* Motor voltages for a differential drive.
*/
struct WheelVoltages {
units::volt_t left = 0_V;
units::volt_t right = 0_V;
};
/**
* Constructs a linear time-varying differential drive controller.
*
* @param plant The drivetrain velocity plant.
* @param trackwidth The drivetrain's trackwidth.
* @param Qelems The maximum desired error tolerance for each state.
* @param Relems The maximum desired control effort for each input.
* @param dt Discretization timestep.
*/
LTVDifferentialDriveController(const frc::LinearSystem<2, 2, 2>& plant,
units::meter_t trackwidth,
const wpi::array<double, 5>& Qelems,
const wpi::array<double, 2>& Relems,
units::second_t dt);
/**
* Move constructor.
*/
LTVDifferentialDriveController(LTVDifferentialDriveController&&) = default;
/**
* Move assignment operator.
*/
LTVDifferentialDriveController& operator=(LTVDifferentialDriveController&&) =
default;
/**
* Returns true if the pose error is within tolerance of the reference.
*/
bool AtReference() const;
/**
* Sets the pose error which is considered tolerable for use with
* AtReference().
*
* @param poseTolerance Pose error which is tolerable.
* @param leftVelocityTolerance Left velocity error which is tolerable.
* @param rightVelocityTolerance Right velocity error which is tolerable.
*/
void SetTolerance(const Pose2d& poseTolerance,
units::meters_per_second_t leftVelocityTolerance,
units::meters_per_second_t rightVelocityTolerance);
/**
* Returns the left and right output voltages of the LTV controller.
*
* The reference pose, linear velocity, and angular velocity should come from
* a drivetrain trajectory.
*
* @param currentPose The current pose.
* @param leftVelocity The current left velocity.
* @param rightVelocity The current right velocity.
* @param poseRef The desired pose.
* @param leftVelocityRef The desired left velocity.
* @param rightVelocityRef The desired right velocity.
*/
WheelVoltages Calculate(const Pose2d& currentPose,
units::meters_per_second_t leftVelocity,
units::meters_per_second_t rightVelocity,
const Pose2d& poseRef,
units::meters_per_second_t leftVelocityRef,
units::meters_per_second_t rightVelocityRef);
/**
* Returns the left and right output voltages of the LTV controller.
*
* The reference pose, linear velocity, and angular velocity should come from
* a drivetrain trajectory.
*
* @param currentPose The current pose.
* @param leftVelocity The left velocity.
* @param rightVelocity The right velocity.
* @param desiredState The desired pose, linear velocity, and angular velocity
* from a trajectory.
*/
WheelVoltages Calculate(const Pose2d& currentPose,
units::meters_per_second_t leftVelocity,
units::meters_per_second_t rightVelocity,
const Trajectory::State& desiredState);
private:
units::meter_t m_trackwidth;
// LUT from drivetrain linear velocity to LQR gain
wpi::interpolating_map<units::meters_per_second_t, Matrixd<2, 5>> m_table;
Vectord<5> m_error;
Vectord<5> m_tolerance;
};
} // namespace frc