Make 2027 build (#2422)

This PR updates everything for 2027. This includes removing GradleRIO, simplifying our wpilib version defintion, updating APIs, updating to Java 21, and more.

Note that photonlibpy is failing because robotpy has not been fully updated yet. Examples are omitted because they need to be updated for our new PhotonPoseEstimator API and still need some changes from WPILIB. photonlib windows build is failing because we're waiting for some upstream changes. Finally, images are failing since they don't have Java 21 yet.
This commit is contained in:
Sam Freund
2026-04-11 12:14:42 -05:00
committed by samfreund
parent 4412df1516
commit 68fc1e7129
111 changed files with 630 additions and 578 deletions

View File

@@ -28,13 +28,12 @@ package org.photonvision.struct;
import org.photonvision.common.dataflow.structures.Packet;
import org.photonvision.common.dataflow.structures.PacketSerde;
import org.photonvision.utils.PacketUtils;
// Assume that the base class lives here and we can import it
import org.photonvision.targeting.*;
// WPILib imports (if any)
import edu.wpi.first.util.struct.Struct;
import org.wpilib.util.struct.Struct;
/**

View File

@@ -28,13 +28,12 @@ package org.photonvision.struct;
import org.photonvision.common.dataflow.structures.Packet;
import org.photonvision.common.dataflow.structures.PacketSerde;
import org.photonvision.utils.PacketUtils;
// Assume that the base class lives here and we can import it
import org.photonvision.targeting.*;
// WPILib imports (if any)
import edu.wpi.first.util.struct.Struct;
import org.wpilib.util.struct.Struct;
/**

View File

@@ -28,13 +28,12 @@ package org.photonvision.struct;
import org.photonvision.common.dataflow.structures.Packet;
import org.photonvision.common.dataflow.structures.PacketSerde;
import org.photonvision.utils.PacketUtils;
// Assume that the base class lives here and we can import it
import org.photonvision.targeting.*;
// WPILib imports (if any)
import edu.wpi.first.util.struct.Struct;
import org.wpilib.util.struct.Struct;
/**

View File

@@ -34,7 +34,7 @@ import org.photonvision.utils.PacketUtils;
import org.photonvision.targeting.*;
// WPILib imports (if any)
import edu.wpi.first.util.struct.Struct;
import org.wpilib.util.struct.Struct;
import org.wpilib.math.geometry.Transform3d;
/**

View File

@@ -34,7 +34,7 @@ import org.photonvision.utils.PacketUtils;
import org.photonvision.targeting.*;
// WPILib imports (if any)
import edu.wpi.first.util.struct.Struct;
import org.wpilib.util.struct.Struct;
import org.wpilib.math.geometry.Transform3d;
/**

View File

@@ -28,13 +28,12 @@ package org.photonvision.struct;
import org.photonvision.common.dataflow.structures.Packet;
import org.photonvision.common.dataflow.structures.PacketSerde;
import org.photonvision.utils.PacketUtils;
// Assume that the base class lives here and we can import it
import org.photonvision.targeting.*;
// WPILib imports (if any)
import edu.wpi.first.util.struct.Struct;
import org.wpilib.util.struct.Struct;
/**

View File

@@ -36,7 +36,7 @@
#include "photon/targeting/TargetCorner.h"
#include <stdint.h>
#include <vector>
#include <wpi/math/geometry/Transform3d.h>
#include <wpi/math/geometry/Transform3d.hpp>
namespace photon {

View File

@@ -34,7 +34,7 @@
// Includes for dependant types
#include <stdint.h>
#include <wpi/math/geometry/Transform3d.h>
#include <wpi/math/geometry/Transform3d.hpp>
namespace photon {

View File

@@ -30,7 +30,7 @@
#include "photon/targeting/TargetCorner.h"
#include <stdint.h>
#include <vector>
#include <wpi/math/geometry/Transform3d.h>
#include <wpi/math/geometry/Transform3d.hpp>
namespace photon {

View File

@@ -28,7 +28,7 @@
// Includes for dependant types
#include <stdint.h>
#include <wpi/math/geometry/Transform3d.h>
#include <wpi/math/geometry/Transform3d.hpp>
namespace photon {

View File

@@ -85,15 +85,15 @@ public class NTTopicSet {
.publish(
PhotonPipelineResult.photonStruct.getTypeString(),
PubSubOption.periodic(0.01),
PubSubOption.sendAll(true),
PubSubOption.keepDuplicates(true));
PubSubOption.SEND_ALL,
PubSubOption.KEEP_DUPLICATES);
resultPublisher =
new PacketPublisher<PhotonPipelineResult>(rawBytesEntry, PhotonPipelineResult.photonStruct);
protoResultPublisher =
subTable
.getProtobufTopic("result_proto", PhotonPipelineResult.proto)
.publish(PubSubOption.periodic(0.01), PubSubOption.sendAll(true));
.publish(PubSubOption.periodic(0.01), PubSubOption.SEND_ALL);
pipelineIndexPublisher = subTable.getIntegerTopic("pipelineIndexState").publish();
pipelineIndexRequestSub = subTable.getIntegerTopic("pipelineIndexRequest").subscribe(0);

View File

@@ -48,7 +48,6 @@ import org.wpilib.math.linalg.VecBuilder;
import org.wpilib.math.numbers.*;
import org.wpilib.math.util.Nat;
import org.wpilib.math.util.Num;
import org.wpilib.vision.camera.OpenCvLoader;
/**
* A set of various utility functions for getting non-OpenCV data into an OpenCV-compatible format,
@@ -269,7 +268,7 @@ public final class OpenCVHelp {
* @return The converted rotation in the NWU coordinate system
*/
private static Rotation3d rotationEDNtoNWU(Rotation3d rot) {
return EDN_TO_NWU.unaryMinus().plus(rot.plus(EDN_TO_NWU));
return EDN_TO_NWU.inverse().rotateBy(rot.rotateBy(EDN_TO_NWU));
}
/**
@@ -279,7 +278,7 @@ public final class OpenCVHelp {
* @return The converted rotation in the EDN coordinate system
*/
private static Rotation3d rotationNWUtoEDN(Rotation3d rot) {
return NWU_TO_EDN.unaryMinus().plus(rot.plus(NWU_TO_EDN));
return NWU_TO_EDN.inverse().rotateBy(rot.rotateBy(NWU_TO_EDN));
}
/**

View File

@@ -46,7 +46,7 @@ public class RotTrlTransform3d {
}
public RotTrlTransform3d(Pose3d initial, Pose3d last) {
this.rot = last.getRotation().minus(initial.getRotation());
this.rot = last.getRotation().relativeTo(initial.getRotation());
this.trl = last.getTranslation().minus(initial.getTranslation().rotateBy(rot));
}
@@ -83,7 +83,7 @@ public class RotTrlTransform3d {
* @return The inverse transformation
*/
public RotTrlTransform3d inverse() {
var inverseRot = rot.unaryMinus();
var inverseRot = rot.inverse();
var inverseTrl = trl.rotateBy(inverseRot).unaryMinus();
return new RotTrlTransform3d(inverseRot, inverseTrl);
}
@@ -124,7 +124,7 @@ public class RotTrlTransform3d {
}
public Rotation3d apply(Rotation3d rot) {
return rot.plus(this.rot);
return rot.rotateBy(this.rot);
}
public List<Rotation3d> applyRots(List<Rotation3d> rots) {

View File

@@ -106,7 +106,8 @@ void wpi::tsp::TimeSyncClient::Tick() {
m_lastPing = ping;
}
void wpi::tsp::TimeSyncClient::UdpCallback(wpi::net::uv::Buffer& buf, size_t nbytes,
void wpi::tsp::TimeSyncClient::UdpCallback(wpi::net::uv::Buffer& buf,
size_t nbytes,
const sockaddr& sender,
unsigned flags) {
uint64_t pong_local_time = m_timeProvider();
@@ -183,8 +184,9 @@ void wpi::tsp::TimeSyncClient::Start() {
using namespace std::chrono_literals;
m_pingTimer->timeout.connect(&wpi::tsp::TimeSyncClient::Tick, this);
m_loopRunner.ExecSync(
[this](wpi::net::uv::Loop&) { m_pingTimer->Start(m_loopDelay, m_loopDelay); });
m_loopRunner.ExecSync([this](wpi::net::uv::Loop&) {
m_pingTimer->Start(m_loopDelay, m_loopDelay);
});
}
void wpi::tsp::TimeSyncClient::Stop() { m_loopRunner.Stop(); }

View File

@@ -23,11 +23,12 @@
#include <wpi/net/UDPClient.hpp>
#include <wpi/net/uv/util.hpp>
#include <wpi/nt/ntcore_cpp.hpp>
#include <wpi/util/Logger.hpp>
#include <wpi/util/print.hpp>
#include <wpi/util/struct/Struct.hpp>
#include <wpi/nt/ntcore_cpp.hpp>
#include "net/TimeSyncStructs.h"
static void ServerLoggerFunc(unsigned int level, const char* file,
unsigned int line, const char* msg) {

View File

@@ -25,7 +25,7 @@
#include <Eigen/LU>
#include <fmt/core.h>
#include <wpi/math/fmt/Eigen.hpp>
#include <wpi/util/timestamp.h>
#include <wpi/util/timestamp.hpp>
#include "../generate/constrained_solvepnp_10_tags_fixed.h"
#include "../generate/constrained_solvepnp_10_tags_free.h"
@@ -47,6 +47,7 @@
#include "../generate/constrained_solvepnp_8_tags_free.h"
#include "../generate/constrained_solvepnp_9_tags_fixed.h"
#include "../generate/constrained_solvepnp_9_tags_free.h"
#include "wpi/nt/ntcore_cpp.hpp"
constexpr bool VERBOSE = false;
@@ -225,7 +226,7 @@ constrained_solvepnp::do_optimization(
constexpr double ERROR_TOL = 1e-4;
for (int iter = 0; iter < 100; iter++) {
auto iter_start = wpi::util::Now();
auto iter_start = wpi::nt::Now();
// Check for diverging iterates
if (x.template lpNorm<Eigen::Infinity>() > 1e20 || !x.allFinite()) {
@@ -343,7 +344,7 @@ constrained_solvepnp::do_optimization(
}
}
auto iter_end = wpi::util::Now();
auto iter_end = wpi::nt::Now();
if constexpr (VERBOSE) {
fmt::println(
"{}: {} uS, ‖∇J‖={}, α={} ({} refinement steps), {} regularization "

View File

@@ -191,8 +191,8 @@ std::optional<photon::PnpResult> EstimateRobotPoseConstrainedSolvePNP(
guess2.X().value(), guess2.Y().value(),
guess2.Rotation().Radians().value()};
wpi::util::expected<constrained_solvepnp::RobotStateMat, slp::ExitStatus> result =
constrained_solvepnp::do_optimization(
wpi::util::expected<constrained_solvepnp::RobotStateMat, slp::ExitStatus>
result = constrained_solvepnp::do_optimization(
headingFree, knownTags.size(), cameraCal, robotToCamera, guessMat,
field2points, pointObservations, gyroTheta.Radians().value(),
gyroErrorScaleFac);
@@ -203,7 +203,8 @@ std::optional<photon::PnpResult> EstimateRobotPoseConstrainedSolvePNP(
photon::PnpResult res{};
res.best = wpi::math::Transform3d{wpi::math::Transform2d{
wpi::units::meter_t{result.value()[0]}, wpi::units::meter_t{result.value()[1]},
wpi::units::meter_t{result.value()[0]},
wpi::units::meter_t{result.value()[1]},
wpi::math::Rotation2d{wpi::units::radian_t{result.value()[2]}}}};
return res;

View File

@@ -45,8 +45,8 @@ class TimeSyncServer {
std::thread m_listener;
private:
void UdpCallback(wpi::net::uv::Buffer& buf, size_t nbytes, const sockaddr& sender,
unsigned flags);
void UdpCallback(wpi::net::uv::Buffer& buf, size_t nbytes,
const sockaddr& sender, unsigned flags);
public:
explicit TimeSyncServer(int port = 5810);

View File

@@ -43,7 +43,7 @@ class CameraTargetRelation {
camToTarg(wpi::math::Transform3d{cameraPose, targetPose}),
camToTargDist(camToTarg.Translation().Norm()),
camToTargDistXY(wpi::units::math::hypot(camToTarg.Translation().X(),
camToTarg.Translation().Y())),
camToTarg.Translation().Y())),
camToTargYaw(wpi::math::Rotation2d{camToTarg.X().to<double>(),
camToTarg.Y().to<double>()}),
camToTargPitch(wpi::math::Rotation2d{camToTargDistXY.to<double>(),

View File

@@ -64,7 +64,7 @@ static wpi::math::Translation3d TranslationNWUtoEDN(
static wpi::math::Rotation3d RotationNWUtoEDN(
const wpi::math::Rotation3d& rot) {
return -NWU_TO_EDN + (rot + NWU_TO_EDN);
return NWU_TO_EDN.Inverse().RotateBy(rot.RotateBy(NWU_TO_EDN));
}
static std::vector<cv::Point3f> TranslationToTVec(
@@ -186,7 +186,7 @@ static wpi::math::Translation3d TranslationEDNToNWU(
static wpi::math::Rotation3d RotationEDNToNWU(
const wpi::math::Rotation3d& rot) {
return -EDN_TO_NWU + (rot + EDN_TO_NWU);
return EDN_TO_NWU.Inverse().RotateBy(rot.RotateBy(EDN_TO_NWU));
}
static wpi::math::Translation3d TVecToTranslation(const cv::Mat& tvecInput) {
@@ -194,9 +194,9 @@ static wpi::math::Translation3d TVecToTranslation(const cv::Mat& tvecInput) {
cv::Mat wrapped{tvecInput.rows, tvecInput.cols, CV_32F};
tvecInput.convertTo(wrapped, CV_32F);
data = wrapped.at<cv::Vec3f>(cv::Point{0, 0});
return TranslationEDNToNWU(wpi::math::Translation3d{wpi::units::meter_t{data[0]},
wpi::units::meter_t{data[1]},
wpi::units::meter_t{data[2]}});
return TranslationEDNToNWU(wpi::math::Translation3d{
wpi::units::meter_t{data[0]}, wpi::units::meter_t{data[1]},
wpi::units::meter_t{data[2]}});
}
static wpi::math::Rotation3d RVecToRotation(const cv::Mat& rvecInput) {

View File

@@ -32,9 +32,10 @@ class RotTrlTransform3d {
RotTrlTransform3d(const wpi::math::Pose3d& initial,
const wpi::math::Pose3d& last)
: trl{last.Translation() - initial.Translation().RotateBy(
last.Rotation() - initial.Rotation())},
rot{last.Rotation() - initial.Rotation()} {}
: trl{last.Translation() -
initial.Translation().RotateBy(
last.Rotation().RelativeTo(initial.Rotation()))},
rot{last.Rotation().RelativeTo(initial.Rotation())} {}
explicit RotTrlTransform3d(const wpi::math::Transform3d& trf)
: RotTrlTransform3d(trf.Rotation(), trf.Translation()) {}
RotTrlTransform3d()
@@ -46,7 +47,7 @@ class RotTrlTransform3d {
}
RotTrlTransform3d Inverse() const {
wpi::math::Rotation3d invRot = -rot;
wpi::math::Rotation3d invRot = rot.Inverse();
wpi::math::Translation3d invTrl = -(trl.RotateBy(invRot));
return RotTrlTransform3d{invRot, invTrl};
}
@@ -75,7 +76,7 @@ class RotTrlTransform3d {
}
wpi::math::Rotation3d Apply(const wpi::math::Rotation3d& rotToApply) const {
return rotToApply + rot;
return rotToApply.RotateBy(rot);
}
std::vector<wpi::math::Rotation3d> ApplyTrls(

View File

@@ -41,11 +41,12 @@ extern "C" {
* Signature: (ZI[D[D[D[D[DDD)[D
*/
JNIEXPORT jdoubleArray JNICALL
Java_org_photonvision_jni_ConstrainedSolvepnpJni_do_1optimization(
JNIEnv* env, jclass, jboolean headingFree, jint nTags,
jdoubleArray cameraCal, jdoubleArray robot2camera, jdoubleArray xGuess,
jdoubleArray field2points, jdoubleArray pointObservations, jdouble gyro_θ,
jdouble gyro_error_scale_fac) {
Java_org_photonvision_jni_ConstrainedSolvepnpJni_do_1optimization
(JNIEnv* env, jclass, jboolean headingFree, jint nTags,
jdoubleArray cameraCal, jdoubleArray robot2camera, jdoubleArray xGuess,
jdoubleArray field2points, jdoubleArray pointObservations, jdouble gyro_θ,
jdouble gyro_error_scale_fac)
{
auto cameraCalVec = convertJDoubleArray(env, cameraCal);
auto robot2cameraVec = convertJDoubleArray(env, robot2camera);
auto xGuessVec = convertJDoubleArray(env, xGuess);

View File

@@ -18,10 +18,11 @@
#include <string>
#include <opencv2/core/mat.hpp>
#include <wpi/util/RawFrame.hpp>
#include <wpi/util/jni_util.hpp>
#include "org_photonvision_jni_CscoreExtras.h"
#include "wpi/cs/cscore_raw.h"
#include "wpi/cs/cscore_raw.hpp"
// from wpilib, licensed under the wpilib BSD license
using namespace wpi::util::java;

View File

@@ -15,8 +15,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <frc/fmt/Eigen.h>
#include <gtest/gtest.h>
#include <wpi/math/fmt/Eigen.hpp>
#include <wpi/util/print.hpp>
#include <wpi/util/timestamp.h>
@@ -159,15 +159,15 @@ void print_cost(casadi_real robot_x, casadi_real robot_y,
x_guess << robot_x, robot_y, robot_theta;
for (int i = 0; i < 200; i++) {
auto start = wpi::util::Now();
auto start = WPI_Now();
auto x_out = constrained_solvepnp::do_optimization(
true, TAG_COUNT,
constrained_solvepnp::CameraCalibration{fx, fy, cx, cy}, robot2camera,
x_guess, field2points, point_observations, 0, 0);
auto end = wpi::util::Now();
auto end = WPI_Now();
wpi::println("{},{},{}", i, static_cast<bool>(x_out), end - start);
wpi::println(
wpi::util::println("{},{},{}", i, static_cast<bool>(x_out), end - start);
wpi::util::println(
"Solution: {}",
x_out.value_or(constrained_solvepnp::RobotStateMat::Identity()));
// std::cout << "iter "