Compare commits

..

1 Commits
main ... 2026

Author SHA1 Message Date
Levi
859fb6c408 [hal] Fix USB serial port assignment in SerialHelper (#8689) 2026-06-07 18:02:13 -07:00
9418 changed files with 604329 additions and 591594 deletions

View File

@@ -6,12 +6,12 @@ apriltag/bin
cameraserver/bin
cameraserver/multiCameraServer/bin
cscore/bin
fields/bin
fieldImages/bin
hal/bin
developerRobot/bin
ntcore/bin
romiVendordep/bin
commandsv2/bin
wpilibNewCommands/bin
wpilibj/bin
wpimath/bin
wpinet/bin

View File

@@ -1,23 +1,18 @@
try-import %workspace%/bazel_auth.rc
try-import %workspace%/user.bazelrc
common --enable_bzlmod --enable_workspace
# Resolves to --config=linux on Linux, --config=macos on Mac, --windows on windows
common --enable_platform_specific_config
common --noenable_bzlmod
# Make bazel 8 work for us.
common --enable_workspace
build --experimental_cc_static_library
build --experimental_cc_shared_library
build --java_language_version=25
build --java_runtime_version=remotejdk_25
build --tool_java_language_version=25
build --tool_java_runtime_version=remotejdk_25
build --java_language_version=17
build --java_runtime_version=roboriojdk_17
build --tool_java_language_version=17
build --tool_java_runtime_version=remotejdk_17
test --test_output=errors
test --test_verbose_timeout_warnings
import %workspace%/shared/bazel/compiler_flags/sanitizers.rc
import %workspace%/shared/bazel/compiler_flags/base_linux_flags.rc
import %workspace%/shared/bazel/compiler_flags/linux_flags.rc
import %workspace%/shared/bazel/compiler_flags/osx_flags.rc
import %workspace%/shared/bazel/compiler_flags/roborio_flags.rc
@@ -34,21 +29,16 @@ build:build_java --test_tag_filters=allwpilib-build-java --build_tag_filters=all
build:build_cpp --test_tag_filters=+allwpilib-build-cpp --build_tag_filters=+allwpilib-build-cpp
build:no_example --test_tag_filters=-wpi-example --build_tag_filters=-wpi-example
test:no_example --test_tag_filters=-wpi-example --build_tag_filters=-wpi-example
common:skip_robotpy --test_tag_filters=-robotpy --build_tag_filters=-robotpy
# Build Buddy BES Setup
build:build_buddy_bes --bes_results_url=https://app.buildbuddy.io/invocation/
build:build_buddy_bes --bes_backend=grpcs://remote.buildbuddy.io
# Common Cache Settings
build:common_cache --remote_timeout=3600
build:common_cache --remote_cache_compression
build:common_cache --experimental_remote_cache_compression_threshold=100
# Build Buddy Cache Setup
build:build_buddy --config=common_cache
build:build_buddy --config=build_buddy_bes
build:build_buddy --bes_results_url=https://app.buildbuddy.io/invocation/
build:build_buddy --bes_backend=grpcs://remote.buildbuddy.io
build:build_buddy --remote_cache=grpcs://remote.buildbuddy.io
build:build_buddy --remote_timeout=3600
# Additional suggestions from buildbuddy for speed
build:build_buddy --experimental_remote_cache_compression
build:build_buddy --experimental_remote_cache_compression_threshold=100
build:build_buddy --noslim_profile
build:build_buddy --experimental_profile_include_target_label
build:build_buddy --experimental_profile_include_primary_output
@@ -56,36 +46,12 @@ build:build_buddy --nolegacy_important_outputs
common:build_buddy_readonly --noremote_upload_local_results
# Bazel-remote cache setup
build:remote_cache --config=common_cache
build:remote_cache --remote_cache=grpcs://gitlib-bazel.wpi.edu
common:remote_cache_readonly --noremote_upload_local_results
# This config should be used locally. It downloads more than the CI version
build:remote_user --config=remote_cache
build:remote_user --config=remote_cache_readonly
build:remote_user --config=build_buddy
build:remote_user --config=build_buddy_readonly
build:remote_user --remote_download_toplevel
common:ci --color=yes
build:ci --config=build_buddy
build:ci --remote_download_minimal
build:ci --progress_report_interval=60 --show_progress_rate_limit=60
build --build_metadata=REPO_URL=https://github.com/wpilibsuite/allwpilib.git
common --define="WPILIB_VERSION=2025.424242.3.1-unknown"
common --define="ROBOTPY_VERSION=2025.424242.3.1"
# List of artifact types to build in CI.
# Anything else gets skipped to speed up CI.
common:ci --repo_env="WPI_PUBLISH_CLASSIFIER_FILTER=headers,sources,linuxsystemcore,linuxsystemcoredebug,linuxsystemcorestatic,linuxsystemcorestaticdebug,linuxx86-64,linuxx86-64debug,linuxx86-64static,linuxx86-64staticdebug,osxuniversal,osxuniversaldebug,osxuniversalstatic,osxuniversalstaticdebug,windowsarm64,windowsarm64debug,windowsarm64static,windowsarm64staticdebug,windowsx86-64,windowsx86-64debug,windowsx86-64static,windowsx86-64staticdebug"
# The 2 configurations for windows are very slow to build each time.
# Instead, skip the cross transition for ARM on x86, and the reverse on x86.
common:ci_windows_x86 --repo_env="WPI_PUBLISH_CLASSIFIER_FILTER=headers,sources,linuxsystemcore,linuxsystemcoredebug,linuxsystemcorestatic,linuxsystemcorestaticdebug,linuxx86-64,linuxx86-64debug,linuxx86-64static,linuxx86-64staticdebug,osxuniversal,osxuniversaldebug,osxuniversalstatic,osxuniversalstaticdebug,windowsx86-64,windowsx86-64debug,windowsx86-64static,windowsx86-64staticdebug"
common:ci_windows_arm --repo_env="WPI_PUBLISH_CLASSIFIER_FILTER=headers,sources,linuxsystemcore,linuxsystemcoredebug,linuxsystemcorestatic,linuxsystemcorestaticdebug,linuxx86-64,linuxx86-64debug,linuxx86-64static,linuxx86-64staticdebug,osxuniversal,osxuniversaldebug,osxuniversalstatic,osxuniversalstaticdebug,windowsarm64,windowsarm64debug,windowsarm64static,windowsarm64staticdebug"
build --@rules_python//python/config_settings:bootstrap_impl=script
build --@rules_python//python/config_settings:venvs_use_declare_symlink=no
try-import %workspace%/bazel_auth.rc

View File

@@ -1 +1 @@
8.5.0
7.3.1

View File

@@ -104,52 +104,25 @@ ForEachMacros:
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeBlocks: Regroup
IncludeCategories:
# C standard library headers
#
# https://en.cppreference.com/w/cpp/header:
# * C compatibility headers
# * Special C compatibility headers
# * Empty C headers
# * Meaningless C headers
# * Unsupported C headers
- Regex: '^<(assert\.h|ctype\.h|errno\.h|fenv\.h|float\.h|inttypes\.h|limits\.h|locale\.h|math\.h|setjmp\.h|signal\.h|stdarg\.h|stddef\.h|stdint\.h|stdio\.h|stdlib\.h|string\.h|time\.h|uchar\.h|wchar\.h|wctype\.h|stdatomic\.h|ccomplex|complex\.h|ctgmath|tgmath\.h|ciso646|cstdalign|cstdbool|iso646\.h|stdalign\.h|stdbool\.h|stdatomic\.h|stdnoreturn\.h|threads\.h)>'
Priority: 1
SortPriority: 0
# Linux system headers
- Regex: '^<((arpa\/|linux\/|net/|netinet\/|sys\/).*|arm_neon\.h|dirent\.h|dlfcn\.h|fcntl\.h|ifaddrs\.h|jni\.h|libgen\.h|poll\.h|spawn\.h|termios\.h|unistd\.h)>'
Priority: 1
SortPriority: 1
# winsock2.h
- Regex: '^<winsock2\.h>'
Priority: 1
SortPriority: 2
# windows.h
- Regex: '^<windows.\h>'
Priority: 1
SortPriority: 3
# Windows def.h headers
- Regex: '^<(comdef\.h|ws2def\.h|ws2ipdef\.h)>'
Priority: 1
SortPriority: 4
# Windows system headers
- Regex: '^<(dbghelp\.h|dbt\.h|delayimp\.h|dshow\.h|io\.h|iphlpapi\.h|ks\.h|ksmedia\.h|memoryapi\.h|mfapi\.h|mferror\.h|mfidl\.h|mfreadwrite\.h|netioapi\.h|ntstatus\.h|shellapi\.h|shlobj\.h|shlwapi\.h|sysinfoapi\.h|windns\.h|windowsx\.h|winternl\.h|ws2tcpip\.h)>'
Priority: 1
SortPriority: 5
# C++ standard library headers (lowercase and underscores with no .h suffix)
- Regex: '^<([a-z_]+|cxxabi\.h)>'
Priority: 2
SortPriority: 6
# Other library headers (angle brackets)
- Regex: '^<.*'
Priority: 3
SortPriority: 7
# Project headers (double quotes)
- Regex: '^".*'
Priority: 4
SortPriority: 8
IncludeIsMainRegex: '(Test|_bench|_test)?$'
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
@@ -240,7 +213,7 @@ RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: true
SortIncludes: false
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false

View File

@@ -68,8 +68,4 @@ Checks:
modernize-use-override,
modernize-use-using,
readability-braces-around-statements'
CheckOptions:
- key: modernize-use-using.IgnoreExternC
value: 'true'
FormatStyle: file
HeaderFilterRegex: '^((?!thirdparty/).)*$'

7
.gitattributes vendored
View File

@@ -1,8 +1,8 @@
*.adoc text eol=lf
*.c text eol=lf
*.cmake text eol=lf
*.clang-format text eol=lf
*.clang-tidy text eol=lf
*.cmake text eol=lf
*.cpp text eol=lf
*.gradle text eol=lf
*.groovy text eol=lf
@@ -20,14 +20,11 @@
*.plist text eol=lf
*.proto text eol=lf
*.py text eol=lf
*.styleguide text eol=lf
*.txt text eol=lf
*.wpiformat text eol=lf
*.wpiformat-license text eol=lf
*.xml text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
# Generated files
*/src/generated/** linguist-generated
*/robotpy_native_build_info.bzl linguist-generated
*/robotpy_pybind_build_info.bzl linguist-generated

2
.github/CODEOWNERS vendored
View File

@@ -27,7 +27,7 @@
/simulation/ @wpilibsuite/simulation
/commandsv2/ @wpilibsuite/commandbased
/wpilibNewCommands/ @wpilibsuite/commandbased
/wpilibcExamples/ @wpilibsuite/wpilib @wpilibsuite/documentation

View File

@@ -5,7 +5,7 @@ runs:
using: "composite"
steps:
- name: Set up Python 3.12
uses: actions/setup-python@v6
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install jinja and protobuf
@@ -18,15 +18,14 @@ runs:
wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
chmod +x protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
shell: bash
- name: Regenerate hal
run: ./hal/generate_usage_reporting.py
shell: bash
- name: Regenerate ntcore
run: ./ntcore/generate_topics.py
shell: bash
- name: Regenerate mrccomm
run: ./hal/generate_nanopb.py
shell: bash
- name: Regenerate imgui
run: |
./thirdparty/imgui_suite/generate_fonts.sh
@@ -37,7 +36,7 @@ runs:
run: |
./wpilibc/generate_hids.py
./wpilibj/generate_hids.py
./commandsv2/generate_hids.py
./wpilibNewCommands/generate_hids.py
shell: bash
- name: Regenerate PWM motor controllers
@@ -47,7 +46,7 @@ runs:
shell: bash
- name: Regenerate mrcal minimath
run: ./tools/wpical/generate_mrcal.py
run: ./wpical/generate_mrcal.py
shell: bash
- name: Regenerate wpimath
@@ -57,11 +56,6 @@ runs:
./wpimath/generate_quickbuf.py --quickbuf_plugin protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
shell: bash
- name: Regenerate Commands v3
run: |
./commandsv3/generate_files.py --quickbuf_plugin protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
shell: bash
- name: Regenerate wpiunits
run: ./wpiunits/generate_units.py
shell: bash

View File

@@ -1,38 +0,0 @@
name: 'Setup Bazel Cache'
description: 'Configures the Bazel remote cache. Prefers WPI bazel-remote (R/W), falls back to BuildBuddy, then readonly WPI cache.'
inputs:
bazel_remote_username:
description: 'WPI Bazel remote cache username'
bazel_remote_password:
description: 'WPI Bazel remote cache password'
bazel_remote_url:
description: 'WPI Bazel remote cache base URL (no credentials or protocol, e.g. gitlib-bazel.wpi.edu)'
default: 'gitlib-bazel.wpi.edu'
buildbuddy_token:
description: 'BuildBuddy API token'
runs:
using: "composite"
steps:
- name: Resolve Bazel cache configuration
env:
CACHE_USER: ${{ inputs.bazel_remote_username }}
CACHE_PASS: ${{ inputs.bazel_remote_password }}
REMOTE_URL: ${{ inputs.bazel_remote_url }}
BB_TOKEN: ${{ inputs.buildbuddy_token }}
shell: bash
run: |
if [ -n "${CACHE_USER}" ] && [ -n "${CACHE_PASS}" ]; then
echo "WPI bazel-remote credentials detected; configuring R/W access"
echo "build:remote_cache --remote_cache=grpcs://${CACHE_USER}:${CACHE_PASS}@${REMOTE_URL}" > bazel_auth.rc
echo "build:ci --config=remote_cache" >> bazel_auth.rc
elif [ -n "${BB_TOKEN}" ]; then
echo "BuildBuddy token detected; configuring BuildBuddy cache"
echo "build:build_buddy --remote_header=x-buildbuddy-api-key=${BB_TOKEN}" > bazel_auth.rc
echo "build:ci --config=build_buddy" >> bazel_auth.rc
else
echo "No cache credentials detected; falling back to readonly WPI cache"
echo "build:ci --config=remote_cache" > bazel_auth.rc
echo "build:ci --config=remote_cache_readonly" >> bazel_auth.rc
fi

View File

@@ -0,0 +1,27 @@
name: 'Setup BuildBuddy acache'
description: 'Sets up the build buddy cache to be readonly / writing based on the presence of environment variables'
inputs:
token:
description: 'Build Buddy API token'
runs:
using: "composite"
steps:
- name: Setup without key
env:
API_KEY: ${{ inputs.token }}
if: ${{ env.API_KEY == '' }}
shell: bash
run: |
echo "No API key secret detected, will setup readonly cache"
echo "build:ci --config=build_buddy_readonly" > bazel_auth.rc
- name: Set with key
env:
API_KEY: ${{ inputs.token }}
if: ${{ env.API_KEY != '' }}
shell: bash
run: |
echo "API Key detected!"
echo "build:build_buddy --remote_header=x-buildbuddy-api-key=${{ env.API_KEY }}" > bazel_auth.rc

161
.github/labeler.yml vendored
View File

@@ -1,96 +1,65 @@
"build":
- changed-files:
- any-glob-to-any-file:
[
.github/actions/**,
.github/workflows/**,
cmake/*,
shared/**,
upstream_utils/**,
"**/*.bzl",
"**/*.cmake",
"**/*.gradle",
"**/CMakeLists.txt",
"**/BUILD.bazel",
]
"component: apriltag":
- changed-files:
- any-glob-to-any-file: apriltag/**
"component: command-based":
- changed-files:
- any-glob-to-any-file: commandsv2/**
"component: commands v3":
- changed-files:
- any-glob-to-any-file: commandsv3/**
"component: cscore":
- changed-files:
- any-glob-to-any-file: cscore/**
"component: datalogtool":
- changed-files:
- any-glob-to-any-file: tools/datalogtool/**
"component: epilogue":
- changed-files:
- any-glob-to-any-file: epilogue-*/**
"component: examples":
- changed-files:
- any-glob-to-any-file: wpilib*Examples/**
"component: glass":
- changed-files:
- any-glob-to-any-file: glass/**
"component: hal":
- changed-files:
- any-glob-to-any-file: hal/**
"component: javac plugin":
- changed-files:
- any-glob-to-any-file: javacPlugin/**
"component: ntcore":
- changed-files:
- any-glob-to-any-file: ntcore/**
"component: outlineviewer":
- changed-files:
- any-glob-to-any-file: tools/outlineviewer/**
"component: romi":
- changed-files:
- any-glob-to-any-file: romiVendordep/**
"component: sysid":
- changed-files:
- any-glob-to-any-file: tools/sysid/**
"component: wpiannotations":
- changed-files:
- any-glob-to-any-file: wpiannotations/**
"component: wpilibc":
- changed-files:
- any-glob-to-any-file: wpilibc/**
"component: wpilibj":
- changed-files:
- any-glob-to-any-file: wpilibj/**
"component: wpimath":
- changed-files:
- any-glob-to-any-file: wpimath/**
"component: wpinet":
- changed-files:
- any-glob-to-any-file: wpinet/**
"component: wpiunits":
- changed-files:
- any-glob-to-any-file: wpiunits/**
"component: wpiutil":
- changed-files:
- any-glob-to-any-file: wpiutil/**
"component: wpical":
- changed-files:
- any-glob-to-any-file: tools/wpical/**
"component: xrp":
- changed-files:
- any-glob-to-any-file: xrpVendordep/**
"component: usage reporting":
- changed-files:
- any-glob-to-any-file: hal/src/generate/**
"os: simulation":
- changed-files:
- any-glob-to-any-file: "**/simulation/**"
"robotpy":
- changed-files:
- any-glob-to-any-file: "**python/**"
"type: testing":
- changed-files:
- any-glob-to-any-file: "**/test/**"
'2027':
- base-branch: '2027'
'component: apriltag':
- changed-files:
- any-glob-to-any-file: apriltag/**
'component: command-based':
- changed-files:
- any-glob-to-any-file: wpilibNewCommands/**
'component: cscore':
- changed-files:
- any-glob-to-any-file: cscore/**
'component: datalogtool':
- changed-files:
- any-glob-to-any-file: datalogtool/**
'component: epilogue':
- changed-files:
- any-glob-to-any-file: epilogue-*/**
'component: examples':
- changed-files:
- any-glob-to-any-file: wpilib*Examples/**
'component: glass':
- changed-files:
- any-glob-to-any-file: glass/**
'component: hal':
- changed-files:
- any-glob-to-any-file: hal/**
'component: ntcore':
- changed-files:
- any-glob-to-any-file: ntcore/**
'component: outlineviewer':
- changed-files:
- any-glob-to-any-file: outlineviewer/**
'component: sysid':
- changed-files:
- any-glob-to-any-file: sysid/**
'component: teamnumbersetter':
- changed-files:
- any-glob-to-any-file: roborioteamnumbersetter/**
'component: wpilibc':
- changed-files:
- any-glob-to-any-file: wpilibc/**
'component: wpilibj':
- changed-files:
- any-glob-to-any-file: wpilibj/**
'component: wpimath':
- changed-files:
- any-glob-to-any-file: wpimath/**
'component: wpinet':
- changed-files:
- any-glob-to-any-file: wpinet/**
'component: wpiunits':
- changed-files:
- any-glob-to-any-file: wpiunits/**
'component: wpiutil':
- changed-files:
- any-glob-to-any-file: wpiutil/**
'component: wpical':
- changed-files:
- any-glob-to-any-file: wpical/**
'component: usage reporting':
- changed-files:
- any-glob-to-any-file: hal/src/generate/**
'attn: NI':
- changed-files:
- any-glob-to-any-file: hal/src/generate/**

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'wpilibsuite/allwpilib' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: jfrog/setup-jfrog-cli@v4
@@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'wpilibsuite/allwpilib' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: jfrog/setup-jfrog-cli@v4

View File

@@ -7,115 +7,78 @@ concurrency:
cancel-in-progress: true
jobs:
build:
build-windows:
strategy:
fail-fast: false
matrix:
include:
- { name: "Linux System Core", classifier: "linuxsystemcore", os: ubuntu-24.04, container: wpilib/debian-base:trixie, action: "build" }
- { name: "Linux System Core Debug", classifier: "linuxsystemcoredebug", os: ubuntu-24.04, container: wpilib/debian-base:trixie, action: "build" }
- { name: "Linux System Core Static", classifier: "linuxsystemcorestatic", os: ubuntu-24.04, container: wpilib/debian-base:trixie, action: "build" }
- { name: "Linux System Core Static Debug", classifier: "linuxsystemcorestaticdebug", os: ubuntu-24.04, container: wpilib/debian-base:trixie, action: "build" }
- { name: "Linux x86-64", classifier: "linuxx86-64,headers,sources", os: ubuntu-24.04, container: wpilib/debian-base:trixie, action: "test" }
- { name: "Linux x86-64 Debug", classifier: "linuxx86-64debug", os: ubuntu-24.04, container: wpilib/debian-base:trixie, action: "test" }
- { name: "Linux x86-64 Static", classifier: "linuxx86-64static", os: ubuntu-24.04, container: wpilib/debian-base:trixie, action: "test" }
- { name: "Linux x86-64 Static Debug", classifier: "linuxx86-64staticdebug", os: ubuntu-24.04, container: wpilib/debian-base:trixie, action: "test" }
- { name: "Windows (native)", os: windows-2022, action: "test", config: "--config=windows", }
- { name: "Windows (arm)", os: windows-2022, action: "build", config: "--config=windows_arm", }
- { name: "macOS", classifier: "osxuniversal,osxuniversaldebug,headers,sources,osxuniversalstatic,osxuniversalstaticdebug,linuxsystemcore,linuxsystemcoredebug,linuxsystemcorestatic,linuxsystemcorestaticdebug", os: macOS-15, container: "", action: "test" }
- { name: "Windows x86-64", classifier: "windowsx86-64,windowsx86-64static,headers,sources", os: windows-2022, container: "", action: "test" }
- { name: "Windows x86-64 Debug", classifier: "windowsx86-64debug,windowsx86-64staticdebug", os: windows-2022, container: "", action: "test" }
- { name: "Windows ARM64", classifier: "windowsarm64,windowsarm64static", os: windows-2022, container: "", action: "build" }
- { name: "Windows ARM64 Debug", classifier: "windowsarm64debug,windowsarm64staticdebug", os: windows-2022, container: "", action: "build" }
- { name: "Windows System Core", classifier: "linuxsystemcore,linuxsystemcorestatic", os: windows-2022, container: "", action: "build" }
- { name: "Windows System Core Debug", classifier: "linuxsystemcoredebug,linuxsystemcorestaticdebug", os: windows-2022, container: "", action: "build" }
name: "${{ matrix.action == 'test' && 'Test' || 'Build' }} ${{ matrix.name }}"
name: "Build ${{ matrix.name }}"
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
steps:
- name: Check disk free space pre-cleanup
run: df -h
- name: Free disk space (Linux)
if: runner.os == 'Linux'
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: false
docker-images: false
swap-storage: false
- name: Free disk space (macOS)
if: runner.os == 'macOS'
# CodeQL: 5G
# go: 748M
# Android: 12G
run: |
rm -rf /Users/runner/hostedtoolcache/CodeQL
rm -rf /Users/runner/hostedtoolcache/go
rm -rf /Users/runner/Library/Android
- name: Check disk free space post-cleanup
run: df -h
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- id: Setup_bazel_cache
uses: ./.github/actions/setup-bazel-cache
- uses: actions/setup-java@v4
with:
bazel_remote_username: ${{ secrets.BAZEL_CACHE_USERNAME }}
bazel_remote_password: ${{ secrets.BAZEL_CACHE_PASSWORD }}
buildbuddy_token: ${{ secrets.BUILDBUDDY_API_KEY }}
distribution: 'zulu'
java-version: 17
architecture: x64
- name: Install apt dependencies
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libgl1-mesa-dev libx11-dev libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev avahi-daemon
- name: Setup avahi-daemon
if: runner.os == 'Linux'
run: |
sudo service dbus start
sudo avahi-daemon -D
- if: runner.os == 'Linux'
uses: bazel-contrib/setup-bazel@0.15.0
- id: Setup_build_buddy
uses: ./.github/actions/setup-build-buddy
with:
bazelisk-cache: true
repository-cache: true
bazelisk-version: 1.x
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: bazel ${{ matrix.action }}
run: |
ACTION='${{ matrix.action }}'
if [ "${ACTION}" = "build" ]; then
TARGETS=:publish
else
TARGETS=...
fi
if [ "${{ runner.os }}" = "Windows" ]; then
bazel --output_user_root=C:\\bazelroot ${ACTION} ${TARGETS} --config=ci -c opt --repo_env=WPI_PUBLISH_CLASSIFIER_FILTER='${{ matrix.classifier }}'
else
bazel ${ACTION} ${TARGETS} --config=ci -c opt --repo_env=WPI_PUBLISH_CLASSIFIER_FILTER='${{ matrix.classifier }}'
fi
run: bazel --output_user_root=C:\\bazelroot ${{ matrix.action }} -k ... --config=ci ${{ matrix.config }} --verbose_failures
shell: bash
- name: Check disk free space
if: always()
run: df -h
build-mac:
name: "Mac"
runs-on: macOS-15
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- id: Setup_build_buddy
uses: ./.github/actions/setup-build-buddy
with:
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: bazel test (release)
run: bazel test -k ... --config=ci -c opt --config=macos --nojava_header_compilation --verbose_failures
shell: bash
build-linux:
strategy:
fail-fast: false
matrix:
include:
- { name: "Linux (native)", os: ubuntu-22.04, action: "test", config: "--config=linux", }
- { name: "Linux (roborio)", os: ubuntu-22.04, action: "build", config: "--config=roborio", }
name: "${{ matrix.name }}"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: bazelbuild/setup-bazelisk@v3
- id: Setup_build_buddy
uses: ./.github/actions/setup-build-buddy
with:
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: bazel ${{ matrix.action }} (release)
run: bazel ${{ matrix.action }} ... --config=ci -c opt ${{ matrix.config }} -k --verbose_failures
buildifier:
name: "buildifier"
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Set up Go 1.15.x
uses: actions/setup-go@v6
uses: actions/setup-go@v5
with:
cache: false
go-version: 1.15.x
@@ -126,7 +89,7 @@ jobs:
cd $(mktemp -d)
GO111MODULE=on go get github.com/bazelbuild/buildtools/buildifier@6.0.0
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Run buildifier
@@ -136,100 +99,11 @@ jobs:
run: git --no-pager diff --exit-code HEAD
- name: Generate diff
run: git diff HEAD > ${{ matrix.platform }}-bazel-lint-fixes.patch
run: git diff HEAD > bazel-lint-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v4
with:
archive: false
path: "*-bazel-lint-fixes.patch"
if: ${{ failure() }}
mod-lock:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- name: Check lockfile
run: bazel mod deps --lockfile_mode=error
- run: bazel mod deps --lockfile_mode=update
if: ${{ failure() }}
- name: Generate diff
run: git diff HEAD > bazel-mod-lock-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v7
with:
archive: false
path: "bazel-mod-lock-fixes.patch"
if: ${{ failure() }}
non_robotpy_pregeneration:
name: "Non-RobotPy Pregen"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
with: { fetch-depth: 0 }
- id: Setup_bazel_cache
uses: ./.github/actions/setup-bazel-cache
with:
bazel_remote_username: ${{ secrets.BAZEL_CACHE_USERNAME }}
bazel_remote_password: ${{ secrets.BAZEL_CACHE_PASSWORD }}
buildbuddy_token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: Run Non-RobotPy pregeneration
run: bazel run --config=ci //:write_pregenerated_files
- name: Check Output
run: git --no-pager diff --exit-code HEAD
- name: Generate diff
run: git diff HEAD > non-robotpy-pregeneration.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v6
with:
name: non-robotpy-pregeneration-fixes
path: non-robotpy-pregeneration.patch
if: ${{ failure() }}
robotpy_pregeneration:
name: "Robotpy Pregeneration"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
with: { fetch-depth: 0 }
- id: Setup_bazel_cache
uses: ./.github/actions/setup-bazel-cache
with:
bazel_remote_username: ${{ secrets.BAZEL_CACHE_USERNAME }}
bazel_remote_password: ${{ secrets.BAZEL_CACHE_PASSWORD }}
buildbuddy_token: ${{ secrets.BUILDBUDDY_API_KEY }}
# You should ensure the headers are correct before trying to pregen files
- name: Test Scan Headers
run: bazel test --config=ci //... -k --test_tag_filters=robotpy_scan_headers --build_tests_only
shell: bash
- name: Run yaml file generation
run: bazel run --config=ci //:write_robotpy_update_yaml_files
- name: Run build file generation
run: bazel run --config=ci //:write_robotpy_generated_pybind_files
- name: Check Output
run: git --no-pager diff --exit-code HEAD
- name: Generate diff
run: git diff HEAD > robotpy-pregeneration.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v6
with:
name: robotpy-pregeneration-fixes
path: robotpy-pregeneration.patch
name: ${{ matrix.platform }}-bazel-lint-fixes
path: bazel-lint-fixes.patch
if: ${{ failure() }}

View File

@@ -16,23 +16,28 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
- os: ubuntu-22.04
name: Android Arm64
abi: arm64-v8a
- os: ubuntu-24.04
- os: ubuntu-22.04
name: Android X64
abi: "x86_64"
name: "Build - ${{ matrix.name }}"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r27d
add-to-path: false
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.9
@@ -40,7 +45,7 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y ninja-build
- name: configure
run: cmake --preset with-sccache -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_WPILIB=OFF -DWITH_GUI=OFF -DWITH_CSCORE=OFF -DWITH_TESTS=OFF -DWITH_SIMULATION_MODULES=OFF -DBUILD_SHARED_LIBS=ON -DCMAKE_TOOLCHAIN_FILE=${{ steps.setup-ndk.outputs.ndk-path }}/build/cmake/android.toolchain.cmake -DANDROID_ABI="${{ matrix.abi }}" -DANDROID_PLATFORM=android-24
run: cmake --preset with-sccache -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_WPILIB=OFF -DWITH_GUI=OFF -DWITH_CSCORE=OFF -DWITH_TESTS=OFF -DWITH_SIMULATION_MODULES=OFF -DWITH_PROTOBUF=OFF -DWITH_JAVA=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_TOOLCHAIN_FILE=${{ steps.setup-ndk.outputs.ndk-path }}/build/cmake/android.toolchain.cmake -DANDROID_ABI="${{ matrix.abi }}" -DANDROID_PLATFORM=android-24
env:
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}

View File

@@ -16,13 +16,14 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
- os: ubuntu-22.04
name: Linux
container: wpilib/systemcore-cross-debian:trixie
flags: "--preset with-sccache -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
container: wpilib/roborio-cross-ubuntu:2025-22.04
flags: "--preset with-java-and-sccache -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
- os: macOS-15
name: macOS
container: ""
env: ""
flags: "--preset with-sccache -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
- os: windows-2022
name: Windows
@@ -35,17 +36,11 @@ jobs:
steps:
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libopencv-dev ninja-build
- name: Setup avahi-daemon
if: runner.os == 'Linux'
run: |
sudo service dbus start
sudo avahi-daemon -D
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java libprotobuf-dev protobuf-compiler ninja-build
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install opencv ninja
run: brew install opencv protobuf@29 ninja
- uses: ilammy/msvc-dev-cmd@v1.13.0
if: runner.os == 'Windows'
@@ -55,9 +50,9 @@ jobs:
uses: lukka/get-cmake@v3.29.3
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.10
uses: mozilla-actions/sccache-action@v0.0.9
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Run vcpkg (Windows only)
if: runner.os == 'Windows'

View File

@@ -0,0 +1,27 @@
name: Comment on PR for robotpy
on:
pull_request_target:
types:
- opened
paths:
- 'wpilibNewCommands/src/**/*.java'
jobs:
comment:
permissions:
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Comment on PR
if: github.repository == 'wpilibsuite/allwpilib'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'This PR modifies commands. Please open a corresponding PR in [Python Commands](https://github.com/robotpy/robotpy-commands-v2/) and include a link to this PR.'
})

View File

@@ -11,31 +11,31 @@ jobs:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: gradle/actions/wrapper-validation@v5
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4
build-docker:
strategy:
fail-fast: false
matrix:
include:
- container: wpilib/systemcore-cross-debian:trixie
artifact-name: Systemcore
build-options: "-Ponlylinuxsystemcore"
- container: wpilib/aarch64-cross-debian:trixie
- container: wpilib/roborio-cross-ubuntu:2025-22.04
artifact-name: Athena
build-options: "-Ponlylinuxathena"
- container: wpilib/raspbian-cross-ubuntu:bookworm-22.04
artifact-name: Arm32
build-options: "-Ponlylinuxarm32"
- container: wpilib/aarch64-cross-ubuntu:bookworm-22.04
artifact-name: Arm64
build-options: "-Ponlylinuxarm64"
- container: wpilib/systemcore-cross-debian:trixie
- container: wpilib/ubuntu-base:22.04
artifact-name: Linux
build-options: "-Ponlylinuxx86-64"
name: "Build - ${{ matrix.artifact-name }}"
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs: [validation]
steps:
- name: Check disk free space pre-cleanup
run: df -h
- name: Free disk space
- name: Free Disk Space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
@@ -45,38 +45,28 @@ jobs:
large-packages: false
docker-images: false
swap-storage: false
- name: Check disk free space post-cleanup
run: df -h
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Build with Gradle
uses: wpilibsuite/docker-run-action@v4
with:
image: ${{ matrix.container }}
options: -v ${{ github.workspace }}:/work -w /work -e ARTIFACTORY_PUBLISH_USERNAME -e ARTIFACTORY_PUBLISH_PASSWORD -e GITHUB_REF -e CI
# Start avahi-daemon and build
run: |
service dbus start
avahi-daemon -D
./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
run: df . && echo $GITHUB_REF && ./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
env:
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- uses: actions/upload-artifact@v7
- name: Check free disk space
run: df .
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: build/allOutputs
- name: Check disk free space
if: always()
run: df -h
build-host:
env:
MACOSX_DEPLOYMENT_TARGET: 13.3
@@ -113,6 +103,12 @@ jobs:
architecture: aarch64
task: "build"
outputs: "build/allOutputs"
- os: windows-2022
artifact-name: Win32FFI
architecture: x86
task: ":ntcoreffi:build"
build-options: "-Pntcoreffibuild \"-Dorg.gradle.jvmargs=-Xmx1096m\""
outputs: "ntcoreffi/build/outputs"
- os: windows-2022
artifact-name: Win64FFI
architecture: x64
@@ -123,11 +119,42 @@ jobs:
runs-on: ${{ matrix.os }}
needs: [validation]
steps:
- name: Check disk free space pre-cleanup
run: df -h
- name: Free disk space (macOS)
if: runner.os == 'macOS'
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
architecture: ${{ matrix.architecture }}
- name: Import Developer ID Certificate
uses: wpilibsuite/import-signing-certificate@v2
with:
certificate-data: ${{ secrets.APPLE_CERTIFICATE_DATA }}
certificate-passphrase: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027'))))
- name: Set Keychain Lock Timeout
run: security set-keychain-settings -lut 21600
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027'))))
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
shell: bash
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Set Java Heap Size
run: sed -i 's/-Xmx2g/-Xmx1g/g' gradle.properties
if: matrix.artifact-name == 'Win32'
- name: Check disk free space (Windows)
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'
- name: Check disk free space pre-cleanup (macOS)
run: df -h .
if: matrix.os == 'macOS-15'
- name: Cleanup disk space
# CodeQL: 5G
# go: 748M
# Android: 12G
@@ -135,36 +162,10 @@ jobs:
rm -rf /Users/runner/hostedtoolcache/CodeQL
rm -rf /Users/runner/hostedtoolcache/go
rm -rf /Users/runner/Library/Android
- name: Check disk free space post-cleanup
run: df -h
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 25
architecture: ${{ matrix.architecture }}
- name: Import Developer ID Certificate
uses: wpilibsuite/import-signing-certificate@v3
with:
certificate-data: ${{ secrets.APPLE_CERTIFICATE_DATA }}
certificate-passphrase: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
- name: Set Keychain Lock Timeout
run: security set-keychain-settings -lut 21600
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
shell: bash
if: startsWith(github.ref, 'refs/tags/v')
if: matrix.os == 'macOS-15'
- name: Check disk free space post-cleanup (macOS)
run: df -h .
if: matrix.os == 'macOS-15'
- name: Build with Gradle
run: ./gradlew ${{ matrix.task }} --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
env:
@@ -174,37 +175,39 @@ jobs:
run: ./gradlew copyAllOutputs --build-cache -PbuildServer -PskipJavaFormat -PdeveloperID=${{ secrets.APPLE_DEVELOPER_ID }} ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
- uses: actions/upload-artifact@v7
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027'))))
- name: Check disk free space (Windows)
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'
- name: Check disk free space (macOS)
run: df -h .
if: matrix.os == 'macOS-15'
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: ${{ matrix.outputs }}
- name: Check disk free space
if: always()
run: df -h
build-documentation:
name: "Build - Documentation"
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs: [validation]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v5
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 26
java-version: 17
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Build with Gradle
run: ./gradlew docs:zipDocs --build-cache -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}
env:
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v4
with:
name: Documentation
path: docs/build/outputs
@@ -212,16 +215,16 @@ jobs:
publish:
name: "Documentation - Publish"
runs-on: ubuntu-22.04
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
needs: [build-documentation]
concurrency: ci-docs-publish
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Download docs artifacts
uses: actions/download-artifact@v7
uses: actions/download-artifact@v4
with:
name: Documentation
- name: Make output directories
@@ -230,8 +233,8 @@ jobs:
mkdir -p docs/tmp/javadoc
- name: Extract docs
run: |
unzip _GROUP_org_wpilib_wpilibc_ID_documentation_CLS.zip -d docs/tmp/doxygen/html
unzip _GROUP_org_wpilib_wpilibj_ID_documentation_CLS.zip -d docs/tmp/javadoc
unzip _GROUP_edu_wpi_first_wpilibc_ID_documentation_CLS.zip -d docs/tmp/doxygen/html
unzip _GROUP_edu_wpi_first_wpilibj_ID_documentation_CLS.zip -d docs/tmp/javadoc
- name: Set environment variables (Development)
run: |
echo "BRANCH=development" >> $GITHUB_ENV
@@ -240,14 +243,14 @@ jobs:
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=beta" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Set environment variables (Release)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=release" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta')
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, '2027')
- name: Install SSH Client 🔑
uses: webfactory/ssh-agent@v0.9.1
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
- name: Deploy 🚀
@@ -260,7 +263,7 @@ jobs:
single-commit: true
folder: docs/tmp
- name: Trigger Workflow
uses: actions/github-script@v8
uses: actions/github-script@v7
with:
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
script: |
@@ -274,12 +277,12 @@ jobs:
combine:
name: Combine
needs: [build-docker, build-host, build-documentation]
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Free disk space
- name: Free Disk Space
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
@@ -289,43 +292,42 @@ jobs:
large-packages: false
docker-images: false
swap-storage: false
- uses: actions/checkout@v6
- uses: actions/checkout@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
with:
repository: wpilibsuite/build-tools
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
with:
path: combiner/products/build/allOutputs
- name: Flatten Artifacts
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
run: rsync -a --delete combiner/products/build/allOutputs/*/* combiner/products/build/allOutputs/
- name: Check version number exists
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
run: |
cat combiner/products/build/allOutputs/version.txt
test -s combiner/products/build/allOutputs/version.txt
- uses: actions/setup-java@v5
- uses: actions/setup-java@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
with:
distribution: 'temurin'
java-version: 25
- name: Combine
java-version: 17
- name: Combine (Main)
if: |
github.repository == 'wpilibsuite/allwpilib' &&
github.ref == 'refs/heads/main'
run: cd combiner && ./gradlew publish -Pallwpilib -Pbuild2027
run: cd combiner && ./gradlew publish -Pallwpilib
env:
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
@@ -333,16 +335,34 @@ jobs:
- name: Combine (Release)
if: |
github.repository == 'wpilibsuite/allwpilib' &&
startsWith(github.ref, 'refs/tags/v')
run: cd combiner && ./gradlew publish -Pallwpilib -PreleaseRepoPublish -Pbuild2027
startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
run: cd combiner && ./gradlew publish -Pallwpilib -PreleaseRepoPublish
env:
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
with:
name: Maven
path: ~/releases
dispatch:
name: dispatch
needs: [combine]
strategy:
matrix:
repo: ['SmartDashboard', 'PathWeaver', 'Shuffleboard', 'RobotBuilder']
runs-on: ubuntu-22.04
steps:
- uses: peter-evans/repository-dispatch@v3
if: |
github.repository == 'wpilibsuite/allwpilib' &&
startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
with:
token: ${{ secrets.TOOL_REPO_ACCESS_TOKEN }}
repository: wpilibsuite/${{ matrix.repo }}
event-type: tag
client-payload: '{"package_name": "allwpilib", "package_version": "${{ github.ref_name }}"}'

View File

@@ -9,6 +9,6 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v6
- uses: actions/labeler@v5
with:
sync-labels: true

View File

@@ -15,14 +15,14 @@ jobs:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: gradle/actions/wrapper-validation@v5
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4
wpiformat:
name: "wpiformat"
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch all history and metadata
@@ -30,13 +30,13 @@ jobs:
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.12
uses: actions/setup-python@v6
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install wpiformat
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2026.64
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.36
- name: Run
run: ${{ runner.temp }}/wpiformat/bin/wpiformat
- name: Check output
@@ -44,9 +44,9 @@ jobs:
- name: Generate diff
run: git diff HEAD > wpiformat-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v4
with:
archive: false
name: wpiformat fixes
path: wpiformat-fixes.patch
if: ${{ failure() }}
- name: Write to job summary
@@ -59,11 +59,11 @@ jobs:
tidy:
name: "clang-tidy"
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs: [validation]
container: wpilib/debian-base:trixie
container: wpilib/ubuntu-base:22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch all history and metadata
@@ -72,13 +72,13 @@ jobs:
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.12
uses: actions/setup-python@v6
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install wpiformat
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2026.64
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.36
- name: Create compile_commands.json
run: |
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio
@@ -87,16 +87,16 @@ jobs:
- name: List changed files
run: ${{ runner.temp }}/wpiformat/bin/wpiformat -list-changed-files
- name: Run clang-tidy release
run: ${{ runner.temp }}/wpiformat/bin/wpiformat -no-format -tidy-changed -compile-commands=build/TargetedCompileCommands/linuxx86-64release
run: ${{ runner.temp }}/wpiformat/bin/wpiformat -no-format -tidy-changed -compile-commands=build/TargetedCompileCommands/linuxx86-64release -vv
- name: Run clang-tidy debug
run: ${{ runner.temp }}/wpiformat/bin/wpiformat -no-format -tidy-changed -compile-commands=build/TargetedCompileCommands/linuxx86-64debug
run: ${{ runner.temp }}/wpiformat/bin/wpiformat -no-format -tidy-changed -compile-commands=build/TargetedCompileCommands/linuxx86-64debug -vv
javaformat:
name: "Java format"
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs: [validation]
container: wpilib/debian-base:trixie
container: wpilib/ubuntu-base:22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch all history and metadata
@@ -111,9 +111,9 @@ jobs:
- name: Generate diff
run: git diff HEAD > javaformat-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v4
with:
archive: false
name: javaformat fixes
path: javaformat-fixes.patch
if: ${{ failure() }}
- name: Write to job summary
@@ -123,27 +123,3 @@ jobs:
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
if: ${{ failure() }}
check-spelling:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- name: misspell
uses: reviewdog/action-misspell@v1
with:
reporter: "github-check"
fail_level: "error"
exclude: |
**/*.svg
**/thirdparty/**
**/upstream_utils/**
**/generated/**
./wpigui/src/main/native/cpp/portable-file-dialogs.*
./wpimath/src/main/native/include/wpi/units/base.hpp
./wpinet/src/main/native/linux/AvahiClient.*
./wpinet/src/main/native/cpp/http_parser.cpp
./wpinet/src/main/native/include/wpi/net/http_parser.hpp
./wpiutil/src/main/native/include/wpi/util/FastQueue.hpp
./wpiutil/src/test/native/cpp/json/**
./wpiutil/src/test/native/cpp/llvm/**
./wpiutil/src/test/native/cpp/span/**

View File

@@ -13,9 +13,9 @@ concurrency:
jobs:
update:
name: "Update"
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run pregen
@@ -27,8 +27,8 @@ jobs:
- name: Generate diff
run: git diff HEAD > pregenerated-files-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v4
with:
archive: false
name: pregenerated-files-fixes
path: pregenerated-files-fixes.patch
if: ${{ failure() }}

View File

@@ -19,38 +19,33 @@ jobs:
- name: asan
cmake-flags: "-DCMAKE_BUILD_TYPE=Asan"
ctest-env: ""
ctest-flags: ""
ctest-flags: "-E 'wpilibc'"
- name: tsan
cmake-flags: "-DCMAKE_BUILD_TYPE=Tsan"
ctest-env: "TSAN_OPTIONS=second_deadlock_stack=1:suppressions=$GITHUB_WORKSPACE/tsan_suppressions.txt"
ctest-flags: "-E 'cscore|cameraserver'"
ctest-env: "TSAN_OPTIONS=second_deadlock_stack=1"
ctest-flags: "-E 'cscore|cameraserver|wpilibc|wpilibNewCommands'"
- name: ubsan
cmake-flags: "-DCMAKE_BUILD_TYPE=Ubsan"
ctest-env: ""
ctest-flags: ""
name: "${{ matrix.name }}"
runs-on: ubuntu-24.04
container: wpilib/debian-base:trixie
container: wpilib/roborio-cross-ubuntu:2025-24.04
steps:
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install -y libopencv-dev clang-18 ninja-build avahi-daemon
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv-java clang-17 libprotobuf-dev protobuf-compiler ninja-build
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.10
uses: mozilla-actions/sccache-action@v0.0.9
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: configure
run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-18 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-18 ${{ matrix.cmake-flags }} ..
run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-17 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-17 -DWITH_JAVA=OFF ${{ matrix.cmake-flags }} ..
env:
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- name: Setup avahi-daemon
run: |
sudo service dbus start
sudo avahi-daemon -D
- name: build
working-directory: build
run: cmake --build . --parallel $(nproc)

View File

@@ -14,8 +14,8 @@ jobs:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: gradle/actions/wrapper-validation@v5
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4
build-docker:
if: (github.repository_owner == 'wpilibsuite' && github.ref == 'refs/heads/main') || github.event_name != 'schedule'
@@ -23,17 +23,20 @@ jobs:
fail-fast: false
matrix:
include:
- container: wpilib/systemcore-cross-debian:trixie
artifact-name: Systemcore
build-options: "-Ponlylinuxsystemcore"
- container: wpilib/aarch64-cross-debian:trixie
- container: wpilib/roborio-cross-ubuntu:2025-22.04
artifact-name: Athena
build-options: "-Ponlylinuxathena"
- container: wpilib/raspbian-cross-ubuntu:bookworm-22.04
artifact-name: Arm32
build-options: "-Ponlylinuxarm32"
- container: wpilib/aarch64-cross-ubuntu:bookworm-22.04
artifact-name: Arm64
build-options: "-Ponlylinuxarm64"
- container: wpilib/systemcore-cross-debian:trixie
- container: wpilib/ubuntu-base:22.04
artifact-name: Linux
build-options: "-Ponlylinuxx86-64"
name: "Build - ${{ matrix.artifact-name }}"
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs: [validation]
steps:
- name: Free Disk Space
@@ -46,7 +49,7 @@ jobs:
large-packages: false
docker-images: false
swap-storage: false
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build with Gradle
@@ -54,14 +57,10 @@ jobs:
with:
image: ${{ matrix.container }}
options: -v ${{ github.workspace }}:/work -w /work -e GITHUB_REF -e CI
# Start avahi-daemon and build
run: |
service dbus start
avahi-daemon -D
./gradlew build -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
run: df . && echo $GITHUB_REF && ./gradlew build -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
- name: Check free disk space
run: df .
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: build/allOutputs
@@ -103,6 +102,12 @@ jobs:
architecture: aarch64
task: "build"
outputs: "build/allOutputs"
- os: windows-2022
artifact-name: Win32FFI
architecture: x86
task: ":ntcoreffi:build"
build-options: "-Pntcoreffibuild \"-Dorg.gradle.jvmargs=-Xmx1096m\""
outputs: "ntcoreffi/build/outputs"
- os: windows-2022
artifact-name: Win64FFI
architecture: x64
@@ -113,16 +118,16 @@ jobs:
runs-on: ${{ matrix.os }}
needs: [validation]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v5
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 25
java-version: 17
architecture: ${{ matrix.architecture }}
- name: Import Developer ID Certificate
uses: wpilibsuite/import-signing-certificate@v3
uses: wpilibsuite/import-signing-certificate@v2
with:
certificate-data: ${{ secrets.APPLE_CERTIFICATE_DATA }}
certificate-passphrase: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
@@ -133,14 +138,16 @@ jobs:
run: security set-keychain-settings -lut 21600
if: |
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' && github.ref == 'refs/heads/main')
- name: Set Java Heap Size
run: sed -i 's/-Xmx2g/-Xmx1g/g' gradle.properties
if: matrix.artifact-name == 'Win32'
- name: Check disk free space (Windows)
if: runner.os == 'Windows'
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'
- name: Check disk free space pre-cleanup (macOS)
if: runner.os == 'macOS'
run: df -h .
if: matrix.os == 'macOS-15'
- name: Cleanup disk space
if: runner.os == 'macOS'
# CodeQL: 5G
# go: 748M
# Android: 12G
@@ -148,9 +155,10 @@ jobs:
rm -rf /Users/runner/hostedtoolcache/CodeQL
rm -rf /Users/runner/hostedtoolcache/go
rm -rf /Users/runner/Library/Android
if: matrix.os == 'macOS-15'
- name: Check disk free space post-cleanup (macOS)
if: runner.os == 'macOS'
run: df -h .
if: matrix.os == 'macOS-15'
- name: Build with Gradle
run: ./gradlew ${{ matrix.task }} -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
- name: Sign Libraries with Developer ID
@@ -158,12 +166,12 @@ jobs:
if: |
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' && github.ref == 'refs/heads/main')
- name: Check disk free space (Windows)
if: runner.os == 'Windows'
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'
- name: Check disk free space (macOS)
if: runner.os == 'macOS'
run: df -h .
- uses: actions/upload-artifact@v7
if: matrix.os == 'macOS-15'
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: ${{ matrix.outputs }}

196
.github/workflows/tools.yml vendored Normal file
View File

@@ -0,0 +1,196 @@
name: Tools
on: [pull_request, push]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
env:
YEAR: 2026
jobs:
build-artifacts:
name: "Build - WPILib"
runs-on: ubuntu-22.04
env:
DISPLAY: ':10'
steps:
- name: Free Disk Space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: false
docker-images: false
swap-storage: false
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gradle/actions/wrapper-validation@v4
- name: Build WPILib with Gradle
uses: wpilibsuite/docker-run-action@v4
with:
image: wpilib/roborio-cross-ubuntu:2025-22.04
options: -v ${{ github.workspace }}:/work -w /work -e GITHUB_REF -e CI -e DISPLAY
run: df . && ./gradlew :wpilibc:publish :wpilibj:publish :wpilibNewCommands:publish :hal:publish :cameraserver:publish :ntcore:publish :cscore:publish :wpimath:publish :wpinet:publish :wpiutil:publish :apriltag:publish :wpiunits:publish :simulation:halsim_gui:publish :simulation:halsim_ds_socket:publish :simulation:halsim_ws_server:publish :simulation:halsim_ws_client:publish :simulation:halsim_xrp:publish :fieldImages:publish :romiVendordep:publish :xrpVendordep:publish :epilogue-processor:publish :epilogue-runtime:publish :thirdparty:googletest:publish -x test -x Javadoc -x doxygen --build-cache && cp -r /root/releases/maven/development /work
- uses: actions/upload-artifact@v4
with:
name: MavenArtifacts
path: |
development
retention-days: 1
Robotbuilder:
name: "Build - RobotBuilder"
needs: [build-artifacts]
runs-on: ubuntu-22.04
env:
DISPLAY: ':10'
steps:
- uses: actions/checkout@v4
with:
repository: wpilibsuite/robotbuilder
fetch-depth: 0
- uses: actions/download-artifact@v4
with:
name: MavenArtifacts
- name: Patch RobotBuilder to use local development
run: cd src/main/resources/export && echo "wpi.maven.useLocal = false" >> java/build.gradle && echo "wpi.maven.useFrcMavenLocalDevelopment = true" >> java/build.gradle && echo "wpi.versions.wpilibVersion = '$YEAR.424242.+'" >> java/build.gradle && echo "wpi.versions.wpimathVersion = '$YEAR.424242.+'" >> java/build.gradle && echo "wpi.maven.useLocal = false" >> cpp/build.gradle && echo "wpi.maven.useFrcMavenLocalDevelopment = true" >> cpp/build.gradle && echo "wpi.versions.wpilibVersion = '$YEAR.424242.+'" >> cpp/build.gradle && echo "wpi.versions.wpimathVersion = '$YEAR.424242.+'" >> cpp/build.gradle
- name: Install and run xvfb
run: sudo apt-get update && sudo apt-get install -y xvfb && Xvfb $DISPLAY &
- name: Move artifacts
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
- name: Build RobotBuilder with Gradle
run: ./gradlew build test --tests 'robotbuilder.exporters.*' -x htmlSanityCheck -PbuildServer -PreleaseMode ; cat build/test-results/test/TEST-robotbuilder.exporters.*.xml ;
- name: Summarize RobotBuilder Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: |
build/test-results/test/TEST*.xml
check_run: false
comment_mode: off
- uses: actions/upload-artifact@v4
if: always()
with:
name: RobotBuilderTestResults
path: |
build/reports/
- uses: actions/upload-artifact@v4
with:
name: RobotBuilder Build
path: |
build/libs/
retention-days: 7
Shuffleboard:
name: "Build - Shuffleboard"
needs: [build-artifacts]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
repository: wpilibsuite/shuffleboard
fetch-depth: 0
- name: Patch Shuffleboard to use local development
run: sed -i "s/wpilibTools.deps.wpilibVersion.*/wpilibTools.deps.wpilibVersion = \'$YEAR\.424242\.+\'/" app/app.gradle && sed -i "s/wpilibTools.deps.wpilibVersion.*/wpilibTools.deps.wpilibVersion = \'$YEAR\.424242\.+\'/" plugins/cameraserver/cameraserver.gradle && sed -i "s/wpilibTools.deps.wpilibVersion.*/wpilibTools.deps.wpilibVersion = \'$YEAR\.424242\.+\'/" plugins/networktables/networktables.gradle
- uses: actions/download-artifact@v4
with:
name: MavenArtifacts
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
- name: Move artifacts
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
- name: Install dependencies
run: sudo apt-get install -y libgtk2.0-0
- name: Build with Gradle
run: ./gradlew build -x Javadoc
- uses: actions/upload-artifact@v4
with:
name: Shuffleboard Build
path: |
build/allOutputs/
retention-days: 7
PathWeaver:
name: "Build - PathWeaver"
needs: [build-artifacts]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
repository: wpilibsuite/PathWeaver
fetch-depth: 0
- name: Patch PathWeaver to use local development
run: sed -i "s/wpilibTools.deps.wpilibVersion.*/wpilibTools.deps.wpilibVersion = \'$YEAR\.424242\.+\'/" dependencies.gradle
- uses: actions/download-artifact@v4
with:
name: MavenArtifacts
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
- name: Move artifacts
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
- name: Build with Gradle
run: ./gradlew build
- uses: actions/upload-artifact@v4
with:
name: PathWeaver Build
path: |
build/allOutputs/
retention-days: 7
# Robotpy:
# name: "Build - Robotpy"
# needs: [build-artifacts]
# runs-on: ubuntu-22.04
# steps:
# - uses: actions/checkout@v4
# with:
# repository: robotpy/mostrobotpy
# fetch-depth: 0
# - uses: actions/download-artifact@v4
# with:
# name: MavenArtifacts
# - name: Move artifacts
# run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
#
# - uses: actions/setup-python@v5
# with:
# python-version: 3.13
#
# - name: Install sccache
# uses: mozilla-actions/sccache-action@v0.0.9
#
# - name: Install deps
# shell: bash
# run: |
# python -m pip --disable-pip-version-check install -r rdev_requirements.txt
#
# - name: Install numpy (needed for stubgen but broken in raspbian CI)
# shell: bash
# run: |
# python -m pip --disable-pip-version-check install numpy
# - name: Patch RobotPy rdev to use local development
# run: git config user.name github-actions && git config user.email github-actions@github.com && set -- ~/releases/maven/development/edu/wpi/first/wpiutil/wpiutil-cpp/*/ ; wpilibversion=$(basename $1) && echo $wpilibversion && sed --regexp-extended -i 's@(wpilib_bin_url =).*@\1 \"file:\/\/'"$HOME"'\/releases\/maven\/development"@' rdev.toml && sed --regexp-extended -i 's/(wpilib_bin_version =).*/\1 \"'"$wpilibversion"'\"/' rdev.toml && ./rdev.sh update-pyproject --commit
# - name: Build + test wheels
# shell: bash
# run: |
# ./rdev.sh ci run
# env:
# RPYBUILD_STRIP_LIBPYTHON: "1"
# RPYBUILD_CC_LAUNCHER: sccache
# SCCACHE_WEBDAV_ENDPOINT: "https://frcmaven.wpi.edu/artifactory/wpilib-generic-cache-cmake-local"
# SCCACHE_WEBDAV_KEY_PREFIX: "sccache-robotpy"
# SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
# SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}

View File

@@ -13,9 +13,9 @@ concurrency:
jobs:
update:
name: "Update"
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch all history and metadata
@@ -23,7 +23,7 @@ jobs:
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.12
uses: actions/setup-python@v6
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Configure committer identity
@@ -42,18 +42,6 @@ jobs:
./argparse_lib.py clone
./argparse_lib.py copy-src
./argparse_lib.py format-patch
- name: Run benchmark.py
run: |
cd upstream_utils
./benchmark.py clone
./benchmark.py copy-src
./benchmark.py format-patch
- name: Run double-conversion.py
run: |
cd upstream_utils
./double-conversion.py clone
./double-conversion.py copy-src
./double-conversion.py format-patch
- name: Run eigen.py
run: |
cd upstream_utils
@@ -132,6 +120,12 @@ jobs:
./mpack.py clone
./mpack.py copy-src
./mpack.py format-patch
- name: Run protobuf.py
run: |
cd upstream_utils
./protobuf.py clone
./protobuf.py copy-src
./protobuf.py format-patch
- name: Run sleipnir.py
run: |
cd upstream_utils
@@ -144,12 +138,6 @@ jobs:
./stb.py clone
./stb.py copy-src
./stb.py format-patch
- name: Run upb.py
run: |
cd upstream_utils
./upb.py clone
./upb.py copy-src
./upb.py format-patch
- name: Add untracked files to index so they count as changes
run: git add -A
- name: Check output

8
.gitignore vendored
View File

@@ -258,11 +258,3 @@ bazel_auth.rc
# Meson
.meson-subproject*
# Copybara user config
shared/bazel/copybara/.copybara.json
# Nix files
shell.nix
flake.nix
flake.lock

54
.styleguide Normal file
View File

@@ -0,0 +1,54 @@
cppHeaderFileInclude {
\.h$
\.hpp$
\.inc$
\.inl$
}
cppSrcFileInclude {
\.cpp$
}
modifiableFileExclude {
cmake/toolchains/
thirdparty/
\.patch$
gradlew
}
generatedFileExclude {
FRCNetComm\.java$
simulation/gz_msgs/src/include/simulation/gz_msgs/msgs\.h$
fieldImages/src/main/native/resources/
apriltag/src/test/resources/
wpilibc/src/generated/
}
repoRootNameOverride {
wpilib
}
includeOtherLibs {
^Eigen/
^cameraserver/
^cscore
^fmt/
^glass/
^google/
^gtest/
^hal/
^imgui
^implot
^mockdata/
^networktables/
^ntcore
^opencv2/
^support/
^units/
^unsupported/
^vision/
^wpi/
^wpigui
^wpimath/
^wpinet/
}

View File

@@ -1,44 +0,0 @@
cHeaderFileInclude {
_c\.h$
}
cppHeaderFileInclude {
(?<!_c)\.h$
\.inc$
\.inl$
}
generatedFileExclude {
assets/
docs/theme\.css$
generated/
hal/src/main/native/systemcore/rev/
python/
robotpyExamples/
resources/
thirdparty/
wpigui/src/main/native/cpp/portable-file-dialogs\.cpp$
wpigui/src/main/native/include/wpi/gui/portable-file-dialogs\.h$
wpimath/src/main/native/include/wpi/units/base\.hpp$
wpinet/src/main/native/cpp/http_parser\.cpp$
wpinet/src/main/native/include/wpi/net/http_parser\.hpp$
wpiutil/src/main/native/include/wpi/util/FastQueue\.hpp$
wpiutil/src/test/native/cpp/json/
wpiutil/src/test/native/cpp/llvm/
wpiutil/src/test/native/cpp/span/
}
modifiableFileExclude {
objcpp/
wpimath/src/test/native/cpp/UnitsTest\.cpp$
wpiutil/src/main/native/cpp/fs\.cpp$
wpiutil/src/main/native/include/wpi/util/fs\.hpp$
}
licenseUpdateExclude {
cscore/src/main/native/cpp/default_init_allocator\.hpp$
wpiutil/src/main/native/cpp/Base64\.cpp$
wpiutil/src/main/native/cpp/sha1\.cpp$
wpiutil/src/main/native/include/wpi/util/sha1\.hpp$
wpinet/src/main/native/linux/AvahiClient\.hpp$
}

View File

@@ -1,283 +0,0 @@
load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files")
load("@rules_java//java:java_binary.bzl", "java_binary")
load("@rules_java//java:java_plugin.bzl", "java_plugin")
load("@rules_pkg//:mappings.bzl", "pkg_files")
load("@rules_python//python:pip.bzl", "compile_pip_requirements")
load("//shared/bazel/rules:publishing.bzl", "publish_all")
load("//shared/bazel/rules/robotpy:compatibility_select.bzl", "robotpy_compatibility_select")
java_plugin(
name = "avaje_jsonb_generator",
processor_class = "io.avaje.jsonb.generator.JsonbProcessor",
visibility = ["//visibility:public"],
deps = [
"@maven//:io_avaje_avaje_jsonb_generator",
],
)
exports_files([
"LICENSE.md",
"ThirdPartyNotices.txt",
])
pkg_files(
name = "license_pkg_files",
srcs = [
"LICENSE.md",
"ThirdPartyNotices.txt",
],
visibility = ["//visibility:public"],
)
# bazel build //:requirements.lock
compile_pip_requirements(
name = "requirements",
extra_args = ["--allow-unsafe"],
requirements_in = "requirements.txt",
requirements_txt = "requirements_lock.txt",
requirements_windows = "//:requirements_windows_lock.txt",
# compile_pip_requirements does not respect target_compatible_with for some of the targets it generates under the hood
tags = ["no-systemcore"],
)
alias(
name = "quickbuf_protoc",
actual = select({
"@platforms//os:windows": "@quickbuffer_protoc_windows//file",
"@rules_bzlmodrio_toolchains//conditions:osx_aarch64": "@quickbuffer_protoc_osx_aarch64//file",
"@rules_bzlmodrio_toolchains//conditions:osx_x86_64": "@quickbuffer_protoc_osx_x86-64//file",
"@rules_bzlmodrio_toolchains//constraints/combined:is_linux": "@quickbuffer_protoc_linux//file",
}),
tags = ["pregeneration"],
visibility = ["//visibility:public"],
)
java_binary(
name = "copybara",
main_class = "com.google.copybara.Main",
runtime_deps = ["@com_github_google_copybara//jar"],
)
# This is a helper to run all of the pregeneration scripts at once.
write_source_files(
name = "write_pregenerated_files",
additional_update_targets = [
"//hal:write_hal",
"//ntcore:write_ntcore",
"//wpilibc:write_wpilibc",
"//wpilibcExamples:write_example_project_list",
"//wpilibj:write_wpilibj",
"//wpilibjExamples:write_example_project_list",
"//commandsv2:write_wpilib_new_commands",
"//commandsv3:write_commandsv3",
"//wpimath:write_wpimath",
"//wpiunits:write_wpiunits",
"//wpiutil:write_wpiutil",
"//robotpyExamples:write_example_project_list",
],
tags = ["pregeneration"],
)
publish_all(
name = "publish",
targets = [
"//apriltag:apriltag-cpp_publish.publish",
"//apriltag:apriltag-java_publish.publish",
"//cameraserver:cameraserver-cpp_publish.publish",
"//cameraserver:cameraserver-java_publish.publish",
"//commandsv2:commandsv2-cpp_publish.publish",
"//commandsv2:commandsv2-java_publish.publish",
"//commandsv3:commandsv3-java_publish.publish",
"//cscore:cscore-cpp_publish.publish",
"//cscore:cscore-java_publish.publish",
"//cscore:cscorejnicvstatic-cpp_publish.publish",
"//datalog:datalog-cpp_publish.publish",
"//datalog:datalog-java_publish.publish",
"//docs:wpilibj_publish.publish",
"//epilogue-processor:processor-java_publish.publish",
"//epilogue-runtime:epilogue-java_publish.publish",
"//fields:fields-cpp_publish.publish",
"//fields:fields-java_publish.publish",
"//glass:glass-cpp_publish.publish",
"//glass:glassapp_publish.publish",
"//glass:glassnt-cpp_publish.publish",
"//hal:hal-java_publish.publish",
"//hal:wpiHal-cpp_publish.publish",
"//javacPlugin:javacPlugin_publish.publish",
"//ntcore:ntcore-cpp_publish.publish",
"//ntcore:ntcore-java_publish.publish",
"//ntcoreffi:ntcoreffi-cpp_publish.publish",
"//romiVendordep:romiVendordep-cpp_publish.publish",
"//romiVendordep:romiVendordep-java_publish.publish",
"//simulation/halsim_ds_socket:halsim_ds_socket-cpp_publish.publish",
"//simulation/halsim_gui:halsim_gui-cpp_publish.publish",
"//simulation/halsim_ws_client:halsim_ws_client-cpp_publish.publish",
"//simulation/halsim_ws_core:halsim_ws_core-cpp_publish.publish",
"//simulation/halsim_ws_server:halsim_ws_server-cpp_publish.publish",
"//simulation/halsim_xrp:halsim_xrp-cpp_publish.publish",
"//thirdparty/catch2:catch2-cpp_publish.publish",
"//thirdparty/googletest:googletest-cpp_publish.publish",
"//thirdparty/imgui_suite:imguiSuite-cpp_publish.publish",
"//tools/datalogtool:datalogtool_publish.publish",
"//tools/outlineviewer:outlineviewer_publish.publish",
"//tools/processstarter:processstarter_publish.publish",
"//tools/sysid:sysid_publish.publish",
"//tools/wpical:wpical_publish.publish",
"//wpiannotations:wpiannotations_publish.publish",
"//wpigui:wpigui-cpp_publish.publish",
"//wpilibc:wpilibc-cpp_publish.publish",
"//wpilibcExamples:commands_publish.publish",
"//wpilibcExamples:examples_publish.publish",
"//wpilibcExamples:templates_publish.publish",
"//wpilibj:wpilibj-java_publish.publish",
"//wpilibjExamples:commands_publish.publish",
"//wpilibjExamples:examples_publish.publish",
"//wpilibjExamples:templates_publish.publish",
"//wpimath:wpimath-cpp_publish.publish",
"//wpimath:wpimath-java_publish.publish",
"//wpinet:wpinet-cpp_publish.publish",
"//wpinet:wpinet-java_publish.publish",
"//wpiunits:wpiunits-java_publish.publish",
"//wpiutil:wpiutil-cpp_publish.publish",
"//wpiutil:wpiutil-java_publish.publish",
"//xrpVendordep:xrpVendordep-cpp_publish.publish",
"//xrpVendordep:xrpVendordep-java_publish.publish",
] + select({
"@platforms//cpu:x86_64": [
"//docs:wpilibc_publish.publish",
],
"//conditions:default": [],
}),
)
write_source_files(
name = "write_robotpy_generated_native_files",
additional_update_targets = [
"//apriltag:robotpy-native-apriltag-generator.generate_build_info",
"//datalog:robotpy-native-datalog-generator.generate_build_info",
"//hal:robotpy-native-wpihal-generator.generate_build_info",
"//ntcore:robotpy-native-ntcore-generator.generate_build_info",
"//romiVendordep:robotpy-native-xrp-generator.generate_build_info",
"//wpilibc:robotpy-native-wpilib-generator.generate_build_info",
"//wpinet:robotpy-native-wpinet-generator.generate_build_info",
"//wpimath:robotpy-native-wpimath-generator.generate_build_info",
"//wpiutil:robotpy-native-wpiutil-generator.generate_build_info",
"//xrpVendordep:robotpy-native-xrp-generator.generate_build_info",
],
tags = [
"pregeneration",
"robotpy",
],
target_compatible_with = robotpy_compatibility_select(),
)
write_source_files(
name = "write_robotpy_generated_pybind_files",
additional_update_targets = [
"//apriltag:robotpy-apriltag-generator.generate_build_info",
"//datalog:robotpy-datalog-generator.generate_build_info",
"//hal:robotpy-hal-generator.generate_build_info",
"//ntcore:pyntcore-generator.generate_build_info",
"//romiVendordep:robotpy-romi-generator.generate_build_info",
"//wpilibc:robotpy-wpilib-generator.generate_build_info",
"//wpinet:robotpy-wpinet-generator.generate_build_info",
"//wpimath:robotpy-wpimath-generator.generate_build_info",
"//wpimath:robotpy-wpimath_test-generator.generate_build_info",
"//wpiutil:robotpy-wpiutil-generator.generate_build_info",
"//xrpVendordep:robotpy-xrp-generator.generate_build_info",
],
tags = [
"pregeneration",
"robotpy",
],
target_compatible_with = robotpy_compatibility_select(),
)
write_source_files(
name = "write_robotpy_update_yaml_files",
additional_update_targets = [
"//apriltag:write_robotpy-apriltag-update-yaml",
"//datalog:write_robotpy-wpilog-update-yaml",
"//hal:write_robotpy-hal-update-yaml",
"//ntcore:write_pyntcore-update-yaml",
"//romiVendordep:write_robotpy-romi-update-yaml",
"//wpilibc:write_robotpy-wpilib-update-yaml",
"//wpinet:write_robotpy-wpinet-update-yaml",
"//wpimath:write_robotpy-wpimath-update-yaml",
"//wpimath:write_robotpy-wpimath-test-update-yaml",
"//wpiutil:write_robotpy-wpiutil-update-yaml",
"//xrpVendordep:write_robotpy-xrp-update-yaml",
],
tags = [
"manual",
"pregeneration",
"robotpy",
],
target_compatible_with = robotpy_compatibility_select(),
)
write_source_files(
name = "write_robotpy_create_imports",
additional_update_targets = [
"//apriltag:robotpy-apriltag-create-imports",
"//datalog:robotpy-wpilog-create-imports",
"//hal:robotpy-hal-create-imports",
"//ntcore:pyntcore-create-imports",
"//romiVendordep:robotpy-romi-create-imports",
"//wpilibc:robotpy-wpilib-create-imports",
"//wpinet:robotpy-wpinet-create-imports",
"//wpimath:robotpy-wpimath-create-imports",
"//wpimath:robotpy-wpimath-test-create-imports",
"//wpiutil:robotpy-wpiutil-create-imports",
"//xrpVendordep:robotpy-xrp-create-imports",
],
tags = [
"manual",
"pregeneration",
"robotpy",
],
target_compatible_with = robotpy_compatibility_select(),
)
write_source_files(
name = "write_robotpy_files",
additional_update_targets = [
":write_robotpy_generated_native_files",
":write_robotpy_generated_pybind_files",
":write_robotpy_update_yaml_files",
],
tags = [
"manual",
"pregeneration",
],
)
write_source_files(
name = "write_all",
additional_update_targets = [
":write_pregenerated_files",
":write_robotpy_files",
],
tags = [
"manual",
"pregeneration",
],
)
# Helper easily run the semiwrap parsing tools on all of the robotpy projects.
filegroup(
name = "robotpy_generated_files",
srcs = [
"//apriltag:robotpy-apriltag.generated_files",
"//datalog:robotpy-wpilog.generated_files",
"//hal:robotpy-hal.generated_files",
"//ntcore:pyntcore.generated_files",
"//wpilibc:robotpy-wpilib.generated_files",
"//wpimath:robotpy-wpimath.generated_files",
"//wpimath:robotpy-wpimath-test.generated_files",
"//wpinet:robotpy-wpinet.generated_files",
"//wpiutil:robotpy-wpiutil.generated_files",
],
tags = ["manual"],
target_compatible_with = robotpy_compatibility_select(),
)

View File

@@ -63,7 +63,9 @@ set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# Options for building certain parts of the repo. Everything is built by default.
option(BUILD_SHARED_LIBS "Build with shared libs" ON)
option(BUILD_SHARED_LIBS "Build with shared libs (needed for JNI)" ON)
option(WITH_JAVA "Include Java and JNI in the build" OFF)
option(WITH_JAVA_SOURCE "Build Java source jars" ${WITH_JAVA})
option(WITH_DOCS "Build Doxygen docs (needs Git for versioning)" OFF)
cmake_dependent_option(
DOCS_WARNINGS_AS_ERRORS
@@ -76,22 +78,46 @@ option(WITH_CSCORE "Build cscore (needs OpenCV)" ON)
option(WITH_NTCORE "Build ntcore" ON)
option(WITH_WPICAL "Build wpical" OFF)
option(WITH_WPIMATH "Build wpimath" ON)
cmake_dependent_option(
WITH_WPIUNITS
"Build wpiunits"
ON
WITH_JAVA
OFF
)
option(WITH_WPILIB "Build hal, wpilibc/j, and developerRobot (needs OpenCV)" ON)
option(WITH_EXAMPLES "Build examples" OFF)
option(WITH_TESTS "Build unit tests (requires internet connection)" ON)
option(WITH_GUI "Build GUI items" ON)
option(WITH_SIMULATION_MODULES "Build simulation modules" ON)
option(WITH_PROTOBUF "Build protobuf support" ON)
option(WITH_BENCHMARK "Build the benchmark project" ON)
# Options for using a package manager (e.g., vcpkg) for certain dependencies.
option(USE_SYSTEM_FMTLIB "Use system fmtlib" OFF)
option(USE_SYSTEM_LIBUV "Use system libuv" OFF)
option(USE_SYSTEM_EIGEN "Use system eigen" OFF)
option(USE_LINKED_AVAHI "Use directly linked Avahi instead of loading at runtime" OFF)
# Options for location of OpenCV Java.
set(OPENCV_JAVA_INSTALL_DIR "" CACHE PATH "Location to search for the OpenCV jar file")
# Options for compilation flags.
option(NO_WERROR "Disable -Werror flag during compilation" OFF)
if(NOT WITH_JAVA OR NOT WITH_CSCORE)
if(NOT "${OPENCV_JAVA_INSTALL_DIR}" STREQUAL "")
message(
WARNING
"
WARNING: OpenCV Java dir set but java is not enabled!
It will be ignored.
"
)
endif()
endif()
wpilib_config(OPTIONS WITH_JAVA REQUIRES BUILD_SHARED_LIBS)
wpilib_config(OPTIONS WITH_SIMULATION_MODULES REQUIRES BUILD_SHARED_LIBS WITH_WPILIB WITH_NTCORE)
wpilib_config(OPTIONS WITH_CSCORE REQUIRES WITH_NTCORE)
@@ -100,7 +126,17 @@ wpilib_config(OPTIONS WITH_GUI REQUIRES WITH_NTCORE WITH_WPIMATH)
wpilib_config(OPTIONS WITH_WPILIB REQUIRES WITH_NTCORE WITH_WPIMATH)
wpilib_config(OPTIONS WITH_WPIMATH WITH_JAVA REQUIRES WITH_WPIUNITS)
set(include_dest include)
set(java_lib_dest java)
if(WITH_JAVA OR WITH_JAVA_SOURCE)
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
find_package(Java REQUIRED COMPONENTS Development)
if(NOT ANDROID)
find_package(JNI REQUIRED COMPONENTS JVM)
endif()
endif()
if(WITH_DOCS)
find_package(Doxygen REQUIRED)
@@ -115,6 +151,14 @@ endif()
find_package(LIBSSH CONFIG 0.7.1)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
if(WITH_PROTOBUF)
set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "" FORCE)
find_package(Protobuf REQUIRED)
find_program(PROTOC_COMPILER protoc REQUIRED)
endif()
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF)
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(isMultiConfig)
@@ -232,9 +276,6 @@ if(WITH_TESTS)
enable_testing()
add_subdirectory(thirdparty/googletest)
include(GoogleTest)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/catch2/extras")
add_subdirectory(thirdparty/catch2)
include(Catch)
endif()
if(USE_SYSTEM_LIBUV)
@@ -247,13 +288,11 @@ endif()
set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)")
set(SELF_DIR "$\{SELF_DIR\}")
set(WPIUNITS_DEP_REPLACE_IMPL "find_dependency(wpiunits)")
set(WPIANNOTATIONS_DEP_REPLACE_IMPL "find_dependency(wpiannotations)")
set(WPIUTIL_DEP_REPLACE "find_dependency(wpiutil)")
set(DATALOG_DEP_REPLACE "find_dependency(datalog)")
add_subdirectory(wpiutil)
add_subdirectory(datalog)
if(WITH_NTCORE)
set(NTCORE_DEP_REPLACE "find_dependency(ntcore)")
set(WPINET_DEP_REPLACE "find_dependency(wpinet)")
@@ -262,22 +301,37 @@ if(WITH_NTCORE)
endif()
if(WITH_WPIMATH)
if(WITH_JAVA)
set(WPIUNITS_DEP_REPLACE ${WPIUNITS_DEP_REPLACE_IMPL})
add_subdirectory(wpiunits)
endif()
set(WPIMATH_DEP_REPLACE "find_dependency(wpimath)")
add_subdirectory(wpimath)
endif()
if(WITH_JAVA)
add_subdirectory(wpiannotations)
endif()
if(WITH_WPIUNITS AND NOT WITH_WPIMATH)
# In case of building wpiunits standalone
set(WPIUNITS_DEP_REPLACE ${WPIUNITS_DEP_REPLACE_IMPL})
add_subdirectory(wpiunits)
endif()
if(WITH_GUI)
add_subdirectory(fields)
add_subdirectory(fieldImages)
add_subdirectory(thirdparty/imgui_suite)
add_subdirectory(wpigui)
add_subdirectory(glass)
add_subdirectory(tools/outlineviewer)
add_subdirectory(tools/sysid)
add_subdirectory(outlineviewer)
add_subdirectory(sysid)
if(WITH_WPICAL)
add_subdirectory(tools/wpical)
add_subdirectory(wpical)
endif()
if(LIBSSH_FOUND)
add_subdirectory(tools/datalogtool)
add_subdirectory(roborioteamnumbersetter)
add_subdirectory(datalogtool)
endif()
endif()
@@ -297,10 +351,14 @@ endif()
if(WITH_WPILIB)
set(APRILTAG_DEP_REPLACE "find_dependency(apriltag)")
set(WPILIBC_DEP_REPLACE "find_dependency(wpilibc)")
set(COMMAND_DEP_REPLACE "find_dependency(commandsv2)")
if(WITH_JAVA)
set(WPILIBJ_DEP_REPLACE "find_dependency(wpilibj)")
endif()
set(WPILIBNEWCOMMANDS_DEP_REPLACE "find_dependency(wpilibNewCommands)")
add_subdirectory(apriltag)
add_subdirectory(wpilibj)
add_subdirectory(wpilibc)
add_subdirectory(commandsv2)
add_subdirectory(wpilibNewCommands)
add_subdirectory(romiVendordep)
add_subdirectory(xrpVendordep)
if(WITH_EXAMPLES)

View File

@@ -24,6 +24,27 @@
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache"
}
},
{
"name": "with-java",
"displayName": "",
"description": "Ninja config with Java",
"generator": "Ninja",
"binaryDir": "build-cmake",
"cacheVariables": {
"WITH_JAVA": "ON"
}
},
{
"name": "with-java-and-sccache",
"displayName": "",
"description": "Ninja config with Java and sccache",
"generator": "Ninja",
"inherits": "with-sccache",
"binaryDir": "build-cmake",
"cacheVariables": {
"WITH_JAVA": "ON"
}
}
]
}

View File

@@ -4,7 +4,6 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
- [General Contribution Rules](#general-contribution-rules)
- [What to Contribute](#what-to-contribute)
- [Design Philosophy](#design-philosophy)
- [Contribution Process](#contribution-process)
- [Coding Guidelines](#coding-guidelines)
- [Submitting Changes](#submitting-changes)
@@ -14,41 +13,29 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
## General Contribution Rules
- Everything in the library must work for the 14000+ teams that will be using it.
- Everything in the library must work for the 4000+ teams that will be using it.
- We need to be able to maintain submitted changes, even if you are no longer working on the project.
- Tool suite changes must be generally useful to a broad range of teams
- Excluding bug fixes, changes in one language generally need to have corresponding changes in other languages.
- Some features, such the addition of C++26 for WPILibC or Functional Interfaces for WPILibJ, are specific to that version of WPILib only. New language features added to C++ must be wrappable in Python for [RobotPy](https://github.com/robotpy).
- Some features, such the addition of C++23 for WPILibC or Functional Interfaces for WPILibJ, are specific to that version of WPILib only. New language features added to C++ must be wrappable in Python for [RobotPy](https://github.com/robotpy).
- Substantial changes often need to have corresponding LabVIEW changes. To do this, we will work with NI on these large changes.
- Changes should have tests.
- Code should be well documented.
- This involves writing tutorials and/or usage guides for your submitted feature. These articles are then hosted on the [WPILib](https://docs.wpilib.org/) documentation website. See the [wpilib-docs repository](https://github.com/wpilibsuite/wpilib-docs) for more information.
- This involves writing tutorials and/or usage guides for your submitted feature. These articles are then hosted on the [WPILib](https://docs.wpilib.org/) documentation website. See the [frc-docs repository](https://github.com/wpilibsuite/frc-docs) for more information.
## What to Contribute
- Bug reports and fixes
- We will generally accept bug fixes without too much question. If they are only implemented for one language, we will implement them for any other necessary languages. Bug reports are also welcome, please submit them to our GitHub issue tracker.
- While we do welcome improvements to the API, there are a few important rules to consider:
- Features must be added to Java (WPILibJ), C++ (WPILibC), and Python with rare exceptions.
- Most of Python (RobotPy) is created by wrapping WPILibC with pybind11 via semiwrap. In general, new user-facing functions or classes should have the proper wrapper configs updated, typically located in a YAML file with the same name as the header. See the [in-repo RobotPy README](./README-RobotPy.md) for more info and how to partially auto-update the configs. However, the command framework is reimplemented in Python, and requires code to be ported instead of being wrapped via semiwrap.
- During competition season, we will not merge any new feature additions or removals. We want to ensure that the API is stable during the season to help minimize issues for teams.
- Features must be added to Java (WPILibJ), C++ (WPILibC), with rare exceptions.
- Most of Python (RobotPy) is created by wrapping WPILibC with pybind11 via robotpy-build. However, new features to the command framework should also be submitted to [robotpy-commands-v2](https://github.com/robotpy/robotpy-commands-v2) as the command framework is reimplemented in Python.
- During competition season, we will not merge any new feature additions. We want to ensure that the API is stable during the season to help minimize issues for teams.
- Ask about large changes before spending a bunch of time on them! See [Contribution Process](#contribution-process) for where to ask.
- Features that make it easier for teams with less experience to be more successful are more likely to be accepted.
- Features in WPILib should be broadly applicable to all teams. Anything that is team specific should not be submitted.
- As a rule, we are happy with the general structure of WPILib. We are not interested in major rewrites of all of WPILib. We are open to talking about ideas, but backwards compatibility is very important for WPILib, so be sure to keep this in mind when proposing major changes.
- While the library may contain support for specific sensors, these are typically items contained in the FIRST Robotics Competition Kit of Parts or commonly used hardware identified by FIRST or core WPILib Developers. If you think a certain sensor should be supported in WPILib, you may submit an issue justifying the reasons why it should be supported and approval will be determined by FIRST or core WPILib Developers. If you are a company interested in getting a sensor into the Kit of Parts, please contact FIRST directly at frcparts@firstinspires.org.
## Design Philosophy
WPILib's general design philosophy strays far away from the traditional Object-Oriented Programming architectures dominant in enterprise codebases. The general points to follow for WPILib are as follows:
- Prefer functions and composition over inheritance. Inheritance is rigid and often prevents evolution, as adding or removing methods from an inherited class risks breakage. For similar reasons, functional interfaces (`std::function` in C++) are preferred over actual interfaces.
- Avoid opaque black-boxes of functionality. Classes like RamseteCommand or HolonomicDriveController (both removed in 2027) are good examples of this. While they look like a good abstraction that helps beginners, the black-box nature means they are [difficult to debug](https://github.com/wpilibsuite/allwpilib/issues/3350) and it's impossible to instrument the internals to figure out what's going on, or they are extremely clunky to use compared to composing the individual components (thus defeating the point of abstracting it away; SwerveControllerCommand construction was [a huge pile of opaque arguments glued together](https://github.com/wpilibsuite/allwpilib/blob/v2026.2.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/RobotContainer.java#L104-L114)). Composition is strongly preferred, with strong documentation and examples describing how to do that composition.
- Error at compile time, not runtime. Despite our best efforts, there will always be people who don't read stack traces (understandable for beginner programmers). Compile time errors show up in builds and in an IDE, which is much easier and faster for people to pinpoint and debug. Use language features to make invalid code impossible to build.
- The Matrix class in Java is an example of this. While clunky due to Java's weak generics system, it enforces correct Matrix dimensions at compile time, with the MatBuilder factory method throwing if the array passed in is the wrong size, which leads to the next point:
- Try to only throw exceptions at code startup, and only for things that are obviously incorrect. Robots shouldn't quit, and it's a real "feels bad" moment when yours does, especially in a match. It's oftentimes better to have a robot continue running when it sees nonsensical state as opposed to outright crashing, since other components are often still functional. If you can't make invalid code a compile time error, throwing at the start of the robot program is the next best solution, but avoid throwing in functions likely to be called throughout a robot's runtime.
- Sometimes the behavior of functions are just incorrect if invalid data is passed in, and throwing is one of the only options. This is a judgement call, but if there are no other options, throwing can be okay.
- An alternative to throwing is logging an error, typically with [the Alerts framework](https://docs.wpilib.org/en/latest/docs/software/telemetry/persistent-alerts.html); this is a good choice for runtime errors. Also see https://github.com/wpilibsuite/allwpilib/issues/6766 for an example of not throwing exceptions, but simply logging an error on invalid data.
- Note that hardware configuration issues such as a sensor not existing isn't necessarily obviously incorrect; it could be that the wrong port was specified, but it could also be unplugged due to external factors. Throwing just because it's unplugged can make for a "feels bad" moment, and should be avoided.
- Generally speaking, we do not accept code for specific sensors. We have to be able to test the sensor in hardware on the WPILib test bed. Additionally, hardware availability for teams is important. Therefore, as a general rule, the library only directly supports hardware that is in the Kit of Parts. If you are a company interested in getting a sensor into the Kit of Parts, please contact FIRST directly at frcparts@firstinspires.org.
## Contribution Process
@@ -70,7 +57,7 @@ While the library should be fully formatted according to the styles, additional
Changes should be submitted as a Pull Request against the main branch of WPILib. For most changes, commits will be squashed upon merge. For particularly large changes, multiple commits are ok, but assume one commit unless asked otherwise. We may ask you to break a PR into multiple standalone PRs or commits for rebase within one PR to separate unrelated changes. No change will be merged unless it is up to date with the current main branch. We do this to make sure that the git history isn't too cluttered.
Particularly large and/or breaking changes should be targeted to the 2027 branch, which targets the [Systemcore Robot Controller](https://community.firstinspires.org/introducing-the-future-mobile-robot-controller). The intent is minimize changes for 2026, to allow development to focus on preparing for 2027.
Particularly large and/or breaking changes should be targeted to the 2027 branch, which targets the [SystemCore Robot Controller](https://community.firstinspires.org/introducing-the-future-mobile-robot-controller). The intent is minimize changes for 2026, to allow development to focus on preparing for 2027.
### Merge Process

View File

@@ -13,12 +13,13 @@ This article contains instructions on building projects using a development buil
Development builds are the per-commit build hosted every time a commit is pushed to the [allwpilib](https://github.com/wpilibsuite/allwpilib/) repository. These builds are then hosted on [artifactory](https://frcmaven.wpi.edu/artifactory/webapp/#/home).
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2027 GradleRIO version, ie `2027.0.0-alpha-5`
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2026 GradleRIO version, ie `2026.1.1`
```groovy
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = 'YEAR.+'
wpi.versions.wpimathVersion = 'YEAR.+'
```
The top of your ``build.gradle`` file should now look similar to the code below. Ignore any differences in versions.
@@ -27,12 +28,13 @@ Java
```groovy
plugins {
id "java"
id "org.wpilib.GradleRIO" version "2027.0.0-alpha-5"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = '2027.+'
wpi.versions.wpilibVersion = '2026.+'
wpi.versions.wpimathVersion = '2026.+'
```
C++
@@ -40,12 +42,13 @@ C++
plugins {
id "cpp"
id "google-test-test-suite"
id "org.wpilib.GradleRIO" version "2027.0.0-alpha-5"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = '2027.+'
wpi.versions.wpilibVersion = '2026.+'
wpi.versions.wpimathVersion = '2026.+'
```
### Development Build Documentation
@@ -61,12 +64,13 @@ Java
```groovy
plugins {
id "java"
id "org.wpilib.GradleRIO" version "2027.0.0-alpha-5"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false
wpi.maven.useWpilibMavenLocalDevelopment = true
wpi.maven.useFrcMavenLocalDevelopment = true
wpi.versions.wpilibVersion = 'YEAR.424242.+'
wpi.versions.wpimathVersion = 'YEAR.424242.+'
```
C++
@@ -74,14 +78,15 @@ C++
plugins {
id "cpp"
id "google-test-test-suite"
id "org.wpilib.GradleRIO" version "2027.0.0-alpha-5"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false
wpi.maven.useWpilibMavenLocalDevelopment = true
wpi.maven.useFrcMavenLocalDevelopment = true
wpi.versions.wpilibVersion = 'YEAR.424242.+'
wpi.versions.wpimathVersion = 'YEAR.424242.+'
```
# Systemcore Development
# roboRIO Development
See the [developerRobot](developerRobot/README.md) subproject.

View File

@@ -6,9 +6,9 @@ The Python script used to generate a subproject's files will always be located i
The templates will be located under `subproject/src/generate/main`, and generated files will be located under `subproject/src/generated/main`.
If the generated file is for C++, the hierarchy should be symmetrical, so if a generated header is located under `subproject/src/generated/main/native/include/wpi/header.h`, the template to generate it should be located under `subproject/src/generate/main/native/include/wpi/template.h.jinja`. You should pretend like `subproject/src/generate/main` is just like `subproject/src/main`, in that the file hierarchy must make sense if the files weren't generated, e.g, headers that would go in `subproject/src/main/native/include/blah` should be in `subproject/src/generated/main/native/include/blah`.
If the generated file is for C++, the hierarchy should be symmetrical, so if a generated header is located under `subproject/src/generated/main/native/include/frc/header.h`, the template to generate it should be located under `subproject/src/generate/main/native/include/frc/template.h.jinja`. You should pretend like `subproject/src/generate/main` is just like `subproject/src/main`, in that the file hierarchy must make sense if the files weren't generated, e.g, headers that would go in `subproject/src/main/native/include/blah` should be in `subproject/src/generated/main/native/include/blah`.
If the generated file is for Java, templates should be located under `subproject/src/generate/main/java`, and the hierarchy for output files should reflect the declared package of the output Java files. For example, a Jinja template at `subproject/src/main/java/template.java.jinja` with the package `org.wpilib.wpilibj` would be used to generate Java files located at `subproject/src/generated/main/java/org/wpilib/wpilibj`
If the generated file is for Java, templates should be located under `subproject/src/generate/main/java`, and the hierarchy for output files should reflect the declared package of the output Java files. For example, a Jinja template at `subproject/src/main/java/template.java.jinja` with the package `edu.wpi.first.wpilibj` would be used to generate Java files located at `subproject/src/generated/main/java/edu/wpi/first/wpilibj`
The JSON files live under `subproject/src/generate` since they apply to both languages. One unique case is JSON files that are used by multiple subprojects, currently only JSON files shared by wpilibc and wpilibj. In that specific case, the JSON files will always be located in wpilibj since Java is the most used language.
@@ -23,6 +23,3 @@ To maintain consistency with other Python scripts, copy an existing `generate_*.
### (Re)Generating files and committing them
Once your Python script is complete, you can run `python generate_<thing>.py` to generate the files. Once you're finished with your files, commit these files to Git. If you regenerated the files and Git indicates the files have changed, but the diff doesn't show any changes, only the line endings have changed. If you expected changes to the generated code, you didn't correctly make changes. If you didn't expect changes, you can ignore this and discard the changes. Also ensure that you've marked the Python script as executable, since this is necessary for CI workflows to run your scripts. To add your script to the CI workflows, edit [.github/workflows/pregen_all.py](.github/workflows/pregen_all.py), and add your script alongside the rest of the scripts.
#### (Re)Generating QuickBuffers files
Regenerating QuickBuffers files requires the Protocol Buffers compiler (protoc), and the QuickBuffers plugin, which can be found on [their releases page](https://github.com/HebiRobotics/QuickBuffers/releases). Once you have both, you can pass their paths into the generation scripts with `--protoc` and `--quickbuf_plugin` and regenerate the files.

View File

@@ -1,23 +1,23 @@
## Publishing Third Party Dependencies
Currently the 3rd party external deps are opencv, libssh, ceres, and gtsam.
Currently the 3rd party deps are imgui, opencv, google test, libssh, and apriltaglib
For publishing these dependencies, the version needs to be manually updated in the publish.gradle file of their respective repository.
Then, upload a new tag for the dependency you want to build for, which will automatically start a build.
The CI workflow should set `RUN_AZURE_ARTIFACTORY_RELEASE` to `true` on tagged runs. Then when the pipeline gets started, the final build outputs will be uploaded to artifactory.
Then, in the azure build for the dependency you want to build for, manually start a pipeline build (As of current, this is the `Run Pipeline` button).
A variable needs to be added called `RUN_AZURE_ARTIFACTORY_RELEASE`, with a value of `true`. Then when the pipeline gets started, the final build outputs will be updated to artifactory.
To use newer versions of C++ dependencies, in `shared/config.gradle`, update the version related to the specific dependency.
For Java dependencies, there is likely a file related to the specific dependency in the shared folder. Update the version in there.
Note, changing artifact locations requires updating the `native-utils` plugin; specifically, the `configureDependencies` method in the `WPINativeUtilsExtension` class.
Note, changing artifact locations (This includes changing the artifact year currently, I have an issue open to change this) requires updating the `native-utils` plugin
## Publishing allwpilib
allwpilib publishes to the development repo on every push to main. To publish a release build, upload a new tag, and a release will automatically be built and published.
### Adding a new robot code dependency/subproject
If a new subproject has been added that is meant for use from robot code, both GradleRIO and the `native-utils` plugin need to be updated. `native-utils` is updated in the same way as 3rd party dependencies. For GradleRIO, update the `WPIJavaDepsExtension` to contain your new subproject's artifacts.
## Publishing desktop tools
Desktop tools publish to the development repo on every push to main. To publish a release build, upload a new tag, and a release will automatically be built and published.
## Publishing VS Code
Before publishing, make sure to update the GradleRIO version in `vscode-wpilib/resources/gradle/version.txt`. Also make sure the Gradle Wrapper version matches the wrapper required by GradleRIO.
Before publishing, make sure to update the gradlerio version in `vscode-wpilib/resources/gradle/version.txt` Also make sure the gradle wrapper version matches the wrapper required by gradlerio.
Upon pushing a tag, a release will be built, and the files will be uploaded to the releases on GitHub.
## Publishing GradleRIO

View File

@@ -1,129 +0,0 @@
module(
name = "allwpilib",
version = "0.0.0",
)
include("//docs:doxygen.MODULE.bazel")
include("//shared/bazel/thirdparty/ceres:ceres.MODULE.bazel")
include("//shared/bazel/thirdparty/libssh:libssh.MODULE.bazel")
include("//shared/bazel/thirdparty/mrclib:mrclib.MODULE.bazel")
bazel_dep(name = "apple_support", version = "2.0.0", repo_name = "build_bazel_apple_support")
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "rules_pkg", version = "1.1.0")
bazel_dep(name = "bazel_features", version = "1.33.0")
bazel_dep(name = "aspect_bazel_lib", version = "2.14.0")
bazel_dep(name = "bazel_skylib", version = "1.8.2")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_java", version = "8.16.1")
bazel_dep(name = "rules_python", version = "1.7.0")
bazel_dep(name = "rules_pycross", version = "0.8.1")
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(
python_version = "3.13",
)
use_repo(
python,
"python_3_13",
"python_3_13_x86_64-unknown-linux-gnu",
)
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
pip.parse(
hub_name = "allwpilib_pip_deps",
python_version = "3.13",
requirements_lock = "//:requirements_lock.txt",
requirements_windows = "//:requirements_windows_lock.txt",
)
use_repo(pip, "allwpilib_pip_deps")
bazel_dep(name = "rules_jvm_external", version = "6.8")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
name = "maven",
artifacts = [
"org.ejml:ejml-simple:0.44.0",
"io.avaje:avaje-jsonb:3.14",
"io.avaje:avaje-jsonb-generator:3.14",
"us.hebi.quickbuf:quickbuf-runtime:1.4",
"com.google.code.gson:gson:2.13.1",
"org.wpilib.thirdparty.opencv:opencv-java:2027-4.13.0-1",
"org.junit.jupiter:junit-jupiter:5.10.1",
"org.junit.platform:junit-platform-console:1.10.1",
"org.junit.platform:junit-platform-launcher:1.10.1",
"org.junit.platform:junit-platform-reporting:1.10.1",
"org.hamcrest:hamcrest-all:1.3",
"com.googlecode.junit-toolbox:junit-toolbox:2.4",
"org.apache.ant:ant:1.10.12",
"org.apache.ant:ant-junit:1.10.12",
"org.mockito:mockito-core:4.1.0",
"com.google.testing.compile:compile-testing:0.21.0",
],
lock_file = "//:maven_install.json",
repositories = [
"https://repo1.maven.org/maven2",
"https://frcmaven.wpi.edu/artifactory/release/",
],
)
use_repo(maven, "maven", "unpinned_maven")
bazel_dep(name = "caseyduquettesc_rules_python_pytest", version = "1.1.1", repo_name = "rules_python_pytest")
bazel_dep(name = "eigen", version = "5.0.1")
local_path_override(
module_name = "eigen",
path = "wpimath/src/main/native/thirdparty/eigen/include",
)
bazel_dep(name = "rules_license", version = "1.0.0")
http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
http_file(
name = "quickbuffer_protoc_linux",
executable = True,
sha256 = "02767f5f662720c121dfaaa9109aefa9b0b1e2415cbe1af9e4713a7b00663696",
urls = ["https://repo1.maven.org/maven2/us/hebi/quickbuf/protoc-gen-quickbuf/1.3.3/protoc-gen-quickbuf-1.3.3-linux-x86_64.exe"],
)
http_file(
name = "quickbuffer_protoc_osx_x86-64",
executable = True,
sha256 = "cdc0b4b188f944dddd4430bcd6f6266ca1aaf8c33940d7bee1ed63fc962d8525",
urls = ["https://repo1.maven.org/maven2/us/hebi/quickbuf/protoc-gen-quickbuf/1.3.3/protoc-gen-quickbuf-1.3.3-osx-x86_64.exe"],
)
http_file(
name = "quickbuffer_protoc_osx_aarch64",
executable = True,
sha256 = "95fa0a22ad2d9fa8f84a31cf3a0670b18c0ad954f73035f60cfb77c2814fb41c",
urls = ["https://repo1.maven.org/maven2/us/hebi/quickbuf/protoc-gen-quickbuf/1.3.3/protoc-gen-quickbuf-1.3.3-osx-aarch_64.exe"],
)
http_file(
name = "quickbuffer_protoc_windows",
executable = True,
sha256 = "8a30b2eaebefbedc3b592118670de1ab6ea1aa19dcf8cdab220e25485ccd4a0a",
urls = ["https://repo1.maven.org/maven2/us/hebi/quickbuf/protoc-gen-quickbuf/1.3.3/protoc-gen-quickbuf-1.3.3-windows-x86_64.exe"],
)
bazel_dep(name = "rules_bzlmodrio_toolchains", version = "2025-1.bcr6")
archive_override(
module_name = "rules_bzlmodrio_toolchains",
integrity = "sha256-ECtFB2KOlySwweRBcndiw0TkAXD2WsYFFhaBeOozqJo=",
urls = ["https://github.com/wpilibsuite/rules_bzlmodrio_toolchains/releases/download/2025-1.bcr6/rules_bzlmodrio_toolchains-2025-1.bcr6.tar.gz"],
)
bazel_dep(name = "protobuf", version = "32.1", repo_name = "com_google_protobuf")
http_jar = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_jar")
http_jar(
name = "com_github_google_copybara",
integrity = "sha256-IHW6y6WXJFjX9RYD+IwVAMwAbEo36fLqonIKR+FaqpQ=",
urls = ["https://github.com/google/copybara/releases/download/v20251027/copybara_deploy.jar"],
)

1168
MODULE.bazel.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,10 +18,10 @@ The first types are Java artifacts. These are usually published as `jar` files.
Example:
```
org.wpilib.wpilibj:wpilibj-java:version
edu.wpi.first.wpilibj:wpilibj-java:version
```
The second types are native artifacts. These are usually published as `zip` files. The `-sources` and `-headers` classifiers contain the sources and headers respectively for the library. Each artifact also contains a classifier for each platform we publish. This platform is in the format `{os}{arch}`. The full list of supported platforms can be found in [native-utils in the Platforms nested class](https://github.com/wpilibsuite/native-utils/blob/main/src/main/java/org/wpilib/nativeutils/WPINativeUtilsExtension.java). If the library is built statically, it will have `static` appended to the classifier. Additionally, if the library was built in debug mode, `debug` will be appended to the classifier. The platform artifact only contains the binaries for a specific platform. Note that the binary artifacts never contain the headers, you always need the `-headers` classifier to get those.
The second types are native artifacts. These are usually published as `zip` files. The `-sources` and `-headers` classifiers contain the sources and headers respectively for the library. Each artifact also contains a classifier for each platform we publish. This platform is in the format `{os}{arch}`. The full list of supported platforms can be found in [native-utils](https://github.com/wpilibsuite/native-utils/blob/main/src/main/java/edu/wpi/first/nativeutils/WPINativeUtilsExtension.java#L94). If the library is built statically, it will have `static` appended to the classifier. Additionally, if the library was built in debug mode, `debug` will be appended to the classifier. The platform artifact only contains the binaries for a specific platform. Note that the binary artifacts never contain the headers, you always need the `-headers` classifier to get those.
If the library is Java and C++ and has a JNI component, the native artifact will have a shared library containing JNI entrypoints alongside the C++ shared library. This JNI shared library will have a `jni` suffix in the file name.
@@ -29,8 +29,8 @@ Native artifacts are published with the base artifact name as their artifact ID,
Example:
```
org.wpilib.wpimath:wpimath-cpp:version:classifier@zip
org.wpilib.wpimath:wpimath-cpp:version:windowsx86-64staticdebug@zip
edu.wpi.first.wpimath:wpimath-cpp:version:classifier@zip
edu.wpi.first.wpimath:wpimath-cpp:version:windowsx86-64staticdebug@zip
```
## Provided Artifacts
@@ -38,7 +38,7 @@ This repository provides the following artifacts. Below each artifact is its dep
For C++, if building with static dependencies, the listed order should be the link order in your linker.
All artifacts are based at `org.wpilib.artifactname` in the repository.
All artifacts are based at `edu.wpi.first.artifactname` in the repository.
* wpiutil
@@ -52,33 +52,32 @@ All artifacts are based at `org.wpilib.artifactname` in the repository.
* wpiutil
* ntcore
* wpinet
* wpiutil
* wpinet
* glass/libglass
* wpigui
* wpimath
* wpiutil
* wpimath
* wpigui
* glass/libglassnt
* wpigui
* ntcore
* wpinet
* wpimath
* wpiutil
* wpinet
* ntcore
* wpimath
* wpigui
* hal
* ntcore
* wpiutil
* halsim
* libglassnt
* libglass
* wpiutil
* wpinet
* ntcore
* wpimath
* wpigui
* wpinet
* wpiutil
* libglass
* libglassnt
* cscore
* opencv
@@ -109,7 +108,7 @@ All artifacts are based at `org.wpilib.artifactname` in the repository.
* wpinet
* wpiutil
* commandsv2
* wpilibNewCommands
* wpilibc
* hal
* cameraserver
@@ -127,16 +126,12 @@ All artifacts are based at `org.wpilib.artifactname` in the repository.
### Third Party Artifacts
This repository provides the builds of the following third party software:
This repository provides the builds of the following third party software.
All artifacts are based at `edu.wpi.first.thirdparty.frcYEAR` in the repository.
* apriltaglib
* googletest
* imgui
Other software can be found in their corresponding GitHub repositories:
* ceres: https://github.com/wpilibsuite/thirdparty-ceres
* gtsam: https://github.com/wpilibsuite/thirdparty-gtsam
* opencv: https://github.com/wpilibsuite/thirdparty-opencv
* libssh: https://github.com/wpilibsuite/thirdparty-libssh
All artifacts are based at `org.wpilib.thirdparty` in the repository.
* opencv
* libssh

View File

@@ -1,10 +1,10 @@
# WPILib Bazel Support
WPILib is normally built with Gradle, but [Bazel](https://www.bazel.build/) can also be used to increase development speed due to the superior caching ability and the ability to use remote caching and remote execution (on select platforms).
WPILib is normally built with Gradle, but [Bazel](https://www.bazel.build/) can also be used to increase development speed due to the superior caching ability and the ability to use remote caching and remote execution (on select platforms)
## Prerequisites
- Install [Bazelisk](https://github.com/bazelbuild/bazelisk/releases) and add it to your path. Bazelisk is a wrapper that will download the correct version of Bazel specified in the repository. Note: You can alias/rename the binary to `bazel` if you want to keep the familiar `bazel build` vs `bazelisk build` syntax.
- Install [Bazelisk](https://github.com/bazelbuild/bazelisk/releases) and add it to your path. Bazelisk is a wrapper that will download the correct version of bazel specified in the repository. Note: You can alias/rename the binary to `bazel` if you want to keep the familiar `bazel build` vs `bazelisk build` syntax.
## Building
To build the entire repository, simply run `bazel build //...`. To run all of the unit tests, run `bazel test //...`
@@ -14,42 +14,12 @@ Other examples:
- `bazel coverage //wpiutil/...` - (*Nix only) - Runs a code coverage report for both C++ and Java on all the targets under wpiutil
## User settings
When invoking Bazel, it will check if `user.bazelrc` exists for additional, user specified flags. You can use these settings to do things like always ignore builds in a specific folder, or limiting the CPU/RAM usage during a build.
When invoking bazel, it will check if `user.bazelrc` exists for additional, user specified flags. You can use these settings to do things like always ignore buildin a specific folder, or limiting the CPU/RAM usage during a build.
Examples:
- `build --build_tag_filters=-wpi-example` - Do not build any targets tagged with `wpi-example` (Currently all of the targets in wpilibcExamples and wpilibjExamples contain this tag)
- `build -c opt` - Always build optimized targets. The default compiler flags were chosen to build as fast as possible, and thus don't contain many optimizations
- `build -k` - `-k` is analogous to the MAKE flag `--keep-going`, so the build will not stop on the first error.
- ```
build --local_resources=memory=HOST_RAM*.5 # Don't use more than half my RAM when building
build --local_resources=cpu=HOST_CPUS-1 # Leave one core alone
build --local_ram_resources=HOST_RAM*.5 # Don't use more than half my RAM when building
build --local_cpu_resources=HOST_CPUS-1 # Leave one core alone
```
Bazel's RAM usage estimation is simplistic and hardcoded, so limiting the allowed number of CPU cores is the best way to reduce memory usage if it becomes a problem.
The default settings build all the release artifact variants relevant for your platform. The overall list of options ends up being, essentially, all the variants of (linux, osx, windows) x (debug, release) x (static, shared) x (aarch64, x86). OSX and Windows are hard to compile for from any other OS, so we by default build for your local OS and the system core, with all the variants.
This can be a bit expensive. If you would like to build a subset, you can specify the repo environmental variable, `WPI_PUBLISH_CLASSIFIER_FILTER`, and pick what you build for. The default is, in the .bazelrc file,
```
common --repo_env="WPI_PUBLISH_CLASSIFIER_FILTER=headers,sources,linuxsystemcore,linuxsystemcoredebug,linuxsystemcorestatic,linuxsystemcorestaticdebug,linuxx86-64,linuxx86-64debug,linuxx86-64static,linuxx86-64staticdebug,osxuniversal,osxuniversaldebug,osxuniversalstatic,osxuniversalstaticdebug,windowsarm64,windowsarm64debug,windowsarm64static,windowsarm64staticdebug,windowsx86-64,windowsx86-64debug,windowsx86-64static,windowsx86-64staticdebug"
```
Modify this to your likings if you want to build less.
## Pregenerating Files
allwpilib uses extensive use of pre-generating files that are later used to build C++ / Java libraries that are tracked by version control. Quite often,
these pre-generation scripts use some configuration file to create multiple files inside of an output directory. While this process could be accomplished
with a `genrule` that would require an explicit listing of every output file, which would be tedious to maintain as well as potentially confusing to people
adding new features those libraries. Therefore, we use `@aspect_bazel_lib` and their `write_source_files` feature to generate these directories. In the event that the generation process creates more than a small handful of predictable files, a custom rule is written to generate the directory.
## Remote Caching
One of the huge benefits of Bazel is its remote caching ability. However, due to Bazel's strict build definitions, it is hard to share remote cache artifacts between different computers unless our toolchains are fully hermetic, which means you are unlikely to be able to reuse the cache artifacts published from the `main` branch on your local machine like you might be able to with the `gradle` or `cmake` caches. Luckily, the GitHub Actions CI machines are generally stable between runs and can reuse cache artifacts, and your local machine should remain stable, so if you set up a free buildbuddy account you can have your fork's CI actions be able to use a personalized cache, as well as your local machine.
For the main `allwpilib` upstream, the cache is only updated on the main branch; pull requests from forks will not be able to modify the cache. However, you can set up your fork to enable its own cache by following the steps below.
### Setting Up API keys
Follow the [buildbuddy authentication](https://www.buildbuddy.io/docs/guide-auth) guide to create keys. For your local machine, it is recommended that you place the following configuration line in either a `user.bazelrc` or `bazel_auth.rc` file in the repositories root directory.
```
build --remote_header=<your api key>
```
To get your forks CI actions using your own buildbuddy cache, follow [GitHub's](https://docs.github.com/en/actions/how-tos/security-for-github-actions/security-guides/using-secrets-in-github-actions) documentation for setting up a repository secret. The secrets key should be `BUILDBUDDY_API_KEY`, and the value should be your buildbuddy API key.

View File

@@ -1,22 +1,22 @@
# WPILib CMake Support
WPILib is normally built with Gradle, however for some systems, such as Linux based coprocessors, Gradle doesn't work correctly, especially if cscore is needed, which requires OpenCV. Furthermore, the CMake build can be used for C++ development because it provides better build caching compared to Gradle. We provide the CMake build for these cases. Although macOS is supported, these docs will only go over Linux and Windows builds, but should mostly work for macOS as well. If you are stuck, you can look at the GitHub workflows for any OS to see how it works. The CMake build does not build Java or Python, only C++.
WPILib is normally built with Gradle, however for some systems, such as Linux based coprocessors, Gradle doesn't work correctly, especially if cscore is needed, which requires OpenCV. Furthermore, the CMake build can be used for C++ development because it provides better build caching compared to Gradle. We provide the CMake build for these cases. Although macOS is supported, these docs will only go over Linux and Windows builds, but should mostly work for macOS as well. If you are stuck, you can look at the GitHub workflows for any OS to see how it works.
## Libraries that get built
* apriltag
* cameraserver
* commandsv2
* cscore
* datalog
* fields
* fieldImages
* hal (simulation HAL only)
* ntcore
* romiVendordep
* simulation extensions
* wpigui
* wpilib (wpilibc, wpilibj, and developerRobot)
* wpilib (wpilibc, wpilibj, and myRobot)
* wpilibNewCommands
* wpimath
* wpinet
* wpiunits
* wpiutil
* xrpVendordep
@@ -24,28 +24,38 @@ WPILib is normally built with Gradle, however for some systems, such as Linux ba
* datalogtool
* glass
* outlineviewer
* roborioteamnumbersetter
* sysid
* wpical
* halsim_gui (if simulation extensions are enabled)
By default, all libraries get built with a default CMake setup. The libraries are built as shared libraries. Data Log Tool is only built if libssh is available.
By default, all libraries get built with a default CMake setup. The libraries are built as shared libraries, and include the JNI libraries as well as building the Java JARs. Data Log Tool and the roboRIO Team Number Setter are only built if libssh is available.
## Prerequisites
The protobuf library and compiler are needed for protobuf generation.
OpenCV needs to be findable by CMake. On systems like the Jetson, this is installed by default. Otherwise, you will need to build OpenCV from source and install it.
If you want JNI and Java, you will need a JDK of at least version 17 installed. In addition, you need a `JAVA_HOME` environment variable set properly and set to the JDK directory.
If you are building with unit tests or simulation modules, you will also need an Internet connection for the initial setup process, as CMake will clone google-test and imgui from GitHub.
## Build Options
The following build options are available:
* `BUILD_SHARED_LIBS` (ON Default)
* This option will cause CMake to build static libraries instead of shared libraries.
* This option will cause CMake to build static libraries instead of shared libraries. If this is off, `WITH_JAVA` must be off. Otherwise CMake will error.
* `WITH_CSCORE` (ON Default)
* This option will cause cscore to be built. Turning this off will implicitly disable cameraserver. If this is off, the OpenCV build requirement is removed.
* `WITH_EXAMPLES` (OFF Default)
* This option will build C++ examples.
* `WITH_GUI` (ON Default)
* This option will build GUI items. If this is off, and `WITH_SIMULATION_MODULES` is on, the simulation GUI will not be built.
* `WITH_JAVA` (OFF Default)
* This option will enable Java and JNI builds. If this is on, `BUILD_SHARED_LIBS` must be on. Otherwise CMake will error.
* `WITH_JAVA_SOURCE` (`WITH_JAVA` Default)
* This option will build Java source JARs for each enabled Java library. This does not require `WITH_JAVA` to be on, allowing source JARs to be built without the compiled JARs if desired.
* `WITH_NTCORE` (ON Default)
* This option will cause ntcore to be built. Turning this off will implicitly disable wpinet, and will cause an error if `WITH_WPILIB` is enabled.
* `WITH_SIMULATION_MODULES` (ON Default)
@@ -53,9 +63,15 @@ The following build options are available:
* `WITH_TESTS` (ON Default)
* This option will build C++ unit tests. These can be run via `ctest -C <config>`, where `<config>` is the build configuration, e.g. `Debug` or `Release`.
* `WITH_WPILIB` (ON Default)
* This option will build the HAL and wpilibc during the build. The HAL is the simulation HAL, unless the external HAL options are used. The CMake build has no capability to build for Systemcore.
* This option will build the HAL and wpilibc/j during the build. The HAL is the simulation HAL, unless the external HAL options are used. The CMake build has no capability to build for the roboRIO.
* `WITH_WPIMATH` (ON Default)
* This option will build the wpimath library. This option must be on to build wpilib.
* `WITH_PROTOBUF` (ON Default)
* This option will build with the protobuf library.
* `WITH_WPIUNITS` (`WITH_JAVA` Default)
* This option will build the wpiunits library. This option must be on to build the Java wpimath library and requires `WITH_JAVA` to also be on.
* `OPENCV_JAVA_INSTALL_DIR`
* Set this option to the location of the archive of the OpenCV Java bindings (it should be called opencv-xxx.jar, with the x'es being version numbers). NOTE: set it to the LOCATION of the file, not the file itself!
* `NO_WERROR` (OFF Default)
* This option will disable the `-Werror` compilation flag for non-MSVC builds.
* `WPILIB_TARGET_WARNINGS`
@@ -75,13 +91,15 @@ If you want to change any of the options, add `-DOPTIONHERE=VALUE` to the `cmake
If you want, you can also use `ccmake` in order to visually set these properties as well. [Here](https://cmake.org/cmake/help/v3.0/manual/ccmake.1.html) is the link to the documentation for that program. On Windows, you can use `cmake-gui` instead.
Note that if you are cross-compiling, you will need to override the protobuf options manually to point to the libraries for the target platform. Leave the protoc binary location as the path to the binary for the host platform, since protoc needs to execute on the host platform.
## Presets
The WPILib CMake setup has a variety of presets for common configurations and options used. The default sets the generator to Ninja and build directory to `build-cmake`. The other preset is `sccache` (sets the C/C++ compiler launcher to sccache).
The WPILib CMake setup has a variety of presets for common configurations and options used. The default sets the generator to Ninja and build directory to `build-cmake`. The other presets are `with-java` (sets `WITH_JAVA=ON`), `sccache` (sets the C/C++ compiler launcher to sccache), and `with-java-sccache` (a comibination of `with-java` and `sccache`.
## Building
Once you have CMake setup. run `cmake --build .` from the directory you configured CMake in. This will build all libraries possible. We recommend running `cmake --build .` with multiple jobs. For allwpilib, a good rule of thumb is one worker for every 2 GB of available RAM. To run a multi-job build, run the following command with x being the number of jobs you want.
Once you have CMake setup. run `cmake --build .` from the directory you configured CMake in. This will build all libraries possible. We recommend running `cmake --build .` with multiple jobs. For allwpilib, a good rule of thumb is one worker for every 2 GB of available RAM. To run a multiple job build, run the following command with x being the number of jobs you want.
```
cmake --build . --parallel x
@@ -101,9 +119,9 @@ sudo cmake --build . --target install
## Preparing to use the installed libraries
On Windows, make sure the directories for the libraries you built are on PATH. For wpilib, the default install location is `C:\Program Files (x86)\allwpilib`. If you built other libraries like OpenCV from source, install them, and add the install directories to PATH. This ensures CMake can locate the libraries.
On Windows, make sure the directories for the libraries you built are on PATH. For wpilib, the default install location is `C:\Program Files (x86)\allwpilib`. If you built other libraries like OpenCV and protobuf from source, install them, and add the install directories to PATH. This ensures CMake can locate the libraries.
You will also want to add the directories where the DLLs are located (usually the `bin` subdirectory of the install directory) to PATH so they can be loaded by your program.
You will also want to add the directories where the DLLs are located (usually the `bin` subdirectory of the install directory) to PATH so they can be loaded by your program. If you are using OpenCV and Java, the `opencv_java` DLL is located in either the `lib` subdirectory if you built but didn't install OpenCV, or the `java` subdirectory if you did install OpenCV.
## Using the installed libraries for C++.
@@ -131,9 +149,40 @@ cmake /path/to/folder/containing/CMakeLists
After that, run `cmake --build .`. That will create your executable. Then you should be able to run `./my_vision_app` to run your application.
## Using the installed libraries for Java
Using the built JARs is more involved than using the C++ libraries, but the additional work involves providing the paths to various libraries and JARs when needed.
Create a new folder to contain your project. Add the following code below to a `CMakeLists.txt` file in that directory.
```cmake
cmake_minimum_required(VERSION 3.11)
project(robot)
find_package(Java REQUIRED COMPONENTS Development)
include(UseJava)
find_package(wpilib REQUIRED)
find_jar(opencvJar opencv-xxx PATHS ENV PATH) # Change to OpenCV version
file(GLOB_RECURSE JAVA_SOURCES *.java)
# If you want Gradle compatibility or you are using one of the templates/examples, comment out the above line and uncomment this line instead:
# file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
add_jar(robot ${JAVA_SOURCES}
INCLUDE_JARS apriltag_jar cscore_jar hal_jar ntcore_jar wpilibNewCommands_jar wpimath_jar wpinet_jar wpiutil_jar wpiunits_jar wpilibj_jar ${opencvJar})
export_jars(TARGETS robot FILE robot.jar)
```
This includes all the built JARs except for the vendordeps. If you are not using a JAR/library, you may remove it.
Add a `Main.java` file to contain your code, and create a build folder. Move into the build folder, and run
```
cmake /path/to/folder/containing/CMakeLists
```
After that, run `cmake --build .` to create your JAR file. To execute the JAR file, you need to include the wpilib JARs and your JAR in the classpath, and execute your Java program's entry point. If you are using cscore or cameraserver, you also need to include the path to the OpenCV JAR. If you built it from source, it will be in your OpenCV build directory. If it's installed on the system, CMake may find it from PATH, but you will likely need to locate the JAR and manually give CMake the JAR directory. If you are on Linux, you will also need to add the path of the libraries to `LD_LIBRARY_PATH`. This can be done by prepending `LD_LIBRARY_PATH=/path/to/libraries` to the Java command. If you need to add more paths, separate them with colons. The final command should look like `java -cp "robot.jar:/path/to/library_jars/*" main.package.Main`, using a semicolon to separate paths instead of a colon if you are on Windows. If you are on Linux, the final command should look more like `LD_LIBRARY_PATH=/path/to/libraries java -cp robot.jar:/path/to/library_jars/* main.package.Main`.
## Using vendordeps
Vendordeps are not included as part of the `wpilib` CMake package. However, if you want to use a vendordep, you need to use `find_package(VENDORDEP)`, where `VENDORDEP` is the name of the vendordep (case-sensitive), like `xrpVendordep` or `romiVendordep`. Note that commandsv2, while a vendordep in normal robot projects, is not built as a vendordep in CMake, and is instead included as part of the `wpilib` CMake package. After you used `find_package`, you can reference the vendordep library like normal (using `target_link_libraries`).
Vendordeps are not included as part of the `wpilib` CMake package. However, if you want to use a vendordep, you need to use `find_package(VENDORDEP)`, where `VENDORDEP` is the name of the vendordep (case-sensitive), like `xrpVendordep` or `romiVendordep`. Note that wpilibNewCommands, while a vendordep in normal robot projects, is not built as a vendordep in CMake, and is instead included as part of the `wpilib` CMake package. After you used `find_package`, you can reference the vendordep library like normal, either by using `target_link_libraries` for C++ or `add_jar` for Java.
## Troubleshooting
Below are some common issues that are run into when building.
@@ -161,3 +210,31 @@ CMake Error at cscore/CMakeLists.txt:3 (find_package):
```
If you get that, you need make sure OpenCV was installed, and then reattempt to configure. If that doesn't work, set the `OpenCV_DIR` variable to the directory where you built OpenCV.
#### Missing Java
If you are missing Java, you will get a message like the following.
```
CMake Error at /usr/share/cmake-3.5/Modules/FindPackageHandleStandardArgs.cmake:148 (message):
Could NOT find Java (missing: Java_JAVA_EXECUTABLE Java_JAR_EXECUTABLE
Java_JAVAC_EXECUTABLE Java_JAVAH_EXECUTABLE Java_JAVADOC_EXECUTABLE)
```
If this happens, make sure you have a JDK of at least version 8 installed, and that your JAVA_HOME variable is set properly to point to the JDK.
In addition, if you do not need Java, you can disable it with `-DWITH_JAVA=OFF`.
#### Java: Can't find dependent libraries
If one of the libraries can't be found, you will get an error similar to this one:
```
java.io.IOException: wpiHaljni could not be loaded from path or an embedded resource.
attempted to load for platform /windows/x86-64/
Last Load Error:
C:\Program Files (x86)\allwpilib\bin\wpiHaljni.dll: Can't find dependent libraries
```
If you get this error, that's usually an indication that not all your libraries are in your PATH. The two libraries that should be in your PATH are OpenCV and protobuf. If the error is coming from cscore, it's likely you're missing OpenCV. Otherwise, it's likely you're missing protobuf.
Note that Linux will not have this specific type of error, as it will usually tell you the dependent library you are missing. In that case, you most likely need to add the library to `LD_LIBRARY_PATH`.

View File

@@ -1,96 +0,0 @@
# robotpy in allwpilib
allwpilib hosts a mirror of RobotPy that can be built with Bazel on Linux. The intent of the mirror is to have breaking changes identified early and fixed by the PR creator so that when WPILib releases are made, there is much less work required to release a RobotPy version that wraps it. It is not a goal for allwpilib to replace the RobotPy repo; it will still be considered the "source of truth" for Python builds and will be responsible for building against all of the applicable architectures and multiple versions of Python.
## Build Process
The upstream RobotPy repository uses TOML configuration files and semiwrap to produce Meson build scripts. The allwpilib fork uses these TOML configuration files to auto-generate Bazel build scripts. In general, each project (wpiutil, wpimath, etc) defines two pybind extensions; one that simply wraps the native library, and another that adds extension(s) that and contains all of the Python files for the library. Both of these subprojects have auto-generated build files; a `robotpy_native_build_info.bzl` for the native wrapper and `robotpy_pybind_build_info.bzl` which defines the extensions and Python library.
## Disabling RobotPy builds
Building the RobotPy software on top of the standard C++/Java software can result in more than doubling the amount of time it takes to compile. To skip building the RobotPy tooling, you can add `--config=skip_robotpy` to the command line or to your `user.bazelrc`.
# Syncing with RobotPy
[Copybara](https://github.com/google/copybara) is used to maintain synchronization between the upstream RobotPy repositories and the allwpilib mirror. GitHub Actions can be manually run, which will create pull requests that will update all of the RobotPy files between the two repositories. The ideal process is that the allwpilib mirror is always building in CI, and once a release is created, the RobotPy team can run the `wpilib -> robotpy` Copybara task, make any fine tuned adjustments and create their release. In the event that additional changes are made on the RobotPy side, they can run the `robotpy -> wpilib` task to push the updates back to the mirror. However, the goal of the mirroring the software here is to be able to more rapidly test changes and will hopefully overwhelmingly eliminate the need for syncs in this direction.
## Creating a user config
The Copybara scripts needs to know information about what repositories it will be pushing the synced changes to. These can be specified on the command line, or you can create a `shared/bazel/copybara/.copybara.json` config file to save your personalized settings to avoid having to type things out every time. To run the full suite of migrations, you need a fork of [allwpilib](https://github.com/wpilibsuite/allwpilib), a fork of [mostrobotpy](https://github.com/robotpy/mostrobotpy), and a fork of RobotPy's [commands-v2](https://github.com/robotpy/robotpy-commands-v2). If you only wish to run a subset of commands (i.e. not sync the commands project), you do not need to include that in your user config.
Example config:
```json
{
"mostrobotpy_local_repo_path": "/home/<username>/git/robotpy/robotpy_monorepo/mostrobotpy",
"mostrobotpy_fork_repo": "https://github.com/<username>/mostrobotpy.git",
"allwpilib_fork_repo": "https://github.com/<username>/allwpilib.git",
"robotpy_commandsv2_fork_repo": "https://github.com/<username>/robotpy-commands-v2.git"
}
```
## Running syncs
- **Pulling changes from mostrobotpy**:
`python3 shared/bazel/copybara/run_copybara.py mostrobotpy_to_allwpilib`
- **Pulling changes from the commands library**:
`python3 shared/bazel/copybara/run_copybara.py commandsv2_to_allwpilib`
- **Pushing changes to the commands library**:
`python3 shared/bazel/copybara/run_copybara.py allwpilib_to_commandsv2`
- **Pushing changes to mostrobotpy**:
This process is slightly more complicated, because you will almost certainly also need to update the Maven artifacts that mostrobopy is using. Because of this, you must also specify the version number that has been published to WPILib's Maven repository. If you are trying to get an early, non-released development build pushed over, you can also add the `--development_build` flag:
`python3 shared/bazel/copybara/run_copybara.py allwpilib_to_mostrobotpy --wpilib_bin_version=2027.0.0-alpha-3-86-g418b381 --development_build -y`
# Debugging Build Errors
The build process is highly automated and automatically parses C++ header files to generate pybind11 bindings. Some of these steps here are considered "pregeneration" steps, and the Bazel build system will update build files as necessary. If a new header is added, or if the contents of a header file has changed, some of the pregeneration scripts might need to be run. If you encounter an error building `robotpy` code, it is recommended that you go through these steps to make sure everything is set up correctly. The examples are for `wpilibc`, but similar build tasks and tests exist for each wrapped project.
## 1. scan-headers
This can be the first source of problems if a new header file has been added. `semiwrap` will look through all of the headers for a library and notify you if a file is not covered by the projects `pyproject.toml` file. Bazel has a test case to ensure the files are up to date.
An example test failure when a new header being introduced. You can run the test with the following command:
bazel run //wpilibc:robotpy-wpilib-scan-headers
```
# wpi
ExpansionHub = "wpi/ExpansionHub.hpp"
ExpansionHubCRServo = "wpi/ExpansionHubCRServo.hpp"
ExpansionHubMotor = "wpi/ExpansionHubMotor.hpp"
ExpansionHubPidConstants = "wpi/ExpansionHubPidConstants.hpp"
ExpansionHubServo = "wpi/ExpansionHubServo.hpp"
```
To fix this, you can copy the lines from the console, and add them to the pyproject.toml file, located here `wpilibc/src/main/python/pyproject.toml`
## 2. update-yaml
This process parses all of the header files, and creates a representation of the classes / enums / etc in YAML. Occasionally, some functions might be ignored or need custom pybind code, which can be added to these files by the user.
There is a Bazel task that you can run to automatically update the files:
`bazel run //wpilibc:write_robotpy-wpilib-update-yaml`
## 3. generate-build-info
This step takes the yaml files, and auto-generates a Bazel build script for the library.
There is a Bazel task that you can run to automatically update the files:
`bazel run //wpilibc:robotpy-wpilib-generator.generate_build_info`
## semiwrap errors
If all of the above steps go smoothly and have their tests pass, but the generated cpp files still won't compile, it is possible that either an update needs to be made to the semiwrap tool to handle the new complex functionality, the new functionality can be ignored, or the new functionality might be better handled with a custom pybind11 implementation. In any case, it is best to reach out to the RobotPy team for guidance.
## Running multiple projects at once
Each project has its own `scan-headers` and various pregeneration tools, but you can run all of them at once with the following commands. Note: Sometimes if something in the dependency chain for a library fails, these amalgamation commands will also fail. If that happens, fix your way up the dependency chain project by project.
```
# Scan Headers
bazel test //... -k --test_tag_filters=robotpy_scan_headers --build_tests_only
# All pregen
bazel run //:write_robotpy_files
```

View File

@@ -3,9 +3,8 @@
[![Gradle](https://github.com/wpilibsuite/allwpilib/actions/workflows/gradle.yml/badge.svg?branch=main)](https://github.com/wpilibsuite/allwpilib/actions/workflows/gradle.yml)
[![C++ Documentation](https://img.shields.io/badge/documentation-c%2B%2B-blue)](https://github.wpilib.org/allwpilib/docs/development/cpp/)
[![Java Documentation](https://img.shields.io/badge/documentation-java-orange)](https://github.wpilib.org/allwpilib/docs/development/java/)
[![Python Documentation](https://img.shields.io/badge/documentation-python-blue)](https://robotpy.readthedocs.io/projects/robotpy/en/latest/)
Welcome to the WPILib project. This repository contains the HAL, CameraServer, Commands (v2 and v3), NTCore, WPIMath, and WPILib projects. These are the core libraries for creating robot programs for Systemcore.
Welcome to the WPILib project. This repository contains the HAL, WPILibJ, and WPILibC projects. These are the core libraries for creating robot programs for the roboRIO.
- [WPILib Project](#wpilib-project)
- [WPILib Mission](#wpilib-mission)
@@ -26,7 +25,7 @@ Welcome to the WPILib project. This repository contains the HAL, CameraServer, C
## WPILib Mission
The WPILib Mission is to enable FIRST Robotics Competition (FRC) and FIRST Tech Challenge (FTC) teams to focus on writing game-specific software rather than focusing on hardware details - "raise the floor, don't lower the ceiling". We work to enable teams with limited programming knowledge and/or mentor experience to be as successful as possible, while not hampering the abilities of teams with more advanced programming capabilities. We support the FRC Kit of Parts/FTC control system components directly in the library. We also strive to keep parity between major features of each language (Java, C++, and Python), so that teams aren't at a disadvantage for choosing a specific programming language. WPILib is an open source project, licensed under the BSD 3-clause license. You can find a copy of the license [here](LICENSE.md).
The WPILib Mission is to enable FIRST Robotics teams to focus on writing game-specific software rather than focusing on hardware details - "raise the floor, don't lower the ceiling". We work to enable teams with limited programming knowledge and/or mentor experience to be as successful as possible, while not hampering the abilities of teams with more advanced programming capabilities. We support Kit of Parts control system components directly in the library. We also strive to keep parity between major features of each language (Java, C++, Python, and NI's LabVIEW), so that teams aren't at a disadvantage for choosing a specific programming language. WPILib is an open source project, licensed under the BSD 3-clause license. You can find a copy of the license [here](LICENSE.md).
# Quick Start
@@ -39,28 +38,26 @@ Below is a list of instructions that guide you through cloning, building, publis
# Building WPILib
Using Gradle makes building WPILib very straightforward. It only has a few dependencies on outside tools, such as the ARM cross compiler for creating Systemcore binaries.
Using Gradle makes building WPILib very straightforward. It only has a few dependencies on outside tools, such as the ARM cross compiler for creating roboRIO binaries.
## Requirements
- [JDK 25](https://adoptium.net/temurin/releases/?version=25)
- [JDK 17](https://adoptium.net/temurin/releases/?version=17)
- Note that the JRE is insufficient; the full JDK is required
- On Ubuntu, run `sudo apt install openjdk-25-jdk`
- On Windows, install the JDK 25 .msi from the link above
- On macOS, install the JDK 25 .pkg from the link above
- On Ubuntu, run `sudo apt install openjdk-17-jdk`
- On Windows, install the JDK 17 .msi from the link above
- On macOS, install the JDK 17 .pkg from the link above
- C++ compiler
- On Linux, install GCC 11 or greater
- On Windows, install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio)
- On macOS, install the Xcode command-line build tools via `xcode-select --install`. Xcode 14 or later is required.
- Raspberry Pi toolchain (optional)
- Run `./gradlew installArm64Toolchain` after cloning this repository
- Systemcore toolchain (required for Systemcore development)
- Run `./gradlew installSystemCoreToolchain` after cloning this repository
- ARM compiler toolchain
- Run `./gradlew installRoboRioToolchain` after cloning this repository
- If the WPILib installer was used, this toolchain is already installed
- Raspberry Pi toolchain (optional)
- Run `./gradlew installArm32Toolchain` after cloning this repository
On macOS ARM, run `softwareupdate --install-rosetta`. This is necessary to be able to use the macOS x86 Systemcore toolchain on ARM.
On linux, run `sudo apt install libx11-dev libgl-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev` to be able to build things depending on glfw.
On macOS ARM, run `softwareupdate --install-rosetta`. This is necessary to be able to use the macOS x86 roboRIO toolchain on ARM.
## Setup
@@ -101,7 +98,7 @@ If opening from a fresh clone, generated java dependencies will not exist. Most
- `cscore`
- `hal`
- `ntcore`
- `commandsv2`
- `wpilibNewCommands`
- `wpimath`
- `wpinet`
- `wpiunits`
@@ -127,10 +124,10 @@ Please read the documentation available [here](DevelopmentBuilds.md)
### Custom toolchain location
If you have installed the WPILib Toolchain to a directory other than the default, or if the Toolchain location is not on your System PATH, you can pass the `toolChainPath` property to specify where it is located. Example:
If you have installed the FRC Toolchain to a directory other than the default, or if the Toolchain location is not on your System PATH, you can pass the `toolChainPath` property to specify where it is located. Example:
```bash
./gradlew build -PtoolChainPath=some/path/to/wpilib/toolchain/bin
./gradlew build -PtoolChainPath=some/path/to/frc/toolchain/bin
```
### Formatting/linting
@@ -181,15 +178,15 @@ If you are building to test with other dependencies or just want to export the b
- stable - Publishes to ~/releases/maven/stable.
- release - Publishes to ~/releases/maven/release.
The Maven artifacts are described in [MavenArtifacts.md](MavenArtifacts.md)
The maven artifacts are described in [MavenArtifacts.md](MavenArtifacts.md)
## Structure and Organization
The main WPILib code you're probably looking for is in WPILibJ and WPILibC. Those directories contain the high-level hardware/robot classes used for interacting with hardware, the Driver Station, and contain the core framework that almost all robot projects use.
The main WPILib code you're probably looking for is in WPILibJ and WPILibC. Those directories are split into shared, sim, and athena. Athena contains the WPILib code meant to run on your roboRIO. Sim is WPILib code meant to run on your computer, and shared is code shared between the two. Shared code must be platform-independent, since it will be compiled with both the ARM cross-compiler and whatever desktop compiler you are using (g++, msvc, etc...).
The src/test directories under each subproject for C++ and Java contain test code that runs on GitHub Actions. When you submit code for review, it is tested by GitHub Actions' runners. If you add new functionality you should make sure to write tests for it so we don't break it in the future.
The integration test directories for C++ and Java contain test code that runs on our test-system. When you submit code for review, it is tested by those programs. If you add new functionality you should make sure to write tests for it so we don't break it in the future.
The hal directory contains more C++ code meant to run on Systemcore. HAL is an acronym for "Hardware Abstraction Layer", and it interfaces with the robot controller to enable hardware interactions. The HAL is split into cpp, sim, and systemcore. The systemcore directory contains the WPILib code meant to run on your Systemcore. Sim is WPILib code meant to run on your computer, and cpp is code shared between the two. Code in the cpp directory must be platform-independent, since it will be compiled with both the ARM cross-compiler and whatever desktop compiler you are using (g++, MSVC, etc...).
The hal directory contains more C++ code meant to run on the roboRIO. HAL is an acronym for "Hardware Abstraction Layer", and it interfaces with the NI Libraries. The NI Libraries contain the low-level code for controlling devices on your robot. The NI Libraries are found in the [ni-libraries](https://github.com/wpilibsuite/ni-libraries) project.
The upstream_utils directory contains scripts for updating copies of thirdparty code in the repository.

View File

@@ -19,37 +19,40 @@ Google Test thirdparty/googletest/include
thirdparty/googletest/src
LLVM wpiutil/src/main/native/thirdparty/llvm
wpiutil/src/test/native/cpp/llvm/
JSON for Classic C++ wpiutil/src/main/native/thirdparty/json
JSON for Modern C++ wpiutil/src/main/native/thirdparty/json
wpiutil/src/test/native/cpp/json/
libuv wpinet/src/main/native/thirdparty/libuv/
fmtlib wpiutil/src/main/native/thirdparty/fmtlib/
sigslot wpiutil/src/main/native/thirdparty/sigslot
tcpsockets wpinet/src/main/native/thirdparty/tcpsockets
MPack wpiutil/src/main/native/thirdparty/mpack
units wpimath/src/main/native/include/wpi/units/
Bootstrap wpinet/src/main/native/resources/bootstrap-*
CoreUI wpinet/src/main/native/resources/coreui-*
Feather Icons wpinet/src/main/native/resources/feather-*
jQuery wpinet/src/main/native/resources/jquery-*
popper.js wpinet/src/main/native/resources/popper-*
units wpimath/src/main/native/include/units/
Eigen wpimath/src/main/native/thirdparty/eigen/include/
Team 254 Library wpimath/src/main/java/org/wpilib/math/spline/SplineParameterizer.java
wpimath/src/main/java/org/wpilib/math/trajectory/TrajectoryParameterizer.java
wpimath/src/main/native/include/wpi/math/spline/SplineParameterizer.hpp
wpimath/src/main/native/include/wpi/math/trajectory/TrajectoryParameterizer.hpp
Team 254 Library wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java
wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java
wpimath/src/main/native/include/frc/spline/SplineParameterizer.h
wpimath/src/main/native/include/frc/trajectory/TrajectoryParameterizer.h
wpimath/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp
Portable File Dialogs wpigui/src/main/native/include/wpi/gui/portable-file-dialogs.h
V8 export-template wpiutil/src/main/native/include/wpi/SymbolExports.hpp
Portable File Dialogs wpigui/src/main/native/include/portable-file-dialogs.h
V8 export-template wpiutil/src/main/native/include/wpi/SymbolExports.h
GCEM wpimath/src/main/native/thirdparty/gcem/include/
Sleipnir wpimath/src/main/native/thirdparty/sleipnir
Debugging wpiutil/src/main/native/thirdparty/debugging
double-conversion wpiutil/src/main/native/thirdparty/double-conversion
argparse wpiutil/src/main/native/thirdparty/argparse/include/wpi/util/argparse.hpp
argparse wpiutil/src/main/native/thirdparty/argparse/include/wpi/argparse.h
apriltag apriltag/src/main/native/thirdparty/apriltag
glfw thirdparty/imgui_suite/glfw
Dear ImGui thirdparty/imgui_suite/imgui
implot thirdparty/imgui_suite/implot
nanopb wpiutil/src/main/native/thirdparty/nanopb
upb wpiutil/src/main/native/thirdparty/upb
mrcal tools/wpical/src/main/native/thirdparty/mrcal
libdogleg tools/wpical/src/main/native/thirdparty/libdogleg
Simd hal/src/main/native/systemcore/simd
AdvantageScope fields/src/main/native/resources/org/wpilib/fields/ftc
Avahi wpinet/src/main/native/linux/AvahiClient.hpp
protobuf wpiutil/src/main/native/thirdparty/protobuf
mrcal wpical/src/main/native/thirdparty/mrcal
libdogleg wpical/src/main/native/thirdparty/libdogleg
Simd hal/src/main/native/athena/simd
Additionally, glfw and nanopb were modified for use in WPILib.
@@ -366,22 +369,35 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
============================
JSON for Classic C++ License
============================
Copyright 2024 Mozilla Foundation
==============================================================================
JSON for Modern C++ License
==============================================================================
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
http://www.apache.org/licenses/LICENSE-2.0
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
==============================================================================
@@ -482,6 +498,128 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
==============================================================================
Bootstrap License
==============================================================================
Copyright (c) 2011-2018 Twitter, Inc.
Copyright (c) 2011-2018 The Bootstrap Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
==============================================================================
CoreUI License
==============================================================================
Copyright (c) 2018 creativeLabs tukasz Holeczek.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
==============================================================================
Feather Icons License
==============================================================================
Copyright (c) 2013-2017 Cole Bemis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
==============================================================================
jQuery License
==============================================================================
Copyright JS Foundation and other contributors, https://js.foundation/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
==============================================================================
popper.js License
==============================================================================
Copyright (c) 2016 Federico Zivolo and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=============
units License
=============
@@ -1046,36 +1184,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================
double-conversion License
=========================
Copyright 2006-2011, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================
argparse License
================
@@ -1247,7 +1355,7 @@ redistribute it freely, subject to the following restrictions:
distribution.
================
upb License
protobuf License
================
Copyright 2008 Google Inc. All rights reserved.
@@ -1565,540 +1673,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
============
AdvantageScope License
============
Copyright (c) 2021-2026 Littleton Robotics. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Littleton Robotics, FRC 6328 ("Mechanical Advantage"),
AdvantageScope, nor the names of other AdvantageScope contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY LITTLETON ROBOTICS AND OTHER ADVANTAGESCOPE
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
LITTLETON ROBOTICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=============
Avahi License
=============
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
<https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Moe Ghoul>, 1 April 1990
Moe Ghoul, President of Vice
That's all there is to it!

125
WORKSPACE
View File

@@ -0,0 +1,125 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Download Extra java rules
http_archive(
name = "rules_jvm_external",
sha256 = "08ea921df02ffe9924123b0686dc04fd0ff875710bfadb7ad42badb931b0fd50",
strip_prefix = "rules_jvm_external-6.1",
url = "https://github.com/bazelbuild/rules_jvm_external/releases/download/6.1/rules_jvm_external-6.1.tar.gz",
)
load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")
rules_jvm_external_deps()
load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_artifacts = [
"org.ejml:ejml-simple:0.44.0",
"com.fasterxml.jackson.core:jackson-annotations:2.19.2",
"com.fasterxml.jackson.core:jackson-core:2.19.2",
"com.fasterxml.jackson.core:jackson-databind:2.19.2",
"us.hebi.quickbuf:quickbuf-runtime:1.4",
"com.google.code.gson:gson:2.13.1",
]
maven_install(
name = "maven",
artifacts = maven_artifacts,
repositories = [
"https://repo1.maven.org/maven2",
"https://frcmaven.wpi.edu/artifactory/release/",
],
)
# Download toolchains
http_archive(
name = "rules_bzlmodrio_toolchains",
sha256 = "ff25b5f9445cbd43759be4c6582b987d1065cf817c593eedc7ada1a699298c84",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_toolchains/releases/download/2025-1.bcr2/rules_bzlmodRio_toolchains-2025-1.bcr2.tar.gz",
)
load("@rules_bzlmodrio_toolchains//:maven_deps.bzl", "setup_legacy_setup_toolchains_dependencies")
setup_legacy_setup_toolchains_dependencies()
load("@rules_bzlmodrio_toolchains//toolchains:load_toolchains.bzl", "load_toolchains")
load_toolchains()
#
http_archive(
name = "rules_bzlmodrio_jdk",
sha256 = "81869fe9860e39b17e4a9bc1d33c1ca2faede7e31d9538ed0712406f753a2163",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_jdk/releases/download/17.0.12-7/rules_bzlmodRio_jdk-17.0.12-7.tar.gz",
)
load("@rules_bzlmodrio_jdk//:maven_deps.bzl", "setup_legacy_setup_jdk_dependencies")
setup_legacy_setup_jdk_dependencies()
register_toolchains(
"@local_roborio//:macos",
"@local_roborio//:linux",
"@local_roborio//:windows",
"@local_systemcore//:macos",
"@local_systemcore//:linux",
"@local_systemcore//:windows",
"@local_raspi_bullseye_32//:macos",
"@local_raspi_bullseye_32//:linux",
"@local_raspi_bullseye_32//:windows",
"@local_raspi_bookworm_32//:macos",
"@local_raspi_bookworm_32//:linux",
"@local_raspi_bookworm_32//:windows",
"@local_bullseye_32//:macos",
"@local_bullseye_32//:linux",
"@local_bullseye_32//:windows",
"@local_bullseye_64//:macos",
"@local_bullseye_64//:linux",
"@local_bullseye_64//:windows",
"@local_bookworm_32//:macos",
"@local_bookworm_32//:linux",
"@local_bookworm_32//:windows",
"@local_bookworm_64//:macos",
"@local_bookworm_64//:linux",
"@local_bookworm_64//:windows",
)
setup_legacy_setup_jdk_dependencies()
http_archive(
name = "bzlmodrio-ni",
sha256 = "fff62c3cb3e83f9a0d0a01f1739477c9ca5e9a6fac05be1ad59dafcd385801f7",
url = "https://github.com/wpilibsuite/bzlmodRio-ni/releases/download/2025.2.0/bzlmodRio-ni-2025.2.0.tar.gz",
)
load("@bzlmodrio-ni//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_ni_cpp_dependencies")
setup_legacy_bzlmodrio_ni_cpp_dependencies()
http_archive(
name = "bzlmodrio-opencv",
sha256 = "ba3f4910ce9cc0e08abff732aeb5835b1bcfd864ca5296edeadcf2935f7e81b9",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2025.4.10.0-3.bcr1/bzlmodRio-opencv-2025.4.10.0-3.bcr1.tar.gz",
)
load("@bzlmodrio-opencv//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_opencv_cpp_dependencies")
setup_legacy_bzlmodrio_opencv_cpp_dependencies()
load("@bzlmodrio-opencv//:maven_java_deps.bzl", "setup_legacy_bzlmodrio_opencv_java_dependencies")
setup_legacy_bzlmodrio_opencv_java_dependencies()
http_archive(
name = "build_bazel_apple_support",
sha256 = "c4bb2b7367c484382300aee75be598b92f847896fb31bbd22f3a2346adf66a80",
url = "https://github.com/bazelbuild/apple_support/releases/download/1.15.1/apple_support.1.15.1.tar.gz",
)
load(
"@build_bazel_apple_support//lib:repositories.bzl",
"apple_support_dependencies",
)
apple_support_dependencies()

View File

@@ -1,33 +0,0 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "pybind11_bazel",
integrity = "sha256-iwRj1wuX2pDS6t6DqiCfhIXisv4y+7CvxSJtZoSAzGw=",
strip_prefix = "pybind11_bazel-2b6082a4d9d163a52299718113fa41e4b7978db5",
urls = ["https://github.com/pybind/pybind11_bazel/archive/2b6082a4d9d163a52299718113fa41e4b7978db5.tar.gz"],
)
http_archive(
name = "pybind11",
build_file = "@pybind11_bazel//:pybind11-BUILD.bazel",
integrity = "sha256-LyCgrwuSGBXg4Wnqf+xjkJhpMjWBuJ194VU0aFU/ai0=",
strip_prefix = "pybind11-3.0.2",
url = "https://github.com/pybind/pybind11/archive/refs/tags/v3.0.2.tar.gz",
)
http_archive(
name = "bzlmodrio-opencv",
sha256 = "947df6f399593a54779b7706da8abaa43865769e02b15533519c800e23be419f",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2027.4.13.0-3/bzlmodRio-opencv-2027.4.13.0-3.tar.gz",
)
load("@bzlmodrio-opencv//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_opencv_cpp_dependencies")
setup_legacy_bzlmodrio_opencv_cpp_dependencies()
# Capture the repository environmental variables which specify the filter list for what architectures to build in CI.
load("//shared/bazel/rules:publishing_rule.bzl", "publishing_repo")
publishing_repo(
name = "com_wpilib_allwpilib_publishing_config",
)

View File

@@ -1,46 +1,21 @@
load("@allwpilib_pip_deps//:requirements.bzl", "requirement")
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_pkg//:mappings.bzl", "pkg_files")
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
load("@rules_python//python:defs.bzl", "py_binary")
load("//apriltag:robotpy_native_build_info.bzl", "define_native_wrapper")
load("//apriltag:robotpy_pybind_build_info.bzl", "apriltag_extension", "define_pybind_library")
load("//shared/bazel/rules:cc_rules.bzl", "wpilib_cc_library", "wpilib_cc_shared_library", "wpilib_cc_static_library")
load("//shared/bazel/rules:java_rules.bzl", "wpilib_java_junit5_test")
load("//shared/bazel/rules:jni_rules.bzl", "wpilib_jni_cc_library", "wpilib_jni_java_library")
load("//shared/bazel/rules:packaging.bzl", "package_default_jni_project")
load("//shared/bazel/rules/gen:gen-resources.bzl", "generate_resources")
load("//shared/bazel/rules/robotpy:build_info_gen.bzl", "generate_robotpy_native_wrapper_build_info", "generate_robotpy_pybind_build_info")
load("//shared/bazel/rules/robotpy:pytest_util.bzl", "robotpy_py_test")
filegroup(
name = "doxygen-files",
srcs = glob([
"src/main/native/include/**/*",
"src/main/native/thirdparty/apriltag/include/**/*",
]),
visibility = ["//visibility:public"],
)
pkg_files(
name = "thirdparty-apriltag-src-pkg",
srcs = glob(["src/main/native/thirdparty/apriltag/include/**"]),
strip_prefix = "src/main/native/thirdparty/apriltag/include",
)
cc_library(
name = "thirdparty-apriltag",
srcs = glob(["src/main/native/thirdparty/apriltag/src/**"]),
hdrs = glob(["src/main/native/thirdparty/apriltag/include/**"]),
copts = select({
"@platforms//os:osx": [
"@bazel_tools//src/conditions:darwin": [
"-Wno-format-nonliteral",
"-Wno-gnu-zero-variadic-macro-arguments",
"-Wno-uninitialized",
"-Wno-sign-compare",
"-Wno-type-limits",
],
"@platforms//os:windows": [
"@bazel_tools//src/conditions:windows": [
"/wd4005",
"/wd4018",
"/wd4244",
@@ -56,93 +31,37 @@ cc_library(
}),
includes = ["src/main/native/thirdparty/apriltag/include/common"],
strip_include_prefix = "src/main/native/thirdparty/apriltag/include",
visibility = ["//visibility:public"],
)
generate_resources(
name = "generate-resources",
namespace = "wpi::apriltag",
namespace = "frc",
prefix = "APRILTAG",
resource_files = glob(["src/main/native/resources/**"]),
visibility = ["//visibility:public"],
)
wpilib_cc_library(
name = "apriltag",
cc_library(
name = "apriltag.static",
srcs = [":generate-resources"] + glob(
["src/main/native/cpp/**"],
exclude = ["src/main/native/cpp/jni/**"],
),
hdrs = glob(["src/main/native/include/**/*"]),
extra_hdr_pkg_files = [":thirdparty-apriltag-src-pkg"],
extra_src_pkg_files = [":apriltag-java-jni-hdrs-pkg"],
local_defines = ["WPILIB_EXPORTS"],
defines = ["WPILIB_EXPORTS"],
strip_include_prefix = "src/main/native/include",
visibility = ["//visibility:public"],
deps = [
":thirdparty-apriltag",
"//wpimath",
"//wpiutil",
"//wpimath:wpimath.static",
"//wpiutil:wpiutil.static",
],
)
wpilib_cc_shared_library(
name = "shared/apriltag",
auto_export_windows_symbols = False,
dynamic_deps = [
"//wpimath:shared/wpimath",
"//wpiutil:shared/wpiutil",
],
visibility = ["//visibility:public"],
deps = [
":apriltag",
":thirdparty-apriltag",
],
)
wpilib_cc_static_library(
name = "static/apriltag",
static_deps = [
"//wpimath:static/wpimath",
"//wpiutil:static/wpiutil",
],
visibility = ["//visibility:public"],
deps = [
":apriltag",
],
)
wpilib_jni_cc_library(
name = "apriltagjni",
srcs = glob(["src/main/native/cpp/jni/**"]),
java_dep = ":apriltag-java",
visibility = ["//visibility:public"],
deps = [
":apriltag",
],
)
wpilib_cc_shared_library(
name = "shared/apriltagjni",
auto_export_windows_symbols = False,
dynamic_deps = [
":shared/apriltag",
"//wpimath:shared/wpimath",
"//wpiutil:shared/wpiutil",
],
use_debug_name = False,
visibility = ["//visibility:public"],
deps = [":apriltagjni"],
)
wpilib_jni_java_library(
java_library(
name = "apriltag-java",
srcs = glob(["src/main/java/**/*.java"]),
extra_source_pkgs = ["resources"],
maven_artifact_name = "apriltag-java",
maven_group_id = "org.wpilib.apriltag",
native_libs = [":apriltagjni"],
plugins = [
"//:avaje_jsonb_generator",
],
resource_strip_prefix = "apriltag/src/main/native/resources",
resources = glob(["src/main/native/resources/**"]),
visibility = ["//visibility:public"],
@@ -150,17 +69,12 @@ wpilib_jni_java_library(
"//wpimath:wpimath-java",
"//wpiutil:wpiutil-java",
"@bzlmodrio-opencv//libraries/java/opencv",
"@maven//:io_avaje_avaje_json_core",
"@maven//:io_avaje_avaje_jsonb",
"@maven//:com_fasterxml_jackson_core_jackson_annotations",
"@maven//:com_fasterxml_jackson_core_jackson_core",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
],
)
pkg_files(
name = "resources",
srcs = glob(["src/main/native/resources/**"]),
strip_prefix = "src/main/native/resources/",
)
cc_test(
name = "apriltag-cpp-test",
size = "small",
@@ -169,22 +83,8 @@ cc_test(
"no-asan",
],
deps = [
":apriltag",
"//thirdparty/googletest",
],
)
wpilib_java_junit5_test(
name = "apriltag-java-test",
srcs = glob(["src/test/java/**/*.java"]),
resource_strip_prefix = "apriltag/src/test/resources",
resources = glob(["src/test/resources/**"]),
deps = [
":apriltag-java",
"//wpimath:wpimath-java",
"//wpiutil:wpiutil-java",
"@bzlmodrio-opencv//libraries/java/opencv",
"@maven//:io_avaje_avaje_jsonb",
":apriltag.static",
"//thirdparty/googletest:googletest.static",
],
)
@@ -192,14 +92,14 @@ cc_binary(
name = "DevMain-Cpp",
srcs = ["src/dev/native/cpp/main.cpp"],
deps = [
":apriltag",
":apriltag.static",
],
)
java_binary(
name = "DevMain-Java",
srcs = ["src/dev/java/org/wpilib/vision/apriltag/DevMain.java"],
main_class = "org.wpilib.vision.apriltag.DevMain",
srcs = ["src/dev/java/edu/wpi/first/apriltag/DevMain.java"],
main_class = "edu.wpi.first.apriltag.DevMain",
deps = [
":apriltag-java",
],
@@ -208,67 +108,5 @@ java_binary(
py_binary(
name = "convert_apriltag_layouts",
srcs = ["convert_apriltag_layouts.py"],
target_compatible_with = select({
"@rules_bzlmodrio_toolchains//constraints/is_systemcore:systemcore": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
)
package_default_jni_project(
name = "apriltag",
maven_artifact_name = "apriltag-cpp",
maven_group_id = "org.wpilib.apriltag",
)
generate_robotpy_native_wrapper_build_info(
name = "robotpy-native-apriltag-generator",
pyproject_toml = "src/main/python/native-pyproject.toml",
third_party_dirs = ["apriltag"],
)
define_native_wrapper(
name = "robotpy-native-apriltag",
pyproject_toml = "src/main/python/native-pyproject.toml",
)
PYBIND_PKGCFG_DEPS = [
"//apriltag:native/apriltag/robotpy-native-apriltag.pc",
"//wpimath:native/wpimath/robotpy-native-wpimath.pc",
"//wpimath:robotpy-wpimath.generated_pkgcfg_files",
"//wpiutil:native/wpiutil/robotpy-native-wpiutil.pc",
"//wpiutil:robotpy-wpiutil.generated_pkgcfg_files",
]
generate_robotpy_pybind_build_info(
name = "robotpy-apriltag-generator",
additional_srcs = [":robotpy-native-apriltag.copy_headers"],
package_root_file = "src/main/python/robotpy_apriltag/__init__.py",
pkgcfgs = PYBIND_PKGCFG_DEPS,
yaml_files = glob(["src/main/python/semiwrap/*.yml"]),
)
apriltag_extension(
srcs = ["src/main/python/robotpy_apriltag/src/main.cpp"],
includes = [
"src/main/python/datalog/",
],
)
define_pybind_library(
name = "robotpy-apriltag",
pkgcfgs = PYBIND_PKGCFG_DEPS,
)
robotpy_py_test(
"python_tests",
srcs = glob(["src/test/python/**/*.py"]),
data = glob([
"src/test/python/*.png",
"src/test/python/*.jpg",
]),
deps = [
":robotpy-apriltag",
requirement("pytest"),
requirement("opencv-python"),
],
tags = ["manual"],
)

View File

@@ -31,11 +31,86 @@ else()
)
endif()
file(GLOB apriltag_jni_src src/main/native/cpp/jni/AprilTagJNI.cpp)
if(WITH_JAVA)
include(UseJava)
set(CMAKE_JNI_TARGET true)
file(GLOB EJML_JARS "${WPILIB_BINARY_DIR}/wpimath/thirdparty/ejml/*.jar")
file(GLOB JACKSON_JARS "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson/*.jar")
find_file(
OPENCV_JAR_FILE
NAMES opencv-${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}.jar
PATHS
${OPENCV_JAVA_INSTALL_DIR}
${OpenCV_INSTALL_PATH}/bin
${OpenCV_INSTALL_PATH}/share/java
${OpenCV_INSTALL_PATH}/share/java/opencv4
${OpenCV_INSTALL_PATH}/share/OpenCV/java
NO_DEFAULT_PATH
)
file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
file(
GLOB_RECURSE JAVA_RESOURCES
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
src/main/native/resources/*.json
)
add_jar(
apriltag_jar
SOURCES ${JAVA_SOURCES}
RESOURCES
NAMESPACE "edu/wpi/first/apriltag" ${JAVA_RESOURCES}
INCLUDE_JARS
wpimath_jar
wpiunits_jar
wpiutil_jar
${EJML_JARS}
${OPENCV_JAR_FILE}
${JACKSON_JARS}
OUTPUT_NAME apriltag
OUTPUT_DIR ${WPILIB_BINARY_DIR}/${java_lib_dest}
GENERATE_NATIVE_HEADERS apriltag_jni_headers
)
set_property(TARGET apriltag_jar PROPERTY FOLDER "java")
install_jar(apriltag_jar DESTINATION ${java_lib_dest})
install_jar_exports(TARGETS apriltag_jar FILE apriltag_jar.cmake DESTINATION share/apriltag)
add_library(apriltagjni ${apriltag_jni_src} ${apriltaglib_src})
wpilib_target_warnings(apriltagjni)
target_link_libraries(apriltagjni PUBLIC apriltag)
set_property(TARGET apriltagjni PROPERTY FOLDER "libraries")
target_link_libraries(apriltagjni PRIVATE apriltag_jni_headers)
add_dependencies(apriltagjni apriltag_jar)
install(TARGETS apriltagjni EXPORT apriltagjni)
export(TARGETS apriltagjni FILE apriltagjni.cmake NAMESPACE apriltagjni::)
endif()
if(WITH_JAVA_SOURCE)
include(UseJava)
include(CreateSourceJar)
add_source_jar(
apriltag_src_jar
BASE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src/main/java
OUTPUT_NAME apriltag-sources
OUTPUT_DIR ${WPILIB_BINARY_DIR}/${java_lib_dest}
)
set_property(TARGET apriltag_src_jar PROPERTY FOLDER "java")
install_jar(apriltag_src_jar DESTINATION ${java_lib_dest})
endif()
generate_resources(
src/main/native/resources/org/wpilib/vision/apriltag
src/main/native/resources/edu/wpi/first/apriltag
generated/main/cpp
APRILTAG
wpi::apriltag
frc
apriltag_resources_src
)
@@ -45,7 +120,7 @@ add_library(apriltag ${apriltag_native_src} ${apriltag_resources_src} ${apriltag
set_target_properties(apriltag PROPERTIES DEBUG_POSTFIX "d")
set_property(TARGET apriltag PROPERTY FOLDER "libraries")
target_compile_features(apriltag PUBLIC cxx_std_23)
target_compile_features(apriltag PUBLIC cxx_std_20)
wpilib_target_warnings(apriltag)
target_link_libraries(apriltag wpimath)

View File

@@ -1,12 +1,10 @@
# AprilTag
A C++ wrapper around the [University of Michigan's AprilTag detector](https://github.com/AprilRobotics/apriltag), alongside a vendored copy of their code with some patches (patches are located in [upstream_utils](../upstream_utils/apriltag_patches/)).
## Adding new field to AprilTagFields
### Adding field JSON
1. Add a field layout CSV file to `src/main/native/resources/org/wpilib/vision/apriltag`
1. Add a field layout CSV file to `src/main/native/resources/edu/wpi/first/apriltag`
1. See docstring in `convert_apriltag_layouts.py` for more
2. Run `convert_apriltag_layouts.py` in the same directory as this readme to generate the JSON
3. That script overwrites all generated JSONs, so undo undesired changes if necessary
@@ -16,15 +14,15 @@ A C++ wrapper around the [University of Michigan's AprilTag detector](https://gi
### Java updates
1. Update `src/main/java/org/wpilib/vision/apriltag/AprilTagFields.java`
1. Update `src/main/java/edu/wpi/first/apriltag/AprilTagFields.java`
1. Add enum value for new field to `AprilTagFields`
2. Update `AprilTagFields.kDefaultField` if necessary
### C++ updates
1. Update `src/main/native/include/wpi/apriltag/AprilTagFields.hpp`
1. Update `src/main/native/include/frc/apriltag/AprilTagFields.h`
1. Add enum value for new field to `AprilTagFields`
2. Update `AprilTagFields::kDefaultField` if necessary
2. Update `src/main/native/cpp/AprilTagFieldLayout.cpp`
2. Update `src/main/native/cpp/AprilTagFields.cpp`
1. Add resource getter prototype like `std::string_view GetResource_2024_crescendo_json()`
2. Add case for new field to switch in `LoadField(AprilTagField field)`
2. Add case for new field to switch in `LoadAprilTagLayoutField()`

View File

@@ -5,3 +5,6 @@ include(CMakeFindDependencyMacro)
@FILENAME_DEP_REPLACE@
include(${SELF_DIR}/apriltag.cmake)
if(@WITH_JAVA@)
include(${SELF_DIR}/apriltag_jar.cmake)
endif()

View File

@@ -4,7 +4,7 @@ apply plugin: 'c'
ext {
nativeName = 'apriltag'
devMain = 'org.wpilib.vision.apriltag.DevMain'
devMain = 'edu.wpi.first.apriltag.DevMain'
useJava = true
useCpp = true
sharedCvConfigs = [
@@ -12,7 +12,7 @@ ext {
apriltagTest: []]
staticCvConfigs = []
def generateTask = createGenerateResourcesTask('main', 'APRILTAG', 'wpi::apriltag', project)
def generateTask = createGenerateResourcesTask('main', 'APRILTAG', 'frc', project)
tasks.withType(CppCompile) {
dependsOn generateTask
@@ -46,7 +46,6 @@ apply from: "${rootDir}/shared/opencv.gradle"
dependencies {
implementation project(':wpimath')
annotationProcessor libs.avaje.jsonb.generator
}
sourceSets {

View File

@@ -1,66 +0,0 @@
# THIS FILE IS AUTO GENERATED
load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory")
load("//shared/bazel/rules/robotpy:robotpy_rules.bzl", "copy_native_file", "generate_native_files", "robotpy_library")
def define_native_wrapper(name, pyproject_toml = None):
pyproject_toml = pyproject_toml or "src/main/python/native-pyproject.toml"
copy_to_directory(
name = "{}.copy_headers".format(name),
srcs = native.glob(["src/main/native/include/**"]) + native.glob(["src/generated/main/native/include/**"], allow_empty = True) + native.glob([
"src/main/native/thirdparty/apriltag/include/**",
]),
out = "native/apriltag/include",
root_paths = ["src/main/native/include/"],
replace_prefixes = {
"apriltag/src/generated/main/native/include": "",
"apriltag/src/main/native/include": "",
"apriltag/src/main/native/thirdparty/apriltag/include": "",
},
verbose = False,
visibility = ["//visibility:public"],
)
libinit_files = ["native/apriltag/_init_robotpy_native_apriltag.py"]
generate_native_files(
name = name,
pyproject_toml = pyproject_toml,
pc_deps = [
"//wpimath:native/wpimath/robotpy-native-wpimath.pc",
"//wpiutil:native/wpiutil/robotpy-native-wpiutil.pc",
],
libinit_files = libinit_files,
pc_files = ["native/apriltag/robotpy-native-apriltag.pc"],
)
copy_native_file(
name = "apriltag",
library = "shared/apriltag",
base_path = "native/apriltag/",
)
robotpy_library(
name = name,
distribution = "robotpy-native-apriltag",
srcs = libinit_files,
data = [
name + ".pc_wrapper",
":apriltag.copy_lib",
"{}.copy_headers".format(name),
],
deps = [
"//wpimath:robotpy-native-wpimath",
"//wpiutil:robotpy-native-wpiutil",
],
summary = "WPILib AprilTag Library",
requires = ["robotpy-native-wpiutil==0.0.0", "robotpy-native-wpimath==0.0.0"],
python_requires = ">=3.11",
strip_path_prefixes = ["apriltag"],
entry_points = {
"pkg_config": [
"apriltag = native.apriltag",
],
},
)

View File

@@ -1,252 +0,0 @@
# THIS FILE IS AUTO GENERATED
load("//shared/bazel/rules/gen:gen-version-file.bzl", "generate_version_file")
load("//shared/bazel/rules/robotpy:robotpy_rules.bzl", "create_pybind_library", "robotpy_library")
load("//shared/bazel/rules/robotpy:semiwrap_helpers.bzl", "gen_libinit", "gen_modinit_hpp", "gen_pkgconf", "resolve_casters", "run_header_gen")
load("//shared/bazel/rules/robotpy:semiwrap_tool_helpers.bzl", "scan_headers", "update_yaml_files")
def apriltag_extension(srcs = [], header_to_dat_deps = [], extra_hdrs = [], includes = []):
APRILTAG_HEADER_GEN = [
struct(
class_name = "AprilTag",
yml_file = "semiwrap/AprilTag.yml",
header_root = "$(execpath :robotpy-native-apriltag.copy_headers)",
header_file = "$(execpath :robotpy-native-apriltag.copy_headers)/wpi/apriltag/AprilTag.hpp",
tmpl_class_names = [],
trampolines = [
("wpi::apriltag::AprilTag", "wpi__apriltag__AprilTag.hpp"),
],
),
struct(
class_name = "AprilTagDetection",
yml_file = "semiwrap/AprilTagDetection.yml",
header_root = "$(execpath :robotpy-native-apriltag.copy_headers)",
header_file = "$(execpath :robotpy-native-apriltag.copy_headers)/wpi/apriltag/AprilTagDetection.hpp",
tmpl_class_names = [],
trampolines = [
("wpi::apriltag::AprilTagDetection", "wpi__apriltag__AprilTagDetection.hpp"),
("wpi::apriltag::AprilTagDetection::Point", "wpi__apriltag__AprilTagDetection__Point.hpp"),
],
),
struct(
class_name = "AprilTagDetector",
yml_file = "semiwrap/AprilTagDetector.yml",
header_root = "$(execpath :robotpy-native-apriltag.copy_headers)",
header_file = "$(execpath :robotpy-native-apriltag.copy_headers)/wpi/apriltag/AprilTagDetector.hpp",
tmpl_class_names = [],
trampolines = [
("wpi::apriltag::AprilTagDetector", "wpi__apriltag__AprilTagDetector.hpp"),
("wpi::apriltag::AprilTagDetector::Config", "wpi__apriltag__AprilTagDetector__Config.hpp"),
("wpi::apriltag::AprilTagDetector::QuadThresholdParameters", "wpi__apriltag__AprilTagDetector__QuadThresholdParameters.hpp"),
("wpi::apriltag::AprilTagDetector::Results", "wpi__apriltag__AprilTagDetector__Results.hpp"),
],
),
struct(
class_name = "AprilTagFieldLayout",
yml_file = "semiwrap/AprilTagFieldLayout.yml",
header_root = "$(execpath :robotpy-native-apriltag.copy_headers)",
header_file = "$(execpath :robotpy-native-apriltag.copy_headers)/wpi/apriltag/AprilTagFieldLayout.hpp",
tmpl_class_names = [],
trampolines = [
("wpi::apriltag::AprilTagFieldLayout", "wpi__apriltag__AprilTagFieldLayout.hpp"),
],
),
struct(
class_name = "AprilTagFields",
yml_file = "semiwrap/AprilTagFields.yml",
header_root = "$(execpath :robotpy-native-apriltag.copy_headers)",
header_file = "$(execpath :robotpy-native-apriltag.copy_headers)/wpi/apriltag/AprilTagFields.hpp",
tmpl_class_names = [],
trampolines = [],
),
struct(
class_name = "AprilTagPoseEstimate",
yml_file = "semiwrap/AprilTagPoseEstimate.yml",
header_root = "$(execpath :robotpy-native-apriltag.copy_headers)",
header_file = "$(execpath :robotpy-native-apriltag.copy_headers)/wpi/apriltag/AprilTagPoseEstimate.hpp",
tmpl_class_names = [],
trampolines = [
("wpi::apriltag::AprilTagPoseEstimate", "wpi__apriltag__AprilTagPoseEstimate.hpp"),
],
),
struct(
class_name = "AprilTagPoseEstimator",
yml_file = "semiwrap/AprilTagPoseEstimator.yml",
header_root = "$(execpath :robotpy-native-apriltag.copy_headers)",
header_file = "$(execpath :robotpy-native-apriltag.copy_headers)/wpi/apriltag/AprilTagPoseEstimator.hpp",
tmpl_class_names = [],
trampolines = [
("wpi::apriltag::AprilTagPoseEstimator", "wpi__apriltag__AprilTagPoseEstimator.hpp"),
("wpi::apriltag::AprilTagPoseEstimator::Config", "wpi__apriltag__AprilTagPoseEstimator__Config.hpp"),
],
),
]
resolve_casters(
name = "apriltag.resolve_casters",
caster_deps = ["//wpimath:src/main/python/wpimath/wpimath-casters.pybind11.json", "//wpiutil:src/main/python/wpiutil/wpiutil-casters.pybind11.json"],
casters_pkl_file = "apriltag.casters.pkl",
dep_file = "apriltag.casters.d",
)
gen_libinit(
name = "apriltag.gen_lib_init",
output_file = "src/main/python/robotpy_apriltag/_init__apriltag.py",
modules = ["native.apriltag._init_robotpy_native_apriltag", "wpiutil._init__wpiutil", "wpimath._init__wpimath"],
)
gen_pkgconf(
name = "apriltag.gen_pkgconf",
libinit_py = "robotpy_apriltag._init__apriltag",
module_pkg_name = "robotpy_apriltag._apriltag",
output_file = "apriltag.pc",
pkg_name = "apriltag",
install_path = "src/main/python/robotpy_apriltag",
project_file = "src/main/python/pyproject.toml",
package_root = "src/main/python/robotpy_apriltag/__init__.py",
)
gen_modinit_hpp(
name = "apriltag.gen_modinit_hpp",
input_dats = [x.class_name for x in APRILTAG_HEADER_GEN],
libname = "_apriltag",
output_file = "semiwrap_init.robotpy_apriltag._apriltag.hpp",
)
run_header_gen(
name = "apriltag",
casters_pickle = "apriltag.casters.pkl",
header_gen_config = APRILTAG_HEADER_GEN,
trampoline_subpath = "src/main/python/robotpy_apriltag",
deps = header_to_dat_deps,
local_native_libraries = [
"//apriltag:robotpy-native-apriltag.copy_headers",
"//wpimath:robotpy-native-wpimath.copy_headers",
"//wpiutil:robotpy-native-wpiutil.copy_headers",
],
)
create_pybind_library(
name = "apriltag",
install_path = "src/main/python/robotpy_apriltag/",
extension_name = "_apriltag",
generated_srcs = [":apriltag.generated_srcs"],
semiwrap_header = [":apriltag.gen_modinit_hpp"],
deps = [
":apriltag.tmpl_hdrs",
":apriltag.trampoline_hdrs",
"//apriltag:apriltag",
"//wpimath:wpimath",
"//wpimath:wpimath_pybind_library",
"//wpiutil:wpiutil",
"//wpiutil:wpiutil_pybind_library",
],
dynamic_deps = [
"//apriltag:shared/apriltag",
"//wpimath:shared/wpimath",
"//wpiutil:shared/wpiutil",
],
extra_hdrs = extra_hdrs,
extra_srcs = srcs,
includes = includes,
)
native.filegroup(
name = "apriltag.generated_files",
srcs = [
"apriltag.gen_modinit_hpp.gen",
"apriltag.header_gen_files",
"apriltag.gen_pkgconf",
"apriltag.gen_lib_init",
],
tags = ["manual", "robotpy"],
)
def define_pybind_library(name, pkgcfgs = []):
# Helper used to generate all files with one target.
native.filegroup(
name = "{}.generated_files".format(name),
srcs = [
"apriltag.generated_files",
],
tags = ["manual", "robotpy"],
visibility = ["//visibility:public"],
)
# Files that will be included in the wheel as data deps
native.filegroup(
name = "{}.generated_pkgcfg_files".format(name),
srcs = [
"src/main/python/robotpy_apriltag/apriltag.pc",
],
tags = ["manual", "robotpy"],
visibility = ["//visibility:public"],
)
# Contains all of the non-python files that need to be included in the wheel
native.filegroup(
name = "{}.extra_files".format(name),
srcs = native.glob(["src/main/python/robotpy_apriltag/**"], exclude = ["src/main/python/robotpy_apriltag/**/*.py"], allow_empty = True),
tags = ["manual", "robotpy"],
)
generate_version_file(
name = "{}.generate_version".format(name),
output_file = "src/main/python/robotpy_apriltag/version.py",
template = "//shared/bazel/rules/robotpy:version_template.in",
)
robotpy_library(
name = name,
distribution = "robotpy-apriltag",
srcs = native.glob(["src/main/python/robotpy_apriltag/**/*.py"]) + [
"src/main/python/robotpy_apriltag/_init__apriltag.py",
"{}.generate_version".format(name),
],
data = [
"{}.generated_pkgcfg_files".format(name),
"{}.extra_files".format(name),
":src/main/python/robotpy_apriltag/_apriltag",
":apriltag.trampoline_hdr_files",
],
imports = ["src/main/python"],
deps = [
"//apriltag:robotpy-native-apriltag",
"//wpimath:robotpy-wpimath",
"//wpiutil:robotpy-wpiutil",
],
strip_path_prefixes = ["apriltag/src/main/python", "apriltag"],
summary = "RobotPy bindings for WPILib's AprilTag library",
project_urls = {"Source code": "https://github.com/robotpy/mostrobotpy"},
author_email = "RobotPy Development Team <robotpy@googlegroups.com>",
requires = ["robotpy-native-apriltag==0.0.0", "robotpy-wpiutil==0.0.0", "robotpy-wpimath==0.0.0"],
python_requires = ">=3.11",
entry_points = {
"pkg_config": ["apriltag = robotpy_apriltag"],
},
visibility = ["//visibility:public"],
)
update_yaml_files(
name = "{}-update-yaml".format(name),
yaml_output_directory = "src/main/python/semiwrap",
extra_hdrs = native.glob(["src/main/python/**/*.h"], allow_empty = True) + [
"//apriltag:robotpy-native-apriltag.copy_headers",
"//wpimath:robotpy-native-wpimath.copy_headers",
"//wpiutil:robotpy-native-wpiutil.copy_headers",
],
package_root_file = "src/main/python/robotpy_apriltag/__init__.py",
pkgcfgs = pkgcfgs,
pyproject_toml = "src/main/python/pyproject.toml",
yaml_files = native.glob(["src/main/python/semiwrap/**"]),
)
scan_headers(
name = "{}-scan-headers".format(name),
extra_hdrs = native.glob(["src/main/python/**/*.h"], allow_empty = True) + [
"//apriltag:robotpy-native-apriltag.copy_headers",
],
package_root_file = "src/main/python/robotpy_apriltag/__init__.py",
pkgcfgs = pkgcfgs,
pyproject_toml = "src/main/python/pyproject.toml",
)

View File

@@ -0,0 +1,20 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.apriltag;
public final class DevMain {
/** Main entry point. */
public static void main(String[] args) {
System.out.println("Hello World!");
AprilTagDetector detector = new AprilTagDetector();
detector.addFamily("tag16h5");
AprilTagDetector.Config config = new AprilTagDetector.Config();
config.refineEdges = false;
detector.setConfig(config);
detector.close();
}
private DevMain() {}
}

View File

@@ -1,20 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.vision.apriltag;
public final class DevMain {
/** Main entry point. */
public static void main(String[] args) {
System.out.println("Hello World!");
AprilTagDetector detector = new AprilTagDetector();
detector.addFamily("tag16h5");
AprilTagDetector.Config config = new AprilTagDetector.Config();
config.refineEdges = false;
detector.setConfig(config);
detector.close();
}
private DevMain() {}
}

View File

@@ -2,10 +2,10 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "wpi/apriltag/AprilTagDetector.hpp"
#include "frc/apriltag/AprilTagDetector.h"
int main() {
wpi::apriltag::AprilTagDetector detector;
frc::AprilTagDetector detector;
detector.AddFamily("tag16h5");
detector.SetConfig({.refineEdges = false});
}

View File

@@ -2,24 +2,24 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.vision.apriltag;
package edu.wpi.first.apriltag;
import io.avaje.jsonb.Json;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.wpi.first.apriltag.jni.AprilTagJNI;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.util.RawFrame;
import java.util.Objects;
import org.wpilib.math.geometry.Pose3d;
import org.wpilib.util.RawFrame;
import org.wpilib.vision.apriltag.jni.AprilTagJNI;
/** Represents an AprilTag's metadata. */
@Json
@SuppressWarnings("MemberName")
public class AprilTag {
/** The tag's ID. */
@Json.Property("ID")
@SuppressWarnings("PMD.PublicFieldNamingConvention")
@JsonProperty(value = "ID")
public int ID;
/** The tag's pose. */
@Json.Property("pose")
@JsonProperty(value = "pose")
public Pose3d pose;
/**
@@ -29,8 +29,10 @@ public class AprilTag {
* @param pose The tag's pose.
*/
@SuppressWarnings("ParameterName")
@Json.Creator
public AprilTag(int ID, Pose3d pose) {
@JsonCreator
public AprilTag(
@JsonProperty(required = true, value = "ID") int ID,
@JsonProperty(required = true, value = "pose") Pose3d pose) {
this.ID = ID;
this.pose = pose;
}

View File

@@ -2,13 +2,13 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.vision.apriltag;
package edu.wpi.first.apriltag;
import edu.wpi.first.math.MatBuilder;
import edu.wpi.first.math.Matrix;
import edu.wpi.first.math.Nat;
import edu.wpi.first.math.numbers.N3;
import java.util.Arrays;
import org.wpilib.math.linalg.MatBuilder;
import org.wpilib.math.linalg.Matrix;
import org.wpilib.math.numbers.N3;
import org.wpilib.math.util.Nat;
/** A detection of an AprilTag tag. */
public class AprilTagDetection {

View File

@@ -2,10 +2,10 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.vision.apriltag;
package edu.wpi.first.apriltag;
import edu.wpi.first.apriltag.jni.AprilTagJNI;
import org.opencv.core.Mat;
import org.wpilib.vision.apriltag.jni.AprilTagJNI;
/**
* An AprilTag detector engine. This is expensive to set up and tear down, so most use cases should
@@ -14,6 +14,7 @@ import org.wpilib.vision.apriltag.jni.AprilTagJNI;
*/
public class AprilTagDetector implements AutoCloseable {
/** Detector configuration. */
@SuppressWarnings("MemberName")
public static class Config {
/**
* How many threads should be used for computation. Default is single-threaded operation (1
@@ -109,6 +110,7 @@ public class AprilTagDetector implements AutoCloseable {
}
/** Quad threshold parameters. */
@SuppressWarnings("MemberName")
public static class QuadThresholdParameters {
/** Threshold used to reject quads containing too few pixels. Default is 300 pixels. */
public int minClusterPixels = 300;

View File

@@ -0,0 +1,300 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.apriltag;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Translation3d;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
* Class for representing a layout of AprilTags on a field and reading them from a JSON format.
*
* <p>The JSON format contains two top-level objects, "tags" and "field". The "tags" object is a
* list of all AprilTags contained within a layout. Each AprilTag serializes to a JSON object
* containing an ID and a Pose3d. The "field" object is a descriptor of the size of the field in
* meters with "width" and "length" values. This is to account for arbitrary field sizes when
* transforming the poses.
*
* <p>Pose3ds in the JSON are measured using the normal FRC coordinate system, NWU with the origin
* at the bottom-right corner of the blue alliance wall. {@link #setOrigin(OriginPosition)} can be
* used to change the poses returned from {@link AprilTagFieldLayout#getTagPose(int)} to be from the
* perspective of a specific alliance.
*
* <p>Tag poses represent the center of the tag, with a zero rotation representing a tag that is
* upright and facing away from the (blue) alliance wall (that is, towards the opposing alliance).
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
public class AprilTagFieldLayout {
/** Common origin positions for the AprilTag coordinate system. */
public enum OriginPosition {
/** Blue alliance wall, right side. */
kBlueAllianceWallRightSide,
/** Red alliance wall, right side. */
kRedAllianceWallRightSide,
}
private final Map<Integer, AprilTag> m_apriltags = new HashMap<>();
@JsonProperty(value = "field")
private FieldDimensions m_fieldDimensions;
private Pose3d m_origin;
/**
* Construct a new AprilTagFieldLayout with values imported from a JSON file.
*
* @param path Path of the JSON file to import from.
* @throws IOException If reading from the file fails.
*/
public AprilTagFieldLayout(String path) throws IOException {
this(Path.of(path));
}
/**
* Construct a new AprilTagFieldLayout with values imported from a JSON file.
*
* @param path Path of the JSON file to import from.
* @throws IOException If reading from the file fails.
*/
public AprilTagFieldLayout(Path path) throws IOException {
AprilTagFieldLayout layout =
new ObjectMapper().readValue(path.toFile(), AprilTagFieldLayout.class);
m_apriltags.putAll(layout.m_apriltags);
m_fieldDimensions = layout.m_fieldDimensions;
setOrigin(OriginPosition.kBlueAllianceWallRightSide);
}
/**
* Construct a new AprilTagFieldLayout from a list of {@link AprilTag} objects.
*
* @param apriltags List of {@link AprilTag}.
* @param fieldLength Length of the field the layout is representing in meters.
* @param fieldWidth Width of the field the layout is representing in meters.
*/
public AprilTagFieldLayout(List<AprilTag> apriltags, double fieldLength, double fieldWidth) {
this(apriltags, new FieldDimensions(fieldLength, fieldWidth));
}
@JsonCreator
private AprilTagFieldLayout(
@JsonProperty(required = true, value = "tags") List<AprilTag> apriltags,
@JsonProperty(required = true, value = "field") FieldDimensions fieldDimensions) {
// To ensure the underlying semantics don't change with what kind of list is passed in
for (AprilTag tag : apriltags) {
m_apriltags.put(tag.ID, tag);
}
m_fieldDimensions = fieldDimensions;
setOrigin(OriginPosition.kBlueAllianceWallRightSide);
}
/**
* Returns a List of the {@link AprilTag AprilTags} used in this layout.
*
* @return The {@link AprilTag AprilTags} used in this layout.
*/
@JsonProperty("tags")
public List<AprilTag> getTags() {
return new ArrayList<>(m_apriltags.values());
}
/**
* Returns the length of the field the layout is representing in meters.
*
* @return length, in meters
*/
@JsonIgnore
public double getFieldLength() {
return m_fieldDimensions.fieldLength;
}
/**
* Returns the length of the field the layout is representing in meters.
*
* @return width, in meters
*/
@JsonIgnore
public double getFieldWidth() {
return m_fieldDimensions.fieldWidth;
}
/**
* Sets the origin based on a predefined enumeration of coordinate frame origins. The origins are
* calculated from the field dimensions.
*
* <p>This transforms the Pose3d objects returned by {@link #getTagPose(int)} to return the
* correct pose relative to a predefined coordinate frame.
*
* @param origin The predefined origin
*/
@JsonIgnore
public final void setOrigin(OriginPosition origin) {
var pose =
switch (origin) {
case kBlueAllianceWallRightSide -> Pose3d.kZero;
case kRedAllianceWallRightSide ->
new Pose3d(
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
new Rotation3d(0, 0, Math.PI));
};
setOrigin(pose);
}
/**
* Sets the origin for tag pose transformation.
*
* <p>This transforms the Pose3d objects returned by {@link #getTagPose(int)} to return the
* correct pose relative to the provided origin.
*
* @param origin The new origin for tag transformations
*/
@JsonIgnore
public final void setOrigin(Pose3d origin) {
m_origin = origin;
}
/**
* Returns the origin used for tag pose transformation.
*
* @return the origin
*/
@JsonIgnore
public Pose3d getOrigin() {
return m_origin;
}
/**
* Gets an AprilTag pose by its ID.
*
* @param ID The ID of the tag.
* @return The pose corresponding to the ID passed in or an empty optional if a tag with that ID
* was not found.
*/
@SuppressWarnings("ParameterName")
public Optional<Pose3d> getTagPose(int ID) {
AprilTag tag = m_apriltags.get(ID);
if (tag == null) {
return Optional.empty();
}
return Optional.of(tag.pose.relativeTo(m_origin));
}
/**
* Serializes a AprilTagFieldLayout to a JSON file.
*
* @param path The path to write to.
* @throws IOException If writing to the file fails.
*/
public void serialize(String path) throws IOException {
serialize(Path.of(path));
}
/**
* Serializes a AprilTagFieldLayout to a JSON file.
*
* @param path The path to write to.
* @throws IOException If writing to the file fails.
*/
public void serialize(Path path) throws IOException {
new ObjectMapper().writeValue(path.toFile(), this);
}
/**
* Get an official {@link AprilTagFieldLayout}.
*
* @param field The loadable AprilTag field layout.
* @return AprilTagFieldLayout of the field.
* @throws UncheckedIOException If the layout does not exist.
*/
public static AprilTagFieldLayout loadField(AprilTagFields field) {
if (field.m_fieldLayout == null) {
try {
field.m_fieldLayout = loadFromResource(field.m_resourceFile);
} catch (IOException e) {
throw new UncheckedIOException(
"Could not load AprilTagFieldLayout from " + field.m_resourceFile, e);
}
}
// Copy layout because the layout's origin is mutable
return new AprilTagFieldLayout(
field.m_fieldLayout.getTags(),
field.m_fieldLayout.getFieldLength(),
field.m_fieldLayout.getFieldWidth());
}
/**
* Deserializes a field layout from a resource within a internal jar file.
*
* <p>Users should use {@link AprilTagFields#loadAprilTagLayoutField()} to load official layouts
* and {@link #AprilTagFieldLayout(String)} for custom layouts.
*
* @param resourcePath The absolute path of the resource
* @return The deserialized layout
* @throws IOException If the resource could not be loaded
*/
public static AprilTagFieldLayout loadFromResource(String resourcePath) throws IOException {
InputStream stream = AprilTagFieldLayout.class.getResourceAsStream(resourcePath);
if (stream == null) {
// Class.getResourceAsStream() returns null if the resource does not exist.
throw new IOException("Could not locate resource: " + resourcePath);
}
InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8);
try {
return new ObjectMapper().readerFor(AprilTagFieldLayout.class).readValue(reader);
} catch (IOException e) {
throw new IOException("Failed to load AprilTagFieldLayout: " + resourcePath);
}
}
@Override
public boolean equals(Object obj) {
return obj instanceof AprilTagFieldLayout layout
&& m_apriltags.equals(layout.m_apriltags)
&& m_origin.equals(layout.m_origin);
}
@Override
public int hashCode() {
return Objects.hash(m_apriltags, m_origin);
}
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
private static class FieldDimensions {
@SuppressWarnings("MemberName")
@JsonProperty(value = "length")
public final double fieldLength;
@SuppressWarnings("MemberName")
@JsonProperty(value = "width")
public final double fieldWidth;
@JsonCreator()
FieldDimensions(
@JsonProperty(required = true, value = "length") double fieldLength,
@JsonProperty(required = true, value = "width") double fieldWidth) {
this.fieldLength = fieldLength;
this.fieldWidth = fieldWidth;
}
}
}

View File

@@ -0,0 +1,52 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.apriltag;
import java.io.UncheckedIOException;
/** Loadable AprilTag field layouts. */
public enum AprilTagFields {
/** 2022 Rapid React. */
k2022RapidReact("2022-rapidreact.json"),
/** 2023 Charged Up. */
k2023ChargedUp("2023-chargedup.json"),
/** 2024 Crescendo. */
k2024Crescendo("2024-crescendo.json"),
/** 2025 Reefscape Welded (see TU 12). */
k2025ReefscapeWelded("2025-reefscape-welded.json"),
/** 2025 Reefscape AndyMark (see TU 12). */
k2025ReefscapeAndyMark("2025-reefscape-andymark.json"),
/** 2026 Rebuilt Welded. */
k2026RebuiltWelded("2026-rebuilt-welded.json"),
/** 2026 Rebuilt AndyMark. */
k2026RebuiltAndymark("2026-rebuilt-andymark.json");
/** Base resource directory. */
public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
/** Alias to the current game. */
public static final AprilTagFields kDefaultField = k2026RebuiltWelded;
/** Resource filename. */
public final String m_resourceFile;
AprilTagFieldLayout m_fieldLayout;
AprilTagFields(String resourceFile) {
m_resourceFile = kBaseResourceDir + resourceFile;
}
/**
* Get a {@link AprilTagFieldLayout} from the resource JSON.
*
* @return AprilTagFieldLayout of the field
* @throws UncheckedIOException If the layout does not exist
* @deprecated Use {@link AprilTagFieldLayout#loadField(AprilTagFields)} instead.
*/
@Deprecated(forRemoval = true, since = "2025")
public AprilTagFieldLayout loadAprilTagLayoutField() {
return AprilTagFieldLayout.loadField(this);
}
}

View File

@@ -2,11 +2,12 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.vision.apriltag;
package edu.wpi.first.apriltag;
import org.wpilib.math.geometry.Transform3d;
import edu.wpi.first.math.geometry.Transform3d;
/** A pair of AprilTag pose estimates. */
@SuppressWarnings("MemberName")
public class AprilTagPoseEstimate {
/**
* Constructs a pose estimate.

View File

@@ -2,14 +2,15 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.vision.apriltag;
package edu.wpi.first.apriltag;
import org.wpilib.math.geometry.Transform3d;
import org.wpilib.vision.apriltag.jni.AprilTagJNI;
import edu.wpi.first.apriltag.jni.AprilTagJNI;
import edu.wpi.first.math.geometry.Transform3d;
/** Pose estimators for AprilTag tags. */
public class AprilTagPoseEstimator {
/** Configuration for the pose estimator. */
@SuppressWarnings("MemberName")
public static class Config {
/**
* Creates a pose estimator configuration.

View File

@@ -2,16 +2,16 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.vision.apriltag.jni;
package edu.wpi.first.apriltag.jni;
import edu.wpi.first.apriltag.AprilTagDetection;
import edu.wpi.first.apriltag.AprilTagDetector;
import edu.wpi.first.apriltag.AprilTagPoseEstimate;
import edu.wpi.first.math.geometry.Transform3d;
import edu.wpi.first.util.RawFrame;
import edu.wpi.first.util.RuntimeLoader;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.wpilib.math.geometry.Transform3d;
import org.wpilib.util.RawFrame;
import org.wpilib.util.runtime.RuntimeLoader;
import org.wpilib.vision.apriltag.AprilTagDetection;
import org.wpilib.vision.apriltag.AprilTagDetector;
import org.wpilib.vision.apriltag.AprilTagPoseEstimate;
/** AprilTag JNI. */
public class AprilTagJNI {

View File

@@ -1,281 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.vision.apriltag;
import io.avaje.jsonb.Json;
import io.avaje.jsonb.Jsonb;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.wpilib.math.geometry.Pose3d;
import org.wpilib.math.geometry.Rotation3d;
import org.wpilib.math.geometry.Translation3d;
/**
* Class for representing a layout of AprilTags on a field and reading them from a JSON format.
*
* <p>The JSON format contains two top-level objects, "tags" and "field". The "tags" object is a
* list of all AprilTags contained within a layout. Each AprilTag serializes to a JSON object
* containing an ID and a Pose3d. The "field" object is a descriptor of the size of the field in
* meters with "width" and "length" values. This is to account for arbitrary field sizes when
* transforming the poses.
*
* <p>Pose3ds in the JSON are measured using the normal FRC coordinate system, NWU with the origin
* at the bottom-right corner of the blue alliance wall. {@link #setOrigin(OriginPosition)} can be
* used to change the poses returned from {@link AprilTagFieldLayout#getTagPose(int)} to be from the
* perspective of a specific alliance.
*
* <p>Tag poses represent the center of the tag, with a zero rotation representing a tag that is
* upright and facing away from the (blue) alliance wall (that is, towards the opposing alliance).
*/
@Json
public class AprilTagFieldLayout {
/** Common origin positions for the AprilTag coordinate system. */
public enum OriginPosition {
/** Blue alliance wall, right side. */
kBlueAllianceWallRightSide,
/** Red alliance wall, right side. */
kRedAllianceWallRightSide,
}
@Json.Ignore private final Map<Integer, AprilTag> m_apriltags = new HashMap<>();
@Json.Property("field")
FieldDimensions m_fieldDimensions;
@Json.Ignore private Pose3d m_origin;
/**
* Construct a new AprilTagFieldLayout with values imported from a JSON file.
*
* @param path Path of the JSON file to import from.
* @throws IOException If reading from the file fails.
*/
public AprilTagFieldLayout(String path) throws IOException {
this(Path.of(path));
}
/**
* Construct a new AprilTagFieldLayout with values imported from a JSON file.
*
* @param path Path of the JSON file to import from.
* @throws IOException If reading from the file fails.
*/
public AprilTagFieldLayout(Path path) throws IOException {
AprilTagFieldLayout layout =
Jsonb.instance().type(AprilTagFieldLayout.class).fromJson(Files.newBufferedReader(path));
m_apriltags.putAll(layout.m_apriltags);
m_fieldDimensions = layout.m_fieldDimensions;
setOrigin(OriginPosition.kBlueAllianceWallRightSide);
}
/**
* Construct a new AprilTagFieldLayout from a list of {@link AprilTag} objects.
*
* @param apriltags List of {@link AprilTag}.
* @param fieldLength Length of the field the layout is representing in meters.
* @param fieldWidth Width of the field the layout is representing in meters.
*/
public AprilTagFieldLayout(List<AprilTag> apriltags, double fieldLength, double fieldWidth) {
this(apriltags, new FieldDimensions(fieldLength, fieldWidth));
}
@Json.Creator
AprilTagFieldLayout(
@Json.Alias("tags") List<AprilTag> apriltags,
@Json.Alias("field") FieldDimensions fieldDimensions) {
// To ensure the underlying semantics don't change with what kind of list is passed in
for (AprilTag tag : apriltags) {
m_apriltags.put(tag.ID, tag);
}
m_fieldDimensions = fieldDimensions;
setOrigin(OriginPosition.kBlueAllianceWallRightSide);
}
/**
* Returns a List of the {@link AprilTag AprilTags} used in this layout.
*
* @return The {@link AprilTag AprilTags} used in this layout.
*/
@Json.Property("tags")
public List<AprilTag> getTags() {
return new ArrayList<>(m_apriltags.values());
}
/**
* Returns the length of the field the layout is representing in meters.
*
* @return length, in meters
*/
public double getFieldLength() {
return m_fieldDimensions.fieldLength;
}
/**
* Returns the length of the field the layout is representing in meters.
*
* @return width, in meters
*/
public double getFieldWidth() {
return m_fieldDimensions.fieldWidth;
}
/**
* Sets the origin based on a predefined enumeration of coordinate frame origins. The origins are
* calculated from the field dimensions.
*
* <p>This transforms the Pose3d objects returned by {@link #getTagPose(int)} to return the
* correct pose relative to a predefined coordinate frame.
*
* @param origin The predefined origin
*/
public final void setOrigin(OriginPosition origin) {
var pose =
switch (origin) {
case kBlueAllianceWallRightSide -> Pose3d.kZero;
case kRedAllianceWallRightSide ->
new Pose3d(
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
new Rotation3d(0, 0, Math.PI));
};
setOrigin(pose);
}
/**
* Sets the origin for tag pose transformation.
*
* <p>This transforms the Pose3d objects returned by {@link #getTagPose(int)} to return the
* correct pose relative to the provided origin.
*
* @param origin The new origin for tag transformations
*/
public final void setOrigin(Pose3d origin) {
m_origin = origin;
}
/**
* Returns the origin used for tag pose transformation.
*
* @return the origin
*/
public Pose3d getOrigin() {
return m_origin;
}
/**
* Gets an AprilTag pose by its ID.
*
* @param ID The ID of the tag.
* @return The pose corresponding to the ID passed in or an empty optional if a tag with that ID
* was not found.
*/
@SuppressWarnings("ParameterName")
public Optional<Pose3d> getTagPose(int ID) {
AprilTag tag = m_apriltags.get(ID);
if (tag == null) {
return Optional.empty();
}
return Optional.of(tag.pose.relativeTo(m_origin));
}
/**
* Serializes a AprilTagFieldLayout to a JSON file.
*
* @param path The path to write to.
* @throws IOException If writing to the file fails.
*/
public void serialize(String path) throws IOException {
serialize(Path.of(path));
}
/**
* Serializes a AprilTagFieldLayout to a JSON file.
*
* @param path The path to write to.
* @throws IOException If writing to the file fails.
*/
public void serialize(Path path) throws IOException {
Jsonb.instance().type(AprilTagFieldLayout.class).toJson(this, Files.newBufferedWriter(path));
}
/**
* Get an official {@link AprilTagFieldLayout}.
*
* @param field The loadable AprilTag field layout.
* @return AprilTagFieldLayout of the field.
* @throws UncheckedIOException If the layout does not exist.
*/
public static AprilTagFieldLayout loadField(AprilTagFields field) {
if (field.fieldLayout == null) {
try {
field.fieldLayout = loadFromResource(field.resourceFile);
} catch (IOException e) {
throw new UncheckedIOException(
"Could not load AprilTagFieldLayout from " + field.resourceFile, e);
}
}
// Copy layout because the layout's origin is mutable
return new AprilTagFieldLayout(
field.fieldLayout.getTags(),
field.fieldLayout.getFieldLength(),
field.fieldLayout.getFieldWidth());
}
/**
* Deserializes a field layout from a resource within a internal jar file.
*
* <p>Users should use {@link #loadField(AprilTagFields)} to load official layouts and {@link
* #AprilTagFieldLayout(String)} for custom layouts.
*
* @param resourcePath The absolute path of the resource
* @return The deserialized layout
* @throws IOException If the resource could not be loaded
*/
public static AprilTagFieldLayout loadFromResource(String resourcePath) throws IOException {
try (InputStream stream = AprilTagFieldLayout.class.getResourceAsStream(resourcePath)) {
if (stream == null) {
// Class.getResourceAsStream() returns null if the resource does not exist.
throw new IOException("Could not locate resource: " + resourcePath);
}
return Jsonb.instance().type(AprilTagFieldLayout.class).fromJson(stream);
}
}
@Override
public boolean equals(Object obj) {
return obj instanceof AprilTagFieldLayout layout
&& m_apriltags.equals(layout.m_apriltags)
&& m_origin.equals(layout.m_origin);
}
@Override
public int hashCode() {
return Objects.hash(m_apriltags, m_origin);
}
static class FieldDimensions {
@Json.Property(value = "length")
public final double fieldLength;
@Json.Property(value = "width")
public final double fieldWidth;
@Json.Creator()
FieldDimensions(
@Json.Alias(value = "length") double fieldLength,
@Json.Alias(value = "width") double fieldWidth) {
this.fieldLength = fieldLength;
this.fieldWidth = fieldWidth;
}
}
}

View File

@@ -1,38 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.vision.apriltag;
/** Loadable AprilTag field layouts. */
public enum AprilTagFields {
/** 2022 Rapid React. */
k2022RapidReact("2022-rapidreact.json"),
/** 2023 Charged Up. */
k2023ChargedUp("2023-chargedup.json"),
/** 2024 Crescendo. */
k2024Crescendo("2024-crescendo.json"),
/** 2025 Reefscape Welded (see TU 12). */
k2025ReefscapeWelded("2025-reefscape-welded.json"),
/** 2025 Reefscape AndyMark (see TU 12). */
k2025ReefscapeAndyMark("2025-reefscape-andymark.json"),
/** 2026 Rebuilt Welded. */
k2026RebuiltWelded("2026-rebuilt-welded.json"),
/** 2026 Rebuilt AndyMark. */
k2026RebuiltAndymark("2026-rebuilt-andymark.json");
/** Base resource directory. */
public static final String kBaseResourceDir = "/org/wpilib/vision/apriltag/";
/** Alias to the current game. */
public static final AprilTagFields kDefaultField = k2026RebuiltWelded;
/** Resource filename. */
public final String resourceFile;
AprilTagFieldLayout fieldLayout;
AprilTagFields(String resourceFile) {
this.resourceFile = kBaseResourceDir + resourceFile;
}
}

View File

@@ -2,11 +2,11 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "wpi/apriltag/AprilTag.hpp"
#include "frc/apriltag/AprilTag.h"
#include <cstring>
#include "wpi/util/json.hpp"
#include <wpi/json.h>
#ifdef _WIN32
#pragma warning(disable : 4200)
@@ -20,9 +20,9 @@
#include "tag16h5.h"
#include "tag36h11.h"
using namespace wpi::apriltag;
using namespace frc;
static bool FamilyToImage(wpi::util::RawFrame* frame, apriltag_family_t* family,
static bool FamilyToImage(wpi::RawFrame* frame, apriltag_family_t* family,
int id) {
image_u8_t* image = apriltag_to_image(family, id);
size_t totalDataSize = image->height * image->stride;
@@ -37,25 +37,25 @@ static bool FamilyToImage(wpi::util::RawFrame* frame, apriltag_family_t* family,
return rv;
}
bool AprilTag::Generate36h11AprilTagImage(wpi::util::RawFrame* frame, int id) {
bool AprilTag::Generate36h11AprilTagImage(wpi::RawFrame* frame, int id) {
apriltag_family_t* tagFamily = tag36h11_create();
bool rv = FamilyToImage(frame, tagFamily, id);
tag36h11_destroy(tagFamily);
return rv;
}
bool AprilTag::Generate16h5AprilTagImage(wpi::util::RawFrame* frame, int id) {
bool AprilTag::Generate16h5AprilTagImage(wpi::RawFrame* frame, int id) {
apriltag_family_t* tagFamily = tag16h5_create();
bool rv = FamilyToImage(frame, tagFamily, id);
tag16h5_destroy(tagFamily);
return rv;
}
void wpi::apriltag::to_json(wpi::util::json& json, const AprilTag& apriltag) {
json = wpi::util::json::object("ID", apriltag.ID, "pose", apriltag.pose);
void frc::to_json(wpi::json& json, const AprilTag& apriltag) {
json = wpi::json{{"ID", apriltag.ID}, {"pose", apriltag.pose}};
}
void wpi::apriltag::from_json(const wpi::util::json& json, AprilTag& apriltag) {
apriltag.ID = json.at("ID").get_int();
apriltag.pose = json.at("pose").get<wpi::math::Pose3d>();
void frc::from_json(const wpi::json& json, AprilTag& apriltag) {
apriltag.ID = json.at("ID").get<int>();
apriltag.pose = json.at("pose").get<Pose3d>();
}

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "wpi/apriltag/AprilTagDetection.hpp"
#include "frc/apriltag/AprilTagDetection.h"
#include <type_traits>
@@ -16,7 +16,7 @@
#include "apriltag.h"
using namespace wpi::apriltag;
using namespace frc;
static_assert(sizeof(AprilTagDetection) == sizeof(apriltag_detection_t),
"structure sizes don't match");

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "wpi/apriltag/AprilTagDetector.hpp"
#include "frc/apriltag/AprilTagDetector.h"
#include <cmath>
#include <utility>
@@ -19,7 +19,7 @@
#include "tag16h5.h"
#include "tag36h11.h"
using namespace wpi::apriltag;
using namespace frc;
AprilTagDetector::Results::Results(void* impl, const private_init&)
: span{reinterpret_cast<AprilTagDetection**>(

View File

@@ -2,41 +2,38 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "wpi/apriltag/AprilTagFieldLayout.hpp"
#include "frc/apriltag/AprilTagFieldLayout.h"
#include <system_error>
#include <utility>
#include <vector>
#include "wpi/units/angle.hpp"
#include "wpi/units/length.hpp"
#include "wpi/util/MemoryBuffer.hpp"
#include "wpi/util/json.hpp"
#include "wpi/util/raw_ostream.hpp"
#include <units/angle.h>
#include <units/length.h>
#include <wpi/MemoryBuffer.h>
#include <wpi/json.h>
#include <wpi/raw_ostream.h>
using namespace wpi::apriltag;
using namespace frc;
AprilTagFieldLayout::AprilTagFieldLayout(std::string_view path) {
auto fileBuffer = wpi::util::MemoryBuffer::GetFile(path);
auto fileBuffer = wpi::MemoryBuffer::GetFile(path);
if (!fileBuffer) {
throw std::runtime_error(fmt::format("Cannot open file: {}", path));
}
auto buf = fileBuffer.value()->GetCharBuffer();
auto json = wpi::util::json::parse_or_throw({buf.data(), buf.size()});
wpi::json json = wpi::json::parse(fileBuffer.value()->GetCharBuffer());
for (auto&& jtag : json.at("tags").get_array()) {
auto tag = jtag.get<AprilTag>();
for (const auto& tag : json.at("tags").get<std::vector<AprilTag>>()) {
m_apriltags[tag.ID] = tag;
}
m_fieldWidth = wpi::units::meter_t{json.at("field").at("width").get_number()};
m_fieldLength =
wpi::units::meter_t{json.at("field").at("length").get_number()};
m_fieldWidth = units::meter_t{json.at("field").at("width").get<double>()};
m_fieldLength = units::meter_t{json.at("field").at("length").get<double>()};
}
AprilTagFieldLayout::AprilTagFieldLayout(std::vector<AprilTag> apriltags,
wpi::units::meter_t fieldLength,
wpi::units::meter_t fieldWidth)
units::meter_t fieldLength,
units::meter_t fieldWidth)
: m_fieldLength(std::move(fieldLength)),
m_fieldWidth(std::move(fieldWidth)) {
for (const auto& tag : apriltags) {
@@ -44,11 +41,11 @@ AprilTagFieldLayout::AprilTagFieldLayout(std::vector<AprilTag> apriltags,
}
}
wpi::units::meter_t AprilTagFieldLayout::GetFieldLength() const {
units::meter_t AprilTagFieldLayout::GetFieldLength() const {
return m_fieldLength;
}
wpi::units::meter_t AprilTagFieldLayout::GetFieldWidth() const {
units::meter_t AprilTagFieldLayout::GetFieldWidth() const {
return m_fieldWidth;
}
@@ -64,27 +61,26 @@ std::vector<AprilTag> AprilTagFieldLayout::GetTags() const {
void AprilTagFieldLayout::SetOrigin(OriginPosition origin) {
switch (origin) {
case OriginPosition::kBlueAllianceWallRightSide:
SetOrigin(wpi::math::Pose3d{});
SetOrigin(Pose3d{});
break;
case OriginPosition::kRedAllianceWallRightSide:
SetOrigin(wpi::math::Pose3d{
wpi::math::Translation3d{m_fieldLength, m_fieldWidth, 0_m},
wpi::math::Rotation3d{0_deg, 0_deg, 180_deg}});
SetOrigin(Pose3d{Translation3d{m_fieldLength, m_fieldWidth, 0_m},
Rotation3d{0_deg, 0_deg, 180_deg}});
break;
default:
throw std::invalid_argument("Invalid origin");
}
}
void AprilTagFieldLayout::SetOrigin(const wpi::math::Pose3d& origin) {
void AprilTagFieldLayout::SetOrigin(const Pose3d& origin) {
m_origin = origin;
}
wpi::math::Pose3d AprilTagFieldLayout::GetOrigin() const {
Pose3d AprilTagFieldLayout::GetOrigin() const {
return m_origin;
}
std::optional<wpi::math::Pose3d> AprilTagFieldLayout::GetTagPose(int ID) const {
std::optional<frc::Pose3d> AprilTagFieldLayout::GetTagPose(int ID) const {
const auto& it = m_apriltags.find(ID);
if (it == m_apriltags.end()) {
return std::nullopt;
@@ -95,46 +91,43 @@ std::optional<wpi::math::Pose3d> AprilTagFieldLayout::GetTagPose(int ID) const {
void AprilTagFieldLayout::Serialize(std::string_view path) {
std::error_code error_code;
wpi::util::raw_fd_ostream output{path, error_code};
wpi::raw_fd_ostream output{path, error_code};
if (error_code) {
throw std::runtime_error(fmt::format("Cannot open file: {}", path));
}
wpi::util::json json = *this;
wpi::json json = *this;
output << json;
output.flush();
}
void wpi::apriltag::to_json(wpi::util::json& json,
const AprilTagFieldLayout& layout) {
void frc::to_json(wpi::json& json, const AprilTagFieldLayout& layout) {
std::vector<AprilTag> tagVector;
tagVector.reserve(layout.m_apriltags.size());
for (const auto& pair : layout.m_apriltags) {
tagVector.push_back(pair.second);
}
auto field = wpi::util::json::object("length", layout.m_fieldLength.value(),
"width", layout.m_fieldWidth.value());
json = wpi::util::json::object("field", std::move(field), "tags",
std::move(tagVector));
json = wpi::json{{"field",
{{"length", layout.m_fieldLength.value()},
{"width", layout.m_fieldWidth.value()}}},
{"tags", tagVector}};
}
void wpi::apriltag::from_json(const wpi::util::json& json,
AprilTagFieldLayout& layout) {
void frc::from_json(const wpi::json& json, AprilTagFieldLayout& layout) {
layout.m_apriltags.clear();
for (auto&& jtag : json.at("tags").get_array()) {
auto tag = jtag.get<AprilTag>();
for (const auto& tag : json.at("tags").get<std::vector<AprilTag>>()) {
layout.m_apriltags[tag.ID] = tag;
}
layout.m_fieldLength =
wpi::units::meter_t{json.at("field").at("length").get_number()};
units::meter_t{json.at("field").at("length").get<double>()};
layout.m_fieldWidth =
wpi::units::meter_t{json.at("field").at("width").get_number()};
units::meter_t{json.at("field").at("width").get<double>()};
}
// Use namespace declaration for forward declaration
namespace wpi::apriltag {
namespace frc {
// C++ generated from resource files
std::string_view GetResource_2022_rapidreact_json();
@@ -145,7 +138,7 @@ std::string_view GetResource_2025_reefscape_andymark_json();
std::string_view GetResource_2026_rebuilt_welded_json();
std::string_view GetResource_2026_rebuilt_andymark_json();
} // namespace wpi::apriltag
} // namespace frc
AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
std::string_view fieldString;
@@ -175,6 +168,10 @@ AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
throw std::invalid_argument("Invalid Field");
}
wpi::util::json json = wpi::util::json::parse_or_throw(fieldString);
wpi::json json = wpi::json::parse(fieldString);
return json.get<AprilTagFieldLayout>();
}
AprilTagFieldLayout frc::LoadAprilTagLayoutField(AprilTagField field) {
return AprilTagFieldLayout::LoadField(field);
}

View File

@@ -2,11 +2,11 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "wpi/apriltag/AprilTagPoseEstimate.hpp"
#include "frc/apriltag/AprilTagPoseEstimate.h"
#include <algorithm>
using namespace wpi::apriltag;
using namespace frc;
double AprilTagPoseEstimate::GetAmbiguity() const {
auto min = (std::min)(error1, error2);

View File

@@ -2,11 +2,11 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "wpi/apriltag/AprilTagPoseEstimator.hpp"
#include "frc/apriltag/AprilTagPoseEstimator.h"
#include <Eigen/QR>
#include "wpi/apriltag/AprilTagDetection.hpp"
#include "frc/apriltag/AprilTagDetection.h"
#ifdef _WIN32
#pragma warning(disable : 4200)
@@ -19,7 +19,7 @@
#include "apriltag.h"
#include "apriltag_pose.h"
using namespace wpi::apriltag;
using namespace frc;
static Eigen::Matrix3d OrthogonalizeRotationMatrix(
const Eigen::Matrix3d& input) {
@@ -42,14 +42,14 @@ static Eigen::Matrix3d OrthogonalizeRotationMatrix(
return Q;
}
static wpi::math::Transform3d MakePose(const apriltag_pose_t& pose) {
static Transform3d MakePose(const apriltag_pose_t& pose) {
if (!pose.R || !pose.t) {
return {};
}
return {wpi::math::Translation3d{wpi::units::meter_t{pose.t->data[0]},
wpi::units::meter_t{pose.t->data[1]},
wpi::units::meter_t{pose.t->data[2]}},
wpi::math::Rotation3d{OrthogonalizeRotationMatrix(
return {Translation3d{units::meter_t{pose.t->data[0]},
units::meter_t{pose.t->data[1]},
units::meter_t{pose.t->data[2]}},
Rotation3d{OrthogonalizeRotationMatrix(
Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>{
pose.R->data})}};
}
@@ -80,7 +80,7 @@ static apriltag_detection_t MakeBasicDet(
return detection;
}
static wpi::math::Transform3d DoEstimateHomography(
static Transform3d DoEstimateHomography(
const apriltag_detection_t* detection,
const AprilTagPoseEstimator::Config& config) {
auto info = MakeDetectionInfo(detection, config);
@@ -89,13 +89,13 @@ static wpi::math::Transform3d DoEstimateHomography(
return MakePose(pose);
}
wpi::math::Transform3d AprilTagPoseEstimator::EstimateHomography(
Transform3d AprilTagPoseEstimator::EstimateHomography(
const AprilTagDetection& detection) const {
return DoEstimateHomography(
reinterpret_cast<const apriltag_detection_t*>(&detection), m_config);
}
wpi::math::Transform3d AprilTagPoseEstimator::EstimateHomography(
Transform3d AprilTagPoseEstimator::EstimateHomography(
std::span<const double, 9> homography) const {
auto detection = MakeBasicDet(homography, nullptr);
auto rv = DoEstimateHomography(&detection, m_config);
@@ -130,22 +130,21 @@ AprilTagPoseEstimate AprilTagPoseEstimator::EstimateOrthogonalIteration(
return rv;
}
static wpi::math::Transform3d DoEstimate(
const apriltag_detection_t* detection,
const AprilTagPoseEstimator::Config& config) {
static Transform3d DoEstimate(const apriltag_detection_t* detection,
const AprilTagPoseEstimator::Config& config) {
auto info = MakeDetectionInfo(detection, config);
apriltag_pose_t pose;
estimate_tag_pose(&info, &pose);
return MakePose(pose);
}
wpi::math::Transform3d AprilTagPoseEstimator::Estimate(
Transform3d AprilTagPoseEstimator::Estimate(
const AprilTagDetection& detection) const {
return DoEstimate(reinterpret_cast<const apriltag_detection_t*>(&detection),
m_config);
}
wpi::math::Transform3d AprilTagPoseEstimator::Estimate(
Transform3d AprilTagPoseEstimator::Estimate(
std::span<const double, 9> homography,
std::span<const double, 8> corners) const {
auto detection = MakeBasicDet(homography, &corners);

View File

@@ -8,15 +8,16 @@
#include <cstring>
#define WPI_RAWFRAME_JNI
#include "org_wpilib_vision_apriltag_jni_AprilTagJNI.h"
#include "wpi/apriltag/AprilTag.hpp"
#include "wpi/apriltag/AprilTagDetector.hpp"
#include "wpi/apriltag/AprilTagPoseEstimator.hpp"
#include "wpi/util/RawFrame.h"
#include "wpi/util/jni_util.hpp"
#include <wpi/RawFrame.h>
#include <wpi/jni_util.h>
using namespace wpi::apriltag;
using namespace wpi::util::java;
#include "edu_wpi_first_apriltag_jni_AprilTagJNI.h"
#include "frc/apriltag/AprilTag.h"
#include "frc/apriltag/AprilTagDetector.h"
#include "frc/apriltag/AprilTagPoseEstimator.h"
using namespace frc;
using namespace wpi::java;
static JavaVM* jvm = nullptr;
@@ -33,16 +34,16 @@ static JException illegalArgEx;
static JException nullPointerEx;
static const JClassInit classes[] = {
{"org/wpilib/vision/apriltag/AprilTagDetection", &detectionCls},
{"org/wpilib/vision/apriltag/AprilTagDetector$Config", &detectorConfigCls},
{"org/wpilib/vision/apriltag/AprilTagDetector$QuadThresholdParameters",
{"edu/wpi/first/apriltag/AprilTagDetection", &detectionCls},
{"edu/wpi/first/apriltag/AprilTagDetector$Config", &detectorConfigCls},
{"edu/wpi/first/apriltag/AprilTagDetector$QuadThresholdParameters",
&detectorQTPCls},
{"org/wpilib/vision/apriltag/AprilTagPoseEstimate", &poseEstimateCls},
{"org/wpilib/math/geometry/Quaternion", &quaternionCls},
{"org/wpilib/math/geometry/Rotation3d", &rotation3dCls},
{"org/wpilib/math/geometry/Transform3d", &transform3dCls},
{"org/wpilib/math/geometry/Translation3d", &translation3dCls},
{"org/wpilib/util/RawFrame", &rawFrameCls}};
{"edu/wpi/first/apriltag/AprilTagPoseEstimate", &poseEstimateCls},
{"edu/wpi/first/math/geometry/Quaternion", &quaternionCls},
{"edu/wpi/first/math/geometry/Rotation3d", &rotation3dCls},
{"edu/wpi/first/math/geometry/Transform3d", &transform3dCls},
{"edu/wpi/first/math/geometry/Translation3d", &translation3dCls},
{"edu/wpi/first/util/RawFrame", &rawFrameCls}};
static const JExceptionInit exceptions[] = {
{"java/lang/IllegalArgumentException", &illegalArgEx},
@@ -161,7 +162,7 @@ static AprilTagDetector::QuadThresholdParameters FromJavaDetectorQTP(
return {
FIELD(int, Int, minClusterPixels),
FIELD(int, Int, maxNumMaxima),
.criticalAngle = wpi::units::radian_t{static_cast<double>(
.criticalAngle = units::radian_t{static_cast<double>(
env->GetDoubleField(jparams, criticalAngleField))},
FIELD(float, Float, maxLineFitMSE),
FIELD(int, Int, minWhiteBlackDiff),
@@ -255,7 +256,7 @@ static jobject MakeJObject(
static_cast<jboolean>(params.deglitch));
}
static jobject MakeJObject(JNIEnv* env, const wpi::math::Translation3d& xlate) {
static jobject MakeJObject(JNIEnv* env, const Translation3d& xlate) {
static jmethodID constructor =
env->GetMethodID(translation3dCls, "<init>", "(DDD)V");
if (!constructor) {
@@ -267,7 +268,7 @@ static jobject MakeJObject(JNIEnv* env, const wpi::math::Translation3d& xlate) {
static_cast<jdouble>(xlate.Y()), static_cast<jdouble>(xlate.Z()));
}
static jobject MakeJObject(JNIEnv* env, const wpi::math::Quaternion& q) {
static jobject MakeJObject(JNIEnv* env, const Quaternion& q) {
static jmethodID constructor =
env->GetMethodID(quaternionCls, "<init>", "(DDDD)V");
if (!constructor) {
@@ -280,9 +281,9 @@ static jobject MakeJObject(JNIEnv* env, const wpi::math::Quaternion& q) {
static_cast<jdouble>(q.Z()));
}
static jobject MakeJObject(JNIEnv* env, const wpi::math::Rotation3d& rot) {
static jobject MakeJObject(JNIEnv* env, const Rotation3d& rot) {
static jmethodID constructor = env->GetMethodID(
rotation3dCls, "<init>", "(Lorg/wpilib/math/geometry/Quaternion;)V");
rotation3dCls, "<init>", "(Ledu/wpi/first/math/geometry/Quaternion;)V");
if (!constructor) {
return nullptr;
}
@@ -291,11 +292,11 @@ static jobject MakeJObject(JNIEnv* env, const wpi::math::Rotation3d& rot) {
return env->NewObject(rotation3dCls, constructor, q.obj());
}
static jobject MakeJObject(JNIEnv* env, const wpi::math::Transform3d& xform) {
static jobject MakeJObject(JNIEnv* env, const Transform3d& xform) {
static jmethodID constructor =
env->GetMethodID(transform3dCls, "<init>",
"(Lorg/wpilib/math/geometry/Translation3d;"
"Lorg/wpilib/math/geometry/Rotation3d;)V");
"(Ledu/wpi/first/math/geometry/Translation3d;"
"Ledu/wpi/first/math/geometry/Rotation3d;)V");
if (!constructor) {
return nullptr;
}
@@ -308,8 +309,8 @@ static jobject MakeJObject(JNIEnv* env, const wpi::math::Transform3d& xform) {
static jobject MakeJObject(JNIEnv* env, const AprilTagPoseEstimate& est) {
static jmethodID constructor =
env->GetMethodID(poseEstimateCls, "<init>",
"(Lorg/wpilib/math/geometry/Transform3d;"
"Lorg/wpilib/math/geometry/Transform3d;DD)V");
"(Ledu/wpi/first/math/geometry/Transform3d;"
"Ledu/wpi/first/math/geometry/Transform3d;DD)V");
if (!constructor) {
return nullptr;
}
@@ -324,36 +325,36 @@ static jobject MakeJObject(JNIEnv* env, const AprilTagPoseEstimate& est) {
extern "C" {
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: createDetector
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_createDetector
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_createDetector
(JNIEnv* env, jclass)
{
return reinterpret_cast<jlong>(new AprilTagDetector);
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: destroyDetector
* Signature: (J)V
*/
JNIEXPORT void JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_destroyDetector
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_destroyDetector
(JNIEnv* env, jclass, jlong det)
{
delete reinterpret_cast<AprilTagDetector*>(det);
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: setDetectorConfig
* Signature: (JLjava/lang/Object;)V
*/
JNIEXPORT void JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_setDetectorConfig
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_setDetectorConfig
(JNIEnv* env, jclass, jlong det, jobject config)
{
if (det == 0) {
@@ -365,12 +366,12 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_setDetectorConfig
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: getDetectorConfig
* Signature: (J)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_getDetectorConfig
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_getDetectorConfig
(JNIEnv* env, jclass, jlong det)
{
if (det == 0) {
@@ -382,12 +383,12 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_getDetectorConfig
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: setDetectorQTP
* Signature: (JLjava/lang/Object;)V
*/
JNIEXPORT void JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_setDetectorQTP
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_setDetectorQTP
(JNIEnv* env, jclass, jlong det, jobject params)
{
if (det == 0) {
@@ -399,12 +400,12 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_setDetectorQTP
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: getDetectorQTP
* Signature: (J)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_getDetectorQTP
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_getDetectorQTP
(JNIEnv* env, jclass, jlong det)
{
if (det == 0) {
@@ -417,12 +418,12 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_getDetectorQTP
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: addFamily
* Signature: (JLjava/lang/String;I)Z
*/
JNIEXPORT jboolean JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_addFamily
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_addFamily
(JNIEnv* env, jclass, jlong det, jstring fam, jint bitsCorrected)
{
if (det == 0) {
@@ -438,12 +439,12 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_addFamily
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: removeFamily
* Signature: (JLjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_removeFamily
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_removeFamily
(JNIEnv* env, jclass, jlong det, jstring fam)
{
if (det == 0) {
@@ -458,12 +459,12 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_removeFamily
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: clearFamilies
* Signature: (J)V
*/
JNIEXPORT void JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_clearFamilies
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_clearFamilies
(JNIEnv* env, jclass, jlong det)
{
if (det == 0) {
@@ -474,12 +475,12 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_clearFamilies
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: detect
* Signature: (JIIIJ)[Ljava/lang/Object;
*/
JNIEXPORT jobjectArray JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_detect
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_detect
(JNIEnv* env, jclass, jlong det, jint width, jint height, jint stride,
jlong bufAddr)
{
@@ -497,12 +498,12 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_detect
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: estimatePoseHomography
* Signature: ([DDDDDD)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_estimatePoseHomography
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePoseHomography
(JNIEnv* env, jclass, jdoubleArray homography, jdouble tagSize, jdouble fx,
jdouble fy, jdouble cx, jdouble cy)
{
@@ -516,18 +517,17 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_estimatePoseHomography
return nullptr;
}
AprilTagPoseEstimator estimator(
{wpi::units::meter_t{tagSize}, fx, fy, cx, cy});
AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
return MakeJObject(env, estimator.EstimateHomography(harr));
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: estimatePoseOrthogonalIteration
* Signature: ([D[DDDDDDI)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_estimatePoseOrthogonalIteration
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePoseOrthogonalIteration
(JNIEnv* env, jclass, jdoubleArray homography, jdoubleArray corners,
jdouble tagSize, jdouble fx, jdouble fy, jdouble cx, jdouble cy, jint nIters)
{
@@ -553,19 +553,18 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_estimatePoseOrthogonalIteration
return nullptr;
}
AprilTagPoseEstimator estimator(
{wpi::units::meter_t{tagSize}, fx, fy, cx, cy});
AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
return MakeJObject(env,
estimator.EstimateOrthogonalIteration(harr, carr, nIters));
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: estimatePose
* Signature: ([D[DDDDDD)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_estimatePose
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePose
(JNIEnv* env, jclass, jdoubleArray homography, jdoubleArray corners,
jdouble tagSize, jdouble fx, jdouble fy, jdouble cx, jdouble cy)
{
@@ -591,46 +590,45 @@ Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_estimatePose
return nullptr;
}
AprilTagPoseEstimator estimator(
{wpi::units::meter_t{tagSize}, fx, fy, cx, cy});
AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
return MakeJObject(env, estimator.Estimate(harr, carr));
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: generate16h5AprilTagImage
* Signature: (Ljava/lang/Object;JI)V
*/
JNIEXPORT void JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_generate16h5AprilTagImage
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_generate16h5AprilTagImage
(JNIEnv* env, jclass, jobject frameObj, jlong framePtr, jint id)
{
auto* frame = reinterpret_cast<wpi::util::RawFrame*>(framePtr);
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
if (!frame) {
nullPointerEx.Throw(env, "frame is null");
return;
}
bool newData = AprilTag::Generate16h5AprilTagImage(frame, id);
wpi::util::SetFrameData(env, rawFrameCls, frameObj, *frame, newData);
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame, newData);
}
/*
* Class: org_wpilib_vision_apriltag_jni_AprilTagJNI
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: generate36h11AprilTagImage
* Signature: (Ljava/lang/Object;JI)V
*/
JNIEXPORT void JNICALL
Java_org_wpilib_vision_apriltag_jni_AprilTagJNI_generate36h11AprilTagImage
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_generate36h11AprilTagImage
(JNIEnv* env, jclass, jobject frameObj, jlong framePtr, jint id)
{
auto* frame = reinterpret_cast<wpi::util::RawFrame*>(framePtr);
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
if (!frame) {
nullPointerEx.Throw(env, "frame is null");
return;
}
// function might reallocate
bool newData = AprilTag::Generate36h11AprilTagImage(frame, id);
wpi::util::SetFrameData(env, rawFrameCls, frameObj, *frame, newData);
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame, newData);
}
} // extern "C"

View File

@@ -0,0 +1,37 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <wpi/RawFrame.h>
#include <wpi/SymbolExports.h>
#include <wpi/json_fwd.h>
#include "frc/geometry/Pose3d.h"
namespace frc {
/**
* Represents an AprilTag's metadata.
*/
struct WPILIB_DLLEXPORT AprilTag {
/// The tag's ID.
int ID;
/// The tag's pose.
Pose3d pose;
bool operator==(const AprilTag&) const = default;
static bool Generate36h11AprilTagImage(wpi::RawFrame* frame, int id);
static bool Generate16h5AprilTagImage(wpi::RawFrame* frame, int id);
};
WPILIB_DLLEXPORT
void to_json(wpi::json& json, const AprilTag& apriltag);
WPILIB_DLLEXPORT
void from_json(const wpi::json& json, AprilTag& apriltag);
} // namespace frc

View File

@@ -0,0 +1,160 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <stdint.h>
#include <span>
#include <string_view>
#include <Eigen/Core>
#include <wpi/SymbolExports.h>
namespace frc {
/**
* A detection of an AprilTag tag.
*/
class WPILIB_DLLEXPORT AprilTagDetection final {
public:
AprilTagDetection() = delete;
AprilTagDetection(const AprilTagDetection&) = delete;
AprilTagDetection& operator=(const AprilTagDetection&) = delete;
/** A point. Used for center and corner points. */
struct Point {
double x;
double y;
};
/**
* Gets the decoded tag's family name.
*
* @return Decoded family name
*/
std::string_view GetFamily() const;
/**
* Gets the decoded ID of the tag.
*
* @return Decoded ID
*/
int GetId() const { return id; }
/**
* Gets how many error bits were corrected. Note: accepting large numbers of
* corrected errors leads to greatly increased false positive rates.
* NOTE: As of this implementation, the detector cannot detect tags with
* a hamming distance greater than 2.
*
* @return Hamming distance (number of corrected error bits)
*/
int GetHamming() const { return hamming; }
/**
* Gets a measure of the quality of the binary decoding process: the
* average difference between the intensity of a data bit versus
* the decision threshold. Higher numbers roughly indicate better
* decodes. This is a reasonable measure of detection accuracy
* only for very small tags-- not effective for larger tags (where
* we could have sampled anywhere within a bit cell and still
* gotten a good detection.)
*
* @return Decision margin
*/
float GetDecisionMargin() const { return decision_margin; }
/**
* Gets the 3x3 homography matrix describing the projection from an
* "ideal" tag (with corners at (-1,1), (1,1), (1,-1), and (-1,
* -1)) to pixels in the image.
*
* @return Homography matrix data
*/
std::span<const double, 9> GetHomography() const;
/**
* Gets the 3x3 homography matrix describing the projection from an
* "ideal" tag (with corners at (-1,1), (1,1), (1,-1), and (-1,
* -1)) to pixels in the image.
*
* @return Homography matrix
*/
Eigen::Matrix3d GetHomographyMatrix() const;
/**
* Gets the center of the detection in image pixel coordinates.
*
* @return Center point
*/
const Point& GetCenter() const { return *reinterpret_cast<const Point*>(c); }
/**
* Gets a corner of the tag in image pixel coordinates. These always
* wrap counter-clock wise around the tag. Index 0 is the bottom left corner.
*
* @param ndx Corner index (range is 0-3, inclusive)
* @return Corner point
*/
const Point& GetCorner(int ndx) const {
return *reinterpret_cast<const Point*>(p[ndx]);
}
/**
* Gets the corners of the tag in image pixel coordinates. These always
* wrap counter-clock wise around the tag. The first set of corner coordinates
* are the coordinates for the bottom left corner.
*
* @param cornersBuf Corner point array (X and Y for each corner in order)
* @return Corner point array (copy of cornersBuf span)
*/
std::span<double, 8> GetCorners(std::span<double, 8> cornersBuf) const {
for (int i = 0; i < 4; i++) {
cornersBuf[i * 2] = p[i][0];
cornersBuf[i * 2 + 1] = p[i][1];
}
return cornersBuf;
}
private:
// This class *must* be standard-layout-compatible with apriltag_detection
// as we use reinterpret_cast from that structure. This means the below
// members must exactly match the contents of the apriltag_detection struct.
// The tag family.
void* family;
// The decoded ID of the tag.
int id;
// How many error bits were corrected? Note: accepting large numbers of
// corrected errors leads to greatly increased false positive rates.
// NOTE: As of this implementation, the detector cannot detect tags with
// a hamming distance greater than 2.
int hamming;
// A measure of the quality of the binary decoding process: the
// average difference between the intensity of a data bit versus
// the decision threshold. Higher numbers roughly indicate better
// decodes. This is a reasonable measure of detection accuracy
// only for very small tags-- not effective for larger tags (where
// we could have sampled anywhere within a bit cell and still
// gotten a good detection.)
float decision_margin;
// The 3x3 homography matrix describing the projection from an
// "ideal" tag (with corners at (-1,1), (1,1), (1,-1), and (-1,
// -1)) to pixels in the image.
void* H;
// The center of the detection in image pixel coordinates.
double c[2];
// The corners of the tag in image pixel coordinates. These always
// wrap counter-clock wise around the tag.
double p[4][2];
};
} // namespace frc

View File

@@ -0,0 +1,262 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <stdint.h>
#include <memory>
#include <span>
#include <string_view>
#include <utility>
#include <units/angle.h>
#include <wpi/StringMap.h>
#include <wpi/SymbolExports.h>
#include "frc/apriltag/AprilTagDetection.h"
namespace frc {
/**
* An AprilTag detector engine. This is expensive to set up and tear down, so
* most use cases should only create one of these, add a family to it, set up
* any other configuration, and repeatedly call Detect().
*/
class WPILIB_DLLEXPORT AprilTagDetector {
public:
/** Detector configuration. */
struct Config {
bool operator==(const Config&) const = default;
/**
* How many threads should be used for computation. Default is
* single-threaded operation (1 thread).
*/
int numThreads = 1;
/**
* Quad decimation. Detection of quads can be done on a lower-resolution
* image, improving speed at a cost of pose accuracy and a slight decrease
* in detection rate. Decoding the binary payload is still done at full
* resolution. Default is 2.0.
*/
float quadDecimate = 2.0f;
/**
* What Gaussian blur should be applied to the segmented image (used for
* quad detection). Very noisy images benefit from non-zero values (e.g.
* 0.8). Default is 0.0.
*/
float quadSigma = 0.0f;
/**
* When true, the edges of the each quad are adjusted to "snap to" strong
* gradients nearby. This is useful when decimation is employed, as it can
* increase the quality of the initial quad estimate substantially.
* Generally recommended to be on (true). Default is true.
*
* Very computationally inexpensive. Option is ignored if
* quad_decimate = 1.
*/
bool refineEdges = true;
/**
* How much sharpening should be done to decoded images. This can help
* decode small tags but may or may not help in odd lighting conditions or
* low light conditions. Default is 0.25.
*/
double decodeSharpening = 0.25;
/**
* Debug mode. When true, the decoder writes a variety of debugging images
* to the current working directory at various stages through the detection
* process. This is slow and should *not* be used on space-limited systems
* such as the RoboRIO. Default is disabled (false).
*/
bool debug = false;
};
/** Quad threshold parameters. */
struct QuadThresholdParameters {
bool operator==(const QuadThresholdParameters&) const = default;
/**
* Threshold used to reject quads containing too few pixels. Default is 300
* pixels.
*/
int minClusterPixels = 300;
/**
* How many corner candidates to consider when segmenting a group of pixels
* into a quad. Default is 10.
*/
int maxNumMaxima = 10;
/**
* Critical angle. The detector will reject quads where pairs of edges have
* angles that are close to straight or close to 180 degrees. Zero means
* that no quads are rejected. Default is 45 degrees.
*/
units::radian_t criticalAngle = 45_deg;
/**
* When fitting lines to the contours, the maximum mean squared error
* allowed. This is useful in rejecting contours that are far from being
* quad shaped; rejecting these quads "early" saves expensive decoding
* processing. Default is 10.0.
*/
float maxLineFitMSE = 10.0f;
/**
* Minimum brightness offset. When we build our model of black & white
* pixels, we add an extra check that the white model must be (overall)
* brighter than the black model. How much brighter? (in pixel values,
* [0,255]). Default is 5.
*/
int minWhiteBlackDiff = 5;
/**
* Whether the thresholded image be should be deglitched. Only useful for
* very noisy images. Default is disabled (false).
*/
bool deglitch = false;
};
/**
* Array of detection results. Each array element is a pointer to an
* AprilTagDetection.
*/
class WPILIB_DLLEXPORT Results
: public std::span<AprilTagDetection const* const> {
struct private_init {};
friend class AprilTagDetector;
public:
Results() = default;
Results(void* impl, const private_init&);
~Results() { Destroy(); }
Results(const Results&) = delete;
Results& operator=(const Results&) = delete;
Results(Results&& rhs) : span{std::move(rhs)}, m_impl{rhs.m_impl} {
rhs.m_impl = nullptr;
}
Results& operator=(Results&& rhs);
private:
void Destroy();
void* m_impl = nullptr;
};
AprilTagDetector();
~AprilTagDetector() { Destroy(); }
AprilTagDetector(const AprilTagDetector&) = delete;
AprilTagDetector& operator=(const AprilTagDetector&) = delete;
AprilTagDetector(AprilTagDetector&& rhs)
: m_impl{rhs.m_impl},
m_families{std::move(rhs.m_families)},
m_qtpCriticalAngle{rhs.m_qtpCriticalAngle} {
rhs.m_impl = nullptr;
}
AprilTagDetector& operator=(AprilTagDetector&& rhs);
/**
* @{
* @name Configuration functions
*/
/**
* Sets detector configuration.
*
* @param config Configuration
*/
void SetConfig(const Config& config);
/**
* Gets detector configuration.
*
* @return Configuration
*/
Config GetConfig() const;
/**
* Sets quad threshold parameters.
*
* @param params Parameters
*/
void SetQuadThresholdParameters(const QuadThresholdParameters& params);
/**
* Gets quad threshold parameters.
*
* @return Parameters
*/
QuadThresholdParameters GetQuadThresholdParameters() const;
/** @} */
/**
* @{
* @name Tag family functions
*/
/**
* Adds a family of tags to be detected.
*
* @param fam Family name, e.g. "tag16h5"
* @param bitsCorrected Maximum number of bits to correct
* @return False if family can't be found
*/
bool AddFamily(std::string_view fam, int bitsCorrected = 2);
/**
* Removes a family of tags from the detector.
*
* @param fam Family name, e.g. "tag16h5"
*/
void RemoveFamily(std::string_view fam);
/**
* Unregister all families.
*/
void ClearFamilies();
/** @} */
/**
* Detect tags from an 8-bit image.
* The image must be grayscale.
*
* @param width width of the image
* @param height height of the image
* @param stride number of bytes between image rows (often the same as width)
* @param buf image buffer
* @return Results (array of AprilTagDetection pointers)
*/
Results Detect(int width, int height, int stride, uint8_t* buf);
/**
* Detect tags from an 8-bit image.
* The image must be grayscale.
*
* @param width width of the image
* @param height height of the image
* @param buf image buffer
* @return Results (array of AprilTagDetection pointers)
*/
Results Detect(int width, int height, uint8_t* buf) {
return Detect(width, height, width, buf);
}
private:
void Destroy();
void DestroyFamilies();
void DestroyFamily(std::string_view name, void* data);
void* m_impl;
wpi::StringMap<void*> m_families;
units::radian_t m_qtpCriticalAngle = 10_deg;
};
} // namespace frc

View File

@@ -0,0 +1,18 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <opencv2/core/mat.hpp>
#include "frc/apriltag/AprilTagDetector.h"
namespace frc {
inline AprilTagDetector::Results AprilTagDetect(AprilTagDetector& detector,
cv::Mat& image) {
return detector.Detect(image.cols, image.rows, image.data);
}
} // namespace frc

View File

@@ -0,0 +1,175 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <optional>
#include <string_view>
#include <unordered_map>
#include <vector>
#include <units/length.h>
#include <wpi/SymbolExports.h>
#include <wpi/json_fwd.h>
#include "frc/apriltag/AprilTag.h"
#include "frc/apriltag/AprilTagFields.h"
#include "frc/geometry/Pose3d.h"
namespace frc {
/**
* Class for representing a layout of AprilTags on a field and reading them from
* a JSON format.
*
* The JSON format contains two top-level objects, "tags" and "field".
* The "tags" object is a list of all AprilTags contained within a layout. Each
* AprilTag serializes to a JSON object containing an ID and a Pose3d. The
* "field" object is a descriptor of the size of the field in meters with
* "width" and "length" values. This is to account for arbitrary field sizes
* when transforming the poses.
*
* Pose3ds in the JSON are measured using the normal FRC coordinate system, NWU
* with the origin at the bottom-right corner of the blue alliance wall.
* SetOrigin(OriginPosition) can be used to change the poses returned from
* GetTagPose(int) to be from the perspective of a specific alliance.
*
* Tag poses represent the center of the tag, with a zero rotation representing
* a tag that is upright and facing away from the (blue) alliance wall (that is,
* towards the opposing alliance). */
class WPILIB_DLLEXPORT AprilTagFieldLayout {
public:
/**
* Common origin positions for the AprilTag coordinate system.
*/
enum class OriginPosition {
/// Blue alliance wall, right side.
kBlueAllianceWallRightSide,
/// Red alliance wall, right side.
kRedAllianceWallRightSide,
};
/**
* Loads an AprilTagFieldLayout from a predefined field
*
* @param field The predefined field
* @return AprilTagFieldLayout of the field
*/
static AprilTagFieldLayout LoadField(AprilTagField field);
AprilTagFieldLayout() = default;
/**
* Construct a new AprilTagFieldLayout with values imported from a JSON file.
*
* @param path Path of the JSON file to import from.
*/
explicit AprilTagFieldLayout(std::string_view path);
/**
* Construct a new AprilTagFieldLayout from a vector of AprilTag objects.
*
* @param apriltags Vector of AprilTags.
* @param fieldLength Length of field the layout is representing.
* @param fieldWidth Width of field the layout is representing.
*/
AprilTagFieldLayout(std::vector<AprilTag> apriltags,
units::meter_t fieldLength, units::meter_t fieldWidth);
/**
* Returns the length of the field the layout is representing.
* @return length
*/
units::meter_t GetFieldLength() const;
/**
* Returns the length of the field the layout is representing.
* @return width
*/
units::meter_t GetFieldWidth() const;
/**
* Returns a vector of all the april tags used in this layout.
* @return list of tags
*/
std::vector<AprilTag> GetTags() const;
/**
* Sets the origin based on a predefined enumeration of coordinate frame
* origins. The origins are calculated from the field dimensions.
*
* This transforms the Pose3ds returned by GetTagPose(int) to return the
* correct pose relative to a predefined coordinate frame.
*
* @param origin The predefined origin
*/
void SetOrigin(OriginPosition origin);
/**
* Sets the origin for tag pose transformation.
*
* This transforms the Pose3ds returned by GetTagPose(int) to return the
* correct pose relative to the provided origin.
*
* @param origin The new origin for tag transformations
*/
void SetOrigin(const Pose3d& origin);
/**
* Returns the origin used for tag pose transformation.
* @return the origin
*/
Pose3d GetOrigin() const;
/**
* Gets an AprilTag pose by its ID.
*
* @param ID The ID of the tag.
* @return The pose corresponding to the ID that was passed in or an empty
* optional if a tag with that ID is not found.
*/
std::optional<Pose3d> GetTagPose(int ID) const;
/**
* Serializes an AprilTagFieldLayout to a JSON file.
*
* @param path The path to write the JSON file to.
*/
void Serialize(std::string_view path);
/*
* Checks equality between this AprilTagFieldLayout and another object.
*/
bool operator==(const AprilTagFieldLayout&) const = default;
private:
std::unordered_map<int, AprilTag> m_apriltags;
units::meter_t m_fieldLength;
units::meter_t m_fieldWidth;
Pose3d m_origin;
friend WPILIB_DLLEXPORT void to_json(wpi::json& json,
const AprilTagFieldLayout& layout);
friend WPILIB_DLLEXPORT void from_json(const wpi::json& json,
AprilTagFieldLayout& layout);
};
WPILIB_DLLEXPORT
void to_json(wpi::json& json, const AprilTagFieldLayout& layout);
WPILIB_DLLEXPORT
void from_json(const wpi::json& json, AprilTagFieldLayout& layout);
/**
* Loads an AprilTagFieldLayout from a predefined field
*
* @param field The predefined field
* @return AprilTagFieldLayout of the field
* @deprecated Use AprilTagFieldLayout::LoadField() instead
*/
[[deprecated("Use AprilTagFieldLayout::LoadField() instead")]]
WPILIB_DLLEXPORT AprilTagFieldLayout
LoadAprilTagLayoutField(AprilTagField field);
} // namespace frc

View File

@@ -0,0 +1,39 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <string_view>
#include <wpi/SymbolExports.h>
namespace frc {
/**
* Loadable AprilTag field layouts.
*/
enum class AprilTagField {
/// 2022 Rapid React.
k2022RapidReact,
/// 2023 Charged Up.
k2023ChargedUp,
/// 2024 Crescendo.
k2024Crescendo,
/// 2025 Reefscape AndyMark (see TU12).
k2025ReefscapeAndyMark,
/// 2025 Reefscape Welded (see TU12).
k2025ReefscapeWelded,
/// 2026 Rebuilt Andymark.
k2026RebuiltAndyMark,
/// 2026 Rebuilt Welded.
k2026RebuiltWelded,
/// Alias to the current game.
kDefaultField = k2026RebuiltWelded,
// This is a placeholder for denoting the last supported field. This should
// always be the last entry in the enum and should not be used by users
kNumFields,
};
} // namespace frc

View File

@@ -0,0 +1,36 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <wpi/SymbolExports.h>
#include "frc/geometry/Transform3d.h"
namespace frc {
/** A pair of AprilTag pose estimates. */
struct WPILIB_DLLEXPORT AprilTagPoseEstimate {
/** Pose 1. */
Transform3d pose1;
/** Pose 2. */
Transform3d pose2;
/** Object-space error of pose 1. */
double error1;
/** Object-space error of pose 2. */
double error2;
/**
* Gets the ratio of pose reprojection errors, called ambiguity. Numbers
* above 0.2 are likely to be ambiguous.
*
* @return The ratio of pose reprojection errors.
*/
double GetAmbiguity() const;
};
} // namespace frc

View File

@@ -0,0 +1,145 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <span>
#include <units/length.h>
#include <wpi/SymbolExports.h>
#include "frc/apriltag/AprilTagPoseEstimate.h"
#include "frc/geometry/Transform3d.h"
namespace frc {
class AprilTagDetection;
/** Pose estimators for AprilTag tags. */
class WPILIB_DLLEXPORT AprilTagPoseEstimator {
public:
/** Configuration for the pose estimator. */
struct Config {
bool operator==(const Config&) const = default;
/** The tag size. */
units::meter_t tagSize;
/** Camera horizontal focal length, in pixels. */
double fx;
/** Camera vertical focal length, in pixels. */
double fy;
/** Camera horizontal focal center, in pixels. */
double cx;
/** Camera vertical focal center, in pixels. */
double cy;
};
/**
* Creates estimator.
*
* @param config Configuration
*/
explicit AprilTagPoseEstimator(const Config& config) : m_config{config} {}
/**
* Sets estimator configuration.
*
* @param config Configuration
*/
void SetConfig(const Config& config) { m_config = config; }
/**
* Gets estimator configuration.
*
* @return Configuration
*/
const Config& GetConfig() const { return m_config; }
/**
* Estimates the pose of the tag using the homography method described in [1].
*
* @param detection Tag detection
* @return Pose estimate
*/
Transform3d EstimateHomography(const AprilTagDetection& detection) const;
/**
* Estimates the pose of the tag using the homography method described in [1].
*
* @param homography Homography 3x3 matrix data
* @return Pose estimate
*/
Transform3d EstimateHomography(std::span<const double, 9> homography) const;
/**
* Estimates the pose of the tag. This returns one or two possible poses for
* the tag, along with the object-space error of each.
*
* This uses the homography method described in [1] for the initial estimate.
* Then Orthogonal Iteration [2] is used to refine this estimate. Then [3] is
* used to find a potential second local minima and Orthogonal Iteration is
* used to refine this second estimate.
*
* [1]: E. Olson, “Apriltag: A robust and flexible visual fiducial system,” in
* 2011 IEEE International Conference on Robotics and Automation,
* May 2011, pp. 34003407.
* [2]: Lu, G. D. Hager and E. Mjolsness, "Fast and globally convergent pose
* estimation from video images," in IEEE Transactions on Pattern
* Analysis and Machine Intelligence, vol. 22, no. 6, pp. 610-622, June 2000.
* doi: 10.1109/34.862199
* [3]: Schweighofer and A. Pinz, "Robust Pose Estimation from a Planar
* Target," in IEEE Transactions on Pattern Analysis and Machine Intelligence,
* vol. 28, no. 12, pp. 2024-2030, Dec. 2006. doi: 10.1109/TPAMI.2006.252
*
* @param detection Tag detection
* @param nIters Number of iterations
* @return Initial and (possibly) second pose estimates
*/
AprilTagPoseEstimate EstimateOrthogonalIteration(
const AprilTagDetection& detection, int nIters) const;
/**
* Estimates the pose of the tag. This returns one or two possible poses for
* the tag, along with the object-space error of each.
*
* @param homography Homography 3x3 matrix data
* @param corners Corner point array (X and Y for each corner in order)
* @param nIters Number of iterations
* @return Initial and (possibly) second pose estimates
*/
AprilTagPoseEstimate EstimateOrthogonalIteration(
std::span<const double, 9> homography, std::span<const double, 8> corners,
int nIters) const;
/**
* Estimates tag pose. This method is an easier to use interface to
* EstimatePoseOrthogonalIteration(), running 50 iterations and returning the
* pose with the lower object-space error.
*
* @param detection Tag detection
* @return Pose estimate
*/
Transform3d Estimate(const AprilTagDetection& detection) const;
/**
* Estimates tag pose. This method is an easier to use interface to
* EstimatePoseOrthogonalIteration(), running 50 iterations and returning the
* pose with the lower object-space error.
*
* @param homography Homography 3x3 matrix data
* @param corners Corner point array (X and Y for each corner in order)
* @return Pose estimate
*/
Transform3d Estimate(std::span<const double, 9> homography,
std::span<const double, 8> corners) const;
private:
Config m_config;
};
} // namespace frc

View File

@@ -1,39 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include "wpi/math/geometry/Pose3d.hpp"
#include "wpi/util/RawFrame.hpp"
#include "wpi/util/SymbolExports.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::apriltag {
/**
* Represents an AprilTag's metadata.
*/
struct WPILIB_DLLEXPORT AprilTag {
/// The tag's ID.
int ID;
/// The tag's pose.
wpi::math::Pose3d pose;
bool operator==(const AprilTag&) const = default;
static bool Generate36h11AprilTagImage(wpi::util::RawFrame* frame, int id);
static bool Generate16h5AprilTagImage(wpi::util::RawFrame* frame, int id);
};
WPILIB_DLLEXPORT
void to_json(wpi::util::json& json, const AprilTag& apriltag);
WPILIB_DLLEXPORT
void from_json(const wpi::util::json& json, AprilTag& apriltag);
} // namespace wpi::apriltag

View File

@@ -1,161 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <stdint.h>
#include <span>
#include <string_view>
#include <Eigen/Core>
#include "wpi/util/SymbolExports.hpp"
namespace wpi::apriltag {
/**
* A detection of an AprilTag tag.
*/
class WPILIB_DLLEXPORT AprilTagDetection final {
public:
AprilTagDetection() = delete;
AprilTagDetection(const AprilTagDetection&) = delete;
AprilTagDetection& operator=(const AprilTagDetection&) = delete;
/** A point. Used for center and corner points. */
struct Point {
double x;
double y;
};
/**
* Gets the decoded tag's family name.
*
* @return Decoded family name
*/
std::string_view GetFamily() const;
/**
* Gets the decoded ID of the tag.
*
* @return Decoded ID
*/
int GetId() const { return id; }
/**
* Gets how many error bits were corrected. Note: accepting large numbers of
* corrected errors leads to greatly increased false positive rates.
* NOTE: As of this implementation, the detector cannot detect tags with
* a hamming distance greater than 2.
*
* @return Hamming distance (number of corrected error bits)
*/
int GetHamming() const { return hamming; }
/**
* Gets a measure of the quality of the binary decoding process: the
* average difference between the intensity of a data bit versus
* the decision threshold. Higher numbers roughly indicate better
* decodes. This is a reasonable measure of detection accuracy
* only for very small tags-- not effective for larger tags (where
* we could have sampled anywhere within a bit cell and still
* gotten a good detection.)
*
* @return Decision margin
*/
float GetDecisionMargin() const { return decision_margin; }
/**
* Gets the 3x3 homography matrix describing the projection from an
* "ideal" tag (with corners at (-1,1), (1,1), (1,-1), and (-1,
* -1)) to pixels in the image.
*
* @return Homography matrix data
*/
std::span<const double, 9> GetHomography() const;
/**
* Gets the 3x3 homography matrix describing the projection from an
* "ideal" tag (with corners at (-1,1), (1,1), (1,-1), and (-1,
* -1)) to pixels in the image.
*
* @return Homography matrix
*/
Eigen::Matrix3d GetHomographyMatrix() const;
/**
* Gets the center of the detection in image pixel coordinates.
*
* @return Center point
*/
const Point& GetCenter() const { return *reinterpret_cast<const Point*>(c); }
/**
* Gets a corner of the tag in image pixel coordinates. These always
* wrap counter-clock wise around the tag. Index 0 is the bottom left corner.
*
* @param ndx Corner index (range is 0-3, inclusive)
* @return Corner point
*/
const Point& GetCorner(int ndx) const {
return *reinterpret_cast<const Point*>(p[ndx]);
}
/**
* Gets the corners of the tag in image pixel coordinates. These always
* wrap counter-clock wise around the tag. The first set of corner coordinates
* are the coordinates for the bottom left corner.
*
* @param cornersBuf Corner point array (X and Y for each corner in order)
* @return Corner point array (copy of cornersBuf span)
*/
std::span<double, 8> GetCorners(std::span<double, 8> cornersBuf) const {
for (int i = 0; i < 4; i++) {
cornersBuf[i * 2] = p[i][0];
cornersBuf[i * 2 + 1] = p[i][1];
}
return cornersBuf;
}
private:
// This class *must* be standard-layout-compatible with apriltag_detection
// as we use reinterpret_cast from that structure. This means the below
// members must exactly match the contents of the apriltag_detection struct.
// The tag family.
void* family;
// The decoded ID of the tag.
int id;
// How many error bits were corrected? Note: accepting large numbers of
// corrected errors leads to greatly increased false positive rates.
// NOTE: As of this implementation, the detector cannot detect tags with
// a hamming distance greater than 2.
int hamming;
// A measure of the quality of the binary decoding process: the
// average difference between the intensity of a data bit versus
// the decision threshold. Higher numbers roughly indicate better
// decodes. This is a reasonable measure of detection accuracy
// only for very small tags-- not effective for larger tags (where
// we could have sampled anywhere within a bit cell and still
// gotten a good detection.)
float decision_margin;
// The 3x3 homography matrix describing the projection from an
// "ideal" tag (with corners at (-1,1), (1,1), (1,-1), and (-1,
// -1)) to pixels in the image.
void* H;
// The center of the detection in image pixel coordinates.
double c[2];
// The corners of the tag in image pixel coordinates. These always
// wrap counter-clock wise around the tag.
double p[4][2];
};
} // namespace wpi::apriltag

View File

@@ -1,261 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <stdint.h>
#include <memory>
#include <span>
#include <string_view>
#include <utility>
#include "wpi/apriltag/AprilTagDetection.hpp"
#include "wpi/units/angle.hpp"
#include "wpi/util/StringMap.hpp"
#include "wpi/util/SymbolExports.hpp"
namespace wpi::apriltag {
/**
* An AprilTag detector engine. This is expensive to set up and tear down, so
* most use cases should only create one of these, add a family to it, set up
* any other configuration, and repeatedly call Detect().
*/
class WPILIB_DLLEXPORT AprilTagDetector {
public:
/** Detector configuration. */
struct Config {
bool operator==(const Config&) const = default;
/**
* How many threads should be used for computation. Default is
* single-threaded operation (1 thread).
*/
int numThreads = 1;
/**
* Quad decimation. Detection of quads can be done on a lower-resolution
* image, improving speed at a cost of pose accuracy and a slight decrease
* in detection rate. Decoding the binary payload is still done at full
* resolution. Default is 2.0.
*/
float quadDecimate = 2.0f;
/**
* What Gaussian blur should be applied to the segmented image (used for
* quad detection). Very noisy images benefit from non-zero values (e.g.
* 0.8). Default is 0.0.
*/
float quadSigma = 0.0f;
/**
* When true, the edges of the each quad are adjusted to "snap to" strong
* gradients nearby. This is useful when decimation is employed, as it can
* increase the quality of the initial quad estimate substantially.
* Generally recommended to be on (true). Default is true.
*
* Very computationally inexpensive. Option is ignored if
* quad_decimate = 1.
*/
bool refineEdges = true;
/**
* How much sharpening should be done to decoded images. This can help
* decode small tags but may or may not help in odd lighting conditions or
* low light conditions. Default is 0.25.
*/
double decodeSharpening = 0.25;
/**
* Debug mode. When true, the decoder writes a variety of debugging images
* to the current working directory at various stages through the detection
* process. This is slow and should *not* be used on space-limited systems
* such as the RoboRIO. Default is disabled (false).
*/
bool debug = false;
};
/** Quad threshold parameters. */
struct QuadThresholdParameters {
bool operator==(const QuadThresholdParameters&) const = default;
/**
* Threshold used to reject quads containing too few pixels. Default is 300
* pixels.
*/
int minClusterPixels = 300;
/**
* How many corner candidates to consider when segmenting a group of pixels
* into a quad. Default is 10.
*/
int maxNumMaxima = 10;
/**
* Critical angle. The detector will reject quads where pairs of edges have
* angles that are close to straight or close to 180 degrees. Zero means
* that no quads are rejected. Default is 45 degrees.
*/
wpi::units::radian_t criticalAngle = 45_deg;
/**
* When fitting lines to the contours, the maximum mean squared error
* allowed. This is useful in rejecting contours that are far from being
* quad shaped; rejecting these quads "early" saves expensive decoding
* processing. Default is 10.0.
*/
float maxLineFitMSE = 10.0f;
/**
* Minimum brightness offset. When we build our model of black & white
* pixels, we add an extra check that the white model must be (overall)
* brighter than the black model. How much brighter? (in pixel values,
* [0,255]). Default is 5.
*/
int minWhiteBlackDiff = 5;
/**
* Whether the thresholded image be should be deglitched. Only useful for
* very noisy images. Default is disabled (false).
*/
bool deglitch = false;
};
/**
* Array of detection results. Each array element is a pointer to an
* AprilTagDetection.
*/
class WPILIB_DLLEXPORT Results
: public std::span<AprilTagDetection const* const> {
struct private_init {};
friend class AprilTagDetector;
public:
Results() = default;
Results(void* impl, const private_init&);
~Results() { Destroy(); }
Results(const Results&) = delete;
Results& operator=(const Results&) = delete;
Results(Results&& rhs) : span{std::move(rhs)}, m_impl{rhs.m_impl} {
rhs.m_impl = nullptr;
}
Results& operator=(Results&& rhs);
private:
void Destroy();
void* m_impl = nullptr;
};
AprilTagDetector();
~AprilTagDetector() { Destroy(); }
AprilTagDetector(const AprilTagDetector&) = delete;
AprilTagDetector& operator=(const AprilTagDetector&) = delete;
AprilTagDetector(AprilTagDetector&& rhs)
: m_impl{rhs.m_impl},
m_families{std::move(rhs.m_families)},
m_qtpCriticalAngle{rhs.m_qtpCriticalAngle} {
rhs.m_impl = nullptr;
}
AprilTagDetector& operator=(AprilTagDetector&& rhs);
/**
* @{
* @name Configuration functions
*/
/**
* Sets detector configuration.
*
* @param config Configuration
*/
void SetConfig(const Config& config);
/**
* Gets detector configuration.
*
* @return Configuration
*/
Config GetConfig() const;
/**
* Sets quad threshold parameters.
*
* @param params Parameters
*/
void SetQuadThresholdParameters(const QuadThresholdParameters& params);
/**
* Gets quad threshold parameters.
*
* @return Parameters
*/
QuadThresholdParameters GetQuadThresholdParameters() const;
/** @} */
/**
* @{
* @name Tag family functions
*/
/**
* Adds a family of tags to be detected.
*
* @param fam Family name, e.g. "tag16h5"
* @param bitsCorrected Maximum number of bits to correct
* @return False if family can't be found
*/
bool AddFamily(std::string_view fam, int bitsCorrected = 2);
/**
* Removes a family of tags from the detector.
*
* @param fam Family name, e.g. "tag16h5"
*/
void RemoveFamily(std::string_view fam);
/**
* Unregister all families.
*/
void ClearFamilies();
/** @} */
/**
* Detect tags from an 8-bit image.
* The image must be grayscale.
*
* @param width width of the image
* @param height height of the image
* @param stride number of bytes between image rows (often the same as width)
* @param buf image buffer
* @return Results (array of AprilTagDetection pointers)
*/
Results Detect(int width, int height, int stride, uint8_t* buf);
/**
* Detect tags from an 8-bit image.
* The image must be grayscale.
*
* @param width width of the image
* @param height height of the image
* @param buf image buffer
* @return Results (array of AprilTagDetection pointers)
*/
Results Detect(int width, int height, uint8_t* buf) {
return Detect(width, height, width, buf);
}
private:
void Destroy();
void DestroyFamilies();
void DestroyFamily(std::string_view name, void* data);
void* m_impl;
wpi::util::StringMap<void*> m_families;
wpi::units::radian_t m_qtpCriticalAngle = 10_deg;
};
} // namespace wpi::apriltag

View File

@@ -1,18 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <opencv2/core/mat.hpp>
#include "wpi/apriltag/AprilTagDetector.hpp"
namespace wpi::apriltag {
inline AprilTagDetector::Results AprilTagDetect(AprilTagDetector& detector,
cv::Mat& image) {
return detector.Detect(image.cols, image.rows, image.data);
}
} // namespace wpi::apriltag

View File

@@ -1,167 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <optional>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "wpi/apriltag/AprilTag.hpp"
#include "wpi/apriltag/AprilTagFields.hpp"
#include "wpi/math/geometry/Pose3d.hpp"
#include "wpi/units/length.hpp"
#include "wpi/util/SymbolExports.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::apriltag {
/**
* Class for representing a layout of AprilTags on a field and reading them from
* a JSON format.
*
* The JSON format contains two top-level objects, "tags" and "field".
* The "tags" object is a list of all AprilTags contained within a layout. Each
* AprilTag serializes to a JSON object containing an ID and a
* wpi::math::Pose3d. The "field" object is a descriptor of the size of the
* field in meters with "width" and "length" values. This is to account for
* arbitrary field sizes when transforming the poses.
*
* Pose3ds in the JSON are measured using the normal FRC coordinate system, NWU
* with the origin at the bottom-right corner of the blue alliance wall.
* SetOrigin(OriginPosition) can be used to change the poses returned from
* GetTagPose(int) to be from the perspective of a specific alliance.
*
* Tag poses represent the center of the tag, with a zero rotation representing
* a tag that is upright and facing away from the (blue) alliance wall (that is,
* towards the opposing alliance). */
class WPILIB_DLLEXPORT AprilTagFieldLayout {
public:
/**
* Common origin positions for the AprilTag coordinate system.
*/
enum class OriginPosition {
/// Blue alliance wall, right side.
kBlueAllianceWallRightSide,
/// Red alliance wall, right side.
kRedAllianceWallRightSide,
};
/**
* Loads an AprilTagFieldLayout from a predefined field
*
* @param field The predefined field
* @return AprilTagFieldLayout of the field
*/
static AprilTagFieldLayout LoadField(AprilTagField field);
AprilTagFieldLayout() = default;
/**
* Construct a new AprilTagFieldLayout with values imported from a JSON file.
*
* @param path Path of the JSON file to import from.
*/
explicit AprilTagFieldLayout(std::string_view path);
/**
* Construct a new AprilTagFieldLayout from a vector of AprilTag objects.
*
* @param apriltags Vector of AprilTags.
* @param fieldLength Length of field the layout is representing.
* @param fieldWidth Width of field the layout is representing.
*/
AprilTagFieldLayout(std::vector<AprilTag> apriltags,
wpi::units::meter_t fieldLength,
wpi::units::meter_t fieldWidth);
/**
* Returns the length of the field the layout is representing.
* @return length
*/
wpi::units::meter_t GetFieldLength() const;
/**
* Returns the length of the field the layout is representing.
* @return width
*/
wpi::units::meter_t GetFieldWidth() const;
/**
* Returns a vector of all the april tags used in this layout.
* @return list of tags
*/
std::vector<AprilTag> GetTags() const;
/**
* Sets the origin based on a predefined enumeration of coordinate frame
* origins. The origins are calculated from the field dimensions.
*
* This transforms the Pose3ds returned by GetTagPose(int) to return the
* correct pose relative to a predefined coordinate frame.
*
* @param origin The predefined origin
*/
void SetOrigin(OriginPosition origin);
/**
* Sets the origin for tag pose transformation.
*
* This transforms the Pose3ds returned by GetTagPose(int) to return the
* correct pose relative to the provided origin.
*
* @param origin The new origin for tag transformations
*/
void SetOrigin(const wpi::math::Pose3d& origin);
/**
* Returns the origin used for tag pose transformation.
* @return the origin
*/
wpi::math::Pose3d GetOrigin() const;
/**
* Gets an AprilTag pose by its ID.
*
* @param ID The ID of the tag.
* @return The pose corresponding to the ID that was passed in or an empty
* optional if a tag with that ID is not found.
*/
std::optional<wpi::math::Pose3d> GetTagPose(int ID) const;
/**
* Serializes an AprilTagFieldLayout to a JSON file.
*
* @param path The path to write the JSON file to.
*/
void Serialize(std::string_view path);
/*
* Checks equality between this AprilTagFieldLayout and another object.
*/
bool operator==(const AprilTagFieldLayout&) const = default;
private:
std::unordered_map<int, AprilTag> m_apriltags;
wpi::units::meter_t m_fieldLength;
wpi::units::meter_t m_fieldWidth;
wpi::math::Pose3d m_origin;
friend WPILIB_DLLEXPORT void to_json(wpi::util::json& json,
const AprilTagFieldLayout& layout);
friend WPILIB_DLLEXPORT void from_json(const wpi::util::json& json,
AprilTagFieldLayout& layout);
};
WPILIB_DLLEXPORT
void to_json(wpi::util::json& json, const AprilTagFieldLayout& layout);
WPILIB_DLLEXPORT
void from_json(const wpi::util::json& json, AprilTagFieldLayout& layout);
} // namespace wpi::apriltag

View File

@@ -1,35 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
namespace wpi::apriltag {
/**
* Loadable AprilTag field layouts.
*/
enum class AprilTagField {
/// 2022 Rapid React.
k2022RapidReact,
/// 2023 Charged Up.
k2023ChargedUp,
/// 2024 Crescendo.
k2024Crescendo,
/// 2025 Reefscape AndyMark (see TU12).
k2025ReefscapeAndyMark,
/// 2025 Reefscape Welded (see TU12).
k2025ReefscapeWelded,
/// 2026 Rebuilt Andymark.
k2026RebuiltAndyMark,
/// 2026 Rebuilt Welded.
k2026RebuiltWelded,
/// Alias to the current game.
kDefaultField = k2026RebuiltWelded,
// This is a placeholder for denoting the last supported field. This should
// always be the last entry in the enum and should not be used by users
kNumFields,
};
} // namespace wpi::apriltag

View File

@@ -1,35 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include "wpi/math/geometry/Transform3d.hpp"
#include "wpi/util/SymbolExports.hpp"
namespace wpi::apriltag {
/** A pair of AprilTag pose estimates. */
struct WPILIB_DLLEXPORT AprilTagPoseEstimate {
/** Pose 1. */
wpi::math::Transform3d pose1;
/** Pose 2. */
wpi::math::Transform3d pose2;
/** Object-space error of pose 1. */
double error1;
/** Object-space error of pose 2. */
double error2;
/**
* Gets the ratio of pose reprojection errors, called ambiguity. Numbers
* above 0.2 are likely to be ambiguous.
*
* @return The ratio of pose reprojection errors.
*/
double GetAmbiguity() const;
};
} // namespace wpi::apriltag

View File

@@ -1,146 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <span>
#include "wpi/apriltag/AprilTagPoseEstimate.hpp"
#include "wpi/math/geometry/Transform3d.hpp"
#include "wpi/units/length.hpp"
#include "wpi/util/SymbolExports.hpp"
namespace wpi::apriltag {
class AprilTagDetection;
/** Pose estimators for AprilTag tags. */
class WPILIB_DLLEXPORT AprilTagPoseEstimator {
public:
/** Configuration for the pose estimator. */
struct Config {
bool operator==(const Config&) const = default;
/** The tag size. */
wpi::units::meter_t tagSize;
/** Camera horizontal focal length, in pixels. */
double fx;
/** Camera vertical focal length, in pixels. */
double fy;
/** Camera horizontal focal center, in pixels. */
double cx;
/** Camera vertical focal center, in pixels. */
double cy;
};
/**
* Creates estimator.
*
* @param config Configuration
*/
explicit AprilTagPoseEstimator(const Config& config) : m_config{config} {}
/**
* Sets estimator configuration.
*
* @param config Configuration
*/
void SetConfig(const Config& config) { m_config = config; }
/**
* Gets estimator configuration.
*
* @return Configuration
*/
const Config& GetConfig() const { return m_config; }
/**
* Estimates the pose of the tag using the homography method described in [1].
*
* @param detection Tag detection
* @return Pose estimate
*/
wpi::math::Transform3d EstimateHomography(
const AprilTagDetection& detection) const;
/**
* Estimates the pose of the tag using the homography method described in [1].
*
* @param homography Homography 3x3 matrix data
* @return Pose estimate
*/
wpi::math::Transform3d EstimateHomography(
std::span<const double, 9> homography) const;
/**
* Estimates the pose of the tag. This returns one or two possible poses for
* the tag, along with the object-space error of each.
*
* This uses the homography method described in [1] for the initial estimate.
* Then Orthogonal Iteration [2] is used to refine this estimate. Then [3] is
* used to find a potential second local minima and Orthogonal Iteration is
* used to refine this second estimate.
*
* [1]: E. Olson, “Apriltag: A robust and flexible visual fiducial system,” in
* 2011 IEEE International Conference on Robotics and Automation,
* May 2011, pp. 34003407.
* [2]: Lu, G. D. Hager and E. Mjolsness, "Fast and globally convergent pose
* estimation from video images," in IEEE Transactions on Pattern
* Analysis and Machine Intelligence, vol. 22, no. 6, pp. 610-622, June 2000.
* doi: 10.1109/34.862199
* [3]: Schweighofer and A. Pinz, "Robust Pose Estimation from a Planar
* Target," in IEEE Transactions on Pattern Analysis and Machine Intelligence,
* vol. 28, no. 12, pp. 2024-2030, Dec. 2006. doi: 10.1109/TPAMI.2006.252
*
* @param detection Tag detection
* @param nIters Number of iterations
* @return Initial and (possibly) second pose estimates
*/
AprilTagPoseEstimate EstimateOrthogonalIteration(
const AprilTagDetection& detection, int nIters) const;
/**
* Estimates the pose of the tag. This returns one or two possible poses for
* the tag, along with the object-space error of each.
*
* @param homography Homography 3x3 matrix data
* @param corners Corner point array (X and Y for each corner in order)
* @param nIters Number of iterations
* @return Initial and (possibly) second pose estimates
*/
AprilTagPoseEstimate EstimateOrthogonalIteration(
std::span<const double, 9> homography, std::span<const double, 8> corners,
int nIters) const;
/**
* Estimates tag pose. This method is an easier to use interface to
* EstimatePoseOrthogonalIteration(), running 50 iterations and returning the
* pose with the lower object-space error.
*
* @param detection Tag detection
* @return Pose estimate
*/
wpi::math::Transform3d Estimate(const AprilTagDetection& detection) const;
/**
* Estimates tag pose. This method is an easier to use interface to
* EstimatePoseOrthogonalIteration(), running 50 iterations and returning the
* pose with the lower object-space error.
*
* @param homography Homography 3x3 matrix data
* @param corners Corner point array (X and Y for each corner in order)
* @return Pose estimate
*/
wpi::math::Transform3d Estimate(std::span<const double, 9> homography,
std::span<const double, 8> corners) const;
private:
Config m_config;
};
} // namespace wpi::apriltag

View File

@@ -1,4 +0,0 @@
robotpy-apriltag
================
RobotPy wrappers around WPILib's version of the apriltag library.

Some files were not shown because too many files have changed in this diff Show More