blob: e82c668b6639775d72b2b0262906eb84ca4c7b2e [file] [log] [blame]
#ifndef FRC971_CONTROL_LOOPS_JACOBIAN_H_
#define FRC971_CONTROL_LOOPS_JACOBIAN_H_
#include <Eigen/Dense>
namespace frc971::control_loops {
template <int num_states, int num_inputs, typename Scalar, typename F>
::Eigen::Matrix<Scalar, num_states, num_inputs> NumericalJacobian(
const F &fn, ::Eigen::Matrix<Scalar, num_inputs, 1> input) {
constexpr Scalar kEpsilon = 1e-5;
::Eigen::Matrix<Scalar, num_states, num_inputs> result =
::Eigen::Matrix<Scalar, num_states, num_inputs>::Zero();
// It's more expensive, but +- epsilon will be more accurate
for (int i = 0; i < num_inputs; ++i) {
::Eigen::Matrix<Scalar, num_inputs, 1> dX_plus = input;
dX_plus(i, 0) += kEpsilon;
::Eigen::Matrix<Scalar, num_inputs, 1> dX_minus = input;
dX_minus(i, 0) -= kEpsilon;
result.col(i) = (fn(dX_plus) - fn(dX_minus)) / (kEpsilon * 2.0);
}
return result;
}
// Implements a numerical jacobian with respect to X for f(X, U, ...).
template <int num_states, int num_u, typename Scalar, typename F,
typename... Args>
::Eigen::Matrix<Scalar, num_states, num_states> NumericalJacobianX(
const F &fn, ::Eigen::Matrix<Scalar, num_states, 1> X,
::Eigen::Matrix<Scalar, num_u, 1> U, Args &&...args) {
return NumericalJacobian<num_states, num_states>(
[&](::Eigen::Matrix<Scalar, num_states, 1> X) {
return fn(X, U, args...);
},
X);
}
// Implements a numerical jacobian with respect to U for f(X, U, ...).
template <int num_states, int num_u, typename Scalar, typename F,
typename... Args>
::Eigen::Matrix<Scalar, num_states, num_u> NumericalJacobianU(
const F &fn, ::Eigen::Matrix<Scalar, num_states, 1> X,
::Eigen::Matrix<Scalar, num_u, 1> U, Args &&...args) {
return NumericalJacobian<num_states, num_u>(
[&](::Eigen::Matrix<Scalar, num_u, 1> U) { return fn(X, U, args...); },
U);
}
} // namespace frc971::control_loops
#endif // FRC971_CONTROL_LOOPS_JACOBIAN_H_