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

Casadi meme #1682

Draft
wants to merge 88 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
881cc52
sleipnir meme
mcm001 Dec 13, 2024
eb6660b
Create casadi_meme
mcm001 Dec 13, 2024
63138a5
run black
mcm001 Dec 13, 2024
b5c939a
make robot2cam dynamic
mcm001 Dec 13, 2024
3f2e555
Use vectors for vectors. what a concept
mcm001 Dec 13, 2024
8efa7d9
sadly doesn't quite work
mcm001 Dec 13, 2024
86ef4c5
ayo??
mcm001 Dec 14, 2024
a0dd988
add benchmark
mcm001 Dec 14, 2024
2511d31
create cmakelists
mcm001 Dec 14, 2024
acd0621
Run benchmarks
mcm001 Dec 14, 2024
3759ae9
yay working C++ optimizer!
mcm001 Dec 15, 2024
6e42ffe
add diagnostic prints
mcm001 Dec 15, 2024
b5f7b03
Tomorrow Problem
mcm001 Dec 15, 2024
cc57f83
de-cringed cmakelists
Bankst Dec 15, 2024
fac3898
static lib for now, formatting, ez tag count change
Bankst Dec 15, 2024
d759e7d
2 tags
Bankst Dec 15, 2024
879a01f
3 tags works
Bankst Dec 15, 2024
35826b5
Fix cost function argument in line search
calcmogul Dec 16, 2024
dee5c78
generate 1-10 tags
Dec 17, 2024
c1b53cc
Regularization notes
mcm001 Dec 22, 2024
08efa65
Split into own file
mcm001 Jan 5, 2025
d4e1f52
foobar
mcm001 Jan 5, 2025
131eeca
actually fix things
mcm001 Jan 5, 2025
f428830
Output to photon-lib
mcm001 Jan 5, 2025
af75714
Generate casadi_meme.c
mcm001 Jan 5, 2025
8a40b64
Regenerate casadi_meme
mcm001 Jan 5, 2025
a1a15ed
Builds with gradle!
mcm001 Jan 5, 2025
f0db313
include .c files
mcm001 Jan 5, 2025
162526d
Fix c source set
mcm001 Jan 5, 2025
7b64719
change c sets
mcm001 Jan 5, 2025
d6b676f
Proof of concept macro hackery
mcm001 Jan 5, 2025
f0eeaf7
Refactor casadi_wrapper
mcm001 Jan 6, 2025
21c66dc
Delete license
mcm001 Jan 6, 2025
92989e3
Clean up api
mcm001 Jan 6, 2025
a6061a7
actually pass heading down
mcm001 Jan 7, 2025
bbc9608
ayyy
mcm001 Jan 7, 2025
164824e
Spam typename
mcm001 Jan 7, 2025
1c82a4d
Move code into test
mcm001 Jan 7, 2025
ae677b2
Delete extra tests (revert)
mcm001 Jan 7, 2025
4f358ef
Fix copy auto mixup
mcm001 Jan 7, 2025
7f7e1c0
Add prints and use wpi expected
mcm001 Jan 7, 2025
0f3c15b
Run lint
mcm001 Jan 7, 2025
ca2a356
Fix test format
mcm001 Jan 7, 2025
b26a014
Memoize regularization delta
mcm001 Jan 7, 2025
218c0d1
Build for rio
mcm001 Jan 7, 2025
b36b404
Final testing on-rio
mcm001 Jan 7, 2025
c7728cd
Move files
mcm001 Jan 7, 2025
d59b5dc
ASDF
mcm001 Jan 7, 2025
8aa2ae3
JNI compiles now
mcm001 Jan 7, 2025
b3f49fe
working JNI!
mcm001 Jan 7, 2025
ffff7d4
add comment
mcm001 Jan 7, 2025
66b5634
Mostly add to visionestimation
mcm001 Jan 7, 2025
66f2a4f
asdf
mcm001 Jan 7, 2025
28b27bc
ayyy kinda
mcm001 Jan 7, 2025
6a00c8a
its so bad
mcm001 Jan 7, 2025
563d490
Fix typo and add prints
mcm001 Jan 7, 2025
5d4efae
Add another macro
mcm001 Jan 7, 2025
ad0eb1a
Add iterative refinement
mcm001 Jan 8, 2025
259c365
guh
mcm001 Jan 8, 2025
b30dc8f
Increase error tolerance, pull in sleipnir conditioning
mcm001 Jan 9, 2025
92ccf11
More tweaks
mcm001 Jan 9, 2025
8b5bebf
More memes
mcm001 Jan 10, 2025
620a53b
run lint
mcm001 Jan 10, 2025
3fbe07e
hack changes in
mcm001 Jan 10, 2025
6eb9268
Add gyro cost
mcm001 Jan 10, 2025
0ecbac0
Pipe new tag costs in
mcm001 Jan 10, 2025
b643b29
hack things in
mcm001 Jan 10, 2025
8515fe6
Need to go allow heading to vary in fixed
mcm001 Jan 10, 2025
c8c3192
whoops
mcm001 Jan 10, 2025
a65ba76
remove prints
mcm001 Jan 10, 2025
04c953f
add print to robot project
mcm001 Jan 10, 2025
e0f85a2
delete extra files
mcm001 Jan 10, 2025
1a0028a
Stuffs
mcm001 Jan 11, 2025
0c54c7a
final tweaks for tn
mcm001 Jan 11, 2025
5da63a3
rename to CONSTRAINED_SOLVEPNP
spacey-sooty Jan 11, 2025
6e76191
whippyformat
spacey-sooty Jan 11, 2025
6ca979e
Kill test that runs forever
mcm001 Jan 12, 2025
8b235af
Delete old main.cpp
mcm001 Jan 12, 2025
73e3212
run lint
mcm001 Jan 12, 2025
792f803
rename all the things
mcm001 Jan 12, 2025
b995414
or maybe actually all the things
mcm001 Jan 12, 2025
c09c87b
Oops
mcm001 Jan 12, 2025
747e952
restore tests
mcm001 Jan 12, 2025
e8d4c4e
revert more changes
mcm001 Jan 12, 2025
3753ed7
update javadocs again
mcm001 Jan 12, 2025
188b49b
fix test oops
mcm001 Jan 12, 2025
1e050e2
Oops
mcm001 Jan 12, 2025
95e3330
Run lint
mcm001 Jan 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .styleguide
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ modifiableFileExclude {
\.rknn$
gradlew
photon-lib/py/photonlibpy/generated/
photon-targeting/src/main/native/cpp/photon/casadi_meme/generate/
photon-targeting/src/generated/
}

Expand Down
158 changes: 153 additions & 5 deletions photon-lib/src/main/java/org/photonvision/PhotonPoseEstimator.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import edu.wpi.first.math.Pair;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Transform3d;
import edu.wpi.first.math.geometry.Translation3d;
Expand Down Expand Up @@ -83,9 +84,32 @@ public enum PoseStrategy {
* Use all visible tags to compute a single pose estimate. This runs on the RoboRIO, and can
* take a lot of time.
*/
MULTI_TAG_PNP_ON_RIO
MULTI_TAG_PNP_ON_RIO,

/**
* Solve a constrained version of the Perspective-n-Point problem with the robot's drivebase
* flat on the floor. This computation takes place on the RoboRIO, and typically takes not more
* than 2ms. See {@link PhotonPoseEstimator.ConstrainedSolvepnpParams} and {@link
* org.photonvision.jni.ConstrainedSolvepnpJni} for details and tuning handles this strategy
* exposes.
*/
CONSTRAINED_SOLVEPNP
}

/**
* Tuning handles we have over the CONSTRAINED_SOLVEPNP {@link PhotonPoseEstimator.PoseStrategy}.
* Internally, the cost function is a sum-squared of pixel reprojection error + (optionally)
* heading error * heading scale factor.
*
* @param headingFree If true, heading is completely free to vary. If false, heading excursions
* from the provided heading measurement will be penalized
* @param headingMeasurement The current heading estimate
* @param headingScaleFactor If headingFree is false, this weights the cost of changing our robot
* heading estimate against the tag corner reprojection error const.
*/
public static final record ConstrainedSolvepnpParams(
boolean headingFree, Rotation2d headingMeasurement, double headingScaleFactor) {}

private AprilTagFieldLayout fieldTags;
private TargetModel tagModel = TargetModel.kAprilTag36h11;
private PoseStrategy primaryStrategy;
Expand Down Expand Up @@ -304,6 +328,8 @@ public Optional<EstimatedRobotPose> update(PhotonPipelineResult cameraResult) {
* <li>The timestamp of the provided pipeline result is the same as in the previous call to
* {@code update()}.
* <li>No targets were found in the pipeline results.
* <li>The strategy is CONSTRAINED_SOLVEPNP, but no constrainedPnpParams were provided (use the
* other function overload).
* </ul>
*
* @param cameraMatrix Camera calibration data for multi-tag-on-rio strategy - can be empty
Expand All @@ -317,6 +343,33 @@ public Optional<EstimatedRobotPose> update(
PhotonPipelineResult cameraResult,
Optional<Matrix<N3, N3>> cameraMatrix,
Optional<Matrix<N8, N1>> distCoeffs) {
return update(cameraResult, cameraMatrix, distCoeffs, null);
}

/**
* Updates the estimated position of the robot. Returns empty if:
*
* <ul>
* <li>The timestamp of the provided pipeline result is the same as in the previous call to
* {@code update()}.
* <li>No targets were found in the pipeline results.
* <li>The strategy is CONSTRAINED_SOLVEPNP, but the provided constrainedPnpParams are null.
* </ul>
*
* @param cameraMatrix Camera calibration data for multi-tag-on-rio strategy - can be empty
* otherwise
* @param distCoeffs Camera calibration data for multi-tag-on-rio strategy - can be empty
* otherwise
* @param constrainedPnpParams Constrained SolvePNP params, if needed. May be null if the strategy
* is not CONSTRAINED_SOLVEPNP
* @return an {@link EstimatedRobotPose} with an estimated pose, timestamp, and targets used to
* create the estimate.
*/
public Optional<EstimatedRobotPose> update(
PhotonPipelineResult cameraResult,
Optional<Matrix<N3, N3>> cameraMatrix,
Optional<Matrix<N8, N1>> distCoeffs,
ConstrainedSolvepnpParams constrainedPnpParams) {
// Time in the past -- give up, since the following if expects times > 0
if (cameraResult.getTimestampSeconds() < 0) {
return Optional.empty();
Expand All @@ -338,13 +391,15 @@ public Optional<EstimatedRobotPose> update(
return Optional.empty();
}

return update(cameraResult, cameraMatrix, distCoeffs, this.primaryStrategy);
return update(
cameraResult, cameraMatrix, distCoeffs, constrainedPnpParams, this.primaryStrategy);
}

private Optional<EstimatedRobotPose> update(
PhotonPipelineResult cameraResult,
Optional<Matrix<N3, N3>> cameraMatrix,
Optional<Matrix<N8, N1>> distCoeffs,
ConstrainedSolvepnpParams meme,
PoseStrategy strategy) {
Optional<EstimatedRobotPose> estimatedPose =
switch (strategy) {
Expand Down Expand Up @@ -373,6 +428,26 @@ private Optional<EstimatedRobotPose> update(
}
}
case MULTI_TAG_PNP_ON_COPROCESSOR -> multiTagOnCoprocStrategy(cameraResult);
case CONSTRAINED_SOLVEPNP -> {
if (cameraMatrix.isEmpty()) {
DriverStation.reportWarning(
"Camera matrix is empty for Constrained SolvePNP",
Thread.currentThread().getStackTrace());
yield Optional.empty();
} else if (distCoeffs.isEmpty()) {
DriverStation.reportWarning(
"Camera matrix is empty for Constrained SolvePNP",
Thread.currentThread().getStackTrace());
yield Optional.empty();
} else if (meme == null) {
DriverStation.reportWarning(
"Metadata is empty for Constrained SolvePNP",
Thread.currentThread().getStackTrace());
yield Optional.empty();
} else {
yield constrainedPnpStrategy(cameraResult, cameraMatrix, distCoeffs, meme);
}
}
};

if (estimatedPose.isPresent()) {
Expand All @@ -382,6 +457,78 @@ private Optional<EstimatedRobotPose> update(
return estimatedPose;
}

private Optional<EstimatedRobotPose> constrainedPnpStrategy(
PhotonPipelineResult result,
Optional<Matrix<N3, N3>> cameraMatrixOpt,
Optional<Matrix<N8, N1>> distCoeffsOpt,
ConstrainedSolvepnpParams meme) {
boolean hasCalibData = cameraMatrixOpt.isPresent() && distCoeffsOpt.isPresent();
// cannot run multitagPNP, use fallback strategy
if (!hasCalibData) {
return update(result, cameraMatrixOpt, distCoeffsOpt, null, this.multiTagFallbackStrategy);
}

// Need heading if heading fixed
if (!meme.headingFree && meme.headingMeasurement == null) {
return update(result, cameraMatrixOpt, distCoeffsOpt, null, this.multiTagFallbackStrategy);
}

Pose3d fieldToRobotSeed;

// Attempt to use multi-tag to get a pose estimate seed
if (result.getMultiTagResult().isPresent()) {
fieldToRobotSeed =
new Pose3d()
.plus(
result
.getMultiTagResult()
.get()
.estimatedPose
.best
.plus(robotToCamera.inverse()));
} else {
// HACK - use fallback strategy to gimme a seed pose
// TODO - make sure nested update doesn't break state
var nestedUpdate =
update(result, cameraMatrixOpt, distCoeffsOpt, null, this.multiTagFallbackStrategy);
if (nestedUpdate.isEmpty()) {
// best i can do is bail
return Optional.empty();
}
fieldToRobotSeed = nestedUpdate.get().estimatedPose;
}

if (!meme.headingFree) {
// If heading fixed, force rotation component
fieldToRobotSeed =
new Pose3d(fieldToRobotSeed.getTranslation(), new Rotation3d(meme.headingMeasurement));
}

var pnpResult =
VisionEstimation.estimateRobotPoseConstrainedSolvepnp(
cameraMatrixOpt.get(),
distCoeffsOpt.get(),
result.getTargets(),
robotToCamera,
fieldToRobotSeed,
fieldTags,
tagModel,
meme.headingFree,
meme.headingMeasurement,
meme.headingScaleFactor);
// try fallback strategy if solvePNP fails for some reason
if (!pnpResult.isPresent())
return update(result, cameraMatrixOpt, distCoeffsOpt, null, this.multiTagFallbackStrategy);
var best = new Pose3d().plus(pnpResult.get().best); // field-to-robot

return Optional.of(
new EstimatedRobotPose(
best,
result.getTimestampSeconds(),
result.getTargets(),
PoseStrategy.CONSTRAINED_SOLVEPNP));
}

private Optional<EstimatedRobotPose> multiTagOnCoprocStrategy(PhotonPipelineResult result) {
if (result.getMultiTagResult().isPresent()) {
var best_tf = result.getMultiTagResult().get().estimatedPose.best;
Expand All @@ -398,7 +545,8 @@ private Optional<EstimatedRobotPose> multiTagOnCoprocStrategy(PhotonPipelineResu
PoseStrategy.MULTI_TAG_PNP_ON_COPROCESSOR));
} else {
// We can never fall back on another multitag strategy
return update(result, Optional.empty(), Optional.empty(), this.multiTagFallbackStrategy);
return update(
result, Optional.empty(), Optional.empty(), null, this.multiTagFallbackStrategy);
}
}

Expand All @@ -409,15 +557,15 @@ private Optional<EstimatedRobotPose> multiTagOnRioStrategy(
boolean hasCalibData = cameraMatrixOpt.isPresent() && distCoeffsOpt.isPresent();
// cannot run multitagPNP, use fallback strategy
if (!hasCalibData || result.getTargets().size() < 2) {
return update(result, cameraMatrixOpt, distCoeffsOpt, this.multiTagFallbackStrategy);
return update(result, cameraMatrixOpt, distCoeffsOpt, null, this.multiTagFallbackStrategy);
}

var pnpResult =
VisionEstimation.estimateCamPosePNP(
cameraMatrixOpt.get(), distCoeffsOpt.get(), result.getTargets(), fieldTags, tagModel);
// try fallback strategy if solvePNP fails for some reason
if (!pnpResult.isPresent())
return update(result, cameraMatrixOpt, distCoeffsOpt, this.multiTagFallbackStrategy);
return update(result, cameraMatrixOpt, distCoeffsOpt, null, this.multiTagFallbackStrategy);
var best =
new Pose3d()
.plus(pnpResult.get().best) // field-to-camera
Expand Down
Loading
Loading