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

Adding lever-arm to GPS factors #1975

Merged
merged 10 commits into from
Jan 17, 2025
Merged

Conversation

mnissov
Copy link
Contributor

@mnissov mnissov commented Jan 13, 2025

I noticed the GPS factors had no option for a non-zero lever arm, the feature became necessary for me so I figured I would make this PR.

Of course similar behavior is possible with setBodyPSensor but this comes at the cost of additional linearization error I believe (ref). As a result the performance is actually different.

@mnissov
Copy link
Contributor Author

mnissov commented Jan 14, 2025

I see the macos-14-xcode-15.4 Release Python 3 check failed. This I have very little experience with, but in hindsight I see the serialization and interfacing were not appropriately updated.

Perhaps I should ask the following questions

  • Is this of interest for GTSAM?
  • Would you prefer the extrinsic added as an estimated state?
  • Do you have any tips for resolving the aforementioned failed check, or how I can test this locally. Because running make check succeeds across the board.

@dellaert
Copy link
Member

I’ll take a look and then answer your questions. If it can be done easily with another state then probably that is preferred.

Copy link
Member

@dellaert dellaert left a comment

Choose a reason for hiding this comment

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

I think this is great, and I think it should be added to a factor. But I propose to add a GpsFactor2 or GpsFactorArm in the same file, because the Jacobians are more expensive. Adding documentation of the nature I suggested in both the old and the new factor would make it easier to see the difference.

PS we will soon start adding notebooks to document factors in the source tree itself. Consider authoring a small colab that demonstrates both factors that we could include :-) That’s not needed for this PR to merge, though.

}

//***************************************************************************
Vector GPSFactor2::evaluateError(const NavState& p,
OptionalMatrixType H) const {
return p.position(H) -nT_;
const Matrix3 rot = p.attitude().matrix();
Copy link
Member

Choose a reason for hiding this comment

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

Please use aRb naming convention, where R transforms b coordinates into a coordinates.

@@ -39,6 +39,7 @@ class GTSAM_EXPORT GPSFactor: public NoiseModelFactorN<Pose3> {
typedef NoiseModelFactorN<Pose3> Base;

Point3 nT_; ///< Position measurement in cartesian coordinates
Point3 B_t_BG_; ///< Lever arm between GPS and BODY frame
Copy link
Member

Choose a reason for hiding this comment

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

The naming convention ‘nT_’ says that T is in the navigation frame. I think if you want to include a lever arm in this factor you need to include more documentation about the frames involved.

@@ -61,10 +64,12 @@ TEST( GPSFactor, Constructor ) {
// Factor
Key key(1);
SharedNoiseModel model = noiseModel::Isotropic::Sigma(3, 0.25);
GPSFactor factor(key, Point3(E, N, U), model);
GPSFactor factor(key, Point3(E, N, U), model, leverArm);
Copy link
Member

Choose a reason for hiding this comment

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

I would not expect you to change an existing test, but rather add an additional test with a non-zero lever arm.

gtsam/navigation/GPSFactor.cpp Show resolved Hide resolved
H->block<3, 3>(0, 6).setZero();
}

return p.position() - (nT_ - rot * B_t_BG_);
Copy link
Member

Choose a reason for hiding this comment

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

This formula is a bit counter-intuitive to me. I would expect to see $h(x)-z$, where $h(x)$ is the measurement prediction and $z$ the measurement, in this case nT_. If I rewrite your equation that way I get

h(x) = p.position() + rot * B_t_BG_

From which I deduce that rot==nRb which matches my definition of NavState, and B_t_BG_==bL_, a lever arm in the body frame. So, I think the documentation/notation I’m looking for is

bL_ is a lever arm in the body frame, denoting the 3D position of the GPS antenna in the body frame. Because the actual location of the antenna depends on both position and attitude, providing a lever arm makes components of the attitude observable in turns.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Lever arm is definitely in body frame, will adapt this to GTSAM format.

@mnissov
Copy link
Contributor Author

mnissov commented Jan 15, 2025

I think I have addressed all your points in the review with new factors GPSFactorArm and GPSFactor2Arm mirroring their namesakes but with an argument for lever arm.

I had some follow-up questions

  • Do you still want the lever-arm coded in as a state instead of as a constant? It would be pretty simple to do so from my side.
  • Regarding notebooks, is there an example of one I can look at? I assume it wouldn't be so hard for me to make one for this.
  • I've left out EstimateState when making GPSFactorArm as there is now some inconsistency about the nTb actually being nTg in a way. Of course this is correctable for the pose, less so for the velocity (missing angular rate I think).

Copy link
Member

@dellaert dellaert left a comment

Choose a reason for hiding this comment

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

This looks great ! You could probably save a bit of copy/pasta by inheriting from the non-arm factors, but that's entirely optional.

gtsam/navigation/GPSFactor.h Outdated Show resolved Hide resolved
gtsam/navigation/GPSFactor.h Outdated Show resolved Hide resolved
gtsam/navigation/GPSFactor.h Outdated Show resolved Hide resolved
gtsam/navigation/GPSFactor.h Show resolved Hide resolved
gtsam/navigation/GPSFactor.h Show resolved Hide resolved
gtsam/navigation/GPSFactor.h Outdated Show resolved Hide resolved
gtsam/navigation/GPSFactor.cpp Show resolved Hide resolved
@dellaert
Copy link
Member

I had some follow-up questions

  • Do you still want the lever-arm coded in as a state instead of as a constant? It would be pretty simple to do so from my side.

I would say that is a different factor, one that would incur a lot of computation, actually: this new state variable would connect all GPS factors. It would be useful in a calibration setting, but not for everyday use, if you already have a calibrated lever arm.

  • Regarding notebooks, is there an example of one I can look at? I assume it wouldn't be so hard for me to make one for this.

Sure: https://www.roboticsbook.org/S64_driving_perception.html, open in colab :-)

  • I've left out EstimateState when making GPSFactorArm as there is now some inconsistency about the nTb actually being nTg in a way. Of course this is correctable for the pose, less so for the velocity (missing angular rate I think).

I don't quite understand this :-)

@mnissov
Copy link
Contributor Author

mnissov commented Jan 16, 2025

This looks great ! You could probably save a bit of copy/pasta by inheriting from the non-arm factors, but that's entirely optional.

Thanks! I had considered inheritance, but I had some trouble with base classes and indirect bases. Plus I think inheritance only saves copy-pasting clone and measurementIn, the rest have, albeit, minor changes. So I didn't pursue this much further.

I don't quite understand this :-)

I'll try to explain it differently, suppose aPab is the position of {b} with respect to {a}, expressed in coordinate frame {a} (for coordinate frames {a} and {b}). If there is a lever arm, the measurement is really wPwg instead of wPwb for world frame {w}, GPS antenna {g}, and body frame {b}.

Therefore EstimateState, if there is a nonzero lever arm, calculates not velocity wVwb but instead wVwg. So if the lever arm is large, while rotating there can be a significant difference. In addition, rotating about {b} means that the position of {g} will change, but not the position of {b}. The position difference can be corrected I think, but the velocity not without knowing the angular rate as well.

This is what I was trying to say. I hope this makes a little more sense.

@dellaert
Copy link
Member

This looks great ! You could probably save a bit of copy/pasta by inheriting from the non-arm factors, but that's entirely optional.

Thanks! I had considered inheritance, but I had some trouble with base classes and indirect bases. Plus I think inheritance only saves copy-pasting clone and measurementIn, the rest have, albeit, minor changes. So I didn't pursue this much further.

Agreed !

I don't quite understand this :-)
This is what I was trying to say. I hope this makes a little more sense.

Yep, I think so :-)

So, this is ready to merge after CI succeeds, yes? I propose we do a notebook in a follow-up PR, if you're game. In fact, we're soon going to add a mechanism to GTSAM to check these in and they will be automatically served. And, we can featsure them as blog posts on gtsam.org, as well.

@mnissov
Copy link
Contributor Author

mnissov commented Jan 16, 2025

Sorry about that, I was a bit slow but I added a factor with the lever arm as a state as well. Ended up calling it GPSFactorArmCalib, which is a bit verbose but I'm struggling to find something else :).

So, this is ready to merge after CI succeeds, yes?

Otherwise yes, should be all good from my side.

I propose we do a notebook in a follow-up PR, if you're game. In fact, we're soon going to add a mechanism to GTSAM to check these in and they will be automatically served. And, we can featsure them as blog posts on gtsam.org, as well.

Yeah I am down for that. The model for this is a numerical example and a bit of description/story-telling?

@dellaert dellaert merged commit 717e3be into borglab:develop Jan 17, 2025
33 checks passed
@dellaert
Copy link
Member

Thanks @mnissov !!! Will be quite useful, makes orientation observable if you have two receivers, as well :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants