Compare commits

...

16 Commits

Author SHA1 Message Date
Sam Freund
b7a0fad54c Set max target limit to 50 (#2320)
## Description

<!-- What changed? Why? (the code + comments should speak for itself on
the "how") -->

<!-- Fun screenshots or a cool video or something are super helpful as
well. If this touches platform-specific behavior, this is where test
evidence should be collected. -->

<!-- Any issues this pull request closes or pull requests this
supersedes should be linked with `Closes #issuenumber`. -->

closes #2318
closes #2319 

For the 2027 game, teams might want to detect more than 10 results.
Therefore, we're increasing the limit.

We also ran into an issue with our sim, where a user can create too many
objects and cause an overflow. We implement that same limit of 50
targets here.

## Meta

Merge checklist:
- [x] Pull Request title is [short, imperative
summary](https://cbea.ms/git-commit/) of proposed changes
- [x] The description documents the _what_ and _why_
- [ ] If this PR changes behavior or adds a feature, user documentation
is updated
- [ ] If this PR touches photon-serde, all messages have been
regenerated and hashes have not changed unexpectedly
- [ ] If this PR touches configuration, this is backwards compatible
with settings back to v2025.3.2
- [ ] If this PR touches pipeline settings or anything related to data
exchange, the frontend typing is updated
- [ ] If this PR addresses a bug, a regression test for it is added
2026-01-21 16:44:49 -08:00
Gold856
e73420d62a Clarify when estimate methods return empty (#2323)
## Description

Clarifies when estimate methods return an empty optional. This also
fixes some parity issues with Constrained SolvePnP not checking the
optional returned by the heading buffer.

Resolves #2322.

## Meta

Merge checklist:
- [x] Pull Request title is [short, imperative
summary](https://cbea.ms/git-commit/) of proposed changes
- [x] The description documents the _what_ and _why_
- [ ] If this PR changes behavior or adds a feature, user documentation
is updated
- [ ] If this PR touches photon-serde, all messages have been
regenerated and hashes have not changed unexpectedly
- [ ] If this PR touches configuration, this is backwards compatible
with settings back to v2025.3.2
- [ ] If this PR touches pipeline settings or anything related to data
exchange, the frontend typing is updated
- [ ] If this PR addresses a bug, a regression test for it is added
2026-01-21 23:13:47 +00:00
Jay Ticku
12f74423d9 Added clarifying details to wiring part of docs for wiring regulator to coprocessor (#2293)
## Description
Added specific details to the wiring section of the photonvision docs
for two wiring methods for connecting a power regulator to a
coprocessor. This aims to help prevent any possible misunderstandings of
how to wire a regulator to a coprocessor.

## Meta

Merge checklist:
- [ ] Pull Request title is [short, imperative
summary](https://cbea.ms/git-commit/) of proposed changes
- [ ] The description documents the _what_ and _why_
- [ ] If this PR changes behavior or adds a feature, user documentation
is updated
- [ ] If this PR touches photon-serde, all messages have been
regenerated and hashes have not changed unexpectedly
- [ ] If this PR touches configuration, this is backwards compatible
with settings back to v2025.3.2
- [ ] If this PR touches pipeline settings or anything related to data
exchange, the frontend typing is updated
- [ ] If this PR addresses a bug, a regression test for it is added

Co-authored-by: Matt Morley <matthew.morley.ca@gmail.com>
2026-01-20 05:36:38 +00:00
Sam Freund
6c9a142622 Propagate changes after modifying OD model (#2299)
## Description

<!-- What changed? Why? (the code + comments should speak for itself on
the "how") -->

<!-- Fun screenshots or a cool video or something are super helpful as
well. If this touches platform-specific behavior, this is where test
evidence should be collected. -->

<!-- Any issues this pull request closes or pull requests this
supersedes should be linked with `Closes #issuenumber`. -->

This PR adjusts how model modification is handled. Any updates to
metadata, or deletion is handled in the frontend after sending data to
the backend. Any creation of models occurs in the backend, and we push
an update to the frontend.

This PR also fixes a typo in PhotonUtils which meant we hit the wrong
endpoint when an IP was passed into the status check.

## Meta

Merge checklist:
- [x] Pull Request title is [short, imperative
summary](https://cbea.ms/git-commit/) of proposed changes
- [x] The description documents the _what_ and _why_
- [ ] If this PR changes behavior or adds a feature, user documentation
is updated
- [ ] If this PR touches photon-serde, all messages have been
regenerated and hashes have not changed unexpectedly
- [ ] If this PR touches configuration, this is backwards compatible
with settings back to v2025.3.2
- [ ] If this PR touches pipeline settings or anything related to data
exchange, the frontend typing is updated
- [ ] If this PR addresses a bug, a regression test for it is added
2026-01-19 11:58:23 -08:00
William H Wang
149c214897 Chore: Updated documentation for Fx/Fy units. (#2314)
https://mrcal.secretsauce.net/lensmodels.html

Updated the labeling for camera calibration results with MRCAL.
2026-01-19 08:46:59 +00:00
Gold856
a952bab4c9 Remove strict WPILib version requirement (#2307) 2026-01-18 18:56:07 +00:00
Space646
bc208bca85 Add comment documenting typo copied from COCO docs (#2253)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
Co-authored-by: Jade Turner <spacey-sooty@proton.me>
Co-authored-by: Matt Morley <matthew.morley.ca@gmail.com>
2026-01-19 01:50:56 +08:00
Matt Morley
dbd6eea4e9 Cache requested calibration format outside of state store (#2310)
## Description

On main, this pop-up lists the wrong resolution. The index in the state
is getting updated out from underneath us. The easiest solution I have
is to just cache this number.

<img width="739" height="1051" alt="image"
src="https://github.com/user-attachments/assets/a4383ea0-aa1b-4446-87f1-ece8c5ea9ad5"
/>

<img width="873" height="1116" alt="image"
src="https://github.com/user-attachments/assets/7da4cd60-f8a6-4ac6-8bc6-9f0d5c165fee"
/>

Not sure of a great way to test this, since it requires the backend
taking calibration snapshots

## Meta

Merge checklist:
- [x] Pull Request title is [short, imperative
summary](https://cbea.ms/git-commit/) of proposed changes
- [x] The description documents the _what_ and _why_
- [ ] If this PR changes behavior or adds a feature, user documentation
is updated
- [ ] If this PR touches photon-serde, all messages have been
regenerated and hashes have not changed unexpectedly
- [ ] If this PR touches configuration, this is backwards compatible
with settings back to v2025.3.2
- [ ] If this PR touches pipeline settings or anything related to data
exchange, the frontend typing is updated
- [ ] If this PR addresses a bug, a regression test for it is added
2026-01-18 16:04:20 +00:00
David Vo
afb73b3918 refactor: separate build, test, and deploy in Python workflow (#2308)
_Test what you build, deploy what you test._

This refactors the Python CI workflow to wait for _all_ tests to pass
before publishing photonlibpy to PyPI.

- build-python-examples reuses the built wheel, removes redundant builds
- Simplify run.sh to not rebuild wheel since it's already installed
2026-01-17 15:46:19 +00:00
David Vo
9011e285d2 Stop shipping unit tests in photonlibpy wheel (#2309) 2026-01-17 14:27:29 +00:00
Matt Morley
8a141904a6 WPILib 2026.2.1 (#2306)
## Description

Find-and-replace 2026.1.1 -> 2026.2.1. This gets us 2026 field layouts
[among other
things](https://github.com/wpilibsuite/allwpilib/releases/tag/v2026.2.1)

<!-- What changed? Why? (the code + comments should speak for itself on
the "how") -->

<!-- Fun screenshots or a cool video or something are super helpful as
well. If this touches platform-specific behavior, this is where test
evidence should be collected. -->

<!-- Any issues this pull request closes or pull requests this
supersedes should be linked with `Closes #issuenumber`. -->

## Meta

Merge checklist:
- [ ] Pull Request title is [short, imperative
summary](https://cbea.ms/git-commit/) of proposed changes
- [ ] The description documents the _what_ and _why_
- [ ] If this PR changes behavior or adds a feature, user documentation
is updated
- [ ] If this PR touches photon-serde, all messages have been
regenerated and hashes have not changed unexpectedly
- [ ] If this PR touches configuration, this is backwards compatible
with settings back to v2025.3.2
- [ ] If this PR touches pipeline settings or anything related to data
exchange, the frontend typing is updated
- [ ] If this PR addresses a bug, a regression test for it is added
2026-01-17 00:55:30 +00:00
Sam Freund
121433fd90 Loosely pin numpy to 2.3 (#2303) 2026-01-15 16:41:39 -06:00
Sam Freund
22567dea74 Smoketest after uploading images (#2294) 2026-01-14 18:39:21 -08:00
Rikhil Chilka
ba4eb621c3 Rknn numpy fix (#2298) 2026-01-14 15:22:35 -06:00
David Vo
43608c5113 fix: simplify version regex and fix rc tag handling (#2296) 2026-01-13 14:17:36 +00:00
Sam Freund
021053d43e Fix parentheses in workflow (#2292)
Mismatched parenthesis meant releases were never created
2026-01-12 17:59:28 -06:00
38 changed files with 317 additions and 247 deletions

View File

@@ -555,32 +555,32 @@ jobs:
minimum_free_mb: ${{ matrix.minimum_free_mb }}
root_location: ${{ matrix.root_location || 'partition=2' }}
shrink_image: ${{ matrix.shrink_image || 'yes' }}
commands: |
chmod +x scripts/armrunner.sh
./scripts/armrunner.sh
java -jar *.jar --smoketest --platform=${{ matrix.plat_override }}
commands: ./scripts/armrunner.sh
- name: Compress image
# Compress the standard images
if: ${{ ! startsWith(matrix.image_suffix, 'rubik') }}
run: |
set -ex
new_jar=$(realpath $(find . -name photonvision\*-linuxarm64.jar))
new_image_name=$(basename "${new_jar/.jar/_${{ matrix.image_suffix }}.img}")
sudo mv ${{ steps.generate_image.outputs.image }} $new_image_name
sudo xz -T 0 -v $new_image_name
sudo xz -T 0 -kv $new_image_name
echo "smoketest_image_loc=${new_image_name}" >> $GITHUB_ENV
- name: Tar built image
- name: Tar built image (Rubik)
# Build the RubikPi3-specific tar file
if: ${{ startsWith(matrix.image_suffix, 'rubik') }}
run: |
set -ex
new_jar=$(realpath $(find . -name photonvision\*-linuxarm64.jar))
new_image_name=$(basename "${new_jar/.jar/_${{ matrix.image_suffix }}.img}")
tardir=$(basename "${new_jar/.jar/_${{ matrix.image_suffix }}.img}")
imagedir=$(dirname ${{ steps.generate_image.outputs.image }})
tardir=${new_image_name}
sudo mkdir --parents ${tardir}
sudo mv ${imagedir}/* ${tardir}/
sudo tar -I 'xz -T0' -cf ${new_image_name}.tar.xz ${tardir} --checkpoint=10000 --checkpoint-action=echo='%T'
sudo cp ${imagedir}/* ${tardir}/
sudo tar -I 'xz -T0' -cf ${tardir}.tar.xz ${tardir} --checkpoint=10000 --checkpoint-action=echo='%T'
# Point smoketest to the old image
echo "smoketest_image_loc=${{ steps.generate_image.outputs.image }}" >> $GITHUB_ENV
- uses: actions/upload-artifact@v6
name: Upload image
@@ -588,6 +588,16 @@ jobs:
name: image-${{ matrix.image_suffix }}
path: photonvision*.xz
# This is done after uploading the image to avoid contaminating the image with logs, caches, etc.
- uses: photonvision/photon-image-runner@HEAD
name: Smoketest Image
with:
image_url: file://${{ env.smoketest_image_loc }}
minimum_free_mb: ${{ matrix.minimum_free_mb }}
root_location: ${{ matrix.root_location || 'partition=2' }}
shrink_image: ${{ matrix.shrink_image || 'yes' }}
commands: java -jar *.jar --smoketest --platform=${{ matrix.plat_override }}
matrix-checker:
# This job always runs last to set the overall result based on the matrix jobs. If any matrix job failed, this job will fail.
# This makes it so that we don't need to add each matrix job individually to CI checks.
@@ -600,7 +610,7 @@ jobs:
release:
# Require smoketest-native so that if those fail, we don't release broken artifacts
needs: [build-photonlib-vendorjson, build-image, combine, build-package-linux, build-package-macos, build-package-windows, run-smoketest-native]
if: github.ref == ('refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && github.repository == 'PhotonVision/photonvision'
if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && github.repository == 'PhotonVision/photonvision'
runs-on: ubuntu-24.04
steps:
# Download all fat JARs

View File

@@ -12,7 +12,7 @@ concurrency:
cancel-in-progress: true
jobs:
build-and-deploy:
build-py:
runs-on: ubuntu-24.04
steps:
@@ -29,38 +29,57 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel pytest mypy
pip install setuptools wheel
- name: Build wheel
working-directory: ./photon-lib/py
run: python setup.py sdist bdist_wheel
- name: Run Unit Tests
working-directory: ./photon-lib/py
run: |
pip install --no-cache-dir dist/*.whl
pytest
- name: Run mypy type checking
run: mypy --show-column-numbers --config-file photon-lib/py/pyproject.toml photon-lib
- name: Upload artifacts
uses: actions/upload-artifact@v6
with:
name: dist
path: ./photon-lib/py/dist/
- name: Publish package distributions to TestPyPI
# Only upload on tags
if: startsWith(github.ref, 'refs/tags/v')
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: ./photon-lib/py/dist/
test-py:
needs: build-py
runs-on: ubuntu-24.04
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: 3.14
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest mypy
- name: Download artifacts
uses: actions/download-artifact@v6
with:
name: dist
path: dist/
- name: Install package
shell: bash
run: pip install --no-cache-dir dist/*.whl
- name: Run Unit Tests
shell: bash
run: pytest --import-mode=importlib photon-lib/py/test/
- name: Run mypy type checking
run: mypy --show-column-numbers --config-file photon-lib/py/pyproject.toml photon-lib
build-python-examples:
needs: build-py
strategy:
matrix:
os: [ubuntu-24.04, windows-2022, macos-14]
@@ -81,28 +100,18 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel pytest mypy
- name: Build wheel
working-directory: ./photon-lib/py
run: python setup.py sdist bdist_wheel
- name: Download artifacts
uses: actions/download-artifact@v6
with:
name: dist
path: ./photon-lib/py/dist/
- name: Build and configure PhotonLibPy
working-directory: ./photon-lib/py
shell: bash
run: |
./buildAndTest.sh
./enableUsingDevBuilds.sh
- name: Run Unit Tests
- name: Install PhotonLibPy package
working-directory: ./photon-lib/py
shell: bash
run: |
pip install --no-cache-dir dist/*.whl
pytest
- name: Run mypy type checking
run: mypy --show-column-numbers --config-file photon-lib/py/pyproject.toml photon-lib
- name: Build Python examples
working-directory: photonlib-python-examples
@@ -113,3 +122,24 @@ jobs:
echo $folder
./run.sh $folder
done
deploy:
needs: [test-py, build-python-examples]
runs-on: ubuntu-24.04
# Only upload on tags
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Download artifacts
uses: actions/download-artifact@v6
with:
name: dist
path: dist/
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: ./dist/
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing

View File

@@ -4,7 +4,7 @@ plugins {
id "cpp"
id "com.diffplug.spotless" version "8.1.0"
id "edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin" version "2020.2"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
id "edu.wpi.first.GradleRIO" version "2026.2.1"
id 'org.photonvision.tools.WpilibTools' version '2.3.3-photon'
id 'com.google.protobuf' version '0.9.3' apply false
id 'edu.wpi.first.GradleJni' version '1.1.0'
@@ -32,7 +32,7 @@ ext.allOutputsFolder = file("$project.buildDir/outputs")
apply from: "versioningHelper.gradle"
ext {
wpilibVersion = "2026.1.1"
wpilibVersion = "2026.2.1"
wpimathVersion = wpilibVersion
openCVYear = "2025"
openCVversion = "4.10.0-3"

View File

@@ -83,7 +83,7 @@ Details about a particular calibration can be viewed by clicking on that resolut
More info on what these parameters mean can be found in [OpenCV's docs](https://docs.opencv.org/4.8.0/d4/d94/tutorial_camera_calibration.html)
:::
- Fx/Fy: Estimated camera focal length, in mm
- Fx/Fy: Estimated camera focal length, in pixels
- Fx/Cy: Estimated camera optical center, in pixels. This should be at about the center of the image
- Distortion: OpenCV camera model distortion coefficients
- FOV: calculated using estimated focal length and image size. Useful for gut-checking calibration results

View File

@@ -108,7 +108,7 @@ When taking in a result from a `PhotonCamera`, PhotonPoseEstimator offers nine p
flat on the floor. This computation takes place on the RoboRIO, and should not take more than 2ms.
This also requires addHeadingData to be called every frame so heading data is up to date.
Calling one of the `estimate<strategy>Pose()` methods on your `PhotonPoseEstimator` will return an `Optional<EstimatedRobotPose>`, which includes a `Pose3d` of the latest estimated pose (using the selected strategy) along with a `double` of the timestamp when the robot pose was estimated. The recommended way to use the estimatePose methods is to
Calling one of the `estimate<strategy>Pose()` methods on your `PhotonPoseEstimator` will return an `Optional<EstimatedRobotPose>`, which will be empty if there are no detected tags, not enough detected tags (for multi-tag strategies), missing data (typically heading data), or if the internal solvers failed (this is a rare scenario). `EstimatedRobotPose` includes a `Pose3d` of the latest estimated pose (using the selected strategy) along with a `double` of the timestamp when the robot pose was estimated. The recommended way to use the estimatePose methods is to
1. do estimation with one of MultiTag methods, check if the result is empty, then
2. fallback to single tag estimation using a method like `estimateLowestAmbiguityPose`.

View File

@@ -2,7 +2,11 @@
## Coprocessor with regulator
1. **IT IS STRONGLY RECOMMENDED** to use one of the recommended power regulators to prevent vision from cutting out from voltage drops while operating the robot. We recommend wiring the regulator directly to the power header pins or using a locking USB C cable. In any case we recommend hot gluing the connector.
1. **IT IS STRONGLY RECOMMENDED** to use one of the recommended power regulators to prevent vision from cutting out from voltage drops while operating the robot. We recommend wiring the regulator directly to the power header pins using either of the two methods listed below or using a locking USB C cable.
* Method 1: Soldering to GPIO Header Pins
* Using 20 AWG or preferably 18 AWG wires, solder two wires from the regulator to the power header pins on the coprocessor and cover with heat-shrink tubing.
* Method 2: Using a Wire-to-Board Connector
* Using a wire-to-board connector with 20 AWG or preferably 18 AWG wires, connect two wires from the regulator to the power header pins on the coprocessor. To prevent the connector from becoming unseated, we recommend applying hot glue to the connector.
2. Run an ethernet cable from your coprocessor to your network switch / radio.

View File

@@ -99,6 +99,7 @@ const patternHeight = ref(8);
const boardType = ref<CalibrationBoardTypes>(CalibrationBoardTypes.Charuco);
const useOldPattern = ref(false);
const tagFamily = ref<CalibrationTagFamilies>(CalibrationTagFamilies.Dict_4X4_1000);
const requestedVideoFormatIndex = ref(0);
// Emperical testing - with stack size limit of 1MB, we can handle at -least- 700k points
const tooManyPoints = computed(
@@ -191,6 +192,7 @@ const startCalibration = () => {
useCameraSettingsStore().currentCameraSettings.currentPipelineIndex = WebsocketPipelineType.Calib3d;
// isCalibrating.value = true;
calibCanceled.value = false;
requestedVideoFormatIndex.value = useStateStore().calibrationData.videoFormatIndex;
};
const showCalibEndDialog = ref(false);
const calibCanceled = ref(false);
@@ -559,7 +561,7 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
{{
useCameraSettingsStore().currentCameraSettings.validVideoFormats.map((f) =>
getResolutionString(f.resolution)
)[useStateStore().calibrationData.videoFormatIndex]
)[requestedVideoFormatIndex]
}}!
</v-card-text>
</template>

View File

@@ -187,7 +187,7 @@ const viewingImg = ref(0);
.getCalibrationCoeffs(props.videoFormat.resolution)
?.cameraIntrinsics.data[0].toFixed(2) || 0.0
}}
mm
px
</td>
</tr>
<tr>
@@ -198,7 +198,7 @@ const viewingImg = ref(0);
.getCalibrationCoeffs(props.videoFormat.resolution)
?.cameraIntrinsics.data[4].toFixed(2) || 0.0
}}
mm
px
</td>
</tr>
<tr>

View File

@@ -26,7 +26,7 @@ const importWidth = ref<number | null>(null);
const importVersion = ref<string | null>(null);
// TODO gray out the button when model is uploading
const handleImport = async () => {
const handleImport = () => {
if (importModelFile.value === null) return;
const formData = new FormData();
@@ -72,13 +72,13 @@ const handleImport = async () => {
importVersion.value = null;
};
const deleteModel = async (model: ObjectDetectionModelProperties) => {
const deleteModel = (model: ObjectDetectionModelProperties) => {
axiosPost("/objectdetection/delete", "delete an object detection model", {
modelPath: model.modelPath
});
};
const renameModel = async (model: ObjectDetectionModelProperties, newName: string) => {
const renameModel = (model: ObjectDetectionModelProperties, newName: string) => {
useStateStore().showSnackbarMessage({
message: "Renaming Object Detection Model...",
color: "secondary",

View File

@@ -22,7 +22,7 @@ export const statusCheck = async (timeout: number, ip?: string): Promise<boolean
while (pollLimit > 0) {
try {
pollLimit--;
await axios.get(ip ? `http://${ip}/status` : "/status");
await axios.get(ip ? `http://${ip}/api/status` : "/status");
return true;
} catch {
// Backend not ready yet, wait and retry

View File

@@ -146,7 +146,7 @@ public class NeuralNetworkModelManager {
"vase",
"scissors",
"teddy bear",
"hair drier",
"hair drier", // Typo in official COCO documentation
"toothbrush"));
nnProps.addModelProperties(

View File

@@ -26,7 +26,7 @@ import org.photonvision.vision.pipeline.result.CVPipelineResult;
public abstract class CVPipeline<R extends CVPipelineResult, S extends CVPipelineSettings>
implements Releasable {
static final int MAX_MULTI_TARGET_RESULTS = 10;
static final int MAX_MULTI_TARGET_RESULTS = 50;
protected S settings;
protected FrameStaticProperties frameStaticProperties;

View File

@@ -271,6 +271,9 @@ class PhotonCameraSim:
camRt = RotTrlTransform3d.makeRelativeTo(cameraPose)
for tgt in targets:
if len(detectableTgts) >= 50:
break
# pose isn't visible, skip to next
if not self.canSeeTargetPose(cameraPose, tgt):
continue

View File

@@ -4,15 +4,13 @@ import subprocess
from setuptools import find_packages, setup
gitDescribeResult = (
subprocess.check_output(
["git", "describe", "--tags", "--match=v*", "--exclude=*rc*", "--always"]
)
subprocess.check_output(["git", "describe", "--tags", "--match=v*", "--always"])
.decode("utf-8")
.strip()
)
m = re.search(
r"(v[0-9]{4}\.[0-9]{1}\.[0-9]{1})-?((?:beta)?(?:alpha)?)-?([0-9\.]*)",
r"v([0-9]{4}\.[0-9]{1}\.[0-9]{1})-?((?:beta|alpha|rc)?)-?([0-9\.]*)",
gitDescribeResult,
)
@@ -26,7 +24,7 @@ if m:
prefix = m.group(1)
maturity = m.group(2)
suffix = m.group(3).replace(".", "")
versionString = f"{prefix}.{maturity}.{suffix}"
versionString = f"{prefix}{maturity}{suffix}"
else:
split = gitDescribeResult.split("-")
if len(split) == 3:
@@ -35,8 +33,7 @@ if m:
versionString = f"{year[1:]}post{commits}"
print("using dev release " + versionString)
else:
year = gitDescribeResult
versionString = year[1:]
versionString = gitDescribeResult[1:]
print("using full release " + versionString)
@@ -60,12 +57,12 @@ setup(
package_data={"photonlibpy": ["py.typed"]},
version=versionString,
install_requires=[
"numpy~=2.4",
"wpilib==2026.1.1",
"robotpy-wpimath==2026.1.1",
"robotpy-apriltag==2026.1.1",
"robotpy-cscore==2026.1.1",
"pyntcore==2026.1.1",
"numpy~=2.3",
"wpilib>=2026.2.1,<2027",
"robotpy-wpimath>=2026.2.1,<2027",
"robotpy-apriltag>=2026.2.1,<2027",
"robotpy-cscore>=2026.2.1,<2027",
"pyntcore>=2026.2.1,<2027",
"opencv-python;platform_machine!='roborio'",
],
description=descriptionStr,

View File

@@ -78,6 +78,35 @@ def test_VisibilityCupidShuffle() -> None:
assert camera.getLatestResult().hasTargets()
def test_bunchaTargets() -> None:
visionSysSim = VisionSystemSim("Test")
camera = PhotonCamera("camera")
cameraSim = PhotonCameraSim(camera)
visionSysSim.addCamera(cameraSim, Transform3d())
cameraSim.prop.setCalibrationFromFOV(640, 480, fovDiag=Rotation2d.fromDegrees(80.0))
for i in range(100):
targetPose = Pose3d(
Translation3d(15.98 + i * 0.1, 0.0, 2.0), Rotation3d(0, 0, math.pi)
)
visionSysSim.addVisionTargets(
[
VisionTargetSim(
targetPose,
TargetModel.createPlanar(width=1.0, height=1.0),
4774 + i,
)
]
)
robotPose = Pose2d(Translation2d(2.0, 0.0), Rotation2d.fromDegrees(5.0))
visionSysSim.update(robotPose)
assert len(camera.getLatestResult().getTargets()) == 50
def test_NotVisibleVert1() -> None:
targetPose = Pose3d(Translation3d(15.98, 0.0, 2.0), Rotation3d(0, 0, math.pi))

View File

@@ -45,7 +45,6 @@ import edu.wpi.first.wpilibj.Alert;
import edu.wpi.first.wpilibj.Alert.AlertType;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.util.WPILibVersion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -189,50 +188,6 @@ public class PhotonCamera implements AutoCloseable {
static void verifyDependencies() {
// spotless:off
if (!WPILibVersion.Version.equals(PhotonVersion.wpilibTargetVersion)) {
String bfw = """
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\s
>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\s
>>> \s
>>> You are running an incompatible version \s
>>> of PhotonVision ! \s
>>> \s
>>> PhotonLib """
+ PhotonVersion.versionString
+ " is built for WPILib "
+ PhotonVersion.wpilibTargetVersion
+ "\n"
+ ">>> but you are using WPILib "
+ WPILibVersion.Version
+ """
\n>>> \s
>>> This is neither tested nor supported. \s
>>> You MUST update WPILib, PhotonLib, or both.
>>> Check `./gradlew dependencies` and ensure\s
>>> all mentions of OpenCV match the version \s
>>> that PhotonLib was built for. If you find a
>>> a mismatched version in a dependency, you\s
>>> must take steps to update the version of \s
>>> OpenCV used in that dependency. If you do\s
>>> not control that dependency and an updated\s
>>> version is not available, contact the \s
>>> developers of that dependency. \s
>>> \s
>>> Your code will now crash. \s
>>> We hope your day gets better. \s
>>> \s
>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\s
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\s
""";
DriverStation.reportWarning(bfw, false);
DriverStation.reportError(bfw, false);
throw new UnsupportedOperationException(bfw);
}
if (!Core.VERSION.equals(PhotonVersion.opencvTargetVersion)) {
String bfw = """

View File

@@ -686,7 +686,7 @@ public class PhotonPoseEstimator {
*
* @param cameraResult A pipeline result from the camera.
* @return An {@link EstimatedRobotPose} with an estimated pose, timestamp, and targets used to
* create the estimate.
* create the estimate, or an empty optional if there's no targets or heading data.
*/
public Optional<EstimatedRobotPose> estimatePnpDistanceTrigSolvePose(
PhotonPipelineResult cameraResult) {
@@ -758,7 +758,8 @@ public class PhotonPoseEstimator {
* @param headingScaleFactor If headingFree is false, this weights the cost of changing our robot
* heading estimate against the tag corner reprojection error cont.
* @return An {@link EstimatedRobotPose} with an estimated pose, timestamp, and targets used to
* create the estimate.
* create the estimate, or an empty optional if there's no targets or heading data, or if the
* solver fails to solve the problem.
*/
public Optional<EstimatedRobotPose> estimateConstrainedSolvepnpPose(
PhotonPipelineResult cameraResult,
@@ -770,6 +771,18 @@ public class PhotonPoseEstimator {
if (!shouldEstimate(cameraResult)) {
return Optional.empty();
}
// Need heading if heading fixed
if (!headingFree) {
if (headingBuffer.getSample(cameraResult.getTimestampSeconds()).isEmpty()) {
return Optional.empty();
} else {
// If heading fixed, force rotation component
seedPose =
new Pose3d(
seedPose.getTranslation(),
new Rotation3d(headingBuffer.getSample(cameraResult.getTimestampSeconds()).get()));
}
}
var pnpResult =
VisionEstimation.estimateRobotPoseConstrainedSolvepnp(
cameraMatrix,
@@ -799,7 +812,8 @@ public class PhotonPoseEstimator {
*
* @param cameraResult A pipeline result from the camera.
* @return An {@link EstimatedRobotPose} with an estimated pose, timestamp, and targets used to
* create the estimate.
* create the estimate, or an empty optional if there's no targets, no multi-tag results, or
* multi-tag is disabled in the web UI.
*/
public Optional<EstimatedRobotPose> estimateCoprocMultiTagPose(
PhotonPipelineResult cameraResult) {
@@ -829,7 +843,8 @@ public class PhotonPoseEstimator {
* @param cameraMatrix Camera intrinsics from camera calibration data
* @param distCoeffs Distortion coefficients from camera calibration data.
* @return An {@link EstimatedRobotPose} with an estimated pose, timestamp, and targets used to
* create the estimate.
* create the estimate, or an empty optional if there's less than 2 targets visible or
* SolvePnP fails.
*/
public Optional<EstimatedRobotPose> estimateRioMultiTagPose(
PhotonPipelineResult cameraResult, Matrix<N3, N3> cameraMatrix, Matrix<N8, N1> distCoeffs) {
@@ -861,7 +876,7 @@ public class PhotonPoseEstimator {
*
* @param cameraResult A pipeline result from the camera.
* @return An {@link EstimatedRobotPose} with an estimated pose, timestamp, and targets used to
* create the estimate.
* create the estimate, or an empty optional if there's no targets.
*/
public Optional<EstimatedRobotPose> estimateLowestAmbiguityPose(
PhotonPipelineResult cameraResult) {
@@ -911,7 +926,7 @@ public class PhotonPoseEstimator {
*
* @param cameraResult A pipeline result from the camera.
* @return An {@link EstimatedRobotPose} with an estimated pose, timestamp, and targets used to
* create the estimate.
* create the estimate, or an empty optional if there's no targets.
*/
public Optional<EstimatedRobotPose> estimateClosestToCameraHeightPose(
PhotonPipelineResult cameraResult) {
@@ -989,7 +1004,7 @@ public class PhotonPoseEstimator {
* @param cameraResult A pipeline result from the camera.
* @param referencePose reference pose to check vector magnitude difference against.
* @return An {@link EstimatedRobotPose} with an estimated pose, timestamp, and targets used to
* create the estimate.
* create the estimate, or an empty optional if there's no targets.
*/
public Optional<EstimatedRobotPose> estimateClosestToReferencePose(
PhotonPipelineResult cameraResult, Pose3d referencePose) {
@@ -1062,7 +1077,7 @@ public class PhotonPoseEstimator {
*
* @param cameraResult A pipeline result from the camera.
* @return An {@link EstimatedRobotPose} with an estimated pose, timestamp, and targets used to
* create the estimate.
* create the estimate, or an empty optional if there's no targets.
*/
public Optional<EstimatedRobotPose> estimateAverageBestTargetsPose(
PhotonPipelineResult cameraResult) {

View File

@@ -442,6 +442,10 @@ public class PhotonCameraSim implements AutoCloseable {
Mat.zeros(videoFrameSize, CvType.CV_8UC1).assignTo(videoSimFrameRaw);
for (var tgt : targets) {
if (detectableTgts.size() >= 50) {
break;
}
// pose isn't visible, skip to next
if (!canSeeTargetPose(cameraPose, tgt)) continue;

View File

@@ -29,7 +29,6 @@
#include <string_view>
#include <vector>
#include <WPILibVersion.h>
#include <frc/Errors.h>
#include <frc/RobotController.h>
#include <frc/Timer.h>
@@ -47,48 +46,6 @@ static constexpr units::second_t WARN_DEBOUNCE_SEC = 5_s;
static constexpr units::second_t HEARTBEAT_DEBOUNCE_SEC = 500_ms;
inline void verifyDependencies() {
if (!(std::string_view{GetWPILibVersion()} ==
std::string_view{photon::PhotonVersion::wpilibTargetVersion})) {
std::string bfw =
"\n\n\n\n\n"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
">>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
">>> \n"
">>> You are running an incompatible version \n"
">>> of PhotonVision ! \n"
">>> \n"
">>> PhotonLib ";
bfw += photon::PhotonVersion::versionString;
bfw += " is built for WPILib ";
bfw += photon::PhotonVersion::wpilibTargetVersion;
bfw +=
"\n"
">>> but you are using WPILib ";
bfw += GetWPILibVersion();
bfw +=
"\n>>> \n"
">>> This is neither tested nor supported. \n"
">>> You MUST update WPILib, PhotonLib, or both.\n"
">>> Check `./gradlew dependencies` and ensure\n"
">>> all mentions of WPILib match the version \n"
">>> that PhotonLib was built for. If you find a"
">>> a mismatched version in a dependency, you\n"
">>> must take steps to update the version of \n"
">>> WPILib used in that dependency. If you do\n"
">>> not control that dependency and an updated\n"
">>> version is not available, contact the \n"
">>> developers of that dependency. \n"
">>> \n"
">>> Your code will now crash. \n"
">>> We hope your day gets better. \n"
">>> \n"
">>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n";
FRC_ReportWarning(bfw);
FRC_ReportError(frc::err::Error, bfw);
throw new std::runtime_error(std::string{bfw});
}
if (!(std::string_view{cv::getVersionString()} ==
std::string_view{photon::PhotonVersion::opencvTargetVersion})) {
std::string bfw =

View File

@@ -634,13 +634,18 @@ PhotonPoseEstimator::EstimateConstrainedSolvepnpPose(
if (!ShouldEstimate(cameraResult)) {
return std::nullopt;
}
// Need heading if heading fixed
if (!headingFree) {
seedPose = frc::Pose3d{
seedPose.Translation(),
frc::Rotation3d{
headingBuffer.Sample(cameraResult.GetTimestamp()).value()}};
if (!headingBuffer.Sample(cameraResult.GetTimestamp())) {
return std::nullopt;
} else {
// If heading fixed, force rotation component
seedPose = frc::Pose3d{
seedPose.Translation(),
frc::Rotation3d{
headingBuffer.Sample(cameraResult.GetTimestamp()).value()}};
}
}
std::vector<photon::PhotonTrackedTarget> targets{
cameraResult.GetTargets().begin(), cameraResult.GetTargets().end()};

View File

@@ -131,6 +131,10 @@ PhotonPipelineResult PhotonCameraSim::Process(
blankFrame.assignTo(videoSimFrameRaw);
for (const auto& tgt : targets) {
if (detectableTgts.size() >= 50) {
break;
}
if (!CanSeeTargetPose(cameraPose, tgt)) {
continue;
}

View File

@@ -294,7 +294,7 @@ class PhotonPoseEstimator {
*
* @param cameraResult A pipeline result from the camera.
* @return An EstimatedRobotPose with an estimated pose, timestamp, and
* targets used to create the estimate.
* targets used to create the estimate, or std::nullopt if there's no targets.
*/
std::optional<EstimatedRobotPose> EstimateLowestAmbiguityPose(
PhotonPipelineResult cameraResult);
@@ -306,7 +306,7 @@ class PhotonPoseEstimator {
*
* @param cameraResult A pipeline result from the camera.
* @return An EstimatedRobotPose with an estimated pose, timestamp and
* targets used to create the estimate.
* targets used to create the estimate, or std::nullopt if there's no targets.
*/
std::optional<EstimatedRobotPose> EstimateClosestToCameraHeightPose(
PhotonPipelineResult cameraResult);
@@ -319,7 +319,7 @@ class PhotonPoseEstimator {
* @param referencePose reference pose to check vector magnitude difference
* against.
* @return An EstimatedRobotPose with an estimated pose, timestamp, and
* targets used to create the estimate.
* targets used to create the estimate, or std::nullopt if there's no targets.
*/
std::optional<EstimatedRobotPose> EstimateClosestToReferencePose(
PhotonPipelineResult cameraResult, frc::Pose3d referencePose);
@@ -331,7 +331,8 @@ class PhotonPoseEstimator {
*
* @param cameraResult A pipeline result from the camera.
* @return An EstimatedRobotPose with an estimated pose, timestamp, and
* targets used to create the estimate.
* targets used to create the estimate or std::nullopt if there's no targets,
* no multi-tag results, or multi-tag is disabled in the web UI.
*/
std::optional<EstimatedRobotPose> EstimateCoprocMultiTagPose(
PhotonPipelineResult cameraResult);
@@ -345,7 +346,8 @@ class PhotonPoseEstimator {
* @param cameraMatrix Camera intrinsics from camera calibration data.
* @param distCoeffs Distortion coefficients from camera calibration data.
* @return An EstimatedRobotPose with an estimated pose, timestamp, and
* targets used to create the estimate.
* targets used to create the estimate, or std::nullopt if there's less than 2
* targets visible or SolvePnP fails.
*/
std::optional<EstimatedRobotPose> EstimateRioMultiTagPose(
PhotonPipelineResult cameraResult, PhotonCamera::CameraMatrix camMat,
@@ -363,7 +365,8 @@ class PhotonPoseEstimator {
*
* @param cameraResult A pipeline result from the camera.
* @return An EstimatedRobotPose with an estimated pose, timestamp, and
* targets used to create the estimate.
* targets used to create the estimate, or std::nullopt if there's no targets
* or heading data.
*/
std::optional<EstimatedRobotPose> EstimatePnpDistanceTrigSolvePose(
PhotonPipelineResult cameraResult);
@@ -372,7 +375,7 @@ class PhotonPoseEstimator {
* Return the average of the best target poses using ambiguity as weight.
* @param cameraResult A pipeline result from the camera.
* @return An EstimatedRobotPose with an estimated pose, timestamp, and
* targets used to create the estimate.
* targets used to create the estimate, or std::nullopt if there's no targets.
*/
std::optional<EstimatedRobotPose> EstimateAverageBestTargetsPose(
PhotonPipelineResult cameraResult);
@@ -401,7 +404,8 @@ class PhotonPoseEstimator {
* changing our robot heading estimate against the tag corner reprojection
* error cost.
* @return An EstimatedRobotPose with an estimated pose, timestamp, and
* targets used to create the estimate.
* targets used to create the estimate, or std::nullopt if there's no targets
* or heading data, or if the solver fails to solve the problem.
*/
std::optional<EstimatedRobotPose> EstimateConstrainedSolvepnpPose(
photon::PhotonPipelineResult cameraResult,

View File

@@ -179,6 +179,28 @@ class VisionSystemSimTest {
assertTrue(result.hasTargets());
}
@Test
public void testBunchaTargets() {
var visionSysSim = new VisionSystemSim("Test");
var camera = new PhotonCamera(inst, "camera");
var cameraSim = new PhotonCameraSim(camera);
visionSysSim.addCamera(cameraSim, new Transform3d());
cameraSim.prop.setCalibration(640, 480, Rotation2d.fromDegrees(80));
for (int i = 0; i < 100; i++) {
final var targetPose =
new Pose3d(new Translation3d(15.98 + i * 0.1, 0, 1), new Rotation3d(0, 0, Math.PI));
visionSysSim.addVisionTargets(new VisionTargetSim(targetPose, new TargetModel(0.5, 0.5), i));
}
var robotPose = new Pose2d(new Translation2d(5, 0), Rotation2d.fromDegrees(5));
visionSysSim.update(robotPose);
var res = waitForSequenceNumber(camera, 1);
assertEquals(50, res.getTargets().size());
}
@Test
public void testNotVisibleVert1() {
final var targetPose =

View File

@@ -117,6 +117,29 @@ TEST_F(VisionSystemSimTest, TestVisibilityCupidShuffle) {
ASSERT_TRUE(camera.GetLatestResult().HasTargets());
}
TEST_F(VisionSystemSimTest, TestBunchaTargets) {
photon::VisionSystemSim visionSysSim{"Test"};
photon::PhotonCamera camera{"camera"};
photon::PhotonCameraSim cameraSim{&camera};
visionSysSim.AddCamera(&cameraSim, frc::Transform3d{});
cameraSim.prop.SetCalibration(640, 480, frc::Rotation2d{80_deg});
std::vector<photon::VisionTargetSim> targets;
for (int i = 0; i < 100; i++) {
targets.emplace_back(
frc::Pose3d{
frc::Translation3d{15.98_m + i * 0.1_m, 0_m, 1_m},
frc::Rotation3d{0_rad, 0_rad, units::radian_t{std::numbers::pi}}},
photon::TargetModel{0.5_m, 0.5_m}, i);
}
visionSysSim.AddVisionTargets(targets);
frc::Pose2d robotPose{frc::Translation2d{5_m, 0_m}, frc::Rotation2d{5_deg}};
visionSysSim.Update(robotPose);
ASSERT_EQ(camera.GetLatestResult().targets.size(), 50u);
}
TEST_F(VisionSystemSimTest, TestNotVisibleVert1) {
frc::Pose3d targetPose{
frc::Translation3d{15.98_m, 0_m, 1_m},

View File

@@ -852,6 +852,12 @@ public class RequestHandler {
ctx.result("There was an error while saving the uploaded object detection models");
logger.error("There was an error while saving the uploaded object detection models");
}
DataChangeService.getInstance()
.publishEvent(
new OutgoingUIEvent<>(
"fullsettings",
UIPhotonConfiguration.programStateToUi(ConfigManager.getInstance().getConfig())));
}
private record DeleteObjectDetectionModelRequest(Path modelPath) {}
@@ -898,17 +904,17 @@ public class RequestHandler {
ctx.status(200).result("Successfully deleted object detection model");
DataChangeService.getInstance()
.publishEvent(
new OutgoingUIEvent<>(
"fullsettings",
UIPhotonConfiguration.programStateToUi(ConfigManager.getInstance().getConfig())));
} catch (Exception e) {
ctx.status(500);
ctx.result("Error deleting object detection model: " + e.getMessage());
logger.error("Error deleting object detection model", e);
}
DataChangeService.getInstance()
.publishEvent(
new OutgoingUIEvent<>(
"fullsettings",
UIPhotonConfiguration.programStateToUi(ConfigManager.getInstance().getConfig())));
}
private record RenameObjectDetectionModelRequest(Path modelPath, String newName) {}
@@ -951,6 +957,12 @@ public class RequestHandler {
NeuralNetworkModelManager.getInstance().discoverModels();
ctx.status(200).result("Successfully renamed object detection model");
DataChangeService.getInstance()
.publishEvent(
new OutgoingUIEvent<>(
"fullsettings",
UIPhotonConfiguration.programStateToUi(ConfigManager.getInstance().getConfig())));
} catch (Exception e) {
ctx.status(500);
ctx.result("Error renaming object detection model: " + e.getMessage());
@@ -970,6 +982,12 @@ public class RequestHandler {
ctx.result("Error clearing object detection models: " + e.getMessage());
logger.error("Error clearing object detection models", e);
}
DataChangeService.getInstance()
.publishEvent(
new OutgoingUIEvent<>(
"fullsettings",
UIPhotonConfiguration.programStateToUi(ConfigManager.getInstance().getConfig())));
}
public static void onDeviceRestartRequest(Context ctx) {

View File

@@ -1,7 +1,7 @@
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
id "edu.wpi.first.GradleRIO" version "2026.2.1"
}
repositories {
@@ -11,8 +11,8 @@ repositories {
wpi.maven.useLocal = false
wpi.maven.useDevelopment = false
wpi.versions.wpilibVersion = "2026.1.1"
wpi.versions.wpimathVersion = "2026.1.1"
wpi.versions.wpilibVersion = "2026.2.1"
wpi.versions.wpimathVersion = "2026.2.1"
// Define my targets (RoboRIO) and artifacts (deployable files)
// This is added by GradleRIO's backing project DeployUtils.

View File

@@ -1,7 +1,7 @@
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
id "edu.wpi.first.GradleRIO" version "2026.2.1"
}
repositories {
@@ -11,8 +11,8 @@ repositories {
wpi.maven.useLocal = false
wpi.maven.useDevelopment = false
wpi.versions.wpilibVersion = "2026.1.1"
wpi.versions.wpimathVersion = "2026.1.1"
wpi.versions.wpilibVersion = "2026.2.1"
wpi.versions.wpimathVersion = "2026.2.1"
// Define my targets (RoboRIO) and artifacts (deployable files)
// This is added by GradleRIO's backing project DeployUtils.

View File

@@ -1,7 +1,7 @@
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
id "edu.wpi.first.GradleRIO" version "2026.2.1"
}
repositories {
@@ -11,8 +11,8 @@ repositories {
wpi.maven.useLocal = false
wpi.maven.useDevelopment = false
wpi.versions.wpilibVersion = "2026.1.1"
wpi.versions.wpimathVersion = "2026.1.1"
wpi.versions.wpilibVersion = "2026.2.1"
wpi.versions.wpimathVersion = "2026.2.1"
// Define my targets (RoboRIO) and artifacts (deployable files)
// This is added by GradleRIO's backing project DeployUtils.

View File

@@ -1,6 +1,6 @@
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
id "edu.wpi.first.GradleRIO" version "2026.2.1"
}
sourceCompatibility = JavaVersion.VERSION_17
@@ -13,8 +13,8 @@ repositories {
}
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = "2026.1.1"
wpi.versions.wpimathVersion = "2026.1.1"
wpi.versions.wpilibVersion = "2026.2.1"
wpi.versions.wpimathVersion = "2026.2.1"
// Define my targets (RoboRIO) and artifacts (deployable files)

View File

@@ -1,6 +1,6 @@
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
id "edu.wpi.first.GradleRIO" version "2026.2.1"
}
sourceCompatibility = JavaVersion.VERSION_17
@@ -9,8 +9,8 @@ targetCompatibility = JavaVersion.VERSION_17
def ROBOT_MAIN_CLASS = "frc.robot.Main"
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = "2026.1.1"
wpi.versions.wpimathVersion = "2026.1.1"
wpi.versions.wpilibVersion = "2026.2.1"
wpi.versions.wpimathVersion = "2026.2.1"
// Define my targets (RoboRIO) and artifacts (deployable files)

View File

@@ -1,6 +1,6 @@
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
id "edu.wpi.first.GradleRIO" version "2026.2.1"
}
sourceCompatibility = JavaVersion.VERSION_17
@@ -9,8 +9,8 @@ targetCompatibility = JavaVersion.VERSION_17
def ROBOT_MAIN_CLASS = "frc.robot.Main"
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = "2026.1.1"
wpi.versions.wpimathVersion = "2026.1.1"
wpi.versions.wpilibVersion = "2026.2.1"
wpi.versions.wpimathVersion = "2026.2.1"
// Define my targets (RoboRIO) and artifacts (deployable files)

View File

@@ -6,7 +6,7 @@
[tool.robotpy]
# Version of robotpy this project depends on
robotpy_version = "2026.1.1"
robotpy_version = "2026.2.1"
# Which extra RobotPy components should be installed
# -> equivalent to `pip install robotpy[extra1, ...]

View File

@@ -6,7 +6,7 @@
[tool.robotpy]
# Version of robotpy this project depends on
robotpy_version = "2026.1.1"
robotpy_version = "2026.2.1"
# Which extra RobotPy components should be installed
# -> equivalent to `pip install robotpy[extra1, ...]

View File

@@ -6,7 +6,7 @@
[tool.robotpy]
# Version of robotpy this project depends on
robotpy_version = "2026.1.1"
robotpy_version = "2026.2.1"
# Which extra RobotPy components should be installed
# -> equivalent to `pip install robotpy[extra1, ...]

View File

@@ -5,19 +5,6 @@ if [ $# -eq 0 ]
exit 1
fi
# To run any example, we want to use photonlib out of this repo
# Build the wheel first
pushd ../photon-lib/py
if [ -d build ]
then rm -rdf build
fi
python3 setup.py bdist_wheel
popd
# Add the output directory to PYTHONPATH to make sure it gets picked up
export PHOTONLIBPY_ROOT=../photon-lib/py
export PYTHONPATH=$PHOTONLIBPY_ROOT
# Move to the right example folder
cd $1

0
scripts/armrunner.sh Normal file → Executable file
View File

View File

@@ -33,6 +33,27 @@
],
"id": "500d656b7cc0ebd7"
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"#### *Numpy Fix* - Important for Google Colab Users\n",
"\n",
"Google Colab comes with an incompatible version of Numpy installed. To fix this, please run the following cells below and **restart your session** when prompted."
],
"id": "b3a9e1a334bce144"
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": [
"%pip uninstall numpy -y\n",
"%pip install \"numpy==1.26.4\""
],
"id": "7156e69495f48f49"
},
{
"metadata": {},
"cell_type": "markdown",
@@ -41,7 +62,7 @@
"\n",
"Please run the cell below to be able to use the `create_onnx` and `create_rknn` functions."
],
"id": "798298b1dbe33d2d"
"id": "51566ff74470e57"
},
{
"metadata": {},
@@ -132,6 +153,7 @@
" os.path.join(ultralytics_folder_name_yolov5, \"requirements.txt\"),\n",
" \"torch<2.6.0\",\n",
" \"onnx==1.18.0\",\n",
" \"numpy==1.26.4\",\n",
" \"onnxscript\",\n",
" ]\n",
" )\n",
@@ -172,7 +194,7 @@
"def run_onnx_conversion_no_anchor(model_path):\n",
" check_or_clone_rockchip_repo(yolo_non_anchor_repo)\n",
" run_pip_install_or_else_exit(\n",
" [\"-e\", ultralytics_default_folder_name, \"onnx==1.18.0\", \"onnxscript\"]\n",
" [\"-e\", ultralytics_default_folder_name, \"onnx==1.18.0\", \"numpy==1.26.4\", \"onnxscript\"]\n",
" )\n",
"\n",
" sys.path.insert(0, os.path.abspath(ultralytics_default_folder_name))\n",
@@ -384,28 +406,7 @@
" except SystemExit:\n",
" print(\"RKNN Conversion failed, see output above\")\n"
],
"id": "ea6869140a61126d"
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"#### *Numpy Fix* - Important for Google Colab Users\n",
"\n",
"Google Colab comes with an incompatible version of Numpy installed. To fix this, please run the following cells below and **restart your session** when prompted."
],
"id": "b3a9e1a334bce144"
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": [
"%pip uninstall numpy -y\n",
"%pip install \"numpy>=1.23.0,<2.0.0\""
],
"id": "7156e69495f48f49"
"id": "4d7a8adee7a03377"
},
{
"metadata": {},