Compare commits
49 Commits
v2025.0.0-
...
v2025.0.0-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bbf49bc6b | ||
|
|
dfed7e3621 | ||
|
|
4dc4ae88de | ||
|
|
c50c657193 | ||
|
|
c04e13ef93 | ||
|
|
5f3dc152c3 | ||
|
|
a64491a59e | ||
|
|
a7319ce1d6 | ||
|
|
02c94ea7ed | ||
|
|
c7ed37789e | ||
|
|
744e522aea | ||
|
|
af03ae0a8b | ||
|
|
31ec9baa95 | ||
|
|
1fc93bd05d | ||
|
|
5bee683661 | ||
|
|
b3d74e56a0 | ||
|
|
b5d48a6503 | ||
|
|
2ea4da0f1e | ||
|
|
152b4391b8 | ||
|
|
4b2787a8b2 | ||
|
|
d8de4a7863 | ||
|
|
14f7155a23 | ||
|
|
d188c37466 | ||
|
|
14fcc5d485 | ||
|
|
1d8d934a8a | ||
|
|
bdb2949b4b | ||
|
|
4cf1c7eee4 | ||
|
|
04ec99f17a | ||
|
|
150561abf2 | ||
|
|
58a0597c86 | ||
|
|
a842581785 | ||
|
|
8dcf0b31a2 | ||
|
|
a99a8e750b | ||
|
|
a0b22cd8a3 | ||
|
|
5d55d215ec | ||
|
|
625dacb020 | ||
|
|
fc8ecac376 | ||
|
|
75e2498f53 | ||
|
|
7a4ea3dd56 | ||
|
|
5e1a93950e | ||
|
|
380546cee0 | ||
|
|
d7a7610917 | ||
|
|
37aaa49b32 | ||
|
|
937bafa8e2 | ||
|
|
3d18ded3f6 | ||
|
|
daa5842fb5 | ||
|
|
6f52267c26 | ||
|
|
acbae88d34 | ||
|
|
986c7020c3 |
105
.github/workflows/build.yml
vendored
@@ -39,8 +39,20 @@ jobs:
|
||||
name: built-client
|
||||
path: photon-client/dist/
|
||||
build-examples:
|
||||
name: "Build Examples"
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2022
|
||||
architecture: x64
|
||||
- os: macos-14
|
||||
architecture: aarch64
|
||||
- os: ubuntu-22.04
|
||||
|
||||
name: "Photonlib - Build Examples - ${{ matrix.os }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -48,31 +60,31 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Fetch tags
|
||||
run: git fetch --tags --force
|
||||
- name: Install RoboRIO Toolchain
|
||||
run: ./gradlew installRoboRioToolchain
|
||||
- name: Install Java 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
- name: Install RoboRIO Toolchain
|
||||
run: ./gradlew installRoboRioToolchain
|
||||
# Need to publish to maven local first, so that C++ sim can pick it up
|
||||
# Still haven't figured out how to make the vendordep file be copied before trying to build examples
|
||||
- name: Publish photonlib to maven local
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew publishtomavenlocal -x check
|
||||
./gradlew photon-targeting:publishtomavenlocal photon-lib:publishtomavenlocal -x check
|
||||
- name: Build Java examples
|
||||
working-directory: photonlib-java-examples
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew copyPhotonlib -x check
|
||||
./gradlew build -x check
|
||||
./gradlew build
|
||||
- name: Build C++ examples
|
||||
working-directory: photonlib-cpp-examples
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew copyPhotonlib -x check
|
||||
./gradlew build -x check
|
||||
./gradlew build
|
||||
build-gradle:
|
||||
name: "Gradle Build"
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -85,7 +97,7 @@ jobs:
|
||||
- name: Fetch tags
|
||||
run: git fetch --tags --force
|
||||
- name: Install Java 17
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
@@ -100,11 +112,11 @@ jobs:
|
||||
- name: Gradle Coverage
|
||||
run: ./gradlew jacocoTestReport
|
||||
- name: Publish Coverage Report
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./photon-server/build/reports/jacoco/test/jacocoTestReport.xml
|
||||
- name: Publish Core Coverage Report
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./photon-core/build/reports/jacoco/test/jacocoTestReport.xml
|
||||
build-offline-docs:
|
||||
@@ -115,6 +127,10 @@ jobs:
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install graphviz
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install graphviz
|
||||
- name: Install dependencies
|
||||
working-directory: docs
|
||||
run: |
|
||||
@@ -129,6 +145,37 @@ jobs:
|
||||
with:
|
||||
name: built-docs
|
||||
path: docs/build/html
|
||||
|
||||
build-photonlib-vendorjson:
|
||||
name: "Build Vendor JSON"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Java 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
||||
# grab all tags
|
||||
- run: git fetch --tags --force
|
||||
|
||||
# Generate the JSON and give it the ""standard""" name maven gives it
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew photon-lib:generateVendorJson
|
||||
export VERSION=$(git describe --tags --match=v*)
|
||||
mv photon-lib/build/generated/vendordeps/photonlib.json photon-lib/build/generated/vendordeps/photonlib-$(git describe --tags --match=v*).json
|
||||
|
||||
# Upload it here so it shows up in releases
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: photonlib-vendor-json
|
||||
path: photon-lib/build/generated/vendordeps/photonlib-*.json
|
||||
|
||||
build-photonlib-host:
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: 13
|
||||
@@ -161,6 +208,7 @@ jobs:
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew photon-targeting:build photon-lib:build -i
|
||||
name: Build with Gradle
|
||||
- run: ./gradlew photon-lib:publish photon-targeting:publish
|
||||
name: Publish
|
||||
env:
|
||||
@@ -283,6 +331,9 @@ jobs:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
architecture: ${{ matrix.architecture }}
|
||||
- name: Install Arm64 Toolchain
|
||||
run: ./gradlew installArm64Toolchain
|
||||
if: ${{ (matrix.artifact-name) == 'LinuxArm64' }}
|
||||
- run: |
|
||||
rm -rf photon-server/src/main/resources/web/*
|
||||
mkdir -p photon-server/src/main/resources/web/docs
|
||||
@@ -301,7 +352,7 @@ jobs:
|
||||
path: photon-server/src/main/resources/web/docs
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew photon-server:shadowJar -PArchOverride=${{ matrix.arch-override }}
|
||||
./gradlew photon-targeting:jar photon-server:shadowJar -PArchOverride=${{ matrix.arch-override }}
|
||||
if: ${{ (matrix.arch-override != 'none') }}
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
@@ -311,6 +362,10 @@ jobs:
|
||||
with:
|
||||
name: jar-${{ matrix.artifact-name }}
|
||||
path: photon-server/build/libs
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: photon-targeting_jar-${{ matrix.artifact-name }}
|
||||
path: photon-targeting/build/libs
|
||||
|
||||
run-smoketest-native:
|
||||
needs: [build-package]
|
||||
@@ -344,7 +399,7 @@ jobs:
|
||||
- run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes libcholmod3 liblapack3 libsuitesparseconfig5
|
||||
if: ${{ (matrix.os) == 'ubuntu-latest' }}
|
||||
if: ${{ (matrix.os) == 'ubuntu-22.04' }}
|
||||
# and actually run the jar
|
||||
- run: java -jar ${{ matrix.extraOpts }} *.jar --smoketest
|
||||
if: ${{ (matrix.os) != 'windows-latest' }}
|
||||
@@ -439,7 +494,7 @@ jobs:
|
||||
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-6/photonvision_opi5pro.img.xz
|
||||
cpu: cortex-a8
|
||||
image_additional_mb: 1024
|
||||
- os: ubuntu-latest
|
||||
- os: ubuntu-22.04
|
||||
artifact-name: LinuxArm64
|
||||
image_suffix: orangepi5max
|
||||
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-6/photonvision_opi5max.img.xz
|
||||
@@ -495,6 +550,11 @@ jobs:
|
||||
with:
|
||||
merge-multiple: true
|
||||
pattern: photonlib-offline
|
||||
# Download vendor json
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
pattern: photonlib-vendor-json
|
||||
# Download all images
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -517,14 +577,14 @@ jobs:
|
||||
# Upload all jars and xz archives
|
||||
# Split into two uploads to work around max size limits in action-gh-releases
|
||||
# https://github.com/softprops/action-gh-release/issues/353
|
||||
- uses: softprops/action-gh-release@v2.0.8
|
||||
- uses: softprops/action-gh-release@v2.0.9
|
||||
with:
|
||||
files: |
|
||||
**/*orangepi5*.xz
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: softprops/action-gh-release@v2.0.8
|
||||
- uses: softprops/action-gh-release@v2.0.9
|
||||
with:
|
||||
files: |
|
||||
**/!(*orangepi5*).xz
|
||||
@@ -534,3 +594,18 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
dispatch:
|
||||
name: dispatch
|
||||
needs: [build-photonlib-vendorjson, release]
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: peter-evans/repository-dispatch@v3
|
||||
if: |
|
||||
github.repository == 'PhotonVision/photonvision' &&
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
token: ${{ secrets.VENDOR_JSON_REPO_PUSH_TOKEN }}
|
||||
repository: PhotonVision/vendor-json-repo
|
||||
event-type: tag
|
||||
client-payload: '{"run_id": "${{ github.run_id }}", "package_version": "${{ github.ref_name }}"}'
|
||||
|
||||
17
.github/workflows/lint-format.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
name: "wpiformat"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch all history and metadata
|
||||
run: |
|
||||
git fetch --prune --unshallow
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
with:
|
||||
python-version: 3.11
|
||||
- name: Install wpiformat
|
||||
run: pip3 install wpiformat==2024.41
|
||||
run: pip3 install wpiformat==2024.45
|
||||
- name: Run
|
||||
run: wpiformat
|
||||
- name: Check output
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
- name: Generate diff
|
||||
run: git diff HEAD > wpiformat-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wpiformat fixes
|
||||
path: wpiformat-fixes.patch
|
||||
@@ -54,16 +54,17 @@ jobs:
|
||||
name: "Java Formatting"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew spotlessCheck
|
||||
name: Run spotless
|
||||
|
||||
client-lint-format:
|
||||
name: "PhotonClient Lint and Formatting"
|
||||
@@ -72,9 +73,9 @@ jobs:
|
||||
working-directory: photon-client
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install Dependencies
|
||||
@@ -87,7 +88,7 @@ jobs:
|
||||
name: "Check server index.html not changed"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch all history and metadata
|
||||
run: |
|
||||
git fetch --prune --unshallow
|
||||
|
||||
2
.github/workflows/photon-code-docs.yml
vendored
@@ -59,7 +59,7 @@ jobs:
|
||||
- name: Fetch tags
|
||||
run: git fetch --tags --force
|
||||
- name: Install Java 17
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
||||
7
.github/workflows/photonvision-docs.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
@@ -26,6 +26,11 @@ jobs:
|
||||
- name: Install and upgrade pip
|
||||
run: python -m pip install --upgrade pip
|
||||
|
||||
- name: Install graphviz
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install graphviz
|
||||
|
||||
- name: Install Python dependencies
|
||||
working-directory: docs
|
||||
run: |
|
||||
|
||||
11
.github/workflows/python.yml
vendored
@@ -21,7 +21,7 @@ on:
|
||||
|
||||
jobs:
|
||||
buildAndDeploy:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install setuptools wheel pytest
|
||||
pip install setuptools wheel pytest mypy
|
||||
|
||||
- name: Build wheel
|
||||
working-directory: ./photon-lib/py
|
||||
@@ -50,6 +50,13 @@ jobs:
|
||||
pip install --no-cache-dir dist/*.whl
|
||||
pytest
|
||||
|
||||
- name: Run mypy type checking
|
||||
uses: liskin/gh-problem-matcher-wrap@v3
|
||||
with:
|
||||
linters: mypy
|
||||
run: |
|
||||
mypy --show-column-numbers --config-file photon-lib/py/pyproject.toml photon-lib
|
||||
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@master
|
||||
|
||||
@@ -9,6 +9,8 @@ build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
apt_packages:
|
||||
- graphviz
|
||||
jobs:
|
||||
post_checkout:
|
||||
# Cancel building pull requests when there aren't changed in the docs directory or YAML file.
|
||||
|
||||
@@ -25,7 +25,7 @@ If you are interested in contributing code or documentation to the project, plea
|
||||
|
||||
Gradle is used for all C++ and Java code, and NPM is used for the web UI. Instructions to compile PhotonVision yourself can be found [in our docs](https://docs.photonvision.org/en/latest/docs/contributing/building-photon.html#compiling-instructions).
|
||||
|
||||
You can run one of the many built in examples straight from the command line, too! They contain a fully featured robot project, and some include simulation support. The projects can be found inside the [`photonlib-java-examples`](photonlib-java-examples) and [`photonlib-cpp-examples`](photonlib-cpp-examples) subdirectories, respectively. Instructions for running these examples directly from the repo are found [in the docs](https://docs.photonvision.org/en/latest/docs/contributing/photonvision/build-instructions.html#running-examples).
|
||||
You can run one of the many built in examples straight from the command line, too! They contain a fully featured robot project, and some include simulation support. The projects can be found inside the [`photonlib-java-examples`](photonlib-java-examples) and [`photonlib-cpp-examples`](photonlib-cpp-examples) subdirectories, respectively. Instructions for running these examples directly from the repo are found [in the docs](https://docs.photonvision.org/en/latest/docs/contributing/building-photon.html#running-examples).
|
||||
|
||||
## Gradle Arguments
|
||||
|
||||
@@ -47,7 +47,7 @@ If you're cross-compiling, you'll need the wpilib toolchain installed. This can
|
||||
|
||||
## Out-of-Source Dependencies
|
||||
|
||||
PhotonVision uses the following additonal out-of-source repositories for building code.
|
||||
PhotonVision uses the following additional out-of-source repositories for building code.
|
||||
|
||||
- Base system images for Raspberry Pi & Orange Pi: https://github.com/PhotonVision/photon-image-modifier
|
||||
- C++ driver for Raspberry Pi CSI cameras: https://github.com/PhotonVision/photon-libcamera-gl-driver
|
||||
|
||||
14
build.gradle
@@ -4,11 +4,10 @@ plugins {
|
||||
id "java"
|
||||
id "cpp"
|
||||
id "com.diffplug.spotless" version "6.24.0"
|
||||
id "edu.wpi.first.NativeUtils" version "2024.6.1" apply false
|
||||
id "edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin" version "2020.2"
|
||||
id "edu.wpi.first.GradleRIO" version "2024.3.2"
|
||||
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
|
||||
id 'edu.wpi.first.WpilibTools' version '1.3.0'
|
||||
id 'com.google.protobuf' version '0.9.4' apply false
|
||||
id 'com.google.protobuf' version '0.9.3' apply false
|
||||
id 'edu.wpi.first.GradleJni' version '1.1.0'
|
||||
}
|
||||
|
||||
@@ -31,14 +30,15 @@ ext.allOutputsFolder = file("$project.buildDir/outputs")
|
||||
apply from: "versioningHelper.gradle"
|
||||
|
||||
ext {
|
||||
wpilibVersion = "2024.3.2"
|
||||
wpilibVersion = "2025.1.1-beta-1"
|
||||
wpimathVersion = wpilibVersion
|
||||
openCVversion = "4.8.0-2"
|
||||
openCVYear = "2024"
|
||||
openCVversion = "4.8.0-4"
|
||||
joglVersion = "2.4.0"
|
||||
javalinVersion = "5.6.2"
|
||||
libcameraDriverVersion = "dev-v2023.1.0-14-g787ab59"
|
||||
libcameraDriverVersion = "dev-v2023.1.0-15-gc8988b3"
|
||||
rknnVersion = "dev-v2024.0.1-4-g0db16ac"
|
||||
frcYear = "2024"
|
||||
frcYear = "2025"
|
||||
mrcalVersion = "dev-v2024.0.0-24-gc1efcf0";
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import argparse
|
||||
import base64
|
||||
from dataclasses import dataclass
|
||||
import json
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import mrcal
|
||||
import numpy as np
|
||||
from wpimath.geometry import Quaternion as _Quat
|
||||
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ extensions = [
|
||||
"sphinx_design",
|
||||
"myst_parser",
|
||||
"sphinx.ext.mathjax",
|
||||
"sphinx.ext.graphviz",
|
||||
]
|
||||
|
||||
# Configure OpenGraph support
|
||||
@@ -119,6 +120,18 @@ html_theme_options = {
|
||||
"color-api-overall": "#101010",
|
||||
"color-inline-code-background": "#0d0d0d",
|
||||
},
|
||||
"footer_icons": [
|
||||
{
|
||||
"name": "GitHub",
|
||||
"url": "https://github.com/photonvision/photonvision",
|
||||
"html": """
|
||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path>
|
||||
</svg>
|
||||
""",
|
||||
"class": "",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
suppress_warnings = ["epub.unknown_project_files"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Filesystem Directory
|
||||
|
||||
PhotonVision stores and loads settings in the {code}`photonvision_config` directory, in the same folder as the PhotonVision JAR is stored. On the Pi image as well as the Gloworm, this is in the {code}`/opt/photonvision` directory. The contents of this directory can be exported as a zip archive from the settings page of the interface, under "export settings". This export will contain everything detailed below. These settings can later be uploaded using "import settings", to restore configurations from previous backups.
|
||||
PhotonVision stores and loads settings in the {code}`photonvision_config` directory, in the same folder as the PhotonVision JAR is stored. On supported hardware, this is in the {code}`/opt/photonvision` directory. The contents of this directory can be exported as a zip archive from the settings page of the interface, under "export settings". This export will contain everything detailed below. These settings can later be uploaded using "import settings", to restore configurations from previous backups.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
@@ -12,20 +12,20 @@ The directory structure is outlined below.
|
||||
```
|
||||
|
||||
- calibImgs
|
||||
- Images saved from the last run of the calibration routine
|
||||
- Images saved from the last run of the calibration routine
|
||||
- cameras
|
||||
- Contains a subfolder for each camera. This folder contains the following files:
|
||||
- pipelines folder, which contains a {code}`json` file for each user-created pipeline.
|
||||
- config.json, which contains all camera-specific configuration. This includes FOV, pitch, current pipeline index, and calibration data
|
||||
- drivermode.json, which contains settings for the driver mode pipeline
|
||||
- Contains a subfolder for each camera. This folder contains the following files:
|
||||
- pipelines folder, which contains a {code}`json` file for each user-created pipeline.
|
||||
- config.json, which contains all camera-specific configuration. This includes FOV, pitch, current pipeline index, and calibration data
|
||||
- drivermode.json, which contains settings for the driver mode pipeline
|
||||
- imgSaves
|
||||
- Contains images saved with the input/output save commands.
|
||||
- Contains images saved with the input/output save commands.
|
||||
- logs
|
||||
- Contains timestamped logs in the format {code}`photonvision-YYYY-MM-D_HH-MM-SS.log`. Note that on Pi or Gloworm these timestamps will likely be significantly behind the real time.
|
||||
- Contains timestamped logs in the format {code}`photonvision-YYYY-MM-D_HH-MM-SS.log`. These timestamps will likely be significantly behind the real time. Coprocessors on the robot have no way to get current time.
|
||||
- hardwareSettings.json
|
||||
- Contains hardware settings. Currently this includes only the LED brightness.
|
||||
- Contains hardware settings. Currently this includes only the LED brightness.
|
||||
- networkSettings.json
|
||||
- Contains network settings, including team number (or remote network tables address), static/dynamic settings, and hostname.
|
||||
- Contains network settings, including team number (or remote network tables address), static/dynamic settings, and hostname.
|
||||
|
||||
## Importing and Exporting Settings
|
||||
|
||||
@@ -41,10 +41,10 @@ The entire settings directory can be exported as a ZIP archive from the settings
|
||||
A variety of files can be imported back into PhotonVision:
|
||||
|
||||
- ZIP Archive ({code}`.zip`)
|
||||
- Useful for restoring a full configuration from a different PhotonVision instance.
|
||||
- Useful for restoring a full configuration from a different PhotonVision instance.
|
||||
- Single Config File
|
||||
- Currently-supported Files
|
||||
- {code}`hardwareConfig.json`
|
||||
- {code}`hardwareSettings.json`
|
||||
- {code}`networkSettings.json`
|
||||
- Useful for simple hardware or network configuration tasks without overwriting all settings.
|
||||
- Currently-supported Files
|
||||
- {code}`hardwareConfig.json`
|
||||
- {code}`hardwareSettings.json`
|
||||
- {code}`networkSettings.json`
|
||||
- Useful for simple hardware or network configuration tasks without overwriting all settings.
|
||||
|
||||
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
@@ -1,6 +1,6 @@
|
||||
# Installation & Setup
|
||||
# Advanced Installation
|
||||
|
||||
This page will help you install PhotonVision on your coprocessor, wire it, and properly setup the networking in order to start tracking targets.
|
||||
This page will help you install PhotonVision on non-supported coprocessor.
|
||||
|
||||
## Step 1: Software Install
|
||||
|
||||
@@ -14,25 +14,5 @@ You only need to install PhotonVision on the coprocessor/device that is being us
|
||||
:maxdepth: 3
|
||||
|
||||
sw_install/index
|
||||
updating
|
||||
```
|
||||
|
||||
## Step 2: Wiring
|
||||
|
||||
This section will walk you through how to wire your coprocessor to get power.
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
wiring
|
||||
```
|
||||
|
||||
## Step 3: Networking
|
||||
|
||||
This section will walk you though how to connect your coprocessor to a network. This section is very important (and easy to get wrong), so we recommend you read it thoroughly.
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
networking
|
||||
prerelease-software
|
||||
```
|
||||
@@ -0,0 +1,23 @@
|
||||
# Installing Pre-Release Versions
|
||||
|
||||
Pre-release/development version of PhotonVision can be tested by installing/downloading artifacts from Github Actions (see below), which are built automatically on commits to open pull requests and to PhotonVision's `master` branch, or by {ref}`compiling PhotonVision locally <docs/contributing/building-photon:Build Instructions>`.
|
||||
|
||||
:::{warning}
|
||||
If testing a pre-release version of PhotonVision with a robot, PhotonLib must be updated to match the version downloaded! If not, packet schema definitions may not match and unexpected things will occur. To update PhotonLib, refer to {ref}`installing specific version of PhotonLib<docs/programming/photonlib/adding-vendordep:Install Specific Version - Java/C++>`.
|
||||
:::
|
||||
|
||||
GitHub Actions builds pre-release version of PhotonVision automatically on PRs and on each commit merged to master. To test a particular commit to master, navigate to the [PhotonVision commit list](https://github.com/PhotonVision/photonvision/commits/master/) and click on the check mark (below). Scroll to "Build / Build fat JAR - PLATFORM", click details, and then summary. From here, JAR and image files can be downloaded to be flashed or uploaded using "Offline Update".
|
||||
|
||||
```{image} images/gh_actions_1.png
|
||||
:alt: Github Actions Badge
|
||||
```
|
||||
|
||||
```{image} images/gh_actions_2.png
|
||||
:alt: Github Actions artifact list
|
||||
```
|
||||
|
||||
Built JAR files (but not image files) can also be downloaded from PRs before they are merged. Navigate to the PR in GitHub, and select Checks at the top. Click on "Build" to display the same artifact list as above.
|
||||
|
||||
```{image} images/gh_actions_3.png
|
||||
:alt: Github Actions artifacts from PR
|
||||
```
|
||||
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -1,16 +1,5 @@
|
||||
# Software Installation
|
||||
|
||||
## Supported Coprocessors
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
raspberry-pi
|
||||
limelight
|
||||
orange-pi
|
||||
snakeyes
|
||||
```
|
||||
|
||||
## Desktop Environments
|
||||
|
||||
```{toctree}
|
||||
@@ -29,5 +18,4 @@ mac-os
|
||||
other-coprocessors
|
||||
advanced-cmd
|
||||
romi
|
||||
gloworm
|
||||
```
|
||||
@@ -23,13 +23,13 @@ $ sudo reboot now
|
||||
Your co-processor will require an Internet connection for this process to work correctly.
|
||||
:::
|
||||
|
||||
For installation on any other co-processors, we recommend reading the {ref}`advanced command line documentation <docs/installation/sw_install/advanced-cmd:Advanced Command Line Usage>`.
|
||||
For installation on any other co-processors, we recommend reading the {ref}`advanced command line documentation <docs/advanced-installation/sw_install/advanced-cmd:Advanced Command Line Usage>`.
|
||||
|
||||
## Updating PhotonVision
|
||||
|
||||
PhotonVision can be updated by downloading the latest jar file, copying it onto the processor, and restarting the service.
|
||||
|
||||
For example, from another computer, run the following commands. Substitute the correct username for "\[user\]" (e.g. Raspberry Pi uses "pi", Orange Pi uses "orangepi".)
|
||||
For example, from another computer, run the following commands. Substitute the correct username for "\[user\]" ( Provided images use username "pi")
|
||||
|
||||
```bash
|
||||
$ scp [jar name].jar [user]@photonvision.local:~/
|
||||
23
docs/source/docs/advanced-installation/sw_install/romi.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Romi Installation
|
||||
|
||||
The [Romi](https://docs.wpilib.org/en/latest/docs/romi-robot/index.html) is a small robot that can be controlled with the WPILib software. The main controller is a Raspberry Pi that must be imaged with [WPILibPi](https://docs.wpilib.org/en/latest/docs/romi-robot/imaging-romi.html) .
|
||||
|
||||
## Installation
|
||||
|
||||
The WPILibPi image includes FRCVision, which reserves USB cameras; to use PhotonVision, we need to edit the `/home/pi/runCamera` script to disable it. First we will need to make the file system writeable; the easiest way to do this is to go to `10.0.0.2` and choose "Writable" at the top.
|
||||
|
||||
SSH into the Raspberry Pi (using Windows command line, or a tool like [Putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/) ) at the Romi's default address `10.0.0.2`. The default user is `pi`, and the password is `raspberry`.
|
||||
|
||||
Follow the process for installing PhotonVision on {ref}`"Other Debian-Based Co-Processor Installation" <docs/advanced-installation/sw_install/other-coprocessors:Other Debian-Based Co-Processor Installation>`. As it mentions this will require an internet connection so plugging into the ethernet jack on the Raspberry Pi will be the easiest solution. The pi must remain writable!
|
||||
|
||||
Next, from the SSH terminal, run `sudo nano /home/pi/runCamera` then arrow down to the start of the exec line and press "Enter" to add a new line. Then add `#` before the exec command to comment it out. Then, arrow up to the new line and type `sleep 10000`. Hit "Ctrl + O" and then "Enter" to save the file. Finally press "Ctrl + X" to exit nano. Now, reboot the Romi by typing `sudo reboot`.
|
||||
|
||||
```{image} images/nano.png
|
||||
|
||||
```
|
||||
|
||||
After it reboots, you should be able to [locate the PhotonVision UI](https://photonvision.github.io/gloworm-docs/docs/quickstart/#finding-gloworm) at: `http://10.0.0.2:5800/`.
|
||||
|
||||
:::{warning}
|
||||
In order for settings, logs, etc. to be saved / take effect, ensure that PhotonVision is in writable mode.
|
||||
:::
|
||||
@@ -1,6 +1,6 @@
|
||||
# 2D AprilTag Tuning / Tracking
|
||||
|
||||
## Tracking Apriltags
|
||||
## Tracking AprilTags
|
||||
|
||||
Before you get started tracking AprilTags, ensure that you have followed the previous sections on installation, wiring and networking. Next, open the Web UI, go to the top right card, and switch to the "AprilTag" or "Aruco" type. You should see a screen similar to the one below.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# About Apriltags
|
||||
# About AprilTags
|
||||
|
||||
```{image} images/pv-apriltag.png
|
||||
:align: center
|
||||
|
||||
@@ -18,7 +18,7 @@ You must install a set of Python dependencies in order to build the documentatio
|
||||
|
||||
In order to build the documentation, you can run the following command in the docs sub-folder. This will automatically build docs every time a file changes, and serves them locally at `localhost:8000` by default.
|
||||
|
||||
`~/photonvision/docs$ sphinx-autobuild --open-browser source/_build/html`
|
||||
`~/photonvision/docs$ sphinx-autobuild --open-browser source source/_build/html`
|
||||
|
||||
## Opening the Documentation
|
||||
|
||||
@@ -26,7 +26,7 @@ The built documentation is located at `docs/build/html/index.html` relative to t
|
||||
|
||||
## Docs Builds on Pull Requests
|
||||
|
||||
Pre-merge builds of docs can be found at: `https://photonvision-docs--PRNUMBER.org.readthedocs.build/en/PRNUMBER/index.html`. These docs are republished on every commit to a pull request made to PhotonVision/photonvision-docs. For example, PR 325 would have pre-merge documentation published to `https://photonvision-docs--325.org.readthedocs.build/en/325/index.html`. Additionally, the pull requrest will have a link directly to the pre-release build of the docs. This build only runs when there is a change to files in the docs sub-folder.
|
||||
Pre-merge builds of docs can be found at: `https://photonvision-docs--PRNUMBER.org.readthedocs.build/en/PRNUMBER/index.html`. These docs are republished on every commit to a pull request made to PhotonVision/photonvision-docs. For example, PR 325 would have pre-merge documentation published to `https://photonvision-docs--325.org.readthedocs.build/en/325/index.html`. Additionally, the pull request will have a link directly to the pre-release build of the docs. This build only runs when there is a change to files in the docs sub-folder.
|
||||
|
||||
## Style Guide
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ This section contains the build instructions from the source code available at [
|
||||
|
||||
**Java Development Kit:**
|
||||
|
||||
This project requires Java Development Kit (JDK) 17 to be compiled. This is the same Java version that comes with WPILib for 2025+. If you don't have this JDK with WPILib, you can follow the instructions to install JDK 17 for your platform [here](https://bell-sw.com/pages/downloads/#jdk-17-lts).
|
||||
This project requires Java Development Kit (JDK) 17 to be compiled. This is the same Java version that comes with WPILib for 2025+. **Windows Users must use the JDK that ships with WPILib.** For other platforms, you can follow the instructions to install JDK 17 for your platform [here](https://bell-sw.com/pages/downloads/#jdk-17-lts).
|
||||
|
||||
**Node JS:**
|
||||
|
||||
@@ -284,3 +284,11 @@ Then, run the examples:
|
||||
> cd photonlib-python-examples
|
||||
> run.bat <example name>
|
||||
```
|
||||
|
||||
#### Downloading Pipeline Artifacts
|
||||
|
||||
Using the [GitHub CLI](https://cli.github.com/), we can download artifacts from pipelines by run ID and name:
|
||||
|
||||
```
|
||||
~/photonvision$ gh run download 11759699679 -n jar-Linux
|
||||
```
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
image-rotation
|
||||
time-sync
|
||||
```
|
||||
|
||||
111
docs/source/docs/contributing/design-descriptions/time-sync.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Time Synchronization Protocol Specification, Version 1.0
|
||||
|
||||
Protocol Revision 1.0, 08/25/2024
|
||||
|
||||
## Background
|
||||
|
||||
In a distributed compute environment like robots, time synchronization between computers is increasingly important. Currently, [NetworkTables Version 4.1](https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc) provides support for time synchronization of clients with the NetworkTables server using binary PING/PONG messages sent over WebSockets. This approach, while fundamentally the same as is described in this memo, has demonstrated some opportunities for improvement:
|
||||
|
||||
- PING/PONG messages are processed in the same queue as other NetworkTables messages. Depending on the underlying implementation and processor speed, this can incur message processing delays and increase client-calculated Round-Trip Time (RTT), and cause messages to arrive at the server timestamped in the future.
|
||||
- Messages use WebSockets over TCP for their transport layer. We don't need the robustness guarantees of TCP as our connection is stateless.
|
||||
|
||||
For these reasons, a time synchronization solution separate from NetworkTables communication was desired. Architecture decisions made to address these issues are:
|
||||
|
||||
- Use the User Datagram Protocol (UDP) transport layer, as we don't need the robustness guarantees afforded by TCP. As a Client, if a PING isn't replied to, we'll just try again at the start of the next PING window. As a bonus, we are free to use UDP port 5810 as NetworkTables only uses TCP Port 5810/5811 as of Version 4.1.
|
||||
- Use a separate thread from the current NetworkTables libUV runner.
|
||||
|
||||
|
||||
## Prior Art
|
||||
|
||||
The [NetworkTables 4.1 timestamp synchronization](https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#timestamps) approach, an implementation of [Cristian's Algorithm](https://en.wikipedia.org/wiki/Cristian%27s_algorithm). We also implement Cristian’s Algorithm.
|
||||
|
||||
The [Precision Time Protocol](https://en.wikipedia.org/wiki/Precision_Time_Protocol#Synchronization) at it's core does something similar with Sync/Delay_Req/Delay_Resp. We do not have (guaranteed) access to hardware timestamping, but we utilize this PING/PONG pattern to estimate total round-trip time.
|
||||
|
||||
|
||||
## Roles
|
||||
|
||||
```{graphviz}
|
||||
digraph CristianAlgorithm {
|
||||
ratio=0.5;
|
||||
bgcolor="transparent";
|
||||
|
||||
node [
|
||||
fontcolor = "#e6e6e6",
|
||||
style = filled,
|
||||
color = "#e6e6e6",
|
||||
fillcolor = "#333333"
|
||||
fontsize=10;
|
||||
]
|
||||
|
||||
edge [
|
||||
color = "#e6e6e6",
|
||||
fontcolor = "#e6e6e6"
|
||||
fontsize=10;
|
||||
]
|
||||
|
||||
rankdir=LR;
|
||||
node [shape=box, style=filled, color=lightblue];
|
||||
|
||||
user_send [label="User Sends T1"];
|
||||
server_receive [label="Server Receives T1"];
|
||||
server_send [label="Server Sends T2"];
|
||||
user_receive [label="User Receives T2"];
|
||||
user_compute [label="User Computes Time"];
|
||||
|
||||
user_send -> server_receive [label="T1 (Request)"];
|
||||
server_receive -> server_send [label="T1 received by server"];
|
||||
server_send -> user_receive [label="T2 sent by server"];
|
||||
user_receive -> user_compute [label="T2 received by user"];
|
||||
user_compute -> user_send [label="Computed Time: T3 = T2 + (deltaT2 - deltaT1)/2"];
|
||||
}
|
||||
```
|
||||
|
||||
Time Synchronization Protocol (TSP) participants can assume either a server role or a client role. The server role is responsible for listening for incoming time synchronization requests from clients and replying appropriately. The client role is responsible for sending "Ping" messages to the server and listening for "Pong" replies to estimate the offset between the server and client time bases.
|
||||
|
||||
All time values shall use units of microseconds. The epoch of the time base this is measured against is unspecified.
|
||||
|
||||
Clients shall periodically (e.g. every few seconds) send, in a manner that minimizes transmission delays, a **TSP Ping Message** that contains the client's current local time.
|
||||
|
||||
When the server receives a **TSP Ping Message** from any client, it shall respond to the client, in a manner that minimizes transmission delays, with a **TSP Pong message** encoding a timestamp of its (the server's) current local time (in microseconds), and the client-provided data value.
|
||||
|
||||
When the client receives a **TSP Pong Message** from the server, it shall verify that the `Client Local Time` corresponds to the currently in-flight TSP Ping message; if not, it shall drop this packet. The round trip time (RTT) shall be computed from the delta between the message's data value and the current local time. If the RTT is less than that from previous measurements, the client shall use the timestamp in the message plus ½ the RTT as the server time equivalent to the current local time, and use this equivalence to compute server time base timestamps from local time for future messages.
|
||||
|
||||
## Transport
|
||||
|
||||
Communication between server and clients shall occur over the User Datagram Protocol (UDP) Port 5810.
|
||||
|
||||
## Message Format
|
||||
|
||||
The message format forgoes CRCs (as these are provided by the Ethernet physical layer) or packet delimination (as our packetsa are assumed be under the network MTU). **TSP Ping** and **TSP Pong** messages shall be encoded in a manor compatible with a WPILib packed struct with respect to byte alignment and endienness.
|
||||
|
||||
### TSP Ping
|
||||
|
||||
| Offset | Format | Data | Notes |
|
||||
| ------ | ------ | ---- | ----- |
|
||||
| 0 | uint8 | Protocol version | This field shall always set to 1 (0b1) for TSP Version 1. |
|
||||
| 1 | uint8 | Message ID | This field shall always be set to 1 (0b1). |
|
||||
| 2 | uint64 | Client Local Time | The client's local time value, at the time this Ping message was sent. |
|
||||
|
||||
### TSP Pong
|
||||
|
||||
| Offset | Format | Data | Notes |
|
||||
| ------ | ------ | ---- | ----- |
|
||||
| 0 | uint8 | Protocol version | This field shall always set to 1 (0b1) for TSP Version 1.
|
||||
| 1 | uint8 | Message ID | This field shall always be set to 2 (0b2).
|
||||
| 2 | uint64 | Client Local Time | The client's local time value from the Ping message that this Pong is generated in response to.
|
||||
| 10 | uint64 | Server Local Time | The current time at the server, at the time this Pong message was sent.
|
||||
|
||||
|
||||
## Optional Protocol Extensions
|
||||
|
||||
Clients may publish statistics to NetworkTables. If they do, they shall publish to a key that is globally unique per participant in the Time Synronization network. If a client implements this, it shall provide the following publishers:
|
||||
|
||||
| Key | Type | Notes |
|
||||
| ------ | ------ | ---- |
|
||||
| offset_us | Integer | The time offset that, when added to the client's local clock, provides server time |
|
||||
| ping_tx_count | Integer | The total number of TSP Ping packets transmitted |
|
||||
| ping_rx_count | Integer | The total number of TSP Ping packets received |
|
||||
| pong_rx_time_us | Integer | The time, in client local time, that the last pong was received |
|
||||
| rtt2_us | Integer | The time in us from last complete (ping transmission to pong reception) |
|
||||
|
||||
PhotonVision has chosen to publish to the sub-table `/photonvision/.timesync/{DEVICE_HOSTNAME}`. Future implementations of this protocol may decide to implement this as a structured data type.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Description
|
||||
|
||||
PhotonVision is a free, fast, and easy-to-use vision processing solution for the *FIRST*Robotics Competition. PhotonVision is designed to get vision working on your robot *quickly*, without the significant cost of other similar solutions.
|
||||
PhotonVision is a free, fast, and easy-to-use vision processing solution for the _FIRST_ Robotics Competition. PhotonVision is designed to get vision working on your robot _quickly_, without the significant cost of other similar solutions.
|
||||
Using PhotonVision, teams can go from setting up a camera and coprocessor to detecting and tracking AprilTags and other targets by simply tuning sliders. With an easy to use interface, comprehensive documentation, and a feature rich vendor dependency, no experience is necessary to use PhotonVision. No matter your resources, using PhotonVision is easy compared to its alternatives.
|
||||
|
||||
## Advantages
|
||||
|
||||
@@ -7,15 +7,15 @@ The following example is from the PhotonLib example repository ([Java](https://g
|
||||
- A Robot
|
||||
- A camera mounted rigidly to the robot's frame, cenetered and pointed forward.
|
||||
- A coprocessor running PhotonVision with an AprilTag or Aurco 2D Pipeline.
|
||||
- [A printout of Apriltag 7](https://firstfrc.blob.core.windows.net/frc2024/FieldAssets/Apriltag_Images_and_User_Guide.pdf), mounted on a rigid and flat surface.
|
||||
- [A printout of AprilTag 7](https://firstfrc.blob.core.windows.net/frc2024/FieldAssets/Apriltag_Images_and_User_Guide.pdf), mounted on a rigid and flat surface.
|
||||
|
||||
## Code
|
||||
|
||||
Now that you have properly set up your vision system and have tuned a pipeline, you can now aim your robot at an AprilTag using the data from PhotonVision. The *yaw* of the target is the critical piece of data that will be needed first.
|
||||
Now that you have properly set up your vision system and have tuned a pipeline, you can now aim your robot at an AprilTag using the data from PhotonVision. The _yaw_ of the target is the critical piece of data that will be needed first.
|
||||
|
||||
Yaw is reported to the roboRIO over Network Tables. PhotonLib, our vender dependency, is the easiest way to access this data. The documentation for the Network Tables API can be found {ref}`here <docs/additional-resources/nt-api:Getting Target Information>` and the documentation for PhotonLib {ref}`here <docs/programming/photonlib/adding-vendordep:What is PhotonLib?>`.
|
||||
|
||||
In this example, while the operator holds a button down, the robot will turn towards the AprilTag using the P term of a PID loop. To learn more about how PID loops work, how WPILib implements them, and more, visit [Advanced Controls (PID)](https://docs.wpilib.org/en/stable/docs/software/advanced-control/introduction/index.html) and [PID Control in WPILib](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/pidcontroller.html#pid-control-in-wpilib).
|
||||
In this example, while the operator holds a button down, the robot will turn towards the AprilTag using the P term of a PID loop. To learn more about how PID loops work, how WPILib implements them, and more, visit [Advanced Controls (PID)](https://docs.wpilib.org/en/stable/docs/software/advanced-control/introduction/index.html) and [PID Control in WPILib](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/controllers/pidcontroller.html#pid-control-in-wpilib).
|
||||
|
||||
```{eval-rst}
|
||||
.. tab-set::
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
@@ -1,6 +1,6 @@
|
||||
# Selecting Hardware
|
||||
|
||||
In order to use PhotonVision, you need a coprocessor and a camera. This page will help you select the right hardware for your team depending on your budget, needs, and experience.
|
||||
In order to use PhotonVision, you need a coprocessor and a camera. Other than the recommended hardware found in the {ref}`quick start guide<docs/quick-start/common-setups:Common Hardware Setups>`, this page will help you select hardware that should work for photonvision even though it is not supported/recommended.
|
||||
|
||||
## Choosing a Coprocessor
|
||||
|
||||
@@ -11,27 +11,19 @@ In order to use PhotonVision, you need a coprocessor and a camera. This page wil
|
||||
- CPU: ARM Cortex-A53 (the CPU on Raspberry Pi 3) or better
|
||||
- At least 8GB of storage
|
||||
- 2GB of RAM
|
||||
- PhotonVision isn't very RAM intensive, but you'll need at least 2GB to run the OS and PhotonVision.
|
||||
- PhotonVision isn't very RAM intensive, but you'll need at least 2GB to run the OS and PhotonVision.
|
||||
- The following IO:
|
||||
- At least 1 USB or MIPI-CSI port for the camera
|
||||
- Note that we only support using the Raspberry Pi's MIPI-CSI port, other MIPI-CSI ports from other coprocessors may not work.
|
||||
- Ethernet port for networking
|
||||
- At least 1 USB or MIPI-CSI port for the camera
|
||||
- Note that we only support using the Raspberry Pi's MIPI-CSI port, other MIPI-CSI ports from other coprocessors will probably not work.
|
||||
- Ethernet port for networking
|
||||
|
||||
### Coprocessor Recommendations
|
||||
|
||||
When selecting a coprocessor, it is important to consider various factors, particularly when it comes to AprilTag detection. Opting for a coprocessor with a more powerful CPU can generally result in higher FPS AprilTag detection, leading to more accurate pose estimation. However, it is important to note that there is a point of diminishing returns, where the benefits of a more powerful CPU may not outweigh the additional cost. Below is a list of supported hardware, along with some notes on each.
|
||||
|
||||
- Orange Pi 5 (\$99)
|
||||
- This is the recommended coprocessor for most teams. It has a powerful CPU that can handle AprilTag detection at high FPS, and is relatively cheap compared to processors of a similar power.
|
||||
- Raspberry Pi 4/5 (\$55-\$80)
|
||||
- This is the recommended coprocessor for teams on a budget. It has a less powerful CPU than the Orange Pi 5, but is still capable of running PhotonVision at a reasonable FPS.
|
||||
- Mini PCs (such as Beelink N5095)
|
||||
- This coprocessor will likely have similar performance to the Orange Pi 5 but has a higher performance ceiling (when using more powerful CPUs). Do note that this would require extra effort to wire to the robot / get set up. More information can be found in the set up guide [here.](https://docs.google.com/document/d/1lOSzG8iNE43cK-PgJDDzbwtf6ASyf4vbW8lQuFswxzw/edit?usp=drivesdk)
|
||||
- Other coprocessors can be used but may require some extra work / command line usage in order to get it working properly.
|
||||
When selecting a coprocessor, it is important to consider various factors, particularly when it comes to AprilTag detection. Opting for a coprocessor with a more powerful CPU can generally result in higher FPS AprilTag detection, leading to more accurate pose estimation. However, it is important to note that there is a point of diminishing returns, where the benefits of a more powerful CPU may not outweigh the additional cost. Other coprocessors can be used but may require some extra work / command line usage in order to get it working properly.
|
||||
|
||||
## Choosing a Camera
|
||||
|
||||
PhotonVision works with Pi Cameras and most USB Cameras, the recommendations below are known to be working and have been tested. Other cameras such as webcams, virtual cameras, etc. are not officially supported and may not work. It is important to note that fisheye cameras should only be used as a driver camera and not for detecting targets.
|
||||
PhotonVision works with Pi Cameras and most USB Cameras. Other cameras such as webcams, virtual cameras, etc. are not officially supported and may not work. It is important to note that fisheye cameras should only be used as a driver camera / gamepeice detection and not for detecting targets / AprilTags.
|
||||
|
||||
PhotonVision relies on [CSCore](https://github.com/wpilibsuite/allwpilib/tree/main/cscore) to detect and process cameras, so camera support is determined based off compatibility with CScore along with native support for the camera within your OS (ex. [V4L compatibility](https://en.wikipedia.org/wiki/Video4Linux) if using a Linux machine like a Raspberry Pi).
|
||||
|
||||
@@ -43,31 +35,17 @@ Logitech Cameras and integrated laptop cameras will not work with PhotonVision d
|
||||
We do not currently support the usage of two of the same camera on the same coprocessor. You can only use two or more cameras if they are of different models or they are from Arducam, which has a [tool that allows for cameras to be renamed](https://docs.arducam.com/UVC-Camera/Serial-Number-Tool-Guide/).
|
||||
:::
|
||||
|
||||
### Recommended Cameras
|
||||
### Cameras Attributes
|
||||
|
||||
For colored shape detection, any non-fisheye camera supported by PhotonVision will work. We recommend the Pi Camera V1 or a high fps USB camera.
|
||||
For colored shape detection, any non-fisheye camera supported by PhotonVision will work. We recommend a high fps USB camera.
|
||||
|
||||
For driver camera, we recommend a USB camera with a fisheye lens, so your driver can see more of the field.
|
||||
|
||||
For AprilTag detection, we recommend you use a global shutter camera that has ~100 degree diagonal FOV. This will allow you to see more AprilTags in frame, and will allow for more accurate pose estimation. You also want a camera that supports high FPS, as this will allow you to update your pose estimator at a higher frequency.
|
||||
|
||||
- Recommendations For AprilTag Detection
|
||||
- Arducam USB OV9281
|
||||
- This is the recommended camera for AprilTag detection as it is a high FPS, global shutter camera USB camera that has a ~70 degree FOV.
|
||||
- Innomaker OV9281
|
||||
- Spinel AR0144
|
||||
- Pi Camera Module V1
|
||||
- The V1 is strongly preferred over the V2 due to the V2 having undesirable FOV choices
|
||||
Another cause of image distortion is 'rolling shutter.' This occurs when the camera captures pixels sequentially from top to bottom, which can also lead to distortion if the camera or object is moving.
|
||||
|
||||
### AprilTags and Motion Blur
|
||||
|
||||
When detecting AprilTags, you want to reduce the "motion blur" as much as possible. Motion blur is the visual streaking/smearing on the camera stream as a result of movement of the camera or object of focus. You want to mitigate this as much as possible because your robot is constantly moving and you want to be able to read as many tags as you possibly can. The possible solutions to this include:
|
||||
|
||||
1. Cranking your exposure as low as it goes and increasing your gain/brightness. This will decrease the effects of motion blur and increase FPS.
|
||||
2. Using a global shutter (as opposed to rolling shutter) camera. This should eliminate most, if not all motion blur.
|
||||
3. Only rely on tags when not moving.
|
||||
|
||||
```{image} images/motionblur.gif
|
||||
```{image} images/rollingshutter.gif
|
||||
:align: center
|
||||
```
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 152 KiB |
@@ -1,60 +0,0 @@
|
||||
# Gloworm Installation
|
||||
|
||||
While not currently in production, PhotonVision still supports Gloworm vision processing cameras.
|
||||
|
||||
## Downloading the Gloworm Image
|
||||
|
||||
Download the latest [Gloworm/Limelight release of PhotonVision](https://github.com/photonvision/photonvision/releases); the image will be suffixed with "image_limelight2.xz". You do not need to extract the downloaded archive.
|
||||
|
||||
## Flashing the Gloworm Image
|
||||
|
||||
Plug a USB C cable from your computer into the USB C port on Gloworm labeled with a download icon.
|
||||
|
||||
Use the 1.18.11 version of [Balena Etcher](https://github.com/balena-io/etcher/releases/tag/v1.18.11) to flash an image onto the coprocessor.
|
||||
|
||||
Run BalenaEtcher as an administrator. Select the downloaded `.zip` file.
|
||||
|
||||
Select the compute module. If it doesn't show up after 30s try using another USB port, initialization may take a while. If prompted, install the recommended missing drivers.
|
||||
|
||||
Hit flash. Wait for flashing to complete, then disconnect your USB C cable.
|
||||
|
||||
:::{warning}
|
||||
Using a version of Balena Etcher older than 1.18.11 may cause bootlooping (the system will repeatedly boot and restart) when imaging your Gloworm. Updating to 1.18.11 will fix this issue.
|
||||
:::
|
||||
|
||||
## Final Steps
|
||||
|
||||
Power your device per its documentation and connect it to a robot network.
|
||||
|
||||
You should be able to locate the camera at `http://photonvision.local:5800/` in your browser on your computer when connected to the robot.
|
||||
|
||||
## Troubleshooting/Setting a Static IP
|
||||
|
||||
A static IP address may be used as an alternative to the mDNS `photonvision.local` address.
|
||||
|
||||
Download and run [Angry IP Scanner](https://angryip.org/download/#windows) to find PhotonVision/your coprocessor on your network.
|
||||
|
||||
```{image} images/angryIP.png
|
||||
```
|
||||
|
||||
Once you find it, set the IP to a desired {ref}`static IP in PhotonVision. <docs/settings:Networking>`
|
||||
|
||||
## Updating PhotonVision
|
||||
|
||||
Download the latest stable .jar from [the releases page](https://github.com/PhotonVision/photonvision/releases), go to the settings tab, and upload the .jar using the Offline Update button.
|
||||
|
||||
:::{note}
|
||||
If you are updating PhotonVision on a Gloworm/Limelight, download the LinuxArm64 .jar file.
|
||||
:::
|
||||
|
||||
As an alternative option - Export your settings, reimage your coprocessor using the instructions above, and import your settings back in.
|
||||
|
||||
## Hardware Troubleshooting
|
||||
|
||||
To turn the LED lights off or on you need to modify the `ledMode` network tables entry or the `camera.setLED` of PhotonLib.
|
||||
|
||||
## Support Links
|
||||
|
||||
- [Website/Documentation](https://photonvision.github.io/gloworm-docs/docs/quickstart/#finding-gloworm) (Note: Gloworm is no longer in production)
|
||||
- [Image](https://github.com/gloworm-vision/pi-img-updator/releases)
|
||||
- [Discord](https://discord.com/invite/DncQRky)
|
||||
@@ -1,24 +0,0 @@
|
||||
# Limelight Installation
|
||||
|
||||
## Imaging
|
||||
|
||||
Limelight imaging is a very similar process to Gloworm, but with extra steps.
|
||||
|
||||
### Base Install Steps
|
||||
|
||||
Due to the similarities in hardware, follow the {ref}`Gloworm install instructions <docs/installation/sw_install/gloworm:Gloworm Installation>`.
|
||||
|
||||
## Hardware-Specific Steps
|
||||
|
||||
Download the hardwareConfig.json file for the version of your Limelight:
|
||||
|
||||
- {download}`Limelight Version 2 <files/Limelight2/hardwareConfig.json>`.
|
||||
- {download}`Limelight Version 2+ <files/Limelight2+/hardwareConfig.json>`.
|
||||
|
||||
:::{note}
|
||||
No hardware config is provided for the Limelight 3 as AprilTags do not require the LEDs (meaning nobody has reverse-engineered what I/O pins drive the LEDs) and the camera FOV is determined as part of calibration.
|
||||
:::
|
||||
|
||||
{ref}`Import the hardwareConfig.json file <docs/additional-resources/config:Importing and Exporting Settings>`. Again, this is **REQUIRED** or target measurements will be incorrect, and LEDs will not work.
|
||||
|
||||
After installation you should be able to [locate the camera](https://photonvision.github.io/gloworm-docs/docs/quickstart/#finding-gloworm) at: `http://photonvision.local:5800/` (not `gloworm.local`, as previously)
|
||||
@@ -1,39 +0,0 @@
|
||||
# Orange Pi Installation
|
||||
|
||||
## Downloading Linux Image
|
||||
|
||||
Starting in 2024, PhotonVision provides pre-configured system images for Orange Pi 5 devices. Download the latest release of the PhotonVision Orange Pi 5 image (.xz file suffixed with `orangepi5.xz`) from the [releases page](https://github.com/PhotonVision/photonvision/releases). You do not need to extract the downloaded archive file. This image is configured with a `pi` user with password `raspberry`.
|
||||
|
||||
For an Orange Pi 4, download the latest release of the Armbian Bullseye CLI image from [here](https://armbian.tnahosting.net/archive/orangepi4/archive/Armbian_23.02.2_Orangepi4_bullseye_current_5.15.93.img.xz).
|
||||
|
||||
## Flashing the Pi Image
|
||||
|
||||
An 8GB or larger SD card is recommended.
|
||||
|
||||
Use the 1.18.11 version of [Balena Etcher](https://github.com/balena-io/etcher/releases/tag/v1.18.11) to flash an image onto a Orange Pi. Select the downloaded image file, select your microSD card, and flash.
|
||||
|
||||
For more detailed instructions on using Etcher, please see the [Etcher website](https://www.balena.io/etcher/).
|
||||
|
||||
:::{warning}
|
||||
Using a version of Balena Etcher older than 1.18.11 may cause bootlooping (the system will repeatedly boot and restart) when imaging your Orange Pi. Updating to 1.18.11 will fix this issue.
|
||||
:::
|
||||
|
||||
Alternatively, you can use the [Raspberry Pi Imager](https://www.raspberrypi.com/software/) to flash the image.
|
||||
|
||||
Select "Choose OS" and then "Use custom" to select the downloaded image file. Select your microSD card and flash.
|
||||
|
||||
:::{note}
|
||||
If you are working on Linux, "dd" can be used in the command line to flash an image.
|
||||
:::
|
||||
|
||||
If you're using an Orange Pi 5, that's it! Orange Pi 4 users will need to install PhotonVision (see below).
|
||||
|
||||
### Initial User Setup (Orange Pi 4 Only)
|
||||
|
||||
Insert the flashed microSD card into your Orange Pi and boot it up. The first boot may take a few minutes as the Pi expands the filesystem. Be sure not to unplug during this process.
|
||||
|
||||
Plug your Orange Pi into a display via HDMI and plug in a keyboard via USB once its powered up. For an Orange Pi 4, complete the initial set up which involves creating a root password and adding a user, as well as setting localization language. Additionally, choose “bash” when prompted.
|
||||
|
||||
## Installing PhotonVision (Orange Pi 4 Only)
|
||||
|
||||
From here, you can follow {ref}`this guide <docs/installation/sw_install/other-coprocessors:Installing Photonvision>`.
|
||||
@@ -1,50 +0,0 @@
|
||||
# Raspberry Pi Installation
|
||||
|
||||
A Pre-Built Raspberry Pi image is available for ease of installation.
|
||||
|
||||
## Downloading the Pi Image
|
||||
|
||||
Download the latest release of the PhotonVision Raspberry image (.xz file) from the [releases page](https://github.com/PhotonVision/photonvision/releases). You do not need to extract the downloaded ZIP file.
|
||||
|
||||
:::{note}
|
||||
Make sure you download the image that ends in '-RasberryPi.xz'.
|
||||
:::
|
||||
|
||||
## Flashing the Pi Image
|
||||
|
||||
An 8GB or larger card is recommended.
|
||||
|
||||
Use the 1.18.11 version of [Balena Etcher](https://github.com/balena-io/etcher/releases/tag/v1.18.11) to flash an image onto a Raspberry Pi. Select the downloaded `.tar.xz` file, select your microSD card, and flash.
|
||||
|
||||
For more detailed instructions on using Etcher, please see the [Etcher website](https://www.balena.io/etcher/).
|
||||
|
||||
:::{warning}
|
||||
Using a version of Balena Etcher older than 1.18.11 may cause bootlooping (the system will repeatedly boot and restart) when imaging your Raspberry Pi. Updating to 1.18.11 will fix this issue.
|
||||
:::
|
||||
|
||||
Alternatively, you can use the [Raspberry Pi Imager](https://www.raspberrypi.com/software/) to flash the image.
|
||||
|
||||
Select "Choose OS" and then "Use custom" to select the downloaded image file. Select your microSD card and flash.
|
||||
|
||||
If you are using a non-standard Pi Camera connected to the CSI port, {ref}`additional configuration may be required. <docs/hardware/picamconfig:Pi Camera Configuration>`
|
||||
|
||||
## Final Steps
|
||||
|
||||
Insert the flashed microSD card into your Raspberry Pi and boot it up. The first boot may take a few minutes as the Pi expands the filesystem. Be sure not to unplug during this process.
|
||||
|
||||
After the initial setup process, your Raspberry Pi should be configured for PhotonVision. You can verify this by making sure your Raspberry Pi and computer are connected to the same network and navigating to `http://photonvision.local:5800` in your browser on your computer.
|
||||
|
||||
## Troubleshooting/Setting a Static IP
|
||||
|
||||
A static IP address may be used as an alternative to the mDNS `photonvision.local` address.
|
||||
|
||||
Download and run [Angry IP Scanner](https://angryip.org/download/#windows) to find PhotonVision/your coprocessor on your network.
|
||||
|
||||
```{image} images/angryIP.png
|
||||
```
|
||||
|
||||
Once you find it, set the IP to a desired {ref}`static IP in PhotonVision. <docs/settings:Networking>`
|
||||
|
||||
## Updating PhotonVision
|
||||
|
||||
To upgrade a Raspberry Pi device with PhotonVision already installed, follow the {ref}`Raspberry Pi update instructions<docs/installation/updating:offline update>`.
|
||||
@@ -1,22 +0,0 @@
|
||||
# Romi Installation
|
||||
|
||||
The [Romi](https://docs.wpilib.org/en/latest/docs/romi-robot/index.html) is a small robot that can be controlled with the WPILib software. The main controller is a Raspberry Pi that must be imaged with [WPILibPi](https://docs.wpilib.org/en/latest/docs/romi-robot/imaging-romi.html) .
|
||||
|
||||
## Installation
|
||||
|
||||
The WPILibPi image includes FRCVision, which reserves USB cameras; to use PhotonVision, we need to edit the `/home/pi/runCamera` script to disable it. First we will need to make the file system writeable; the easiest way to do this is to go to `10.0.0.2` and choose "Writable" at the top.
|
||||
|
||||
SSH into the Raspberry Pi (using Windows command line, or a tool like [Putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/) ) at the Romi's default address `10.0.0.2`. The default user is `pi`, and the password is `raspberry`.
|
||||
|
||||
Follow the process for installing PhotonVision on {ref}`"Other Debian-Based Co-Processor Installation" <docs/installation/sw_install/other-coprocessors:Other Debian-Based Co-Processor Installation>`. As it mentions this will require an internet connection so plugging into the ethernet jack on the Raspberry Pi will be the easiest solution. The pi must remain writable!
|
||||
|
||||
Next, from the SSH terminal, run `sudo nano /home/pi/runCamera` then arrow down to the start of the exec line and press "Enter" to add a new line. Then add `#` before the exec command to comment it out. Then, arrow up to the new line and type `sleep 10000`. Hit "Ctrl + O" and then "Enter" to save the file. Finally press "Ctrl + X" to exit nano. Now, reboot the Romi by typing `sudo reboot`.
|
||||
|
||||
```{image} images/nano.png
|
||||
```
|
||||
|
||||
After it reboots, you should be able to [locate the PhotonVision UI](https://photonvision.github.io/gloworm-docs/docs/quickstart/#finding-gloworm) at: `http://10.0.0.2:5800/`.
|
||||
|
||||
:::{warning}
|
||||
In order for settings, logs, etc. to be saved / take effect, ensure that PhotonVision is in writable mode.
|
||||
:::
|
||||
@@ -1,56 +0,0 @@
|
||||
# SnakeEyes Installation
|
||||
|
||||
A Pre-Built Raspberry Pi image with configuration for [the SnakeEyes Raspberry Pi Hat](https://www.playingwithfusion.com/productview.php?pdid=133&catid=1014) is available for ease of setup.
|
||||
|
||||
## Downloading the SnakeEyes Image
|
||||
|
||||
Download the latest release of the SnakeEyes-specific PhotonVision Pi image from the [releases page](https://github.com/PlayingWithFusion/SnakeEyesDocs/releases). You do not need to extract the downloaded ZIP file.
|
||||
|
||||
## Flashing the SnakeEyes Image
|
||||
|
||||
An 8GB or larger card is recommended.
|
||||
|
||||
Use the 1.18.11 version of [Balena Etcher](https://github.com/balena-io/etcher/releases/tag/v1.18.11) to flash an image onto a Raspberry Pi. Select the downloaded `.zip` file, select your microSD card, and flash.
|
||||
|
||||
For more detailed instructions on using Etcher, please see the [Etcher website](https://www.balena.io/etcher/).
|
||||
|
||||
:::{warning}
|
||||
Using a version of Balena Etcher older than 1.18.11 may cause bootlooping (the system will repeatedly boot and restart) when imaging your Raspberry Pi. Updating to 1.18.11 will fix this issue.
|
||||
:::
|
||||
|
||||
Alternatively, you can use the [Raspberry Pi Imager](https://www.raspberrypi.com/software/) to flash the image.
|
||||
|
||||
Select "Choose OS" and then "Use custom" to select the downloaded image file. Select your microSD card and flash.
|
||||
|
||||
## Final Steps
|
||||
|
||||
Insert the flashed microSD card into your Raspberry Pi and boot it up. The first boot may take a few minutes as the Pi expands the filesystem. Be sure not to unplug during this process.
|
||||
|
||||
After the initial setup process, your Raspberry Pi should be configured for PhotonVision. You can verify this by making sure your Raspberry Pi and computer are connected to the same network and navigating to `http://photonvision.local:5800` in your browser on your computer.
|
||||
|
||||
## Troubleshooting/Setting a Static IP
|
||||
|
||||
A static IP address may be used as an alternative to the mDNS `photonvision.local` address.
|
||||
|
||||
Download and run [Angry IP Scanner](https://angryip.org/download/#windows) to find PhotonVision/your coprocessor on your network.
|
||||
|
||||
```{image} images/angryIP.png
|
||||
```
|
||||
|
||||
Once you find it, set the IP to a desired {ref}`static IP in PhotonVision. <docs/settings:Networking>`
|
||||
|
||||
## Updating PhotonVision
|
||||
|
||||
Download the latest xxxxx-LinuxArm64.jar from [our releases page](https://github.com/PhotonVision/photonvision/releases), go to the settings tab, and upload the .jar using the Offline Update button.
|
||||
|
||||
As an alternative option - Export your settings, reimage your coprocessor using the instructions above, and import your settings back in.
|
||||
|
||||
## Hardware Troubleshooting
|
||||
|
||||
To turn the LED lights off or on you need to modify the `ledMode` network tables entry or the `camera.setLED` of PhotonLib.
|
||||
|
||||
## Support Links
|
||||
|
||||
- [Website](https://www.playingwithfusion.com/productview.php?pdid=133)
|
||||
- [Image](https://github.com/PlayingWithFusion/SnakeEyesDocs/releases/latest)
|
||||
- [Documentation](https://github.com/PlayingWithFusion/SnakeEyesDocs/blob/master/PhotonVision/readme.md)
|
||||
@@ -1,54 +0,0 @@
|
||||
# Updating PhotonVision
|
||||
|
||||
PhotonVision provides many different files on a single release page. Each release contains JAR files for performing "offline updates" of a device with PhotonVision already installed, as well as full image files to "flash" to supported coprocessors.
|
||||
|
||||
```{image} images/release-page.png
|
||||
:alt: Example GitHub release page
|
||||
```
|
||||
|
||||
In the example release above, we see:
|
||||
|
||||
- Image files for flashing directly to supported coprocessors.
|
||||
|
||||
- Raspberry Pi 3/4/5/CM4: follow our {ref}`Raspberry Pi flashing instructions<docs/installation/sw_install/raspberry-pi:raspberry pi installation>`.
|
||||
- For LimeLight devices: follow our {ref}`LimeLight flashing instructions<docs/installation/sw_install/limelight:imaging>`.
|
||||
- For Orange Pi 5 devices: follow our {ref}`Orange Pi flashing instructions<docs/installation/sw_install/orange-pi:orange pi installation>`.
|
||||
|
||||
- JAR files for the suite of supported operating systems for use with Offline Update. In general:
|
||||
|
||||
- Raspberry Pi, Limelight, and Orange Pi: use images suffixed with -linuxarm64.jar. For example: {code}`photonvision-v2024.1.1-linuxarm64.jar`
|
||||
- Beelink and other Intel/AMD-based Mini-PCs: use images suffixed with -linuxx64.jar. For example: {code}`photonvision-v2024.1.1-linuxx64.jar`
|
||||
|
||||
## Offline Update
|
||||
|
||||
Unless noted in the release page, an offline update allows you to quickly upgrade the version of PhotonVision running on a coprocessor with PhotonVision already installed on it.
|
||||
|
||||
Unless otherwise noted on the release page, config files should be backward compatible with previous version of PhotonVision, and this offline update process should preserve any pipelines and calibrations previously performed. For paranoia, we suggest exporting settings from the Settings tab prior to performing an offline update.
|
||||
|
||||
:::{note}
|
||||
Carefully review release notes to ensure that reflashing the device (for supported devices) or other installation steps are not required, as dependencies needed for PhotonVision may change between releases
|
||||
:::
|
||||
|
||||
## Installing Pre-Release Versions
|
||||
|
||||
Pre-release/development version of PhotonVision can be tested by installing/downloading artifacts from Github Actions (see below), which are built automatically on commits to open pull requests and to PhotonVision's `master` branch, or by {ref}`compiling PhotonVision locally <docs/contributing/building-photon:Build Instructions>`.
|
||||
|
||||
:::{warning}
|
||||
If testing a pre-release version of PhotonVision with a robot, PhotonLib must be updated to match the version downloaded! If not, packet schema definitions may not match and unexpected things will occur. To update PhotonLib, refer to {ref}`installing specific version of PhotonLib<docs/programming/photonlib/adding-vendordep:Install Specific Version - Java/C++>`.
|
||||
:::
|
||||
|
||||
GitHub Actions builds pre-release version of PhotonVision automatically on PRs and on each commit merged to master. To test a particular commit to master, navigate to the [PhotonVision commit list](https://github.com/PhotonVision/photonvision/commits/master/) and click on the check mark (below). Scroll to "Build / Build fat JAR - PLATFORM", click details, and then summary. From here, JAR and image files can be downloaded to be flashed or uploaded using "Offline Update".
|
||||
|
||||
```{image} images/gh_actions_1.png
|
||||
:alt: Github Actions Badge
|
||||
```
|
||||
|
||||
```{image} images/gh_actions_2.png
|
||||
:alt: Github Actions artifact list
|
||||
```
|
||||
|
||||
Built JAR files (but not image files) can also be downloaded from PRs before they are merged. Navigate to the PR in GitHub, and select Checks at the top. Click on "Build" to display the same artifact list as above.
|
||||
|
||||
```{image} images/gh_actions_3.png
|
||||
:alt: Github Actions artifacts from PR
|
||||
```
|
||||
@@ -1,42 +0,0 @@
|
||||
# Wiring
|
||||
|
||||
## Off-Robot Wiring
|
||||
|
||||
Plugging your coprocessor into the wall via a power brick will suffice for off robot wiring.
|
||||
|
||||
:::{note}
|
||||
Please make sure your chosen power supply can provide enough power for your coprocessor. Undervolting (where enough power isn't being supplied) can cause many issues.
|
||||
:::
|
||||
|
||||
## On-Robot Wiring
|
||||
|
||||
:::{note}
|
||||
We recommend users use the [SnakeEyes Pi Hat](https://www.playingwithfusion.com/productview.php?pdid=133) as it provides passive power over ethernet (POE) and other useful features to simplify wiring and make your life easier.
|
||||
:::
|
||||
|
||||
### Recommended: Coprocessor with Passive POE (Gloworm, Pi with SnakeEyes, Limelight)
|
||||
|
||||
1. Plug the [passive POE injector](https://www.revrobotics.com/rev-11-1210/) into the coprocessor and wire it to PDP/PDH (NOT the VRM).
|
||||
2. Add a breaker to relevant slot in your PDP/PDH
|
||||
3. Run an ethernet cable from the passive POE injector to your network switch / radio (we *STRONGLY* recommend the usage of a network switch, see the [networking](networking.md) section for more info.)
|
||||
|
||||
### Coprocessor without Passive POE
|
||||
|
||||
1a. Option 1: Get a micro USB (may be USB-C if using a newer Pi) pigtail cable and connect the wire ends to a regulator like [this](https://www.pololu.com/product/4082). Then, wire the regulator into your PDP/PDH and the Micro USB / USB C into your coprocessor.
|
||||
|
||||
1b. Option 2: Use a USB power bank to power your coprocessor. Refer to this year's robot rulebook on legal implementations of this.
|
||||
|
||||
2. Run an ethernet cable from your Pi to your network switch / radio (we *STRONGLY* recommend the usage of a network switch, see the [networking](networking.md) section for more info.)
|
||||
|
||||
This diagram shows how to use the recommended regulator to power a coprocessor.
|
||||
|
||||
```{image} images/pololu-diagram.png
|
||||
:alt: A flowchart-type diagram showing how to connect wires from the PDP or PDH to
|
||||
: the recommended voltage regulator and then a Coprocessor.
|
||||
```
|
||||
|
||||
:::{note}
|
||||
The regulator comes with optional screw terminals that may be used to connect the PDP/PDH and Coprocessor power wires if you do not wish to solder them.
|
||||
:::
|
||||
|
||||
Once you have wired your coprocessor, you are now ready to install PhotonVision.
|
||||
@@ -7,6 +7,7 @@ PhotonVision supports object detection using neural network accelerator hardware
|
||||
For the 2024 season, PhotonVision ships with a **pre-trained NOTE detector** (shown above), as well as a mechanism for swapping in custom models. Future development will focus on enabling lower friction management of multiple custom models.
|
||||
|
||||
```{image} images/notes-ui.png
|
||||
|
||||
```
|
||||
|
||||
## Tracking Objects
|
||||
@@ -32,6 +33,10 @@ Compared to other pipelines, object detection exposes very few tuning handles. T
|
||||
|
||||
The same area, aspect ratio, and target orientation/sort parameters from {ref}`reflective pipelines <docs/reflectiveAndShape/contour-filtering:Reflective>` are also exposed in the object detection card.
|
||||
|
||||
## Letterboxing
|
||||
|
||||
Photonvision will letterbox your camera frame to 640x640. This means that if you select a resolution that is larger than 640 it will be scaled down to fit inside a 640x640 frame with black bars if needed. Smaller frames will be scaled up with black bars if needed.
|
||||
|
||||
## Training Custom Models
|
||||
|
||||
Coming soon!
|
||||
|
||||
@@ -14,7 +14,7 @@ You can control the vision LEDs of supported hardware via PhotonLib using the `s
|
||||
// Blink the LEDs.
|
||||
camera.SetLED(photonlib::VisionLEDMode::kBlink);
|
||||
|
||||
.. code-block:: Python
|
||||
.. code-block:: Python
|
||||
|
||||
# Coming Soon!
|
||||
```
|
||||
|
||||
@@ -62,7 +62,7 @@ You can also get the pipeline latency from a pipeline result using the `getLaten
|
||||
// Get the pipeline latency.
|
||||
units::second_t latency = result.GetLatency();
|
||||
|
||||
.. code-block:: Python
|
||||
.. code-block:: Python
|
||||
|
||||
# Coming Soon!
|
||||
```
|
||||
|
||||
@@ -24,7 +24,7 @@ The API documentation can be found in here: [Java](https://github.wpilib.org/all
|
||||
// The parameter for LoadAPrilTagLayoutField will be different depending on the game.
|
||||
frc::AprilTagFieldLayout aprilTagFieldLayout = frc::LoadAprilTagLayoutField(frc::AprilTagField::k2024Crescendo);
|
||||
|
||||
.. code-block:: Python
|
||||
.. code-block:: Python
|
||||
|
||||
# Coming Soon!
|
||||
|
||||
@@ -81,7 +81,7 @@ The PhotonPoseEstimator has a constructor that takes an `AprilTagFieldLayout` (s
|
||||
photonlib::RobotPoseEstimator estimator(
|
||||
aprilTags, photonlib::CLOSEST_TO_REFERENCE_POSE, cameras);
|
||||
|
||||
.. code-block:: Python
|
||||
.. code-block:: Python
|
||||
|
||||
kRobotToCam = wpimath.geometry.Transform3d(
|
||||
wpimath.geometry.Translation3d(0.5, 0.0, 0.5),
|
||||
@@ -123,7 +123,9 @@ Calling `update()` on your `PhotonPoseEstimator` will return an `EstimatedRobotP
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: Python
|
||||
.. code-block:: Python
|
||||
|
||||
# Coming Soon!
|
||||
|
||||
|
||||
|
||||
|
||||
22
docs/source/docs/quick-start/arducam-cameras.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Arducam Cameras
|
||||
|
||||
Arducam cameras are supported for setups with multiple devices. This is possible because Arducam provides software that allows you to assign truly different device names to each camera. This feature is particularly useful in complex setups where multiple cameras are used simultaneously.
|
||||
|
||||
## Setting Up Arducam Cameras
|
||||
|
||||
1. **Download Arducam Software**: [Download and install the Arducam software from their official website.](https://docs.arducam.com/UVC-Camera/Serial-Number-Tool-Guide/)
|
||||
|
||||
2. **Assign Device Names**: Use the Arducam software and Arducam [documentation](https://docs.arducam.com/UVC-Camera/Serial-Number-Tool-Guide/) to give each camera a unique device name. This will help in distinguishing between multiple cameras in your setup.
|
||||
|
||||
## Steps to Configure in PhotonVision
|
||||
|
||||
1. **Open PhotonVision Settings**: Navigate to the cameras page in PhotonVision.
|
||||
|
||||
2. **Select Camera Model**: Select the proper camera. Use the Arducam model selector to specify the model of each Arducam camera connected to your system.
|
||||
|
||||
3. **Save Settings**: Ensure that you save the settings after selecting the appropriate camera model for each device.
|
||||
|
||||
```{image} images/setArducamModel.png
|
||||
:alt: The camera model can be selected from the Arudcam model selector in the cameras tab
|
||||
:align: center
|
||||
```
|
||||
33
docs/source/docs/quick-start/camera-calibration.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Camera Calibration
|
||||
|
||||
:::{important}
|
||||
In order to detect AprilTags and use 3D mode, your camera must be calibrated at the desired resolution! Inaccurate calibration will lead to poor performance.
|
||||
:::
|
||||
|
||||
If you’re not using cameras in 3D mode, calibration is optional, but it can still offer benefits. Calibrating cameras helps refine the pitch and yaw values, leading to more accurate positional data in every mode. {ref}`For a more in-depth view<docs/calibration/calibration:Calibrating Your Camera>`.
|
||||
|
||||
## Print the Calibration Target
|
||||
|
||||
- Downloaded from our [demo site](https://demo.photonvision.org/#/cameras), or directly from your coprocessors cameras tab.
|
||||
- Use the Charuco calibration board:
|
||||
- Board Type: Charuco
|
||||
- Tag Family: 4x4
|
||||
- Pattern Spacing: 1.00in
|
||||
- Marker Size: 0.75in
|
||||
- Board Height : 8
|
||||
- Board Width : 8
|
||||
|
||||
## Prepare the Calibration Target
|
||||
|
||||
- Measure Accurately: Use calipers to measure the actual size of the squares and markers. Accurate measurements are crucial for effective calibration.
|
||||
- Ensure Flatness: The calibration board must be perfectly flat, without any wrinkles or bends, to avoid introducing errors into the calibration process.
|
||||
|
||||
## Calibrate your Camera
|
||||
|
||||
- Take lots of photos: It's recommended to capture more than 50 images to properly calibrate your camera for accuracy. 12 is the bare minimum and may not provide good results.
|
||||
- Other Tips
|
||||
- Move the board not the camera.
|
||||
- Take photos of lots of angles: The more angles the more better (up to 45 deg).
|
||||
- A couple of up close images is good.
|
||||
- Cover the entire cameras fov.
|
||||
- Avoid images with the board facing straight towards the camera.
|
||||
44
docs/source/docs/quick-start/common-setups.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Common Hardware Setups
|
||||
|
||||
## Coprocessors
|
||||
|
||||
:::{note}
|
||||
The Orange Pi 5 is the only currently supported device for object detection.
|
||||
:::
|
||||
|
||||
- Orange Pi 5 4GB
|
||||
- Able to process two object detection streams at once while also processing 1 to 2 AprilTag streams at 1280x800 (30fps).
|
||||
- Raspberry Pi 5 2GB
|
||||
- A good cheaper option. Doesn't support object detection. Able to process 2 AprilTag streams at 1280x800 (30fps).
|
||||
|
||||
## SD Cards
|
||||
|
||||
- 8GB or larger micro SD card
|
||||
- Many teams have found that an industrial micro sd card are much more stable in competition. One example is the SanDisk industrial 16GB micro SD card.
|
||||
|
||||
## Cameras
|
||||
|
||||
- AprilTag
|
||||
|
||||
- Innomaker or Arducam OV9281 UVC USB cameras.
|
||||
|
||||
- Object Detection
|
||||
|
||||
- Arducam OV9782 works well with its global shutter.
|
||||
- Most other fixed-focus color UVC USB webcams.
|
||||
|
||||
- Driver Camera
|
||||
- OV9281
|
||||
- OV9782
|
||||
- Pi Camera Module V1 {ref}`(More setup info)<docs/hardware/picamconfig:Pi Camera Configuration>`
|
||||
- Most other fixed-focus UVC USB webcams
|
||||
|
||||
## Power
|
||||
|
||||
- Pololu S13V30F5 Regulator
|
||||
|
||||
- Wide power range input. Recommended by many teams.
|
||||
|
||||
- Redux Robotics Zinc-V Regulator
|
||||
|
||||
- Recently released for the 2025 season, offering reliable and easy integration.
|
||||
BIN
docs/source/docs/quick-start/images/OrangePiPololu.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
docs/source/docs/quick-start/images/OrangePiPololuPigtail.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
docs/source/docs/quick-start/images/OrangePiZinc.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
docs/source/docs/quick-start/images/OrangePiZincUSBC.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
docs/source/docs/quick-start/images/RPiPololu.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
docs/source/docs/quick-start/images/RPiPololuPigtail.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
docs/source/docs/quick-start/images/RPiZinc.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
docs/source/docs/quick-start/images/RPiZincUSBC.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
docs/source/docs/quick-start/images/editCameraName.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
docs/source/docs/quick-start/images/editHostname.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
docs/source/docs/quick-start/images/motionblur.png
Normal file
|
After Width: | Height: | Size: 394 KiB |
|
After Width: | Height: | Size: 826 KiB |
BIN
docs/source/docs/quick-start/images/networking-diagram.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
docs/source/docs/quick-start/images/setArducamModel.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
13
docs/source/docs/quick-start/index.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Quick Start
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 2
|
||||
|
||||
common-setups
|
||||
quick-install
|
||||
wiring
|
||||
networking
|
||||
arducam-cameras
|
||||
camera-calibration
|
||||
quick-configure
|
||||
```
|
||||
@@ -2,28 +2,53 @@
|
||||
|
||||
## Physical Networking
|
||||
|
||||
:::{note}
|
||||
When using PhotonVision off robot, you *MUST* plug the coprocessor into a physical router/radio. You can then connect your laptop/device used to view the webdashboard to the same network. Any other networking setup will not work and will not be supported in any capacity.
|
||||
:::{warning}
|
||||
When using PhotonVision off robot, you _MUST_ plug the coprocessor into a physical router/radio. You can then connect your laptop/device used to view the webdashboard to the same network. Any other networking setup will not work and will not be supported in any capacity.
|
||||
:::
|
||||
|
||||
After imaging your coprocessor, run an ethernet cable from your coprocessor to a router/radio and power on your coprocessor by plugging it into the wall. Then connect whatever device you're using to view the webdashboard to the same network and navigate to photonvision.local:5800.
|
||||
::::{tab-set}
|
||||
|
||||
PhotonVision *STRONGLY* recommends the usage of a network switch on your robot. This is because the second radio port on the current FRC radios is known to be buggy and cause frequent connection issues that are detrimental during competition. An in-depth guide on how to install a network switch can be found [on FRC 900's website](https://team900.org/blog/ZebraSwitch/).
|
||||
:::{tab-item} New Radio (2025 - present)
|
||||
|
||||
```{danger}
|
||||
Ensure that DIP switches 1 and 2 are turned off; otherwise, the radio PoE feature will fry your coprocessor. [More info.](https://frc-radio.vivid-hosting.net/getting-started/passive-power-over-ethernet-poe-for-downstream-devices)
|
||||
```
|
||||
|
||||
```{image} images/networking-diagram-vividhosting.png
|
||||
:alt: Wiring using a network switch and the new vivid hosting radio
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::{tab-item} Old Radio (pre 2025)
|
||||
|
||||
PhotonVision _STRONGLY_ recommends the usage of a network switch on your robot. This is because the second radio port on the old FRC radios is known to be buggy and cause frequent connection issues that are detrimental during competition. An in-depth guide on how to install a network switch can be found [on FRC 900's website](https://zebracorns.org/blog/ZebraSwitch/).
|
||||
|
||||
```{image} images/networking-diagram.png
|
||||
:alt: Correctly set static IP
|
||||
:alt: Wiring using a network switch and the old open mesh radio
|
||||
```
|
||||
|
||||
:::
|
||||
::::
|
||||
|
||||
## Network Hostname
|
||||
|
||||
Rename each device from the default "Photonvision" to a unique hostname (e.g., "Photon-OrangePi-Left" or "Photon-RPi5-Back"). This helps differentiate multiple coprocessors on your network, making it easier to manage them. Navigate to the settings page and scroll down to the network section. You will find the hostname is set to "photonvision" by default, this can only contain letters (A-Z), numeric characters (0-9), and the minus sign (-).
|
||||
|
||||
```{image} images/editHostname.png
|
||||
:alt: The hostname can be edited in the settings page under the network section.
|
||||
```
|
||||
|
||||
## Digital Networking
|
||||
|
||||
PhotonVision *STRONGLY* recommends the usage of Static IPs as it increases reliability on the field and when using PhotonVision in general. To properly set up your static IP, follow the steps below:
|
||||
PhotonVision _STRONGLY_ recommends the usage of Static IPs as it increases reliability on the field and when using PhotonVision in general. To properly set up your static IP, follow the steps below:
|
||||
|
||||
:::{warning}
|
||||
Only use a static IP when connected to the **robot radio**, and never when testing at home, unless you are well versed in networking or have the relevant "know how".
|
||||
:::
|
||||
|
||||
1. Ensure your robot is on and you are connected to the robot network.
|
||||
2. Navigate to `photonvision.local:5800` (this may be different if you are using a Gloworm / Limelight) in your browser.
|
||||
2. Navigate to `photonvision.local:5800`in your browser.
|
||||
3. Open the settings tab on the left pane.
|
||||
4. Under the Networking section, set your team number.
|
||||
5. Change your IP to Static.
|
||||
57
docs/source/docs/quick-start/quick-configure.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Quick Configure
|
||||
|
||||
## Settings to configure
|
||||
|
||||
### Team number
|
||||
|
||||
In order for photonvision to connect to the roborio it needs to know your team number.
|
||||
|
||||
### Camera Nickname
|
||||
|
||||
You **must** nickname your cameras in photonvision to ensure that every camera has a unique name. This is how we will identify cameras in robot code. The camera can be nickname using the edit button next to the camera name in the upper right of the Dashboard tab.
|
||||
|
||||
```{image} images/editCameraName.png
|
||||
:align: center
|
||||
```
|
||||
|
||||
## Pipeline Settings
|
||||
|
||||
### AprilTag
|
||||
|
||||
When using an Orange Pi 5 with an Arducam OV9281 teams will usually change the following settings. For more info on AprilTag settings please review {ref}`this<docs/apriltag-pipelines/2D-tracking-tuning:2D AprilTag Tuning / Tracking>`.
|
||||
|
||||
- Resolution:
|
||||
- 1280x800
|
||||
- Decimate:
|
||||
- 2
|
||||
- Mode:
|
||||
- 3D
|
||||
- Exposure and Gain:
|
||||
- Adjust these to achieve good brightness without flicker and low motion blur. This may vary based on lighting conditions in your competition environment.
|
||||
- Enable MultiTag
|
||||
- Set arducam specific camera type selector to OV9281
|
||||
|
||||
#### AprilTags and Motion Blur and Rolling Shutter
|
||||
|
||||
When detecting AprilTags, it's important to minimize 'motion blur' as much as possible. Motion blur appears as visual streaking or smearing in the camera feed, resulting from the movement of either the camera or the object in focus. Reducing this effect is essential, as the robot is often in motion, and a clearer image allows for detecting as many tags as possible. This is not to be confused with {ref}`rolling shutter<docs/hardware/selecting-hardware:Cameras Attributes>`.
|
||||
|
||||
- Fixes
|
||||
- Lower your exposure as low as possible. Using gain and brightness to account for lack of brightness.
|
||||
- Other Options:
|
||||
- Don't use/rely vision measurements while moving.
|
||||
|
||||
```{image} images/motionblur.png
|
||||
:align: center
|
||||
```
|
||||
|
||||
### Object Detection
|
||||
|
||||
When using an Orange Pi 5 with an OV9782 teams will usually change the following settings. For more info on object detection settings please review {ref}`this<docs/objectDetection/about-object-detection:About Object Detection>`.
|
||||
|
||||
- Resolution:
|
||||
- Resolutions higher than 640x640 may not result in any higher detection accuracy and may lower {ref}`performance<docs/objectDetection/about-object-detection:Letterboxing>`.
|
||||
- Confidence:
|
||||
- 0.75 - 0.95 Lower values are fpr detecting warn game pieces or less ideal game pieces. Higher for less warn, more ideal game pieces.
|
||||
- White Balance Temperature:
|
||||
- Adjust this to achieve better color accuracy. This may be needed to increase confidence.
|
||||
- Set arducam specific camera type selector to OV9782
|
||||
38
docs/source/docs/quick-start/quick-install.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Quick Install
|
||||
|
||||
## Install the latest image of photonvision for your coprocessor
|
||||
|
||||
- For the supported coprocessors
|
||||
- RPI 3,4,5
|
||||
- Orange Pi 5
|
||||
- Limelight
|
||||
|
||||
For installing on non-supported devices {ref}`see. <docs/advanced-installation/sw_install/index:Software Installation>`
|
||||
|
||||
[Download the latest preconfigured image of photonvision for your coprocessor](https://github.com/PhotonVision/photonvision/releases/latest)
|
||||
|
||||
| Coprocessor | Image filename | Jar |
|
||||
| -------------------- | ---------------------------------------------------- | ------------------------------------- |
|
||||
| OrangePi 5 | photonvision-{version}-linuxarm64_orangepi5.img.xz | photonvision-{version}-linuxarm64.jar |
|
||||
| Raspberry Pi 3, 4, 5 | photonvision-{version}-linuxarm64_RaspberryPi.img.xz | photonvision-{version}-linuxarm64.jar |
|
||||
| Limelight 2 | photonvision-{version}-linuxarm64_limelight2.img.xz | photonvision-{version}-linuxarm64.jar |
|
||||
| Limelight 3 | photonvision-{version}-linuxarm64_limelight3.img.xz | photonvision-{version}-linuxarm64.jar |
|
||||
|
||||
:::{warning}
|
||||
Balena Etcher 1.18.11 is a known working version. Other versions may cause issues such as bootlooping (the system will repeatedly boot and restart) when imaging your device.
|
||||
:::
|
||||
|
||||
Use the 1.18.11 version of [Balena Etcher](https://github.com/balena-io/etcher/releases/tag/v1.18.11) to flash the image onto the coprocessors micro sd card. Select the downloaded `.img.xz` file, select your microSD card, and flash.
|
||||
|
||||
Limelights have a different installation processes. Simply connect the limelight to your computer using the proper usb cable. Select the compute module. If it doesn’t show up after 30s try using another USB port, initialization may take a while. If prompted, install the recommended missing drivers. Select the image, and flash.
|
||||
|
||||
Unless otherwise noted in release notes or if updating from the prior years version, to update PhotonVision after the initial installation, use the offline update option in the settings page with the downloaded jar file from the latest release.
|
||||
|
||||
:::{note}
|
||||
Limelight 2, 2+, and 3 will need a [custom hardware config file](https://github.com/PhotonVision/photonvision/tree/master/docs/source/docs/advanced-installation/sw_install/files) for lighting to work. Currently only limelight 2 and 2+ files are available.
|
||||
:::
|
||||
|
||||
:::{note}
|
||||
Raspberry Pi installations may also use the [Raspberry Pi Imager](https://www.raspberrypi.com/software/) to flash the image.
|
||||
|
||||
:::
|
||||
93
docs/source/docs/quick-start/wiring.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Wiring
|
||||
|
||||
## 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.
|
||||
|
||||
2. Run an ethernet cable from your Pi to your network switch / radio.
|
||||
|
||||
This diagram shows how to use the recommended regulator to power a coprocessor.
|
||||
|
||||
::::{tab-set}
|
||||
|
||||
:::{tab-item} Orange Pi Zinc V USB C
|
||||
|
||||
```{image} images/OrangePiZincUSBC.png
|
||||
:alt: Wiring the opi5 to the pdp using the Redux Robotics Zinc V and usb c
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::{tab-item} Orange Pi 5 Zinc V
|
||||
|
||||
```{image} images/OrangePiZinc.png
|
||||
:alt: Wiring the opi5 to the pdp using the Redux Robotics Zinc V
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::{tab-item} Orange Pi 5 Pololu S13V30F5
|
||||
|
||||
```{image} images/OrangePiPololu.png
|
||||
:alt: Wiring the opi5 to the pdp using the Pololu S13V30F5
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::{tab-item} Orange Pi 5 Pololu S13V30F5 Pigtail
|
||||
|
||||
```{image} images/OrangePiPololuPigtail.png
|
||||
:alt: Wiring the opi5 to the pdp using the Pololu S13V30F5 and a usb c pigtail
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::{tab-item} Raspberry Pi 5 Zinc V USB C
|
||||
|
||||
```{image} images/RPiZincUSBC.png
|
||||
:alt: Wiring the RPI5 to the pdp using the Redux Robotics Zinc V and usb c
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::{tab-item} Raspberry Pi 5 Zinc V
|
||||
|
||||
```{image} images/RPiZinc.png
|
||||
:alt: Wiring the RPI5 to the pdp using the Redux Robotics Zinc V
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::{tab-item} Raspberry Pi 5 Pololu S13V30F5
|
||||
|
||||
```{image} images/RPiPololu.png
|
||||
:alt: Wiring the RPI5 to the pdp using the Pololu S13V30F5
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::{tab-item} Raspberry Pi 5 Pololu S13V30F5 Pigtail
|
||||
|
||||
```{image} images/RPiPololuPigtail.png
|
||||
:alt: Wiring the RPI5 to the pdp using the Pololu S13V30F5 and a usb c pigtail
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::::
|
||||
|
||||
Pigtails can be purchased from many sources we recommend [(USB C)](https://ctr-electronics.com/products/usb-type-c-wire-breakout?_pos=19&_sid=bf06b6a6b&_ss=r) [(Micro USB)](https://ctr-electronics.com/products/usb-micro-power-wire-breakout?pr_prod_strat=e5_desc&pr_rec_id=10bf36ce7&pr_rec_pid=7863771070637&pr_ref_pid=7863771103405&pr_seq=uniform)
|
||||
|
||||
## Coprocessor with Passive POE (Pi with SnakeEyes and Limelight)
|
||||
|
||||
1. Plug the [passive POE injector](https://www.revrobotics.com/rev-11-1210/) into the coprocessor and wire it to PDP/PDH (NOT the VRM).
|
||||
2. Add a breaker to relevant slot in your PDP/PDH
|
||||
3. Run an ethernet cable from the passive POE injector to your network switch / radio.
|
||||
|
||||
## Off-Robot Wiring
|
||||
|
||||
Plugging your coprocessor into the wall via a power brick will suffice for off robot wiring.
|
||||
|
||||
:::{note}
|
||||
Please make sure your chosen power supply can provide enough power for your coprocessor. Undervolting (where enough power isn't being supplied) can cause many issues.
|
||||
:::
|
||||
@@ -17,6 +17,6 @@ If solvePNP is working correctly, the target should be displayed as a small rect
|
||||
</video>
|
||||
```
|
||||
|
||||
## Contour Simplification (Non-Apriltag)
|
||||
## Contour Simplification (Non-AprilTag)
|
||||
|
||||
3D mode internally computes a polygon that approximates the target contour being tracked. This polygon is used to detect the extreme corners of the target. The contour simplification slider changes how far from the original contour the approximation is allowed to deviate. Note that the approximate polygon is drawn on the output image for tuning.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Hardware in the loop simulation is using a physical device, such as a supported co-processor running PhotonVision, to enhance simulation capabilities. This is useful for developing and validating code before the camera is attached to a robot, as well as reducing the work required to use WPILib simulation with PhotonVision.
|
||||
|
||||
Before continuing, ensure PhotonVision is installed on your device. Instructions can be found {ref}`here <docs/installation/index:Installation & Setup>` for all devices.
|
||||
Before continuing, ensure PhotonVision is installed on your device. Instructions can be found {ref}`here <docs/advanced-installation/index:Advanced Installation>` for all devices.
|
||||
|
||||
Your coprocessor and computer running simulation will have to be connected to the same network, like a home router. Connecting the coprocessor directly to the computer will not work.
|
||||
|
||||
@@ -26,9 +26,11 @@ Ethernet adapter Ethernet:
|
||||
Subnet Mask . . . . . . . . . . . : 255.255.255.0
|
||||
Default Gateway . . . . . . . . . : 192.168.254.254
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
```{image} images/coproc-client-to-desktop-sim.png
|
||||
|
||||
```
|
||||
|
||||
No code changes are required, PhotonLib should function similarly to normal operation.
|
||||
@@ -36,4 +38,5 @@ No code changes are required, PhotonLib should function similarly to normal oper
|
||||
Now launch simulation, and you should be able to see the PhotonVision table on your simulation's NetworkTables dashboard.
|
||||
|
||||
```{image} images/hardware-in-the-loop-sim.png
|
||||
|
||||
```
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Simulation Support in PhotonLib in Java
|
||||
|
||||
|
||||
## What Is Simulated?
|
||||
|
||||
Simulation is a powerful tool for validating robot code without access to a physical robot. Read more about [simulation in WPILib](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html).
|
||||
@@ -8,18 +7,18 @@ Simulation is a powerful tool for validating robot code without access to a phys
|
||||
In Java, PhotonLib can simulate cameras on the field and generate target data approximating what would be seen in reality. This simulation attempts to include the following:
|
||||
|
||||
- Camera Properties
|
||||
- Field of Vision
|
||||
- Lens distortion
|
||||
- Image noise
|
||||
- Framerate
|
||||
- Latency
|
||||
- Field of Vision
|
||||
- Lens distortion
|
||||
- Image noise
|
||||
- Framerate
|
||||
- Latency
|
||||
- Target Data
|
||||
- Detected / minimum-area-rectangle corners
|
||||
- Center yaw/pitch
|
||||
- Contour image area percentage
|
||||
- Fiducial ID
|
||||
- Fiducial ambiguity
|
||||
- Fiducial solvePNP transform estimation
|
||||
- Detected / minimum-area-rectangle corners
|
||||
- Center yaw/pitch
|
||||
- Contour image area percentage
|
||||
- Fiducial ID
|
||||
- Fiducial ambiguity
|
||||
- Fiducial solvePNP transform estimation
|
||||
- Camera Raw/Processed Streams (grayscale)
|
||||
|
||||
:::{note}
|
||||
@@ -29,7 +28,7 @@ Simulation does NOT include the following:
|
||||
- Image Thresholding Process (camera gain, brightness, etc)
|
||||
- Pipeline switching
|
||||
- Snapshots
|
||||
:::
|
||||
:::
|
||||
|
||||
This scope was chosen to balance fidelity of the simulation with the ease of setup, in a way that would best benefit most teams.
|
||||
|
||||
@@ -226,7 +225,7 @@ Each `VisionSystemSim` has its own built-in `Field2d` for displaying object pose
|
||||
```
|
||||
|
||||
:::{figure} images/SimExampleField.png
|
||||
*A* `VisionSystemSim`*'s internal* `Field2d` *customized with target images and colors*
|
||||
_A_ `VisionSystemSim`_'s internal_ `Field2d` _customized with target images and colors_
|
||||
:::
|
||||
|
||||
A `PhotonCameraSim` can also draw and publish generated camera frames to a MJPEG stream similar to an actual PhotonVision process.
|
||||
@@ -245,8 +244,8 @@ A `PhotonCameraSim` can also draw and publish generated camera frames to a MJPEG
|
||||
cameraSim.enableDrawWireframe(true);
|
||||
```
|
||||
|
||||
These streams follow the port order mentioned in {ref}`docs/installation/networking:Camera Stream Ports`. For example, a single simulated camera will have its raw stream at `localhost:1181` and processed stream at `localhost:1182`, which can also be found in the CameraServer tab of Shuffleboard like a normal camera stream.
|
||||
These streams follow the port order mentioned in {ref}`docs/quick-start/networking:Camera Stream Ports`. For example, a single simulated camera will have its raw stream at `localhost:1181` and processed stream at `localhost:1182`, which can also be found in the CameraServer tab of Shuffleboard like a normal camera stream.
|
||||
|
||||
:::{figure} images/SimExampleFrame.png
|
||||
*A frame from the processed stream of a simulated camera viewing some 2023 AprilTags with the field wireframe enabled*
|
||||
_A frame from the processed stream of a simulated camera viewing some 2023 AprilTags with the field wireframe enabled_
|
||||
:::
|
||||
|
||||
@@ -26,7 +26,7 @@ Please refer to our comprehensive {ref}`networking troubleshooting tips <docs/tr
|
||||
|
||||
Try these steps to {ref}`troubleshoot your camera connection <docs/troubleshooting/camera-troubleshooting:Camera Troubleshooting>`.
|
||||
|
||||
If you are using a USB camera, it is possible your USB Camera isn't supported by CSCore and therefore won't work with PhotonVision. See {ref}`supported hardware page for more information <docs/hardware/selecting-hardware:Recommended Cameras>`, or the above Camera Troubleshooting page for more information on determining this locally.
|
||||
If you are using a USB camera, it is possible your USB Camera isn't supported by CSCore and therefore won't work with PhotonVision.
|
||||
|
||||
### Camera is consistently returning incorrect values when in 3D mode
|
||||
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
# Networking Troubleshooting
|
||||
|
||||
Before reading further, ensure that you follow all the recommendations {ref}`in our networking section <docs/installation/networking:Physical Networking>`. You should follow these guidelines in order for PhotonVision to work properly; other networking setups are not officially supported.
|
||||
Before reading further, ensure that you follow all the recommendations {ref}`in our networking section <docs/quick-start/networking:Physical Networking>`. You should follow these guidelines in order for PhotonVision to work properly; other networking setups are not officially supported.
|
||||
|
||||
## Checklist
|
||||
|
||||
A few issues make up the majority of support requests. Run through this checklist quickly to catch some common mistakes.
|
||||
|
||||
- Is your camera connected to the robot's radio through a {ref}`network switch <docs/installation/networking:Physical Networking>`?
|
||||
- Ethernet straight from a laptop to a coprocessor will not work (most likely), due to the unreliability of link-local connections.
|
||||
- Even if there's a switch between your laptop and coprocessor, you'll still want a radio or router in the loop somehow.
|
||||
- The FRC radio is the *only* router we will officially support due to the innumerable variations between routers.
|
||||
- Is your camera connected to the robot's radio through a {ref}`network switch <docs/quick-start/networking:Physical Networking>`?
|
||||
- Ethernet straight from a laptop to a coprocessor will not work (most likely), due to the unreliability of link-local connections.
|
||||
- Even if there's a switch between your laptop and coprocessor, you'll still want a radio or router in the loop somehow.
|
||||
- The FRC radio is the _only_ router we will officially support due to the innumerable variations between routers.
|
||||
- (Raspberry Pi, Orange Pi & Limelight only) have you flashed the correct image, and is it up to date?
|
||||
- Limelights 2/2+ and Gloworms should be flashed using the Limelight 2 image (eg, `photonvision-v2024.2.8-linuxarm64_limelight2.img.xz`).
|
||||
- Limelights 3 should be flashed using the Limelight 3 image (eg, `photonvision-v2024.2.8-linuxarm64_limelight3.img.xz`).
|
||||
- Raspberry Pi devices (including Pi 3, Pi 4, CM3 and CM4) should be flashed using the Raspberry Pi image (eg, `photonvision-v2024.2.8-linuxarm64_RaspberryPi.img.xz`).
|
||||
- Orange Pi 5 devices should be flashed using the Orange Pi 5 image (eg, `photonvision-v2024.2.8-linuxarm64_orangepi5.img.xz`).
|
||||
- Orange Pi 5+ devices should be flashed using the Orange Pi 5+ image (eg, `photonvision-v2024.2.8-linuxarm64_orangepi5plus.img.xz`).
|
||||
- Limelights 2/2+ should be flashed using the Limelight 2 image (eg, `photonvision-v2024.2.8-linuxarm64_limelight2.img.xz`).
|
||||
- Limelights 3 should be flashed using the Limelight 3 image (eg, `photonvision-v2024.2.8-linuxarm64_limelight3.img.xz`).
|
||||
- Raspberry Pi devices (including Pi 3, Pi 4, CM3 and CM4) should be flashed using the Raspberry Pi image (eg, `photonvision-v2024.2.8-linuxarm64_RaspberryPi.img.xz`).
|
||||
- Orange Pi 5 devices should be flashed using the Orange Pi 5 image (eg, `photonvision-v2024.2.8-linuxarm64_orangepi5.img.xz`).
|
||||
- Orange Pi 5+ devices should be flashed using the Orange Pi 5+ image (eg, `photonvision-v2024.2.8-linuxarm64_orangepi5plus.img.xz`).
|
||||
- Is your robot code using a **2024** version of WPILib, and is your coprocessor using the most up to date **2024** release?
|
||||
- 2022, 2023 and 2024 versions of either cannot be mix-and-matched!
|
||||
- Your PhotonVision version can be checked on the {ref}`settings tab<docs/settings:settings>`.
|
||||
- 2022, 2023 and 2024 versions of either cannot be mix-and-matched!
|
||||
- Your PhotonVision version can be checked on the {ref}`settings tab<docs/settings:settings>`.
|
||||
- Is your team number correctly set on the {ref}`settings tab<docs/settings:settings>`?
|
||||
|
||||
### photonvision.local Not Found
|
||||
|
||||
@@ -2,28 +2,35 @@
|
||||
:alt: PhotonVision
|
||||
```
|
||||
|
||||
Welcome to the official documentation of PhotonVision! PhotonVision is the free, fast, and easy-to-use vision processing solution for the *FIRST* Robotics Competition. PhotonVision is designed to get vision working on your robot *quickly*, without the significant cost of other similar solutions. PhotonVision supports a variety of COTS hardware, including the Raspberry Pi 3 and 4, the [Gloworm smart camera](https://photonvision.github.io/gloworm-docs/docs/quickstart/#finding-gloworm), the [SnakeEyes Pi hat](https://www.playingwithfusion.com/productview.php?pdid=133), and the Orange Pi 5.
|
||||
Welcome to the official documentation of PhotonVision! PhotonVision is the free, fast, and easy-to-use vision processing solution for the _FIRST_ Robotics Competition. PhotonVision is designed to get vision working on your robot _quickly_, without the significant cost of other similar solutions. PhotonVision supports a variety of COTS hardware, including the Raspberry Pi 3, 4, and 5, the [SnakeEyes Pi hat](https://www.playingwithfusion.com/productview.php?pdid=133), and the Orange Pi 5.
|
||||
|
||||
# Content
|
||||
|
||||
```{eval-rst}
|
||||
.. grid:: 2
|
||||
|
||||
.. grid-item-card:: Getting Started
|
||||
:link: docs/installation/index
|
||||
.. grid-item-card:: Quick Start
|
||||
:link: docs/quick-start/index
|
||||
:link-type: doc
|
||||
|
||||
Get started with installing PhotonVision, creating a pipeline, and tuning it for usage in competitions.
|
||||
Quick start to using Photonvision.
|
||||
|
||||
.. grid-item-card:: Advanced Installation
|
||||
:link: docs/advanced-installation/index
|
||||
:link-type: doc
|
||||
|
||||
Get started with installing PhotonVision on non-supported hardware.
|
||||
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. grid:: 2
|
||||
|
||||
.. grid-item-card:: Programming Reference and PhotonLib
|
||||
:link: docs/programming/index
|
||||
:link-type: doc
|
||||
|
||||
Learn more about PhotonLib, our vendor dependency which makes it easier for teams to retrieve vision data, make various calculations, and more.
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. grid:: 2
|
||||
|
||||
.. grid-item-card:: Integration
|
||||
:link: docs/integration/index
|
||||
@@ -31,21 +38,26 @@ Welcome to the official documentation of PhotonVision! PhotonVision is the free,
|
||||
|
||||
Pick how to use vision processing results to control a physical robot.
|
||||
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. grid:: 2
|
||||
|
||||
.. grid-item-card:: Code Examples
|
||||
:link: docs/examples/index
|
||||
:link-type: doc
|
||||
|
||||
View various step by step guides on how to use data from PhotonVision in your code, along with game-specific examples.
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. grid:: 2
|
||||
|
||||
.. grid-item-card:: Hardware
|
||||
:link: docs/hardware/index
|
||||
:link-type: doc
|
||||
|
||||
Select appropriate hardware for high-quality and easy vision target detection.
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. grid:: 2
|
||||
|
||||
.. grid-item-card:: Contributing
|
||||
:link: docs/contributing/index
|
||||
@@ -77,8 +89,9 @@ PhotonVision is licensed under the [GNU GPL v3](https://www.gnu.org/licenses/gpl
|
||||
:maxdepth: 0
|
||||
|
||||
docs/description
|
||||
docs/quick-start/index
|
||||
docs/hardware/index
|
||||
docs/installation/index
|
||||
docs/advanced-installation/index
|
||||
docs/settings
|
||||
```
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ const resetTempSettingsStruct = () => {
|
||||
const settingsValid = ref(true);
|
||||
|
||||
const isValidNetworkTablesIP = (v: string | undefined): boolean => {
|
||||
// Check if it is a valid team number between 1-9999
|
||||
const teamNumberRegex = /^[1-9][0-9]{0,3}$/;
|
||||
// Check if it is a valid team number between 1-99999 (5 digits)
|
||||
const teamNumberRegex = /^[1-9][0-9]{0,4}$/;
|
||||
// Check if it is a team number longer than 5 digits
|
||||
const badTeamNumberRegex = /^[0-9]{5,}$/;
|
||||
const badTeamNumberRegex = /^[0-9]{6,}$/;
|
||||
|
||||
if (v === undefined) return false;
|
||||
if (teamNumberRegex.test(v)) return true;
|
||||
|
||||
@@ -142,7 +142,7 @@ export interface CameraCalibrationResult {
|
||||
distCoeffs: JsonMatOfDouble;
|
||||
observations: BoardObservation[];
|
||||
calobjectWarp?: number[];
|
||||
// We might have to omit observations for bandwith, so backend will send us this
|
||||
// We might have to omit observations for bandwidth, so backend will send us this
|
||||
numSnapshots: number;
|
||||
meanErrors: number[];
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ plugins {
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
ext.licenseFile = file("$rootDir/LICENSE")
|
||||
apply from: "${rootDir}/shared/common.gradle"
|
||||
|
||||
wpilibTools.deps.wpilibVersion = wpi.versions.wpilibVersion.get()
|
||||
@@ -24,7 +25,7 @@ nativeConfig.dependencies.add wpilibTools.deps.wpilib("ntcore")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("cscore")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("apriltag")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("hal")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilibOpenCv("frc" + wpi.frcYear.get(), wpi.versions.opencvVersion.get())
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilibOpenCv("frc" + openCVYear, wpi.versions.opencvVersion.get())
|
||||
|
||||
dependencies {
|
||||
// JOGL stuff (currently we only distribute for aarch64, which is Pi 4)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
rootProject.name = 'photon-core'
|
||||
@@ -84,15 +84,7 @@ public class CameraConfiguration {
|
||||
this.calibrations = new ArrayList<>();
|
||||
this.otherPaths = alternates;
|
||||
|
||||
logger.debug(
|
||||
"Creating USB camera configuration for "
|
||||
+ cameraType
|
||||
+ " "
|
||||
+ baseName
|
||||
+ " (AKA "
|
||||
+ nickname
|
||||
+ ") at "
|
||||
+ path);
|
||||
logger.debug("Creating USB camera configuration for " + this.toShortString());
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
@@ -120,15 +112,7 @@ public class CameraConfiguration {
|
||||
this.usbPID = usbPID;
|
||||
this.usbVID = usbVID;
|
||||
|
||||
logger.debug(
|
||||
"Creating camera configuration for "
|
||||
+ cameraType
|
||||
+ " "
|
||||
+ baseName
|
||||
+ " (AKA "
|
||||
+ nickname
|
||||
+ ") at "
|
||||
+ path);
|
||||
logger.debug("Loaded camera configuration for " + toShortString());
|
||||
}
|
||||
|
||||
public void addPipelineSettings(List<CVPipelineSettings> settings) {
|
||||
@@ -189,6 +173,30 @@ public class CameraConfiguration {
|
||||
return Arrays.stream(otherPaths).filter(path -> path.contains("/by-path/")).findFirst();
|
||||
}
|
||||
|
||||
public String toShortString() {
|
||||
return "CameraConfiguration [baseName="
|
||||
+ baseName
|
||||
+ ", uniqueName="
|
||||
+ uniqueName
|
||||
+ ", nickname="
|
||||
+ nickname
|
||||
+ ", path="
|
||||
+ path
|
||||
+ ", otherPaths="
|
||||
+ Arrays.toString(otherPaths)
|
||||
+ ", cameraType="
|
||||
+ cameraType
|
||||
+ ", cameraQuirks="
|
||||
+ cameraQuirks
|
||||
+ ", FOV="
|
||||
+ FOV
|
||||
+ "]"
|
||||
+ ", PID="
|
||||
+ usbPID
|
||||
+ ", VID="
|
||||
+ usbVID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CameraConfiguration [baseName="
|
||||
|
||||
@@ -340,7 +340,8 @@ public class ConfigManager {
|
||||
|
||||
/**
|
||||
* Disable flushing settings to disk as part of our JVM exit hook. Used to prevent uploading all
|
||||
* settings from getting its new configs overwritten at program exit and before theyre all loaded.
|
||||
* settings from getting its new configs overwritten at program exit and before they're all
|
||||
* loaded.
|
||||
*/
|
||||
public void disableFlushOnShutdown() {
|
||||
this.flushOnShutdown = false;
|
||||
|
||||
@@ -151,7 +151,11 @@ public class PhotonConfiguration {
|
||||
generalSubmap.put("availableModels", NeuralNetworkModelManager.getInstance().getModels());
|
||||
generalSubmap.put(
|
||||
"supportedBackends", NeuralNetworkModelManager.getInstance().getSupportedBackends());
|
||||
generalSubmap.put("hardwareModel", hardwareConfig.deviceName);
|
||||
generalSubmap.put(
|
||||
"hardwareModel",
|
||||
hardwareConfig.deviceName.isEmpty()
|
||||
? Platform.getHardwareModel()
|
||||
: hardwareConfig.deviceName);
|
||||
generalSubmap.put("hardwarePlatform", Platform.getPlatformName());
|
||||
settingsSubmap.put("general", generalSubmap);
|
||||
// AprilTagFieldLayout
|
||||
|
||||
@@ -265,7 +265,7 @@ public class SqlConfigProvider extends ConfigProvider {
|
||||
JacksonUtils.deserialize(
|
||||
getOneConfigFile(conn, GlobalKeys.HARDWARE_CONFIG), HardwareConfig.class);
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not deserialize hardware config! Loading defaults");
|
||||
logger.error("Could not deserialize hardware config! Loading defaults", e);
|
||||
hardwareConfig = new HardwareConfig();
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ public class SqlConfigProvider extends ConfigProvider {
|
||||
JacksonUtils.deserialize(
|
||||
getOneConfigFile(conn, GlobalKeys.HARDWARE_SETTINGS), HardwareSettings.class);
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not deserialize hardware settings! Loading defaults");
|
||||
logger.error("Could not deserialize hardware settings! Loading defaults", e);
|
||||
hardwareSettings = new HardwareSettings();
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ public class SqlConfigProvider extends ConfigProvider {
|
||||
JacksonUtils.deserialize(
|
||||
getOneConfigFile(conn, GlobalKeys.NETWORK_CONFIG), NetworkConfig.class);
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not deserialize network config! Loading defaults");
|
||||
logger.error("Could not deserialize network config! Loading defaults", e);
|
||||
networkConfig = new NetworkConfig();
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ public class SqlConfigProvider extends ConfigProvider {
|
||||
JacksonUtils.deserialize(
|
||||
getOneConfigFile(conn, GlobalKeys.ATFL_CONFIG_FILE), AprilTagFieldLayout.class);
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not deserialize apriltag layout! Loading defaults");
|
||||
logger.error("Could not deserialize apriltag layout! Loading defaults", e);
|
||||
try {
|
||||
atfl = AprilTagFieldLayout.loadField(AprilTagFields.kDefaultField);
|
||||
} catch (UncheckedIOException e2) {
|
||||
|
||||
@@ -20,7 +20,7 @@ package org.photonvision.common.dataflow.networktables;
|
||||
import edu.wpi.first.math.geometry.Transform3d;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEvent;
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import edu.wpi.first.networktables.NetworkTablesJNI;
|
||||
import java.util.List;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
@@ -146,13 +146,19 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
|
||||
List.of(),
|
||||
result.inputAndOutputFrame);
|
||||
else acceptedResult = result;
|
||||
var now = WPIUtilJNI.now();
|
||||
var captureMicros = MathUtils.nanosToMicros(acceptedResult.getImageCaptureTimestampNanos());
|
||||
var now = NetworkTablesJNI.now();
|
||||
var captureMicros = MathUtils.nanosToMicros(result.getImageCaptureTimestampNanos());
|
||||
|
||||
var offset = NetworkTablesManager.getInstance().getOffset();
|
||||
|
||||
// Transform the metadata timestamps from the local nt::Now timebase to the Time Sync Server's
|
||||
// timebase
|
||||
var simplified =
|
||||
new PhotonPipelineResult(
|
||||
acceptedResult.sequenceID,
|
||||
captureMicros,
|
||||
now,
|
||||
captureMicros + offset,
|
||||
now + offset,
|
||||
NetworkTablesManager.getInstance().getTimeSinceLastPong(),
|
||||
TrackedTarget.simpleFromTrackedTargets(acceptedResult.targets),
|
||||
acceptedResult.multiTagResult);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.photonvision.common.dataflow.networktables;
|
||||
|
||||
import edu.wpi.first.apriltag.AprilTagFieldLayout;
|
||||
import edu.wpi.first.networktables.LogMessage;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEvent;
|
||||
import edu.wpi.first.networktables.NetworkTableEvent.Kind;
|
||||
@@ -26,7 +27,6 @@ import edu.wpi.first.networktables.StringSubscriber;
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Consumer;
|
||||
import org.photonvision.PhotonVersion;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.configuration.NetworkConfig;
|
||||
@@ -34,6 +34,7 @@ import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||
import org.photonvision.common.hardware.HardwareManager;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.LogLevel;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.scripting.ScriptEventType;
|
||||
import org.photonvision.common.scripting.ScriptManager;
|
||||
@@ -41,32 +42,39 @@ import org.photonvision.common.util.TimedTaskManager;
|
||||
import org.photonvision.common.util.file.JacksonUtils;
|
||||
|
||||
public class NetworkTablesManager {
|
||||
private static final Logger logger =
|
||||
new Logger(NetworkTablesManager.class, LogGroup.NetworkTables);
|
||||
|
||||
private final NetworkTableInstance ntInstance = NetworkTableInstance.getDefault();
|
||||
private final String kRootTableName = "/photonvision";
|
||||
private final String kFieldLayoutName = "apriltag_field_layout";
|
||||
public final NetworkTable kRootTable = ntInstance.getTable(kRootTableName);
|
||||
|
||||
private final NTLogger m_ntLogger = new NTLogger();
|
||||
|
||||
private boolean m_isRetryingConnection = false;
|
||||
|
||||
private StringSubscriber m_fieldLayoutSubscriber =
|
||||
kRootTable.getStringTopic(kFieldLayoutName).subscribe("");
|
||||
|
||||
private final TimeSyncManager m_timeSync = new TimeSyncManager(kRootTable);
|
||||
|
||||
private NetworkTablesManager() {
|
||||
ntInstance.addLogger(255, 255, (event) -> {}); // to hide error messages
|
||||
ntInstance.addConnectionListener(true, m_ntLogger); // to hide error messages
|
||||
ntInstance.addLogger(
|
||||
LogMessage.kInfo, LogMessage.kCritical, this::logNtMessage); // to hide error messages
|
||||
ntInstance.addConnectionListener(true, this::checkNtConnectState); // to hide error messages
|
||||
|
||||
ntInstance.addListener(
|
||||
m_fieldLayoutSubscriber, EnumSet.of(Kind.kValueAll), this::onFieldLayoutChanged);
|
||||
|
||||
TimedTaskManager.getInstance().addTask("NTManager", this::ntTick, 5000);
|
||||
|
||||
// Get the UI state in sync with the backend. NT should fire a callback when it first connects
|
||||
// to the robot
|
||||
broadcastConnectedStatus();
|
||||
}
|
||||
|
||||
public void registerTimedTasks() {
|
||||
m_timeSync.start();
|
||||
TimedTaskManager.getInstance().addTask("NTManager", this::ntTick, 5000);
|
||||
}
|
||||
|
||||
private static NetworkTablesManager INSTANCE;
|
||||
|
||||
public static NetworkTablesManager getInstance() {
|
||||
@@ -74,43 +82,72 @@ public class NetworkTablesManager {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static final Logger logger = new Logger(NetworkTablesManager.class, LogGroup.General);
|
||||
private void logNtMessage(NetworkTableEvent event) {
|
||||
String levelmsg = "DEBUG";
|
||||
LogLevel pvlevel = LogLevel.DEBUG;
|
||||
if (event.logMessage.level >= LogMessage.kCritical) {
|
||||
pvlevel = LogLevel.ERROR;
|
||||
levelmsg = "CRITICAL";
|
||||
} else if (event.logMessage.level >= LogMessage.kError) {
|
||||
pvlevel = LogLevel.ERROR;
|
||||
levelmsg = "ERROR";
|
||||
} else if (event.logMessage.level >= LogMessage.kWarning) {
|
||||
pvlevel = LogLevel.WARN;
|
||||
levelmsg = "WARNING";
|
||||
} else if (event.logMessage.level >= LogMessage.kInfo) {
|
||||
pvlevel = LogLevel.INFO;
|
||||
levelmsg = "INFO";
|
||||
}
|
||||
|
||||
private static class NTLogger implements Consumer<NetworkTableEvent> {
|
||||
private boolean hasReportedConnectionFailure = false;
|
||||
logger.log(
|
||||
"NT: "
|
||||
+ levelmsg
|
||||
+ " "
|
||||
+ event.logMessage.level
|
||||
+ ": "
|
||||
+ event.logMessage.message
|
||||
+ " ("
|
||||
+ event.logMessage.filename
|
||||
+ ":"
|
||||
+ event.logMessage.line
|
||||
+ ")",
|
||||
pvlevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(NetworkTableEvent event) {
|
||||
var isConnEvent = event.is(Kind.kConnected);
|
||||
var isDisconnEvent = event.is(Kind.kDisconnected);
|
||||
public void checkNtConnectState(NetworkTableEvent event) {
|
||||
var isConnEvent = event.is(Kind.kConnected);
|
||||
var isDisconnEvent = event.is(Kind.kDisconnected);
|
||||
|
||||
if (!hasReportedConnectionFailure && isDisconnEvent) {
|
||||
var msg =
|
||||
String.format(
|
||||
"NT lost connection to %s:%d! (NT version %d). Will retry in background.",
|
||||
event.connInfo.remote_ip,
|
||||
event.connInfo.remote_port,
|
||||
event.connInfo.protocol_version);
|
||||
logger.error(msg);
|
||||
HardwareManager.getInstance().setNTConnected(false);
|
||||
if (isDisconnEvent) {
|
||||
var msg =
|
||||
String.format(
|
||||
"NT lost connection to %s:%d! (NT version %d). Will retry in background.",
|
||||
event.connInfo.remote_ip,
|
||||
event.connInfo.remote_port,
|
||||
event.connInfo.protocol_version);
|
||||
logger.error(msg);
|
||||
HardwareManager.getInstance().setNTConnected(false);
|
||||
|
||||
hasReportedConnectionFailure = true;
|
||||
getInstance().broadcastConnectedStatus();
|
||||
} else if (isConnEvent && event.connInfo != null) {
|
||||
var msg =
|
||||
String.format(
|
||||
"NT connected to %s:%d! (NT version %d)",
|
||||
event.connInfo.remote_ip,
|
||||
event.connInfo.remote_port,
|
||||
event.connInfo.protocol_version);
|
||||
logger.info(msg);
|
||||
HardwareManager.getInstance().setNTConnected(true);
|
||||
getInstance().broadcastConnectedStatus();
|
||||
} else if (isConnEvent && event.connInfo != null) {
|
||||
var msg =
|
||||
String.format(
|
||||
"NT connected to %s:%d! (NT version %d)",
|
||||
event.connInfo.remote_ip,
|
||||
event.connInfo.remote_port,
|
||||
event.connInfo.protocol_version);
|
||||
logger.info(msg);
|
||||
HardwareManager.getInstance().setNTConnected(true);
|
||||
|
||||
hasReportedConnectionFailure = false;
|
||||
ScriptManager.queueEvent(ScriptEventType.kNTConnected);
|
||||
getInstance().broadcastVersion();
|
||||
getInstance().broadcastConnectedStatus();
|
||||
}
|
||||
ScriptManager.queueEvent(ScriptEventType.kNTConnected);
|
||||
getInstance().broadcastVersion();
|
||||
getInstance().broadcastConnectedStatus();
|
||||
|
||||
m_timeSync.reportNtConnected();
|
||||
} else if (isConnEvent) {
|
||||
logger.warn("Got connection event with no connection info??");
|
||||
} else {
|
||||
logger.warn("Got a non-sensical connection message that is neither connect nor disconnect?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,9 +205,16 @@ public class NetworkTablesManager {
|
||||
} else {
|
||||
setClientMode(config.ntServerAddress);
|
||||
}
|
||||
|
||||
m_timeSync.setConfig(config);
|
||||
|
||||
broadcastVersion();
|
||||
}
|
||||
|
||||
public long getOffset() {
|
||||
return m_timeSync.getOffset();
|
||||
}
|
||||
|
||||
private void setClientMode(String ntServerAddress) {
|
||||
ntInstance.stopServer();
|
||||
ntInstance.startClient4("photonvision");
|
||||
@@ -211,4 +255,8 @@ public class NetworkTablesManager {
|
||||
"[NetworkTablesManager] Could not connect to the robot! Will retry in the background...");
|
||||
}
|
||||
}
|
||||
|
||||
public long getTimeSinceLastPong() {
|
||||
return m_timeSync.getTimeSinceLastPong();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.networktables;
|
||||
|
||||
import edu.wpi.first.cscore.CameraServerJNI;
|
||||
import edu.wpi.first.networktables.IntegerPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import org.photonvision.common.configuration.NetworkConfig;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.TimedTaskManager;
|
||||
import org.photonvision.jni.PhotonTargetingJniLoader;
|
||||
import org.photonvision.jni.TimeSyncClient;
|
||||
import org.photonvision.jni.TimeSyncServer;
|
||||
|
||||
public class TimeSyncManager {
|
||||
private static final Logger logger = new Logger(TimeSyncManager.class, LogGroup.NetworkTables);
|
||||
|
||||
private TimeSyncClient m_client = null;
|
||||
private TimeSyncServer m_server = null;
|
||||
|
||||
private NetworkTableInstance ntInstance;
|
||||
IntegerPublisher m_offsetPub;
|
||||
IntegerPublisher m_rtt2Pub;
|
||||
IntegerPublisher m_pingsPub;
|
||||
IntegerPublisher m_pongsPub;
|
||||
IntegerPublisher m_lastPongTimePub;
|
||||
|
||||
public TimeSyncManager(NetworkTable kRootTable) {
|
||||
if (!PhotonTargetingJniLoader.isWorking) {
|
||||
logger.error("PhotonTargetingJNI was not loaded! Cannot do time-sync");
|
||||
}
|
||||
|
||||
this.ntInstance = kRootTable.getInstance();
|
||||
|
||||
// Need this subtable to be unique per coprocessor. TODO: consider using MAC address or
|
||||
// something similar for metrics?
|
||||
var timeTable = kRootTable.getSubTable(".timesync").getSubTable(CameraServerJNI.getHostname());
|
||||
m_offsetPub = timeTable.getIntegerTopic("offset_us").publish();
|
||||
m_rtt2Pub = timeTable.getIntegerTopic("rtt2_us").publish();
|
||||
m_pingsPub = timeTable.getIntegerTopic("ping_tx_count").publish();
|
||||
m_pongsPub = timeTable.getIntegerTopic("pong_rx_count").publish();
|
||||
m_lastPongTimePub = timeTable.getIntegerTopic("pong_rx_time_us").publish();
|
||||
|
||||
// default to being a client
|
||||
logger.debug("Starting TimeSyncClient on localhost (for now)");
|
||||
m_client = new TimeSyncClient("127.0.0.1", 5810, 1.0);
|
||||
}
|
||||
|
||||
// Since we're spinning off tasks in a new thread, be careful and start it seperately
|
||||
public void start() {
|
||||
if (!PhotonTargetingJniLoader.isWorking) {
|
||||
logger.error("PhotonTargetingJNI was not loaded! Cannot start");
|
||||
}
|
||||
|
||||
TimedTaskManager.getInstance().addTask("TimeSyncManager::tick", this::tick, 1000);
|
||||
}
|
||||
|
||||
public synchronized long getOffset() {
|
||||
if (!PhotonTargetingJniLoader.isWorking) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if we're a client, return the offset to server time
|
||||
if (m_client != null) return m_client.getOffset();
|
||||
// if we're a server, our time (nt::Now) is the same as network time
|
||||
if (m_server != null) return 0;
|
||||
|
||||
// ????? should never hit
|
||||
logger.error("Client and server and null?");
|
||||
return 0;
|
||||
}
|
||||
|
||||
synchronized void setConfig(NetworkConfig config) {
|
||||
if (!PhotonTargetingJniLoader.isWorking) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_client == null && m_server == null) {
|
||||
throw new RuntimeException("Neither client nor server are null?");
|
||||
}
|
||||
|
||||
// if not already running a server, set it up
|
||||
if (config.runNTServer && m_server == null) {
|
||||
// tear down anything old
|
||||
if (m_client != null) {
|
||||
logger.debug("Tearing down old client");
|
||||
m_client.stop();
|
||||
m_client = null;
|
||||
}
|
||||
|
||||
logger.debug("Starting TimeSyncServer");
|
||||
m_server = new TimeSyncServer(5810);
|
||||
m_server.start();
|
||||
} else
|
||||
// if not already running a client, set it up
|
||||
if (m_client == null) {
|
||||
// tear down anything old
|
||||
if (m_server != null) {
|
||||
logger.debug("Tearing down old server");
|
||||
m_server.stop();
|
||||
m_server = null;
|
||||
}
|
||||
|
||||
// Guess at IP -- tick will take care of changing this (may take up to 1 second)
|
||||
logger.debug("Starting TimeSyncClient on localhost (for now)");
|
||||
m_client = new TimeSyncClient("127.0.0.1", 5810, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void tick() {
|
||||
if (m_client != null) {
|
||||
var conns = ntInstance.getConnections();
|
||||
|
||||
if (conns.length > 0) {
|
||||
var newServer = conns[0].remote_ip;
|
||||
if (!m_client.getServer().equals(newServer)) {
|
||||
logger.debug("Changing TimeSyncClient server to " + newServer);
|
||||
m_client.setServer(newServer);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_client != null) {
|
||||
var m = m_client.getPingMetadata();
|
||||
|
||||
m_offsetPub.set(m.offset);
|
||||
m_rtt2Pub.set(m.rtt2);
|
||||
m_pingsPub.set(m.pingsSent);
|
||||
m_pongsPub.set(m.pongsReceived);
|
||||
m_lastPongTimePub.set(m.lastPongTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized long getTimeSinceLastPong() {
|
||||
if (m_client != null) {
|
||||
return m_client.getPingMetadata().timeSinceLastPong();
|
||||
} else if (m_server != null) {
|
||||
return 0;
|
||||
} else {
|
||||
// ????
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Restart our timesync client if NT just connected */
|
||||
public synchronized void reportNtConnected() {
|
||||
if (m_client != null) {
|
||||
// restart (in java code; we could just add a reset metrics function...)
|
||||
logger.debug(
|
||||
"NT (re)connected -- restarting Time Sync Client at " + m_client.getServer() + ":5810");
|
||||
m_client.stop();
|
||||
m_client = new TimeSyncClient(m_client.getServer(), 5810, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public enum PigpioCommand {
|
||||
PCMD_WVDEL(50), // int wave_delete(unsigned wave_id)
|
||||
PCMD_WVTX(51), // int wave_tx_send(unsigned wave_id) (once)
|
||||
PCMD_WVTXR(52), // int wave_tx_send(unsigned wave_id) (repeat)
|
||||
PCMD_GDC(83), // int get_duty_cyle(unsigned user_gpio)
|
||||
PCMD_GDC(83), // int get_duty_cycle(unsigned user_gpio)
|
||||
PCMD_HP(86), // int hardware_pwm(unsigned gpio, unsigned PWMfreq, unsigned PWMduty)
|
||||
PCMD_WVTXM(100); // int wave_tx_send(unsigned wave_id, unsigned wave_mode)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public class RK3588Cmds extends LinuxCmds {
|
||||
// CPU Temperature
|
||||
/* The RK3588 chip has 7 thermal zones that can be accessed via:
|
||||
* /sys/class/thermal/thermal_zoneX/temp
|
||||
* where X is an interger from 0 to 6.
|
||||
* where X is an integer from 0 to 6.
|
||||
*
|
||||
* || Zone || Location || Comments ||
|
||||
* | 0 | soc | soc thermal (near the center of the chip) |
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.logging;
|
||||
|
||||
import edu.wpi.first.util.RuntimeDetector;
|
||||
import org.photonvision.common.util.TimedTaskManager;
|
||||
import org.photonvision.jni.QueuedFileLogger;
|
||||
|
||||
/**
|
||||
* Listens for and reproduces Linux kernel logs, from /var/log/kern.log, into the Photon logger
|
||||
* ecosystem
|
||||
*/
|
||||
public class KernelLogLogger {
|
||||
private static KernelLogLogger INSTANCE;
|
||||
|
||||
public static KernelLogLogger getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new KernelLogLogger();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
QueuedFileLogger listener = null;
|
||||
Logger logger = new Logger(KernelLogLogger.class, LogGroup.General);
|
||||
|
||||
public KernelLogLogger() {
|
||||
if (RuntimeDetector.isLinux()) {
|
||||
listener = new QueuedFileLogger("/var/log/kern.log");
|
||||
} else {
|
||||
System.out.println("NOT for klogs");
|
||||
}
|
||||
|
||||
// arbitrary frequency to grab logs. The underlying native buffer will grow unbounded without
|
||||
// this, lol
|
||||
TimedTaskManager.getInstance().addTask("outputPrintk", this::outputNewPrintks, 1000);
|
||||
}
|
||||
|
||||
public void outputNewPrintks() {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var msg : listener.getNewlines()) {
|
||||
// We currently set all logs to debug regardless of their actual level
|
||||
logger.log(msg, LogLevel.DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,4 +25,6 @@ public enum LogGroup {
|
||||
General,
|
||||
Config,
|
||||
CSCore,
|
||||
NetworkTables,
|
||||
System,
|
||||
}
|
||||
|
||||
@@ -30,8 +30,34 @@ import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||
import org.photonvision.common.util.TimedTaskManager;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
/** TODO: get rid of static {} blocks and refactor to singleton pattern */
|
||||
public class Logger {
|
||||
private static final HashMap<LogGroup, LogLevel> levelMap = new HashMap<>();
|
||||
private static final List<LogAppender> currentAppenders = new ArrayList<>();
|
||||
|
||||
private static final UILogAppender uiLogAppender = new UILogAppender();
|
||||
|
||||
// // TODO why's the logger care about this? split it out
|
||||
// private static KernelLogLogger klogListener = null;
|
||||
|
||||
static {
|
||||
levelMap.put(LogGroup.Camera, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.General, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.WebServer, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.Data, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.VisionModule, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.Config, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.CSCore, LogLevel.TRACE);
|
||||
levelMap.put(LogGroup.NetworkTables, LogLevel.DEBUG);
|
||||
levelMap.put(LogGroup.System, LogLevel.DEBUG);
|
||||
|
||||
currentAppenders.add(new ConsoleLogAppender());
|
||||
currentAppenders.add(uiLogAppender);
|
||||
addFileAppender(PathManager.getInstance().getLogPath());
|
||||
|
||||
cleanLogs(PathManager.getInstance().getLogsDir());
|
||||
}
|
||||
|
||||
public static final String ANSI_RESET = "\u001B[0m";
|
||||
public static final String ANSI_BLACK = "\u001B[30m";
|
||||
public static final String ANSI_RED = "\u001B[31m";
|
||||
@@ -50,8 +76,6 @@ public class Logger {
|
||||
private static final List<Pair<String, LogLevel>> uiBacklog = new ArrayList<>();
|
||||
private static boolean connected = false;
|
||||
|
||||
private static final UILogAppender uiLogAppender = new UILogAppender();
|
||||
|
||||
private final String className;
|
||||
private final LogGroup group;
|
||||
|
||||
@@ -89,26 +113,6 @@ public class Logger {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static final HashMap<LogGroup, LogLevel> levelMap = new HashMap<>();
|
||||
private static final List<LogAppender> currentAppenders = new ArrayList<>();
|
||||
|
||||
static {
|
||||
levelMap.put(LogGroup.Camera, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.General, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.WebServer, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.Data, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.VisionModule, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.Config, LogLevel.INFO);
|
||||
levelMap.put(LogGroup.CSCore, LogLevel.TRACE);
|
||||
}
|
||||
|
||||
static {
|
||||
currentAppenders.add(new ConsoleLogAppender());
|
||||
currentAppenders.add(uiLogAppender);
|
||||
addFileAppender(PathManager.getInstance().getLogPath());
|
||||
cleanLogs(PathManager.getInstance().getLogsDir());
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public static void addFileAppender(Path logFilePath) {
|
||||
var file = logFilePath.toFile();
|
||||
@@ -200,7 +204,7 @@ public class Logger {
|
||||
return logLevel.code <= levelMap.get(group).code;
|
||||
}
|
||||
|
||||
void log(String message, LogLevel level) {
|
||||
public void log(String message, LogLevel level) {
|
||||
if (shouldLog(level)) {
|
||||
log(message, level, group, className);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.photonvision.common.networking;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
@@ -71,10 +72,7 @@ public class NetworkManager {
|
||||
// Start tasks to monitor the network interface(s)
|
||||
var ethernetDevices = NetworkUtils.getAllWiredInterfaces();
|
||||
for (NMDeviceInfo deviceInfo : ethernetDevices) {
|
||||
var task = "deviceStatus-" + deviceInfo.devName;
|
||||
if (!TimedTaskManager.getInstance().taskActive(task)) {
|
||||
TimedTaskManager.getInstance().addTask(task, deviceStatus(deviceInfo.devName), 5000);
|
||||
}
|
||||
monitorDevice(deviceInfo.devName, 5000);
|
||||
}
|
||||
|
||||
var physicalDevices = NetworkUtils.getAllActiveWiredInterfaces();
|
||||
@@ -258,14 +256,22 @@ public class NetworkManager {
|
||||
}
|
||||
|
||||
// Detects changes in the carrier and reinitializes after re-connect
|
||||
private Runnable deviceStatus(String devName) {
|
||||
Path file = Path.of("/sys/class/net/{device}/carrier".replace("{device}", devName));
|
||||
logger.debug("Watching network interface at path: " + file.toString());
|
||||
var last = new Object() {boolean carrier = true;};
|
||||
return () ->
|
||||
{
|
||||
private void monitorDevice(String devName, int millisInterval) {
|
||||
String taskName = "deviceStatus-" + devName;
|
||||
if (TimedTaskManager.getInstance().taskActive(taskName)) {
|
||||
// task is already running
|
||||
return;
|
||||
}
|
||||
Path path = Paths.get("/sys/class/net/{device}/carrier".replace("{device}", devName));
|
||||
if (Files.notExists(path)) {
|
||||
logger.error("Can't find " + path + ", so can't monitor " + devName);
|
||||
return;
|
||||
}
|
||||
logger.debug("Watching network interface at path: " + path);
|
||||
var last = new Object() {boolean carrier = true; boolean exceptionLogged = false;};
|
||||
Runnable task = () -> {
|
||||
try {
|
||||
boolean carrier = Files.readString(file).trim().equals("1");
|
||||
boolean carrier = Files.readString(path).trim().equals("1");
|
||||
if (carrier != last.carrier) {
|
||||
if (carrier) {
|
||||
// carrier came back
|
||||
@@ -276,9 +282,16 @@ public class NetworkManager {
|
||||
}
|
||||
}
|
||||
last.carrier = carrier;
|
||||
} catch (Exception e) {
|
||||
logger.error("Could not check network status", e);
|
||||
}
|
||||
};
|
||||
last.exceptionLogged = false;
|
||||
} catch (Exception e) {
|
||||
if (!last.exceptionLogged) {
|
||||
// Log the exception only once, but keep trying
|
||||
logger.error("Could not check network status for " + devName, e);
|
||||
last.exceptionLogged = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TimedTaskManager.getInstance().addTask(taskName, task, millisInterval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,71 +18,20 @@
|
||||
package org.photonvision.common.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import edu.wpi.first.apriltag.jni.AprilTagJNI;
|
||||
import edu.wpi.first.cscore.CameraServerCvJNI;
|
||||
import edu.wpi.first.cscore.CameraServerJNI;
|
||||
import edu.wpi.first.hal.JNIWrapper;
|
||||
import edu.wpi.first.math.WPIMathJNI;
|
||||
import edu.wpi.first.math.geometry.Translation2d;
|
||||
import edu.wpi.first.math.util.Units;
|
||||
import edu.wpi.first.net.WPINetJNI;
|
||||
import edu.wpi.first.networktables.NetworkTablesJNI;
|
||||
import edu.wpi.first.util.CombinedRuntimeLoader;
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import java.awt.HeadlessException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.highgui.HighGui;
|
||||
import org.photonvision.jni.WpilibLoader;
|
||||
import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
|
||||
|
||||
public class TestUtils {
|
||||
private static boolean has_loaded = false;
|
||||
|
||||
public static boolean loadLibraries() {
|
||||
if (has_loaded) return true;
|
||||
|
||||
NetworkTablesJNI.Helper.setExtractOnStaticLoad(false);
|
||||
WPIUtilJNI.Helper.setExtractOnStaticLoad(false);
|
||||
WPIMathJNI.Helper.setExtractOnStaticLoad(false);
|
||||
CameraServerJNI.Helper.setExtractOnStaticLoad(false);
|
||||
CameraServerCvJNI.Helper.setExtractOnStaticLoad(false);
|
||||
// OpenCvLoader.Helper.setExtractOnStaticLoad(false);
|
||||
JNIWrapper.Helper.setExtractOnStaticLoad(false);
|
||||
WPINetJNI.Helper.setExtractOnStaticLoad(false);
|
||||
AprilTagJNI.Helper.setExtractOnStaticLoad(false);
|
||||
|
||||
// wpimathjni is a bit odd, it's all in the wpimathjni shared lib, but the java side stuff has
|
||||
// been split.
|
||||
// ArmFeedforwardJNI.Helper.setExtractOnStaticLoad(false);
|
||||
// DAREJNI.Helper.setExtractOnStaticLoad(false);
|
||||
// EigenJNI.Helper.setExtractOnStaticLoad(false);
|
||||
// Ellipse2dJNI.Helper.setExtractOnStaticLoad(false);
|
||||
// Pose3dJNI.Helper.setExtractOnStaticLoad(false);
|
||||
// StateSpaceUtilJNI.Helper.setExtractOnStaticLoad(false);
|
||||
// TrajectoryUtilJNI.Helper.setExtractOnStaticLoad(false);
|
||||
|
||||
try {
|
||||
CombinedRuntimeLoader.loadLibraries(
|
||||
TestUtils.class,
|
||||
"wpiutiljni",
|
||||
"wpimathjni",
|
||||
"ntcorejni",
|
||||
"wpinetjni",
|
||||
"wpiHaljni",
|
||||
Core.NATIVE_LIBRARY_NAME,
|
||||
"cscorejni",
|
||||
"apriltagjni");
|
||||
|
||||
has_loaded = true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
has_loaded = false;
|
||||
}
|
||||
|
||||
return has_loaded;
|
||||
return WpilibLoader.loadLibraries();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@@ -33,6 +33,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
|
||||
public class JacksonUtils {
|
||||
public static class UIMap extends HashMap<String, Object> {}
|
||||
@@ -76,6 +77,10 @@ public class JacksonUtils {
|
||||
}
|
||||
|
||||
public static <T> T deserialize(String s, Class<T> ref) throws IOException {
|
||||
if (s.length() == 0) {
|
||||
throw new EofException("Provided empty string for class " + ref.getName());
|
||||
}
|
||||
|
||||
PolymorphicTypeValidator ptv =
|
||||
BasicPolymorphicTypeValidator.builder().allowIfBaseType(ref).build();
|
||||
ObjectMapper objectMapper =
|
||||
|
||||