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

AprilTag ID to Pose API #4421

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
28f447d
First attempt at apriltag id-pose API
brennenputh Sep 17, 2022
c9a0ed1
Merge branch 'main' into feature/apriltag-id-to-pose
brennenputh Sep 17, 2022
442b7be
Implement pose mirroring on C++ side
brennenputh Sep 17, 2022
1b23a91
Fix longhand typing for some variables
brennenputh Sep 17, 2022
908d086
Fix RelativeTo call in GetTagPose
brennenputh Sep 17, 2022
cf3537d
Fix SetShouldMirror function
brennenputh Sep 17, 2022
ada36d1
Change capitalization of Apriltag to AprilTag
brennenputh Sep 17, 2022
ad1c97c
Add test for serialization and deserialization of Apriltags
brennenputh Sep 17, 2022
fd64362
Create JSON serialization test for Java
brennenputh Sep 18, 2022
1a6b894
Make Java side of AprilTagFieldLayout less interconnected
brennenputh Sep 18, 2022
b8b85c0
Move AprilTagFieldLayout and AprilTagUtil java files to wpilibj
brennenputh Sep 18, 2022
ca927e2
Decouple AprilTag serialization/deserialization from AprilTagFieldLay…
brennenputh Sep 18, 2022
06b5c64
Move AprilTagFieldLayout.cpp to WPILibC
brennenputh Sep 18, 2022
33bc5b7
Fix alliance set function on C++ AprilTagFieldLayout
brennenputh Sep 18, 2022
7dd88b0
Minimize imports
brennenputh Sep 18, 2022
908a111
Use proper unit conversion functions
brennenputh Sep 18, 2022
e1f7e24
Merge branch 'main' into feature/apriltag-id-to-pose
brennenputh Sep 18, 2022
bfac873
Fix gradle check task
brennenputh Sep 18, 2022
b517215
Run wpiformat
brennenputh Sep 18, 2022
1a3432e
Fix some docs
brennenputh Sep 18, 2022
e14ef1e
Run javaFormat
brennenputh Sep 18, 2022
1fd8b42
Fix link tags in javadocs
brennenputh Sep 18, 2022
640a859
Resolve clang-tidy warnings
brennenputh Sep 18, 2022
82cf25b
Fix checkstyle, line over 100 chars
brennenputh Sep 18, 2022
874ee26
Change GetTagPose to be more idiomatic
brennenputh Sep 19, 2022
8619734
Remove GetTags from AprilTagFieldLayout C++
brennenputh Sep 19, 2022
1c020df
Clean up the Java AprilTagFieldLayout
brennenputh Sep 20, 2022
279b0f4
Remove leftover function declaration
brennenputh Sep 20, 2022
6ab6c44
Move FromJson and ToJson methods to AprilTagFieldLayout
brennenputh Sep 22, 2022
15fa827
TESTS BROKEN (large refactor part 1)
brennenputh Sep 23, 2022
a691ebc
PART 2 OF POSSIBLY MANY
brennenputh Sep 24, 2022
6e7ab6e
Make general improvements
brennenputh Sep 24, 2022
d983d8e
A single asterisk
brennenputh Sep 24, 2022
ff77fbc
Initialize default value for m_mirror, fixes tests
brennenputh Sep 25, 2022
14bafe0
Remove JNI and use Jackson instead, make tests consistent between C++…
brennenputh Sep 25, 2022
8fe658b
Fix check
brennenputh Sep 25, 2022
5ca50c7
Merge branch 'main' into feature/apriltag-id-to-pose
brennenputh Sep 25, 2022
9f2179d
Remove AprilTagUtil
brennenputh Sep 25, 2022
12ebbab
Serialize Rotation3d as Quaternion
brennenputh Sep 26, 2022
7d5ba74
Run wpiformat and spotlessApply
brennenputh Sep 26, 2022
dd03821
Make to and from json methods friend functions
brennenputh Sep 26, 2022
1d6f560
Rename function to be consistent with Java
brennenputh Sep 26, 2022
b31bc5a
Run wpiformat
brennenputh Sep 26, 2022
8c6201b
Remove exteraneous imports, add DLLEXPORT where needed, and remove un…
brennenputh Sep 26, 2022
b731467
Add degreesToRadians
brennenputh Sep 26, 2022
77d07e6
Run wpiformat
brennenputh Sep 26, 2022
6e38d9d
Extra consistency
brennenputh Sep 26, 2022
2815f69
Add doxygen docs to AprilTagFieldLayout
brennenputh Sep 26, 2022
b5528f3
Run wpiformat
brennenputh Sep 26, 2022
97892d3
Add AprilTagPoseMirroringTest and format AprilTagFieldLayout
brennenputh Sep 26, 2022
e6675cf
Remove unused includes
brennenputh Sep 26, 2022
87fb00f
Fix docs formatting
brennenputh Sep 27, 2022
4ddd39c
Remove move semantics from constructor, use copy semantics
brennenputh Sep 27, 2022
7eb6bc1
One more docs pass
brennenputh Sep 27, 2022
da26b32
Fix up docs
brennenputh Sep 27, 2022
0f6e9e7
Make Java and C++ docs more consistent and add class doc
brennenputh Sep 27, 2022
a60f3ab
Re-add const to constructor delaration
brennenputh Sep 27, 2022
038c32f
Revert last commit
brennenputh Sep 27, 2022
6a4ab7a
Continue id to ID change
brennenputh Sep 27, 2022
093d632
Pass by value and move
brennenputh Sep 27, 2022
fd2907b
Add warning suppression
brennenputh Sep 27, 2022
ad28a61
Clean up includes
brennenputh Sep 27, 2022
7e1e506
Run wpiformat
brennenputh Sep 27, 2022
477081f
Add missing include and forward declaration
calcmogul Sep 28, 2022
3a92d68
Add field descriptor object C++
brennenputh Sep 29, 2022
97f7989
Add field size descriptor to Java
brennenputh Sep 29, 2022
fcf9f88
Change to meters units C++
brennenputh Sep 29, 2022
db70d06
Switch units to meters and change FieldSize to FieldDimensions Java
brennenputh Sep 29, 2022
6c0670e
Remove FieldDimensions from public interface
brennenputh Sep 29, 2022
65e03cf
Suppress warnings properly
brennenputh Sep 29, 2022
348670e
Remove debug code
brennenputh Sep 30, 2022
b09c476
Merge branch 'main' into feature/apriltag-id-to-pose
AMereBagatelle Oct 21, 2022
9b1e261
Merge branch 'main' into feature/apriltag-id-to-pose
brennenputh Oct 31, 2022
1e087a4
Change getTagPose method to remove streams and return an Optional
brennenputh Nov 3, 2022
9e4f238
Change to use std::optional
brennenputh Nov 4, 2022
2001cd2
Change to use std::optional
brennenputh Nov 4, 2022
e5fed5b
Fix accidental include
brennenputh Nov 4, 2022
fcea333
Update docs
brennenputh Nov 4, 2022
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
16 changes: 16 additions & 0 deletions wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,22 @@ public static native void discreteAlgebraicRiccatiEquation(
public static native void rankUpdate(
double[] mat, int rows, double[] vec, double sigma, boolean lowerTriangular);

/**
* Deserializes an Apriltag layout JSON into a double[] of elements.
*
* @param json The JSON containing the serialized trajectory.
* @return A double array with the trajectory states.
*/
public static native double[] deserializeApriltagLayout(String json);

/**
* Serializes the Apriltag layout into a JSON string.
*
* @param elements The elements of the layout.
* @return A JSON containing the serialized layout.
*/
public static native String serializeApriltagLayout(double[] elements);

public static class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package edu.wpi.first.math.apriltag;

import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Translation3d;

import java.util.HashMap;
import java.util.Map;

public class ApriltagFieldLayout {
private final Map<Integer, Pose3d> m_tags = new HashMap<>();
private boolean m_mirror = false;

public ApriltagFieldLayout(Map<Integer, Pose3d> tags) {
// To ensure the underlying semantics don't change with what kind of map is passed in
m_tags.putAll(tags);
}

public void setShouldMirror(boolean mirror) {
m_mirror = mirror;
}

public Map<Integer, Pose3d> getTags() {
return m_tags;
}

public Pose3d getTag(int id) {
Pose3d tag = m_tags.get(id);
if(m_mirror) {
tag = tag.relativeTo(new Pose3d(new Translation3d(16.4592, 8.2296, 0), new Rotation3d(0, 0, 180)));
}
return tag;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package edu.wpi.first.math.apriltag;

import edu.wpi.first.math.WPIMathJNI;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Quaternion;
import edu.wpi.first.math.geometry.Rotation3d;

import java.util.HashMap;
import java.util.Map;

public class ApriltagUtil {
/**
* Creates an Apriltag layout from a double[] of elements.
*
* @param elements A double[] containing the raw elements of the Apriltag layout.
* @return An Apriltag layout created from the elements.
*/
private static ApriltagFieldLayout createApriltagFieldLayoutFromElements(double[] elements) {
// Make sure that the elements have the correct length.
if (elements.length % 7 != 0) {
throw new ApriltagLayoutSerializationException(
"An error occurred when converting trajectory elements into a trajectory.");
}

// Create a list of states from the elements.
Map<Integer, Pose3d> apriltagLayout = new HashMap<>();
for (int i = 0; i < elements.length; i += 7) {
apriltagLayout.put(
(int) elements[i],
new Pose3d(
elements[i + 1],
elements[i + 2],
elements[i + 3],
new Rotation3d(new Quaternion(
elements[i + 4],
elements[i + 5],
elements[i + 6],
elements[i + 7]
))
)
);
}
return new ApriltagFieldLayout(apriltagLayout);
}

/**
* Returns a double[] of elements from the given Apriltag layout.
*
* @param apriltagFieldLayout The Apriltag field layout to retrieve raw elements from.
* @return A double[] of elements from the given trajectory.
*/
private static double[] getElementsFromApriltagFieldLayout(ApriltagFieldLayout apriltagFieldLayout) {
// Create a double[] of elements and fill it from the trajectory states.
double[] elements = new double[apriltagFieldLayout.getTags().size() * 7];

for (int i = 0; i < apriltagFieldLayout.getTags().size() * 7; i += 7) {
var entry = apriltagFieldLayout.getTags().entrySet().stream().toList().get(i / 7);
elements[i] = entry.getKey();
elements[i + 1] = entry.getValue().getX();
elements[i + 2] = entry.getValue().getY();
elements[i + 3] = entry.getValue().getZ();
elements[i + 4] = entry.getValue().getRotation().getQuaternion().getW();
elements[i + 5] = entry.getValue().getRotation().getQuaternion().getX();
elements[i + 6] = entry.getValue().getRotation().getQuaternion().getY();
elements[i + 7] = entry.getValue().getRotation().getQuaternion().getZ();
}
return elements;
}

public static ApriltagFieldLayout deserializeApriltagFieldLayout(String json) {
return createApriltagFieldLayoutFromElements(WPIMathJNI.deserializeApriltagLayout(json));
}

public static String serializeApriltagFieldLayout(ApriltagFieldLayout apriltagFieldLayout) {
return WPIMathJNI.serializeApriltagLayout(getElementsFromApriltagFieldLayout(apriltagFieldLayout));
}

public static class ApriltagLayoutSerializationException extends RuntimeException {
public ApriltagLayoutSerializationException(String message) {
super(message);
}
}
}
37 changes: 37 additions & 0 deletions wpimath/src/main/native/cpp/apriltag/ApriltagFieldLayout.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

#include "frc/apriltag/ApriltagFieldLayout.h"

#include <wpi/json.h>
#include "frc/geometry/Pose3d.h"
#include "units/angle.h"
#include "units/length.h"

using namespace frc;

frc::Pose3d ApriltagFieldLayout::GetTagPose(int id) const {
auto returnPose = Pose3d{};
brennenputh marked this conversation as resolved.
Show resolved Hide resolved
for(Apriltag tag : m_apriltags) {
brennenputh marked this conversation as resolved.
Show resolved Hide resolved
if(tag.id == id) {
returnPose = tag.pose;
}
}
if(m_mirror) {
returnPose = returnPose.RelativeTo(Pose3d{units::inch_t{54}, units::inch_t{27}, returnPose.Z(), Rotation3d{units::radian_t{0}, units::radian_t{0}, units::radian_t{180}}});
brennenputh marked this conversation as resolved.
Show resolved Hide resolved
}
return returnPose;
}

void frc::to_json(wpi::json& json, const ApriltagFieldLayout::Apriltag& apriltag) {
json = wpi::json{
{"id", apriltag.id},
{"pose", apriltag.pose}
};
}

void frc::from_json(const wpi::json& json, ApriltagFieldLayout::Apriltag& apriltag) {
apriltag.id = json.at("id").get<int>();
apriltag.pose = json.at("pose").get<Pose3d>();
}
54 changes: 54 additions & 0 deletions wpimath/src/main/native/cpp/apriltag/ApriltagUtil.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

#include "frc/apriltag/ApriltagUtil.h"

#include <system_error>
#include "frc/apriltag/ApriltagFieldLayout.h"

#include <fmt/format.h>
#include <wpi/SmallString.h>
#include <wpi/json.h>
#include <wpi/raw_istream.h>
#include <wpi/raw_ostream.h>

using namespace frc;

void ApriltagUtil::ToJson(const ApriltagFieldLayout& apriltagFieldLayout,
std::string_view path) {
std::error_code error_code;

wpi::raw_fd_ostream output{path, error_code};
if (error_code) {
throw std::runtime_error(fmt::format("Cannot open file: {}", path));
}

wpi::json json = apriltagFieldLayout.GetTags();
output << json;
output.flush();
}

ApriltagFieldLayout ApriltagUtil::FromJson(std::string_view path) {
std::error_code error_code;

wpi::raw_fd_istream input{path, error_code};
if (error_code) {
throw std::runtime_error(fmt::format("Cannot open file: {}", path));
}

wpi::json json;
input >> json;

return ApriltagFieldLayout{json.get<std::vector<ApriltagFieldLayout::Apriltag>>()};
}

std::string ApriltagUtil::SerializeApriltagLayout(const ApriltagFieldLayout& apriltagFieldLayout) {
wpi::json json = apriltagFieldLayout.GetTags();
return json.dump();
}

ApriltagFieldLayout ApriltagUtil::DeserializeApriltagLayout(std::string_view jsonStr) {
wpi::json json = wpi::json::parse(jsonStr);
return ApriltagFieldLayout{json.get<std::vector<ApriltagFieldLayout::Apriltag>>()};
}
101 changes: 101 additions & 0 deletions wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
#include "Eigen/QR"
#include "drake/math/discrete_algebraic_riccati_equation.h"
#include "edu_wpi_first_math_WPIMathJNI.h"
#include "frc/apriltag/ApriltagFieldLayout.h"
#include "frc/apriltag/ApriltagUtil.h"
#include "frc/geometry/Pose3d.h"
Copy link
Member

Choose a reason for hiding this comment

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

This include can be removed.

Copy link
Member

Choose a reason for hiding this comment

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

This include is still here and should be removed.

#include "frc/geometry/Rotation3d.h"
#include "frc/trajectory/TrajectoryUtil.h"
#include "units/angle.h"
#include "units/length.h"
#include "unsupported/Eigen/MatrixFunctions"

using namespace wpi::java;
Expand Down Expand Up @@ -93,6 +99,51 @@ frc::Trajectory CreateTrajectoryFromElements(wpi::span<const double> elements) {
return frc::Trajectory(states);
}

std::vector<double> GetElementsFromApriltagLayout(
const frc::ApriltagFieldLayout& apriltagFieldLayout) {
std::vector<double> elements;
elements.reserve(apriltagFieldLayout.GetTags().size() * 8);

for (auto&& apriltag : apriltagFieldLayout.GetTags()) {
elements.push_back(apriltag.id);
elements.push_back(apriltag.pose.X().value());
elements.push_back(apriltag.pose.Y().value());
elements.push_back(apriltag.pose.Z().value());
elements.push_back(apriltag.pose.Rotation().GetQuaternion().W());
elements.push_back(apriltag.pose.Rotation().GetQuaternion().X());
elements.push_back(apriltag.pose.Rotation().GetQuaternion().Y());
elements.push_back(apriltag.pose.Rotation().GetQuaternion().Z());
}

return elements;
}

frc::ApriltagFieldLayout CreateApriltagLayoutFromElements(wpi::span<const double> elements) {
// Make sure that the elements have the correct length.
assert(elements.size() % 8 == 0);

// Create a vector of states from the elements.
std::vector<frc::ApriltagFieldLayout::Apriltag> apriltags;
apriltags.reserve(elements.size() / 8);

for (size_t i = 0; i < elements.size(); i += 8) {
apriltags.emplace_back(frc::ApriltagFieldLayout::Apriltag{
static_cast<int>(elements[0]),
frc::Pose3d{units::meter_t{elements[i + 1]},
units::meter_t{elements[i + 2]},
units::meter_t{elements[i + 3]},
frc::Rotation3d{frc::Quaternion{
elements[i + 4],
elements[i + 5],
elements[i + 6],
elements[i + 7]}}}});
}

return frc::ApriltagFieldLayout(apriltags);
}



extern "C" {

/*
Expand Down Expand Up @@ -308,6 +359,56 @@ Java_edu_wpi_first_math_WPIMathJNI_serializeTrajectory
}
}

/*
* Class: edu_wpi_first_math_WPIMathJNI
* Method: deserializeApriltagLayout
* Signature: (Ljava/lang/String;)[D
*/
JNIEXPORT jdoubleArray JNICALL
Java_edu_wpi_first_math_WPIMathJNI_deserializeApriltagLayout
(JNIEnv* env, jclass, jstring json)
{
try {
auto apriltagFieldLayout = frc::ApriltagUtil::DeserializeApriltagLayout(
JStringRef{env, json}.c_str());
std::vector<double> elements = GetElementsFromApriltagLayout(apriltagFieldLayout);
return MakeJDoubleArray(env, elements);
} catch (std::exception& e) {
jclass cls = env->FindClass(
"edu/wpi/first/math/apriltag/ApriltagUtil$"
"ApriltagLayoutSerializationException");
if (cls) {
env->ThrowNew(cls, e.what());
}
return nullptr;
}
}

/*
* Class: edu_wpi_first_math_WPIMathJNI
* Method: serializeApriltagLayout
* Signature: ([D)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL
Java_edu_wpi_first_math_WPIMathJNI_serializeApriltagLayout
(JNIEnv* env, jclass, jdoubleArray elements)
{
try {
auto apriltagFieldLayout =
CreateApriltagLayoutFromElements(JDoubleArrayRef{env, elements});
return MakeJString(env,
frc::ApriltagUtil::SerializeApriltagLayout(apriltagFieldLayout));
} catch (std::exception& e) {
jclass cls = env->FindClass(
"edu/wpi/first/math/apriltag/ApriltagUtil$"
"ApriltagLayoutSerializationException");
if (cls) {
env->ThrowNew(cls, e.what());
}
return nullptr;
}
}

/*
* Class: edu_wpi_first_math_WPIMathJNI
* Method: rankUpdate
Expand Down
Loading