From a2dfe486797e295735ed8e3165404af95653aa53 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 12 Feb 2023 00:07:07 -0500 Subject: [PATCH] Create C++ Apriltag example (#794) * Create C++ Apriltag example * Delete libphotonlibcamera.so * Update PhotonCameraWrapper.h * Delete extra files Update .gitignore --- .gitignore | 2 + photon-server/lib/libphotonlibcamera.so | Bin 145784 -> 0 bytes .../aimandrange/build.gradle | 2 +- .../aimattarget/build.gradle | 2 +- .../apriltagExample/.gitignore | 1 + .../apriltagExample/WPILib-License.md | 24 ++ .../apriltagExample/build.gradle | 116 +++++++++ .../apriltagExample/gradlew | 240 ++++++++++++++++++ .../apriltagExample/gradlew.bat | 91 +++++++ .../apriltagExample/settings.gradle | 30 +++ .../apriltagExample/simgui-ds.json | 102 ++++++++ .../src/main/cpp/Drivetrain.cpp | 92 +++++++ .../apriltagExample/src/main/cpp/Robot.cpp | 61 +++++ .../src/main/include/Drivetrain.h | 140 ++++++++++ .../src/main/include/PhotonCameraWrapper.h | 46 ++++ .../apriltagExample/src/main/include/Robot.h | 50 ++++ .../apriltagExample/src/test/cpp/main.cpp | 34 +++ photonlib-cpp-examples/examples.txt | 1 + .../getinrange/build.gradle | 2 +- .../aimandrange/build.gradle | 2 +- .../aimattarget/build.gradle | 2 +- .../apriltagExample/build.gradle | 2 +- .../src/main/java/frc/robot/Robot.java | 2 +- .../getinrange/build.gradle | 2 +- .../simaimandrange/build.gradle | 2 +- .../simposeest/build.gradle | 2 +- 26 files changed, 1040 insertions(+), 10 deletions(-) delete mode 100644 photon-server/lib/libphotonlibcamera.so create mode 100644 photonlib-cpp-examples/apriltagExample/.gitignore create mode 100644 photonlib-cpp-examples/apriltagExample/WPILib-License.md create mode 100644 photonlib-cpp-examples/apriltagExample/build.gradle create mode 100755 photonlib-cpp-examples/apriltagExample/gradlew create mode 100644 photonlib-cpp-examples/apriltagExample/gradlew.bat create mode 100644 photonlib-cpp-examples/apriltagExample/settings.gradle create mode 100644 photonlib-cpp-examples/apriltagExample/simgui-ds.json create mode 100644 photonlib-cpp-examples/apriltagExample/src/main/cpp/Drivetrain.cpp create mode 100644 photonlib-cpp-examples/apriltagExample/src/main/cpp/Robot.cpp create mode 100644 photonlib-cpp-examples/apriltagExample/src/main/include/Drivetrain.h create mode 100644 photonlib-cpp-examples/apriltagExample/src/main/include/PhotonCameraWrapper.h create mode 100644 photonlib-cpp-examples/apriltagExample/src/main/include/Robot.h create mode 100644 photonlib-cpp-examples/apriltagExample/src/test/cpp/main.cpp diff --git a/.gitignore b/.gitignore index 6d862765b..721d1619a 100644 --- a/.gitignore +++ b/.gitignore @@ -153,3 +153,5 @@ photon-server/src/main/resources/nativelibraries/apriltag/* photonlib-java-examples/*/vendordeps/* photonlib-cpp-examples/*/vendordeps/* + +networktables.json diff --git a/photon-server/lib/libphotonlibcamera.so b/photon-server/lib/libphotonlibcamera.so deleted file mode 100644 index f11ed7338aa36b6de39128cbd96cbdc37587015e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145784 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%0Dg9n0v~K&LVK4bXXv-Uqs#(fdJfW%T=? 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/photonlib-cpp-examples/apriltagExample/gradlew.bat b/photonlib-cpp-examples/apriltagExample/gradlew.bat new file mode 100644 index 000000000..53a6b238d --- /dev/null +++ b/photonlib-cpp-examples/apriltagExample/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/photonlib-cpp-examples/apriltagExample/settings.gradle b/photonlib-cpp-examples/apriltagExample/settings.gradle new file mode 100644 index 000000000..adeb0f45c --- /dev/null +++ b/photonlib-cpp-examples/apriltagExample/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +rootProject.name = 'aimattarget' + +pluginManagement { + repositories { + mavenLocal() + jcenter() + gradlePluginPortal() + String frcYear = '2023' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name 'frcHome' + url frcHomeMaven + } + } +} diff --git a/photonlib-cpp-examples/apriltagExample/simgui-ds.json b/photonlib-cpp-examples/apriltagExample/simgui-ds.json new file mode 100644 index 000000000..7b5100502 --- /dev/null +++ b/photonlib-cpp-examples/apriltagExample/simgui-ds.json @@ -0,0 +1,102 @@ +{ + "Keyboard 0 Settings": { + "window": { + "visible": true + } + }, + "keyboardJoysticks": [ + { + "axisConfig": [ + {}, + { + "decKey": 87, + "incKey": 83 + }, + { + "decayRate": 0.0, + "keyRate": 0.009999999776482582 + }, + {}, + { + "decKey": 65, + "incKey": 68 + } + ], + "axisCount": 6, + "buttonCount": 4, + "buttonKeys": [ + 90, + 88, + 67, + 86 + ], + "povConfig": [ + { + "key0": 328, + "key135": 323, + "key180": 322, + "key225": 321, + "key270": 324, + "key315": 327, + "key45": 329, + "key90": 326 + } + ], + "povCount": 1 + }, + { + "axisConfig": [ + { + "decKey": 74, + "incKey": 76 + }, + { + "decKey": 73, + "incKey": 75 + } + ], + "axisCount": 2, + "buttonCount": 4, + "buttonKeys": [ + 77, + 44, + 46, + 47 + ], + "povCount": 0 + }, + { + "axisConfig": [ + { + "decKey": 263, + "incKey": 262 + }, + { + "decKey": 265, + "incKey": 264 + } + ], + "axisCount": 2, + "buttonCount": 6, + "buttonKeys": [ + 260, + 268, + 266, + 261, + 269, + 267 + ], + "povCount": 0 + }, + { + "axisCount": 0, + "buttonCount": 0, + "povCount": 0 + } + ], + "robotJoysticks": [ + { + "guid": "Keyboard0" + } + ] +} diff --git a/photonlib-cpp-examples/apriltagExample/src/main/cpp/Drivetrain.cpp b/photonlib-cpp-examples/apriltagExample/src/main/cpp/Drivetrain.cpp new file mode 100644 index 000000000..ad778e254 --- /dev/null +++ b/photonlib-cpp-examples/apriltagExample/src/main/cpp/Drivetrain.cpp @@ -0,0 +1,92 @@ +/* + * MIT License + * + * Copyright (c) 2022 PhotonVision + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "Drivetrain.h" + +#include +#include + +void Drivetrain::SetSpeeds(const frc::DifferentialDriveWheelSpeeds& speeds) { + auto leftFeedforward = m_feedforward.Calculate(speeds.left); + auto rightFeedforward = m_feedforward.Calculate(speeds.right); + double leftOutput = m_leftPIDController.Calculate(m_leftEncoder.GetRate(), + speeds.left.value()); + double rightOutput = m_rightPIDController.Calculate(m_rightEncoder.GetRate(), + speeds.right.value()); + + m_leftGroup.SetVoltage(units::volt_t{leftOutput} + leftFeedforward); + m_rightGroup.SetVoltage(units::volt_t{rightOutput} + rightFeedforward); +} + +void Drivetrain::Drive(units::meters_per_second_t xSpeed, + units::radians_per_second_t rot) { + SetSpeeds(m_kinematics.ToWheelSpeeds({xSpeed, 0_mps, rot})); +} + +void Drivetrain::UpdateOdometry() { + m_poseEstimator.Update(m_gyro.GetRotation2d(), + units::meter_t{m_leftEncoder.GetDistance()}, + units::meter_t{m_rightEncoder.GetDistance()}); +} + +void Drivetrain::ResetOdometry(const frc::Pose2d& pose) { + m_leftEncoder.Reset(); + m_rightEncoder.Reset(); + m_drivetrainSimulator.SetPose(pose); + m_poseEstimator.ResetPosition( + m_gyro.GetRotation2d(), units::meter_t{m_leftEncoder.GetDistance()}, + units::meter_t{m_rightEncoder.GetDistance()}, pose); +} + +void Drivetrain::SimulationPeriodic() { + // To update our simulation, we set motor voltage inputs, update the + // simulation, and write the simulated positions and velocities to our + // simulated encoder and gyro. We negate the right side so that positive + // voltages make the right side move forward. + + m_drivetrainSimulator.SetInputs(units::volt_t{m_leftGroup.Get()} * + frc::RobotController::GetInputVoltage(), + units::volt_t{m_rightGroup.Get()} * + frc::RobotController::GetInputVoltage()); + m_drivetrainSimulator.Update(20_ms); + + m_leftEncoderSim.SetDistance(m_drivetrainSimulator.GetLeftPosition().value()); + m_leftEncoderSim.SetRate(m_drivetrainSimulator.GetLeftVelocity().value()); + m_rightEncoderSim.SetDistance( + m_drivetrainSimulator.GetRightPosition().value()); + m_rightEncoderSim.SetRate(m_drivetrainSimulator.GetRightVelocity().value()); + m_gyroSim.SetAngle(-m_drivetrainSimulator.GetHeading().Degrees().value()); +} + +void Drivetrain::Periodic() { + UpdateOdometry(); + + auto result = m_pcw.Update(m_poseEstimator.GetEstimatedPosition()); + if (result) { + m_poseEstimator.AddVisionMeasurement( + result.value().estimatedPose.ToPose2d(), result.value().timestamp); + } + + m_fieldSim.SetRobotPose(m_poseEstimator.GetEstimatedPosition()); +} diff --git a/photonlib-cpp-examples/apriltagExample/src/main/cpp/Robot.cpp b/photonlib-cpp-examples/apriltagExample/src/main/cpp/Robot.cpp new file mode 100644 index 000000000..4d3742aeb --- /dev/null +++ b/photonlib-cpp-examples/apriltagExample/src/main/cpp/Robot.cpp @@ -0,0 +1,61 @@ +/* + * MIT License + * + * Copyright (c) 2022 PhotonVision + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "Robot.h" + +#include +#include + +void Robot::RobotInit() { + if constexpr (frc::RobotBase::IsSimulation()) { + auto inst = nt::NetworkTableInstance::GetDefault(); + inst.StopServer(); + // set the NT server if simulating this code. + // "localhost" for photon on desktop, or "photonvision.local" or + // "[ip-address]" for coprocessor + inst.SetServer("localhost"); + inst.StartClient4("Robot Simulation"); + } +} + +void Robot::TeleopPeriodic() { + // Get the x speed. We are inverting this because Xbox controllers return + // negative values when we push forward. + const auto xSpeed = -m_controller.GetLeftY() * Drivetrain::kMaxSpeed; + + // Get the rate of angular rotation. We are inverting this because we want a + // positive value when we pull to the left (remember, CCW is positive in + // mathematics). Xbox controllers return positive values when you pull to + // the right by default. + auto rot = -m_controller.GetRightX() * Drivetrain::kMaxAngularSpeed; + + m_drive.Drive(xSpeed, rot); +} + +void Robot::RobotPeriodic() { m_drive.Periodic(); } +void Robot::SimulationPeriodic() { m_drive.SimulationPeriodic(); } + +#ifndef RUNNING_FRC_TESTS +int main() { return frc::StartRobot(); } +#endif diff --git a/photonlib-cpp-examples/apriltagExample/src/main/include/Drivetrain.h b/photonlib-cpp-examples/apriltagExample/src/main/include/Drivetrain.h new file mode 100644 index 000000000..7995030ba --- /dev/null +++ b/photonlib-cpp-examples/apriltagExample/src/main/include/Drivetrain.h @@ -0,0 +1,140 @@ +/* + * MIT License + * + * Copyright (c) 2022 PhotonVision + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PhotonCameraWrapper.h" + +/** + * Represents a differential drive style drivetrain. + */ +class Drivetrain { + public: + Drivetrain() { + m_gyro.Reset(); + + // We need to invert one side of the drivetrain so that positive voltages + // result in both sides moving forward. Depending on how your robot's + // gearbox is constructed, you might have to invert the left side instead. + m_rightGroup.SetInverted(true); + + // Set the distance per pulse for the drive encoders. We can simply use the + // distance traveled for one rotation of the wheel divided by the encoder + // resolution. + m_leftEncoder.SetDistancePerPulse(2 * std::numbers::pi * kWheelRadius / + kEncoderResolution); + m_rightEncoder.SetDistancePerPulse(2 * std::numbers::pi * kWheelRadius / + kEncoderResolution); + + m_leftEncoder.Reset(); + m_rightEncoder.Reset(); + + m_rightGroup.SetInverted(true); + + frc::SmartDashboard::PutData("Field", &m_fieldSim); + } + + static constexpr units::meters_per_second_t kMaxSpeed = + 3.0_mps; // 3 meters per second + static constexpr units::radians_per_second_t kMaxAngularSpeed{ + std::numbers::pi}; // 1/2 rotation per second + + void SetSpeeds(const frc::DifferentialDriveWheelSpeeds& speeds); + void Drive(units::meters_per_second_t xSpeed, + units::radians_per_second_t rot); + void UpdateOdometry(); + void ResetOdometry(const frc::Pose2d& pose); + + frc::Pose2d GetPose() const { return m_poseEstimator.GetEstimatedPosition(); } + + void SimulationPeriodic(); + void Periodic(); + + private: + static constexpr units::meter_t kTrackWidth = 0.381_m * 2; + static constexpr double kWheelRadius = 0.0508; // meters + static constexpr int kEncoderResolution = 4096; + + frc::PWMSparkMax m_leftLeader{1}; + frc::PWMSparkMax m_leftFollower{2}; + frc::PWMSparkMax m_rightLeader{3}; + frc::PWMSparkMax m_rightFollower{4}; + + frc::MotorControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; + frc::MotorControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; + + frc::Encoder m_leftEncoder{0, 1}; + frc::Encoder m_rightEncoder{2, 3}; + + frc2::PIDController m_leftPIDController{8.5, 0.0, 0.0}; + frc2::PIDController m_rightPIDController{8.5, 0.0, 0.0}; + + frc::AnalogGyro m_gyro{0}; + + frc::DifferentialDriveKinematics m_kinematics{kTrackWidth}; + + frc::DifferentialDrivePoseEstimator m_poseEstimator{ + m_kinematics, m_gyro.GetRotation2d(), + units::meter_t{m_leftEncoder.GetDistance()}, + units::meter_t{m_rightEncoder.GetDistance()}, frc::Pose2d{}}; + + PhotonCameraWrapper m_pcw; + + // Gains are for example purposes only - must be determined for your own + // robot! + frc::SimpleMotorFeedforward m_feedforward{1_V, 3_V / 1_mps}; + + // Simulation classes help us simulate our robot + frc::sim::AnalogGyroSim m_gyroSim{m_gyro}; + frc::sim::EncoderSim m_leftEncoderSim{m_leftEncoder}; + frc::sim::EncoderSim m_rightEncoderSim{m_rightEncoder}; + frc::Field2d m_fieldSim; + frc::LinearSystem<2, 2, 2> m_drivetrainSystem = + frc::LinearSystemId::IdentifyDrivetrainSystem( + 1.98_V / 1_mps, 0.2_V / 1_mps_sq, 1.5_V / 1_mps, 0.3_V / 1_mps_sq); + frc::sim::DifferentialDrivetrainSim m_drivetrainSimulator{ + m_drivetrainSystem, kTrackWidth, frc::DCMotor::CIM(2), 8, 2_in}; +}; diff --git a/photonlib-cpp-examples/apriltagExample/src/main/include/PhotonCameraWrapper.h b/photonlib-cpp-examples/apriltagExample/src/main/include/PhotonCameraWrapper.h new file mode 100644 index 000000000..e45517b8f --- /dev/null +++ b/photonlib-cpp-examples/apriltagExample/src/main/include/PhotonCameraWrapper.h @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2022 PhotonVision + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once +#include +#include + +#include + +#include +#include + +class PhotonCameraWrapper { + public: + photonlib::PhotonPoseEstimator m_poseEstimator{ + frc::LoadAprilTagLayoutField(frc::AprilTagField::k2023ChargedUp), + photonlib::LOWEST_AMBIGUITY, + std::move(photonlib::PhotonCamera{"WPI2023"}), frc::Transform3d{}}; + + inline std::optional Update( + frc::Pose2d estimatedPose) { + m_poseEstimator.SetReferencePose(frc::Pose3d(estimatedPose)); + return m_poseEstimator.Update(); + } +}; diff --git a/photonlib-cpp-examples/apriltagExample/src/main/include/Robot.h b/photonlib-cpp-examples/apriltagExample/src/main/include/Robot.h new file mode 100644 index 000000000..9570d66f8 --- /dev/null +++ b/photonlib-cpp-examples/apriltagExample/src/main/include/Robot.h @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2022 PhotonVision + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Drivetrain.h" + +class Robot : public frc::TimedRobot { + public: + void RobotInit() override; + void RobotPeriodic() override; + void TeleopPeriodic() override; + void SimulationPeriodic() override; + + private: + frc::XboxController m_controller{0}; + + Drivetrain m_drive; +}; diff --git a/photonlib-cpp-examples/apriltagExample/src/test/cpp/main.cpp b/photonlib-cpp-examples/apriltagExample/src/test/cpp/main.cpp new file mode 100644 index 000000000..e9ee94e8b --- /dev/null +++ b/photonlib-cpp-examples/apriltagExample/src/test/cpp/main.cpp @@ -0,0 +1,34 @@ +/* + * MIT License + * + * Copyright (c) 2022 PhotonVision + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "gtest/gtest.h" + +int main(int argc, char** argv) { + HAL_Initialize(500, 0); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/photonlib-cpp-examples/examples.txt b/photonlib-cpp-examples/examples.txt index 54fc7fa00..25f764852 100644 --- a/photonlib-cpp-examples/examples.txt +++ b/photonlib-cpp-examples/examples.txt @@ -1,3 +1,4 @@ aimandrange getinrange aimattarget +apriltagExample diff --git a/photonlib-cpp-examples/getinrange/build.gradle b/photonlib-cpp-examples/getinrange/build.gradle index b30042967..10e9a0f76 100644 --- a/photonlib-cpp-examples/getinrange/build.gradle +++ b/photonlib-cpp-examples/getinrange/build.gradle @@ -1,7 +1,7 @@ plugins { id "cpp" id "google-test-test-suite" - id "edu.wpi.first.GradleRIO" version "2023.2.1" + id "edu.wpi.first.GradleRIO" version "2023.3.2" id "com.dorongold.task-tree" version "2.1.0" } diff --git a/photonlib-java-examples/aimandrange/build.gradle b/photonlib-java-examples/aimandrange/build.gradle index a5629b00c..a7b059a15 100644 --- a/photonlib-java-examples/aimandrange/build.gradle +++ b/photonlib-java-examples/aimandrange/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.2.1" + id "edu.wpi.first.GradleRIO" version "2023.3.2" } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/photonlib-java-examples/aimattarget/build.gradle b/photonlib-java-examples/aimattarget/build.gradle index 0c26b0633..1536aafcc 100644 --- a/photonlib-java-examples/aimattarget/build.gradle +++ b/photonlib-java-examples/aimattarget/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.2.1" + id "edu.wpi.first.GradleRIO" version "2023.3.2" } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/photonlib-java-examples/apriltagExample/build.gradle b/photonlib-java-examples/apriltagExample/build.gradle index a5629b00c..a7b059a15 100644 --- a/photonlib-java-examples/apriltagExample/build.gradle +++ b/photonlib-java-examples/apriltagExample/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.2.1" + id "edu.wpi.first.GradleRIO" version "2023.3.2" } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/photonlib-java-examples/apriltagExample/src/main/java/frc/robot/Robot.java b/photonlib-java-examples/apriltagExample/src/main/java/frc/robot/Robot.java index 3a258bc42..184446f2c 100644 --- a/photonlib-java-examples/apriltagExample/src/main/java/frc/robot/Robot.java +++ b/photonlib-java-examples/apriltagExample/src/main/java/frc/robot/Robot.java @@ -46,7 +46,7 @@ public class Robot extends TimedRobot { // set the NT server if simulating this code. // "localhost" for photon on desktop, or "photonvision.local" / "[ip-address]" for coprocessor instance.setServer("localhost"); - instance.startClient4("myRobot"); + instance.startClient4("Robot Simulation"); } m_controller = new XboxController(0); diff --git a/photonlib-java-examples/getinrange/build.gradle b/photonlib-java-examples/getinrange/build.gradle index a5629b00c..a7b059a15 100644 --- a/photonlib-java-examples/getinrange/build.gradle +++ b/photonlib-java-examples/getinrange/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.2.1" + id "edu.wpi.first.GradleRIO" version "2023.3.2" } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/photonlib-java-examples/simaimandrange/build.gradle b/photonlib-java-examples/simaimandrange/build.gradle index a5629b00c..a7b059a15 100644 --- a/photonlib-java-examples/simaimandrange/build.gradle +++ b/photonlib-java-examples/simaimandrange/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.2.1" + id "edu.wpi.first.GradleRIO" version "2023.3.2" } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/photonlib-java-examples/simposeest/build.gradle b/photonlib-java-examples/simposeest/build.gradle index a5629b00c..a7b059a15 100644 --- a/photonlib-java-examples/simposeest/build.gradle +++ b/photonlib-java-examples/simposeest/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.2.1" + id "edu.wpi.first.GradleRIO" version "2023.3.2" } sourceCompatibility = JavaVersion.VERSION_11