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

[photon-lib] Sim multitag result #973

Merged
merged 7 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,8 @@ photonlib-java-examples/*/vendordeps/*
photonlib-cpp-examples/*/vendordeps/*

*/networktables.json
*/networktables.json.bck
photonlib-cpp-examples/*/networktables.json.bck
photonlib-java-examples/*/networktables.json.bck
*.sqlite
photon-server/src/main/resources/web/index.html
24 changes: 20 additions & 4 deletions photon-lib/src/main/java/org/photonvision/PhotonCamera.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.wpi.first.apriltag.AprilTagFieldLayout;
import edu.wpi.first.apriltag.AprilTagFields;
import edu.wpi.first.math.MatBuilder;
import edu.wpi.first.math.MathSharedStore;
import edu.wpi.first.math.Matrix;
Expand Down Expand Up @@ -121,6 +122,9 @@ public static void setVersionCheckEnabled(boolean enabled) {

Packet packet = new Packet(1);

private static AprilTagFieldLayout lastSetTagLayout =
AprilTagFields.kDefaultField.loadAprilTagLayoutField();

// Existing is enough to make this multisubscriber do its thing
private final MultiSubscriber m_topicNameSubscriber;

Expand Down Expand Up @@ -332,20 +336,32 @@ public boolean isConnected() {
}

/**
* Set the Apriltag Field Layout used by all PhotonVision coprocessors that are (or might later)
* connect to this robot. The topic is marked as persistant, so even if you only call this once
* ever, it will be saved on the RoboRIO and pushed out to all NT clients when code reboots.
* Get the last set {@link AprilTagFieldLayout}. The tag layout is shared between all PhotonVision
* coprocessors on the same NT instance-- this method returns the most recent layout set. If no
* layout has been set by the user, this is equal to {@link AprilTagFields#kDefaultField}.
*
* @return The last set layout
*/
public AprilTagFieldLayout getAprilTagFieldLayout() {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is kinda weird... I'm not sure of a better way to do this since we need the ATFL to calculate the multitag result. This does not check what layout the coprocessor has, just the last one set from user code. All coprocessors on a NT instance share the same tag layout (and updating the layout through one camera should mean the others also can get that updated layout), but this uses a static field and technically assumes all coprocessors are on the same NT instance.

It could exist in PhotonCameraSim instead, but that would require code duplication specifically for sim when setting a tag layout over NT.

The field could also be accessed directly and the getter method removed, but that would require PhotonCameraSim to be in the same package as PhotonCamera.

return lastSetTagLayout;
}

/**
* Set the {@link AprilTagFieldLayout} used by all PhotonVision coprocessors that are (or might
* later) connect to this robot. The topic is marked as persistent, so even if you only call this
* once ever, it will be saved on the RoboRIO and pushed out to all NT clients when code reboots.
* PhotonVision will also store a copy of this layout locally on the coprocessor, but subscribes
* to this topic and the local copy will be updated when this function is called.
*
* @param layout The layout to use for *all* PhotonVision cameras
* @return Success of serializing the JSON. This does *not* mean that all PhotonVision clients
* have updated their internal layouts.
*/
public boolean setApriltagFieldLayout(AprilTagFieldLayout layout) {
public boolean setAprilTagFieldLayout(AprilTagFieldLayout layout) {
try {
var layout_json = new ObjectMapper().writeValueAsString(layout);
atflPublisher.set(layout_json);
lastSetTagLayout = layout;
return true;
} catch (JsonProcessingException e) {
MathSharedStore.reportError("Error setting ATFL in " + this.getName(), e.getStackTrace());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
Expand All @@ -52,6 +53,8 @@
import org.photonvision.estimation.OpenCVHelp;
import org.photonvision.estimation.RotTrlTransform3d;
import org.photonvision.estimation.TargetModel;
import org.photonvision.estimation.VisionEstimation;
import org.photonvision.targeting.MultiTargetPNPResults;
import org.photonvision.targeting.PNPResults;
import org.photonvision.targeting.PhotonPipelineResult;
import org.photonvision.targeting.PhotonTrackedTarget;
Expand Down Expand Up @@ -524,11 +527,26 @@ public PhotonPipelineResult process(
videoSimProcessed.putFrame(videoSimFrameProcessed);
} else videoSimProcessed.setConnectionStrategy(ConnectionStrategy.kForceClose);

// put this simulated data to NT
// calculate multitag results
var multitagResults = new MultiTargetPNPResults();
var tagLayout = cam.getAprilTagFieldLayout();
var visibleLayoutTags = VisionEstimation.getVisibleLayoutTags(detectableTgts, tagLayout);
if (visibleLayoutTags.size() > 1) {
List<Integer> usedIDs =
visibleLayoutTags.stream().map(t -> t.ID).sorted().collect(Collectors.toList());
var pnpResults =
VisionEstimation.estimateCamPosePNP(
prop.getIntrinsics(), prop.getDistCoeffs(), detectableTgts, tagLayout);
multitagResults = new MultiTargetPNPResults(pnpResults, usedIDs);
}

// sort target order
if (sortMode != null) {
detectableTgts.sort(sortMode.getComparator());
}
return new PhotonPipelineResult(latencyMillis, detectableTgts);

// put this simulated data to NT
return new PhotonPipelineResult(latencyMillis, detectableTgts, multitagResults);
}

/**
Expand Down