Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add equality constraints in LQR. #17327

Merged

Conversation

hongkai-dai
Copy link
Contributor

@hongkai-dai hongkai-dai commented Jun 3, 2022

This change is Reviewable

Copy link
Contributor Author

@hongkai-dai hongkai-dai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+@RussTedrake for feature review please, thanks!

Reviewable status: LGTM missing from assignee RussTedrake(platform), needs at least two assigned reviewers, missing label for release notes (waiting on @RussTedrake)

Copy link
Contributor

@RussTedrake RussTedrake left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for being slow. I've found a few small issues. PTAL.

Reviewed 5 of 5 files at r1, all commit messages.
Reviewable status: 6 unresolved discussions, LGTM missing from assignee RussTedrake(platform), needs at least two assigned reviewers, missing label for release notes (waiting on @hongkai-dai)


systems/controllers/linear_quadratic_regulator.h line 20 at r1 (raw file):

///
///   @f[ \dot{x} = Ax + Bu @f]
///   @f[ \min_u \int_0^\infty x'Qx + u'Ru + 2x'Nu dt @f]

nit: could we move the dynamic to underneath the objective, so that it's in the standard minimize _ subject to __ form?


systems/controllers/linear_quadratic_regulator.h line 32 at r1 (raw file):

/// zero-sized, N will be treated as a num_states x num_inputs zero matrix.
/// @param F A constraint matrix of size num_constraints x num_states. rank(F)
/// must be < num_states. If the matrix is zero-sized, F will be treated as a 0

"if the matrix is zero-sized". that's not quite the condition you check; because you check if rows != 0, then cols must == N. if the user passes an 4 x 0 matrix, that would still be zero-sized, but will fail the DRAKE_DEMAND.


systems/controllers/linear_quadratic_regulator.cc line 44 at r1 (raw file):

  if (R_cholesky.info() != Eigen::Success)
    throw std::runtime_error("R must be positive definite");
  // The rows of P are the basis for the null-space of F, namely PPᵀ = I and

nit: orthonormal basis.


systems/controllers/linear_quadratic_regulator.cc line 49 at r1 (raw file):

  if (F.rows() != 0) {
    // Compute the null-space based on QR decomposition. If Fᵀ*S = [Q1 Q2][R; 0]
    // = Q1*R, then take P=Q2ᵀ, where S is the permutation matrix.

yikes. Q, R, and S already have different meanings in this function, making this documentation pretty confusing.


systems/controllers/linear_quadratic_regulator.cc line 52 at r1 (raw file):

    // We introduce projected state y, such that y = Px.
    // We form a new dynamical system
    // ẏ = PAPᵀx + PBu

the x should be y.


systems/controllers/linear_quadratic_regulator.cc line 88 at r1 (raw file):

      //   = -R⁻¹(BᵀS+NᵀPᵀP)
      ret.K = R_cholesky.solve(B.transpose() * ret.S +
                               N.transpose() * P->transpose() * (*P));

P^T * P is the identity; you shouldn't need that here?

Copy link
Contributor Author

@hongkai-dai hongkai-dai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 4 unresolved discussions, LGTM missing from assignee RussTedrake(platform), needs at least two assigned reviewers, missing label for release notes (waiting on @RussTedrake)


systems/controllers/linear_quadratic_regulator.h line 32 at r1 (raw file):

Previously, RussTedrake (Russ Tedrake) wrote…

"if the matrix is zero-sized". that's not quite the condition you check; because you check if rows != 0, then cols must == N. if the user passes an 4 x 0 matrix, that would still be zero-sized, but will fail the DRAKE_DEMAND.

Done. I also changed the documentation for N above. We also checked DRAKE_DEMAND(N.cols() == m).


systems/controllers/linear_quadratic_regulator.cc line 49 at r1 (raw file):

Previously, RussTedrake (Russ Tedrake) wrote…

yikes. Q, R, and S already have different meanings in this function, making this documentation pretty confusing.

Done. Good call. I factored out the function to compute the kernel.


systems/controllers/linear_quadratic_regulator.cc line 52 at r1 (raw file):

Previously, RussTedrake (Russ Tedrake) wrote…

the x should be y.

Done.


systems/controllers/linear_quadratic_regulator.cc line 88 at r1 (raw file):

Previously, RussTedrake (Russ Tedrake) wrote…

P^T * P is the identity; you shouldn't need that here?

I don't think P^T*P is identity, we have P * P^T being identity, but swapping the order of P and P^T won't give us identity matrix, unless they are both full-rank square matrices.

One example is P = [1/sqrt(2), 1/sqrt(2)], we can see that P * P^T = 1, but P^T * P = [0.5, 0.5; 0.5 0.5]

Copy link
Contributor

@RussTedrake RussTedrake left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

Reviewed 2 of 2 files at r2, all commit messages.
Reviewable status: 1 unresolved discussion, needs at least two assigned reviewers, missing label for release notes (waiting on @hongkai-dai)


systems/controllers/linear_quadratic_regulator.cc line 88 at r1 (raw file):

Previously, hongkai-dai (Hongkai Dai) wrote…

I don't think P^T*P is identity, we have P * P^T being identity, but swapping the order of P and P^T won't give us identity matrix, unless they are both full-rank square matrices.

One example is P = [1/sqrt(2), 1/sqrt(2)], we can see that P * P^T = 1, but P^T * P = [0.5, 0.5; 0.5 0.5]

My bad! Retracted.

Copy link
Contributor Author

@hongkai-dai hongkai-dai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+@EricCousineau-TRI for platform review please, thanks!

Reviewable status: 1 unresolved discussion, LGTM missing from assignee EricCousineau-TRI(platform), missing label for release notes (waiting on @EricCousineau-TRI and @RussTedrake)


systems/controllers/linear_quadratic_regulator.cc line 88 at r1 (raw file):

Previously, RussTedrake (Russ Tedrake) wrote…

My bad! Retracted.

Done.

Copy link
Contributor

@EricCousineau-TRI EricCousineau-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm: plaform, with very small nits / BTWs. Nice!

Reviewed 3 of 5 files at r1, 2 of 2 files at r2, all commit messages.
Reviewable status: 3 unresolved discussions, missing label for release notes (waiting on @hongkai-dai and @RussTedrake)


systems/controllers/linear_quadratic_regulator.h line 21 at r2 (raw file):

///   @f[ \min_u \int_0^\infty x'Qx + u'Ru + 2x'Nu dt @f]
///   @f[ \dot{x} = Ax + Bu @f]
///   @f[ Fx=0 @f]

nit Above code has a space between =. Consider using that here.


systems/controllers/linear_quadratic_regulator.h line 32 at r2 (raw file):

/// 0, N will be treated as a num_states x num_inputs zero matrix.
/// @param F A constraint matrix of size num_constraints x num_states. rank(F)
/// must be < num_states. If the F.rows() == 0, F will be treated as a 0

BTW the F.rows() sounds a bit odd. Consider removing the


systems/controllers/test/linear_quadratic_regulator_test.cc line 18 at r2 (raw file):

namespace {

GTEST_TEST(TestLQR, TestException) {

BTW (pre-existing) For acronyms, style guide says to prefer TestLqr over TestLQR
https://drake.mit.edu/styleguide/cppguide.html#General_Naming_Rules


systems/controllers/test/linear_quadratic_regulator_test.cc line 124 at r2 (raw file):

// Test if the LQR solution satisfies the HJB equality
// minᵤ xᵀQx + uᵀRu + 2xᵀNu + 2xᵀS(Ax+Bu) = 0
void TestLQRwHJB(

nit Not the most readable name.

Consider expanding to TestLQRwithHJB, or following style guide for acroynyms, TestLqrWithHjb


systems/controllers/test/linear_quadratic_regulator_test.cc line 134 at r2 (raw file):

  const int nx = A.rows();
  const int nu = B.cols();
  // We first try to remove the constraint F*x=0 by considering a new slack

BTW Missing space around = (for consistency)


systems/controllers/test/linear_quadratic_regulator_test.cc line 149 at r2 (raw file):

    ASSERT_EQ(qr_F.info(), Eigen::Success);
    const Eigen::MatrixXd F_Q = qr_F.matrixQ();
    P = F_Q.rightCols(nx - qr_F.rank()).transpose();

BTW Consider exposing ComputerKernel in internal, which you could then re-use here.

@EricCousineau-TRI EricCousineau-TRI added the release notes: feature This pull request contains a new feature label Jun 9, 2022
Copy link
Contributor Author

@hongkai-dai hongkai-dai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 1 unresolved discussion (waiting on @RussTedrake)


systems/controllers/linear_quadratic_regulator.cc line 88 at r1 (raw file):

Previously, hongkai-dai (Hongkai Dai) wrote…

Done.

I added some documentation to explain it here.


systems/controllers/test/linear_quadratic_regulator_test.cc line 18 at r2 (raw file):

Previously, EricCousineau-TRI (Eric Cousineau) wrote…

BTW (pre-existing) For acronyms, style guide says to prefer TestLqr over TestLQR
https://drake.mit.edu/styleguide/cppguide.html#General_Naming_Rules

Done.


systems/controllers/test/linear_quadratic_regulator_test.cc line 124 at r2 (raw file):

Previously, EricCousineau-TRI (Eric Cousineau) wrote…

nit Not the most readable name.

Consider expanding to TestLQRwithHJB, or following style guide for acroynyms, TestLqrWithHjb

DOne.


systems/controllers/test/linear_quadratic_regulator_test.cc line 149 at r2 (raw file):

Previously, EricCousineau-TRI (Eric Cousineau) wrote…

BTW Consider exposing ComputerKernel in internal, which you could then re-use here.

For the moment I am hesitant to expose this function. The reason is that there are many approaches to compute the kernels, and here I use QR decomposition with column pivoting. One can also try other rank-revealing decomposition to compute the kernel. I put a TODO here to expose this function in the future. Would this be good?

Copy link
Contributor

@EricCousineau-TRI EricCousineau-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 3 of 3 files at r3, all commit messages.
Dismissed @RussTedrake from a discussion.
Reviewable status: :shipit: complete! all discussions resolved, LGTM from assignees EricCousineau-TRI(platform),RussTedrake(platform) (waiting on @hongkai-dai)


systems/controllers/test/linear_quadratic_regulator_test.cc line 149 at r2 (raw file):

Previously, hongkai-dai (Hongkai Dai) wrote…

For the moment I am hesitant to expose this function. The reason is that there are many approaches to compute the kernels, and here I use QR decomposition with column pivoting. One can also try other rank-revealing decomposition to compute the kernel. I put a TODO here to expose this function in the future. Would this be good?

OK Yup, works for me!

@EricCousineau-TRI EricCousineau-TRI merged commit 6e09df7 into RobotLocomotion:master Jun 9, 2022
@hongkai-dai hongkai-dai deleted the constrained_lqr branch July 19, 2022 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release notes: feature This pull request contains a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants