Compare commits

..

1 Commits

Author SHA1 Message Date
Alex Henning
8856927fd8 Added support for Jenkins to generate doxygen.
Change-Id: I2febfe42cc9301446f796376cfe8e1ef6744f19a
2014-08-12 15:06:43 -04:00
7392 changed files with 462910 additions and 1013908 deletions

View File

@@ -1,258 +0,0 @@
---
Language: Cpp
BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterAttributes: Always
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
- 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
IndentCaseLabels: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
Macros:
- 'HAL_ENUM(name)=enum name'
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: NextLine
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
PPIndentWidth: -1
QualifierAlignment: Leave
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: pb
BasedOnStyle: google
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: false
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++20
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseTab: Never
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...

View File

@@ -1,69 +0,0 @@
Checks:
'bugprone-assert-side-effect,
bugprone-bool-pointer-implicit-conversion,
bugprone-copy-constructor-init,
bugprone-dangling-handle,
bugprone-dynamic-static-initializers,
bugprone-forward-declaration-namespace,
bugprone-forwarding-reference-overload,
bugprone-inaccurate-erase,
bugprone-incorrect-roundings,
bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
bugprone-multiple-statement-macro,
bugprone-parent-virtual-call,
bugprone-posix-return,
bugprone-sizeof-container,
bugprone-sizeof-expression,
bugprone-spuriously-wake-up-functions,
bugprone-string-constructor,
bugprone-string-integer-assignment,
bugprone-string-literal-with-embedded-nul,
bugprone-suspicious-enum-usage,
bugprone-suspicious-include,
bugprone-suspicious-memset-usage,
bugprone-suspicious-missing-comma,
bugprone-suspicious-semicolon,
bugprone-suspicious-string-compare,
bugprone-throw-keyword-missing,
bugprone-too-small-loop-variable,
bugprone-undefined-memory-manipulation,
bugprone-undelegated-constructor,
bugprone-unhandled-self-assignment,
bugprone-unused-raii,
bugprone-virtual-near-miss,
cert-err52-cpp,
cert-err60-cpp,
cert-mem57-cpp,
cert-oop57-cpp,
cert-oop58-cpp,
clang-diagnostic-*,
-clang-diagnostic-deprecated-declarations,
-clang-diagnostic-#warnings,
-clang-diagnostic-pedantic,
clang-analyzer-*,
cppcoreguidelines-slicing,
google-build-namespaces,
google-explicit-constructor,
google-global-names-in-headers,
google-readability-casting,
google-runtime-operator,
misc-definitions-in-headers,
misc-misplaced-const,
misc-new-delete-overloads,
misc-non-copyable-objects,
modernize-avoid-bind,
modernize-concat-nested-namespaces,
modernize-make-shared,
modernize-make-unique,
modernize-pass-by-value,
modernize-use-default-member-init,
modernize-use-noexcept,
modernize-use-nullptr,
modernize-use-override,
modernize-use-using,
readability-braces-around-statements'
FormatStyle: file

View File

@@ -1,6 +0,0 @@
color: false
definitions: []
line_length: 100
list_expansion: favour-inlining
quiet: false
unsafe: false

13
.gitattributes vendored
View File

@@ -1,13 +0,0 @@
*.cpp text eol=lf
*.gradle text eol=lf
*.h text eol=lf
*.inc text eol=lf
*.java text eol=lf
*.json text eol=lf
*.md text eol=lf
*.xml text eol=lf
# Generated files
hal/src/generated/** linguist-generated
ntcore/src/generated/** linguist-generated
wpimath/src/generated/** linguist-generated

36
.github/CODEOWNERS vendored
View File

@@ -1,36 +0,0 @@
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners.
# More details are here: https://help.github.com/articles/about-codeowners/
# The '*' pattern is global owners.
# Order is important. The last matching pattern has the most precedence.
# The folders are ordered as a tree: by depth then alphabetically.
# This should make it easy to add new rules without breaking existing ones.
# Global rule:
* @wpilibsuite/wpilib
/cameraserver/ @wpilibsuite/camera-server
/cscore/ @wpilibsuite/camera-server
/hal/ @wpilibsuite/hardware
/hal/src/main/java/**/sim/ @wpilibsuite/simulation
/hal/src/main/native/include/mockdata/ @wpilibsuite/simulation
/hal/src/main/native/include/simulation/ @wpilibsuite/simulation
/hal/src/main/native/sim/ @wpilibsuite/simulation
/ntcore/ @wpilibsuite/network-tables
/simulation/ @wpilibsuite/simulation
/wpilibNewCommands/ @wpilibsuite/commandbased
/wpilibcExamples/ @wpilibsuite/wpilib @wpilibsuite/documentation
/wpilibjExamples/ @wpilibsuite/wpilib @wpilibsuite/documentation
/wpiutil/ @PeterJohnson

View File

@@ -1,31 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. ...
2. ...
- Link to code:
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows 11]
- Project Information: [In Visual Studio Code, press the WPILib button and choose WPILib: Open Project Information. Press the copy button and paste the data here. If not using VS Code, please include WPILib version, Gradle version, Java version, C++ version (if applicable), and any third party libraries and versions]
**Additional context**
Add any other context about the problem here.

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -1,20 +0,0 @@
---
name: Question
about: Ask about features or parts of this project
title: ''
labels: ''
assignees: ''
---
**Describe the question you have.**
A clear and concise description of what you want clarification on.
**Describe the reason for your confusion.**
A clear and concise description of why the question requires clarification and what's confusing.
**Is your question related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Additional context**
Add any other context or screenshots about the question here.

View File

@@ -1,107 +0,0 @@
name: CMake
on: [pull_request, push]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
name: Linux
container: wpilib/roborio-cross-ubuntu:2024-22.04
flags: "-DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
- os: macOS-12
name: macOS
container: ""
env: "PATH=\"/usr/local/opt/protobuf@3/bin:$PATH\""
flags: "-DCMAKE_BUILD_TYPE=Release -DWITH_JAVA=OFF -DWITH_EXAMPLES=ON -DCMAKE_LIBRARY_PATH=/usr/local/opt/protobuf@3/lib -DProtobuf_INCLUDE_DIR=/usr/local/opt/protobuf@3/include -DProtobuf_PROTOC_EXECUTABLE=/usr/local/opt/protobuf@3/bin/protoc"
name: "Build - ${{ matrix.name }}"
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
steps:
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3 libprotobuf-dev protobuf-compiler ninja-build
- name: Install QuickBuffers (Linux)
if: runner.os == 'Linux'
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf_1.3.3_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.3_amd64.deb
- name: Install opencv (macOS)
run: brew install opencv protobuf@3 ninja
if: runner.os == 'macOS'
- name: Set up Python 3.8 (macOS)
if: runner.os == 'macOS'
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.3
- name: Install jinja
run: python -m pip install jinja2
- uses: actions/checkout@v4
- name: configure
run: cmake -S . -B build -G "Ninja" -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache ${{ matrix.flags }}
env:
SCCACHE_GHA_ENABLED: "true"
- name: build
working-directory: build
run: cmake --build . --parallel $(nproc)
env:
SCCACHE_GHA_ENABLED: "true"
- name: test
working-directory: build
run: ctest --output-on-failure
build-windows:
name: "Build - Windows"
runs-on: windows-2022
steps:
- uses: ilammy/msvc-dev-cmd@v1
- name: Install CMake
uses: lukka/get-cmake@v3.27.6
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.3
- name: Install jinja
run: python -m pip install jinja2
- uses: actions/checkout@v4
- name: Run vcpkg
uses: lukka/run-vcpkg@v11.1
with:
vcpkgDirectory: ${{ runner.workspace }}/vcpkg
vcpkgGitCommitId: 37c3e63a1306562f7f59c4c3c8892ddd50fdf992 # HEAD on 2024-02-24
- name: configure
run: cmake -S . -B build -G "Ninja" -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_BUILD_TYPE=Release -DWITH_JAVA=OFF -DWITH_EXAMPLES=ON -DUSE_SYSTEM_FMTLIB=ON -DUSE_SYSTEM_LIBUV=ON -DUSE_SYSTEM_EIGEN=OFF -DCMAKE_TOOLCHAIN_FILE=${{ runner.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_INSTALL_OPTIONS=--clean-after-build -DVCPKG_TARGET_TRIPLET=x64-windows-release -DVCPKG_HOST_TRIPLET=x64-windows-release
env:
SCCACHE_GHA_ENABLED: "true"
- name: build
working-directory: build
run: cmake --build . --parallel $(nproc)
env:
SCCACHE_GHA_ENABLED: "true"
- name: test
working-directory: build
# UnitTest_test segfaults on exit occasionally
run: ctest --output-on-failure -E 'UnitTest'

View File

@@ -1,58 +0,0 @@
name: Comment Commands
on:
issue_comment:
types: [ created ]
jobs:
format:
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/format')
runs-on: ubuntu-22.04
steps:
- name: React Rocket
uses: actions/github-script@v7
with:
script: |
const {owner, repo} = context.issue
github.rest.reactions.createForIssueComment({
owner,
repo,
comment_id: context.payload.comment.id,
content: "rocket",
});
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}
- name: Fetch all history and metadata
run: |
git checkout -b pr
git branch -f main origin/main
- name: Checkout PR
run: |
gh pr checkout $NUMBER
env:
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
NUMBER: ${{ github.event.issue.number }}
- name: Set up Python 3.8
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17
- name: Install wpiformat
run: pip3 install wpiformat
- name: Run wpiformat
run: wpiformat
- name: Run spotlessApply
run: ./gradlew spotlessApply
- name: Commit
run: |
# Set credentials
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Commit
git commit -am "Formatting fixes"
git push

View File

@@ -1,66 +0,0 @@
name: Documentation
on: [push, workflow_dispatch]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
BASE_PATH: allwpilib/docs
jobs:
publish:
name: "Documentation - Publish"
runs-on: ubuntu-22.04
if: github.repository_owner == 'wpilibsuite' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
concurrency: ci-docs-publish
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 13
- name: Set environment variables (Development)
run: |
echo "BRANCH=development" >> $GITHUB_ENV
if: github.ref == 'refs/heads/main'
- name: Set environment variables (Tag)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=beta" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
- 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')
- name: Build with Gradle
run: ./gradlew docs:generateJavaDocs docs:doxygen -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
- name: Install SSH Client 🔑
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4.4.1
with:
ssh-key: true
repository-name: wpilibsuite/wpilibsuite.github.io
branch: allwpilib-${{ env.BRANCH }}
clean: true
single-commit: true
folder: docs/build/docs
- name: Trigger Workflow
uses: actions/github-script@v6
with:
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
script: |
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: 'wpilibsuite.github.io',
workflow_id: 'static.yml',
ref: 'main',
})

View File

@@ -1,42 +0,0 @@
#!/usr/bin/env python3
import argparse
import json
def main():
parser = argparse.ArgumentParser(
description="Fix compile_commands.json generated by Gradle"
)
parser.add_argument("filename", help="compile_commands.json location")
cmd_args = parser.parse_args()
# Read JSON
with open(cmd_args.filename) as f:
data = json.load(f)
for obj in data:
out_args = []
# Filter out -isystem flags that cause false positives
iter_args = iter(obj["arguments"])
for arg in iter_args:
if arg == "-isystem":
next_arg = next(iter_args)
# /usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/include/xmmintrin.h:54:1:
# error: conflicting types for '_mm_prefetch' [clang-diagnostic-error]
if not next_arg.startswith("/usr/lib/gcc/"):
out_args += ["-isystem", next_arg]
else:
out_args.append(arg)
obj["arguments"] = out_args
# Write JSON
with open(cmd_args.filename, "w") as f:
json.dump(data, f, indent=2)
if __name__ == "__main__":
main()

View File

@@ -1,14 +0,0 @@
name: "Validate Gradle Wrapper"
on: [pull_request, push]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1

View File

@@ -1,264 +0,0 @@
name: Gradle
on: [pull_request, push]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
build-docker:
strategy:
fail-fast: false
matrix:
include:
- container: wpilib/roborio-cross-ubuntu:2024-22.04
artifact-name: Athena
build-options: "-Ponlylinuxathena"
- container: wpilib/raspbian-cross-ubuntu:bullseye-22.04
artifact-name: Arm32
build-options: "-Ponlylinuxarm32"
- container: wpilib/aarch64-cross-ubuntu:bullseye-22.04
artifact-name: Arm64
build-options: "-Ponlylinuxarm64"
- container: wpilib/ubuntu-base:22.04
artifact-name: Linux
build-options: "-Ponlylinuxx86-64"
name: "Build - ${{ matrix.artifact-name }}"
runs-on: ubuntu-22.04
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
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
- name: Build with Gradle
uses: addnab/docker-run-action@v3
with:
image: ${{ matrix.container }}
options: -v ${{ github.workspace }}:/work -w /work -e ARTIFACTORY_PUBLISH_USERNAME -e ARTIFACTORY_PUBLISH_PASSWORD -e GITHUB_REF -e CI
run: df . && rm -f semicolon_delimited_script && 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 }}
- name: Check free disk space
run: df .
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: build/allOutputs
build-host:
env:
MACOSX_DEPLOYMENT_TARGET: 12
strategy:
fail-fast: false
matrix:
include:
- os: windows-2022
artifact-name: Win64Debug
architecture: x64
task: "build"
build-options: "-PciDebugOnly"
outputs: "build/allOutputs"
build-dir: "c:\\work"
- os: windows-2022
artifact-name: Win64Release
architecture: x64
build-options: "-PciReleaseOnly"
task: "copyAllOutputs"
outputs: "build/allOutputs"
build-dir: "c:\\work"
- os: windows-2022
artifact-name: WinArm64Debug
architecture: x64
task: "build"
build-options: "-PciDebugOnly -Pbuildwinarm64 -Ponlywindowsarm64"
outputs: "build/allOutputs"
build-dir: "c:\\work"
- os: windows-2022
artifact-name: WinArm64Release
architecture: x64
build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64"
task: "copyAllOutputs"
outputs: "build/allOutputs"
build-dir: "c:\\work"
- os: macOS-12
artifact-name: macOS
architecture: x64
task: "build"
outputs: "build/allOutputs"
build-dir: "."
- os: windows-2022
artifact-name: Win32
architecture: x86
task: ":ntcoreffi:build"
outputs: "ntcoreffi/build/outputs"
build-dir: "c:\\work"
name: "Build - ${{ matrix.artifact-name }}"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17
architecture: ${{ matrix.architecture }}
- name: Import Developer ID Certificate
uses: wpilibsuite/import-signing-certificate@v1
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_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
- name: Set Keychain Lock Timeout
run: security set-keychain-settings -lut 3600
if: |
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' &&
(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')
- name: Set Java Heap Size
run: sed -i 's/-Xmx2g/-Xmx1g/g' gradle.properties
if: matrix.artifact-name == 'Win32'
- name: Configure build directory (Windows)
run: xcopy . ${{ matrix.build-dir }} /i /s /e /h /q
if: matrix.os == 'windows-2022'
- name: Check disk free space (Windows)
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'
- name: Build with Gradle
run: ./gradlew ${{ matrix.task }} --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
working-directory: ${{ matrix.build-dir }}
env:
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- name: Sign Libraries with Developer ID
run: ./gradlew copyAllOutputs --build-cache -PbuildServer -PskipJavaFormat -PdeveloperID=${{ secrets.APPLE_DEVELOPER_ID }} ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
working-directory: ${{ matrix.build-dir }}
if: |
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
- name: Check disk free space (Windows)
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: ${{ matrix.build-dir }}/${{ matrix.outputs }}
build-documentation:
name: "Build - Documentation"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
- name: Build with Gradle
run: ./gradlew docs:zipDocs --build-cache -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
env:
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- uses: actions/upload-artifact@v4
with:
name: Documentation
path: docs/build/outputs
combine:
name: Combine
needs: [build-docker, build-host, build-documentation]
runs-on: ubuntu-22.04
steps:
- name: Free Disk Space
if: |
github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
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
if: |
github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
with:
repository: wpilibsuite/build-tools
- uses: actions/download-artifact@v4
if: |
github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
with:
path: combiner/products/build/allOutputs
- name: Flatten Artifacts
if: |
github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
run: rsync -a --delete combiner/products/build/allOutputs/*/* combiner/products/build/allOutputs/
- name: Check version number exists
if: |
github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
run: |
cat combiner/products/build/allOutputs/version.txt
test -s combiner/products/build/allOutputs/version.txt
- uses: actions/setup-java@v4
if: |
github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
with:
distribution: 'zulu'
java-version: 17
- name: Combine (Main)
if: |
github.repository_owner == 'wpilibsuite' &&
github.ref == 'refs/heads/main'
run: cd combiner && ./gradlew publish -Pallwpilib
env:
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- name: Combine (Release)
if: |
github.repository_owner == 'wpilibsuite' &&
startsWith(github.ref, 'refs/tags/v')
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@v4
if: |
github.repository_owner == 'wpilibsuite' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
with:
name: Maven
path: ~/releases

View File

@@ -1,113 +0,0 @@
name: Lint and Format
on:
pull_request:
push:
branches-ignore:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
wpiformat:
name: "wpiformat"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch all history and metadata
run: |
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.8
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install wpiformat
run: pip3 install wpiformat==2024.32
- name: Run
run: wpiformat
- name: Check output
run: git --no-pager diff --exit-code HEAD
- name: Generate diff
run: git diff HEAD > wpiformat-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v4
with:
name: wpiformat fixes
path: wpiformat-fixes.patch
if: ${{ failure() }}
- name: Write to job summary
run: |
echo '```diff' >> $GITHUB_STEP_SUMMARY
cat wpiformat-fixes.patch >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
if: ${{ failure() }}
tidy:
name: "clang-tidy"
runs-on: ubuntu-22.04
container: wpilib/roborio-cross-ubuntu:2023-22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch all history and metadata
run: |
git config --global --add safe.directory /__w/allwpilib/allwpilib
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.8
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install wpiformat
run: pip3 install wpiformat
- name: Create compile_commands.json
run: |
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio
./.github/workflows/fix_compile_commands.py build/TargetedCompileCommands/linuxx86-64release/compile_commands.json
./.github/workflows/fix_compile_commands.py build/TargetedCompileCommands/linuxx86-64debug/compile_commands.json
- name: List changed files
run: wpiformat -list-changed-files
- name: Run clang-tidy release
run: wpiformat -no-format -tidy-changed -compile-commands=build/TargetedCompileCommands/linuxx86-64release -vv
- name: Run clang-tidy debug
run: wpiformat -no-format -tidy-changed -compile-commands=build/TargetedCompileCommands/linuxx86-64debug -vv
javaformat:
name: "Java format"
runs-on: ubuntu-22.04
container: wpilib/ubuntu-base:22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch all history and metadata
run: |
git config --global --add safe.directory /__w/allwpilib/allwpilib
git checkout -b pr
git branch -f main origin/main
- name: Run Java format
run: ./gradlew javaFormat spotbugsMain spotbugsTest spotbugsDev
- name: Check output
run: git --no-pager diff --exit-code HEAD
- name: Generate diff
run: git diff HEAD > javaformat-fixes.patch
if: ${{ failure() }}
documentation:
name: "Documentation"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17
- name: Build with Gradle
run: ./gradlew docs:zipDocs -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}

View File

@@ -1,46 +0,0 @@
name: Check Pregenerated Files
on:
pull_request:
push:
branches-ignore:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
update:
name: "Update"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python 3.9
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Install jinja
run: python -m pip install jinja2
- name: Install protobuf dependencies
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler && 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
- name: Run hal
run: ./hal/generate_usage_reporting.py
- name: Run ntcore
run: ./ntcore/generate_topics.py
- name: Run wpimath
run: ./wpimath/generate_numbers.py && ./wpimath/generate_quickbuf.py protoc protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
- name: Add untracked files to index so they count as changes
run: git add -A
- name: Check output
run: git --no-pager diff --exit-code HEAD
- name: Generate diff
run: git diff HEAD > pregenerated-files-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v4
with:
name: pregenerated-files-fixes
path: pregenerated-files-fixes.patch
if: ${{ failure() }}

View File

@@ -1,59 +0,0 @@
name: Sanitizers
on: [pull_request, push]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- name: asan
cmake-flags: "-DCMAKE_BUILD_TYPE=Asan"
ctest-env: ""
ctest-flags: "-E 'wpilibc'"
- name: tsan
cmake-flags: "-DCMAKE_BUILD_TYPE=Tsan"
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-22.04
container: wpilib/roborio-cross-ubuntu:2024-22.04
steps:
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3 clang-14 libprotobuf-dev protobuf-compiler ninja-build
- name: Install QuickBuffers
if: runner.os == 'Linux'
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf_1.3.3_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.3_amd64.deb
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.3
- name: Install jinja
run: python -m pip install jinja2
- 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-14 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-14 -DWITH_JAVA=OFF ${{ matrix.cmake-flags }} ..
env:
SCCACHE_GHA_ENABLED: "true"
- name: build
working-directory: build
run: cmake --build . --parallel $(nproc)
env:
SCCACHE_GHA_ENABLED: "true"
- name: test
working-directory: build
run: ${{ matrix.ctest-env }} ctest --output-on-failure ${{ matrix.ctest-flags }}

View File

@@ -1,76 +0,0 @@
name: Upstream utils
on:
pull_request:
push:
branches-ignore:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
update:
name: "Update"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch all history and metadata
run: |
git checkout -b pr
git branch -f main origin/main
- name: Set up Python 3.9
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Configure committer identity
run: |
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
- name: Run update_eigen.py
run: |
cd upstream_utils
./update_eigen.py
- name: Run update_fmt.py
run: |
cd upstream_utils
./update_fmt.py
- name: Run update_gcem.py
run: |
cd upstream_utils
./update_gcem.py
- name: Run update_json.py
run: |
cd upstream_utils
./update_json.py
- name: Run update_libuv.py
run: |
cd upstream_utils
./update_libuv.py
- name: Run update_llvm.py
run: |
cd upstream_utils
./update_llvm.py
- name: Run update_mpack.py
run: |
cd upstream_utils
./update_mpack.py
- name: Run update_stack_walker.py
run: |
cd upstream_utils
./update_stack_walker.py
- name: Run update_memory.py
run: |
cd upstream_utils
./update_memory.py
- name: Run update_protobuf.py
run: |
cd upstream_utils
./update_protobuf.py
- name: Add untracked files to index so they count as changes
run: git add -A
- name: Check output
run: git --no-pager diff --exit-code HEAD

246
.gitignore vendored
View File

@@ -1,86 +1,21 @@
# WPIlib Specific
dependency-reduced-pom.xml
doxygen.log
build*/
!buildSrc/
simgui-ds.json
simgui-window.json
simgui.json
networktables.json
# Created by the jenkins test script
test-reports
# IntelliJ
*.iml
*.ipr
*.iws
.idea/
out/
# Fleet
.fleet
# Created by http://www.gitignore.io
### Linux ###
wpilibc/build/
hal/build/
networktables/cpp/build/
build/
networktables/OutlineViewer/nbproject/private
*~
target/
dist/
bin/
.project
.cproject
.settings/
.classpath
**/dependency-reduced-pom.xml
# KDE directory preferences
.directory
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Java ###
#Java File extentions
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
@@ -88,156 +23,3 @@ Temporary Items
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### C++ ###
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Compiled Dynamic libraries
*.dylib
*.dll
# Fortran module files
*.mod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
### CMake ###
CMakeCache.txt
CMakeFiles
cmake_install.cmake
install_manifest.txt
### Gradle ###
.gradle
build/
# Ignore Gradle GUI config
gradle-app.setting
### Vagrant ###
.vagrant/
### Eclipse ###
*.pydevproject
.metadata
.gradle
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
### Python ###
*.pyc
__pycache__
# Gradle wrapper
!gradle-wrapper.jar
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
# sbteclipse plugin
.target
# TeXlipse plugin
.texlipse
#catkin stuff
package.xml
# Doxygen stuff
NO
# Simulation folder stuff
!simulation/install_resources/*
# VSCode
.vscode/
#classpaths and projects
.project
.classpath
#Visual Studio
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
CMakeSettings.json
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.cachefile
*.VC.db
*.VC.VC.opendb
*.VC.db-shm
*.VC.db-wal
*.sln
*.vcxproj
*.vcxproj.filters
# Visual Studio 2015 cache/options directory
.vs/
# compile_commands
compile_commands.json
# clang configuration and clangd cache
.clang
.clangd/
.cache/
imgui.ini
# Bazel
/.ijwb/
/bazel-*
user.bazelrc
coverage_report/

5
.gitreview Normal file
View File

@@ -0,0 +1,5 @@
[gerrit]
host=usfirst.collab.net
port=29418
project=allwpilib
defaultbranch=master

View File

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

View File

@@ -1,3 +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.

View File

@@ -1,6 +0,0 @@
{
"enableCppIntellisense": true,
"currentLanguage": "cpp",
"projectYear": "intellisense",
"teamNumber": 0
}

29
Building.md Normal file
View File

@@ -0,0 +1,29 @@
Building everything requires Maven
mvn package -DembeddedJDKHome=/home/patrick/Downloads/arm-jdk1.7.0_45/
TODO... Explain maven....
TODO.. how to import into eclipse correctly...
Building C++ only
------------------
C++ requires cmake if not run from maven, and is much faster.
Make a new directory and then run:
```
mkdir build && cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=../arm-toolchain.cmake
make # multicore add -j(num of cpu cores + 1), so -j3 on dual core for faster compile
make install DESTDIR=/some/dir/you/want/to/put/all/headers/and/libs #optional
``
Alternatively, if you like IDEs, you can import it directly into QtDeveloper, or a number of other IDEs such as Code::Blocks or Eclipse. See CMake documentation for details.
Eclipse demo:
```
cd ..
mkdir build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../allwpilib/arm-toolchain.cmake .. -G "Eclipse CDT4 - Unix Makefiles"
```
and then import that directory into eclipse. Eclipse will detect a CDT project and standard tools will work.
GCC versions
------------
Update arm-toolchain.cmake if the triplet changes (eg using Ubuntu repo arm compiler is arm-linux-gnueabi) or in a non-standard location. Currently it assumes that the compiler is on the path.

View File

@@ -1,416 +1,13 @@
# Disable in-source builds to prevent source tree corruption.
if(" ${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL " ${CMAKE_CURRENT_BINARY_DIR}")
message(
FATAL_ERROR
"
FATAL: In-source builds are not allowed.
You should create a separate directory for build files.
"
)
endif()
cmake_minimum_required(VERSION 2.8)
project(All-WPILib)
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=2 -Wall -Wextra -Wno-unused-parameter -fPIC")
SET(CMAKE_SKIP_BUILD_RPATH TRUE)
if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
set(CMAKE_SYSTEM_VERSION 10.0.18362.0 CACHE STRING INTERNAL FORCE)
set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION 10.0.18362.0 CACHE STRING INTERNAL FORCE)
endif()
cmake_minimum_required(VERSION 3.11)
project(allwpilib)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
# Make timestamps of extracted files from FetchContent the time of extraction
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif()
message(STATUS "Platform version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
set(WPILIB_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
include(CPack)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${WPILIB_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${WPILIB_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${WPILIB_BINARY_DIR}/bin)
set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${WPILIB_BINARY_DIR}/jar)
# use, i.e. don't skip the full RPATH for the build tree
set(CMAKE_SKIP_BUILD_RPATH FALSE)
# when building, don't use the install RPATH already
# (but later on when installing)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
# add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# the RPATH to be used when installing, but only if it's not a system directory
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
if("${isSystemDir}" STREQUAL "-1")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
endif()
# Options for building certain parts of the repo. Everything is built by default.
option(BUILD_SHARED_LIBS "Build with shared libs (needed for JNI)" ON)
option(WITH_JAVA "Include Java and JNI in the build" ON)
option(WITH_JAVA_SOURCE "Build Java source jars" ${WITH_JAVA})
option(WITH_CSCORE "Build cscore (needs OpenCV)" ON)
option(WITH_NTCORE "Build ntcore" ON)
option(WITH_WPIMATH "Build wpimath" ON)
option(WITH_WPIUNITS "Build wpiunits" ON)
option(WITH_WPILIB "Build hal, wpilibc/j, and myRobot (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)
# Options for external HAL.
option(WITH_EXTERNAL_HAL "Use a separately built HAL" OFF)
set(EXTERNAL_HAL_FILE "" CACHE FILEPATH "Location to look for an external HAL CMake File")
# 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)
# 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)
# Set default build type to release with debug info (i.e. release mode optimizations
# are performed, but debug info still exists).
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "" FORCE)
endif()
if(WITH_JAVA AND NOT BUILD_SHARED_LIBS)
message(
FATAL_ERROR
"
FATAL: Cannot build static libs with Java enabled.
Static libs requires both BUILD_SHARED_LIBS=OFF and
WITH_JAVA=OFF
"
)
endif()
if(WITH_SIMULATION_MODULES AND NOT BUILD_SHARED_LIBS)
message(
FATAL_ERROR
"
FATAL: Cannot build static libs with simulation modules enabled.
Static libs requires both BUILD_SHARED_LIBS=OFF and
WITH_SIMULATION_MODULES=OFF
"
)
endif()
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()
if(NOT WITH_WPILIB AND WITH_SIMULATION_MODULES)
message(
FATAL_ERROR
"
FATAL: Cannot build simulation modules with wpilib disabled.
Enable wpilib by setting WITH_WPILIB=ON
"
)
endif()
if(NOT WITH_NTCORE AND WITH_CSCORE)
message(
FATAL_ERROR
"
FATAL: Cannot build cameraserver without ntcore.
Enable ntcore by setting WITH_NTCORE=ON
"
)
endif()
if(NOT WITH_NTCORE AND WITH_GUI)
message(
FATAL_ERROR
"
FATAL: Cannot build GUI modules without ntcore.
Enable ntcore by setting WITH_NTCORE=ON
"
)
endif()
if(NOT WITH_NTCORE AND WITH_SIMULATION_MODULES)
message(
FATAL_ERROR
"
FATAL: Cannot build simulation modules without ntcore.
Enable ntcore by setting WITH_NTCORE=ON
"
)
endif()
if(NOT WITH_NTCORE AND WITH_WPILIB)
message(
FATAL_ERROR
"
FATAL: Cannot build wpilib without ntcore.
Enable ntcore by setting WITH_NTCORE=ON
"
)
endif()
if(NOT WITH_WPIMATH AND WITH_WPILIB)
message(
FATAL_ERROR
"
FATAL: Cannot build wpilib without wpimath.
Enable wpimath by setting WITH_WPIMATH=ON
"
)
endif()
if(NOT WITH_WPIUNITS AND WITH_WPIMATH AND WITH_JAVA)
message(
FATAL_ERROR
"
FATAL: Cannot build Java wpimath without wpiunits.
Enable wpiunits by setting WITH_WPIUNITS=ON or disable the Java build by setting WITH_JAVA=OFF
"
)
endif()
set(include_dest include)
set(java_lib_dest java)
set(jni_lib_dest jni)
if(USE_SYSTEM_LIBUV)
set(LIBUV_SYSTEM_REPLACE
"
find_dependency(libuv CONFIG)
"
)
endif()
if(USE_SYSTEM_EIGEN)
set(EIGEN_SYSTEM_REPLACE "find_package(Eigen3 CONFIG)")
endif()
find_package(LIBSSH 0.7.1)
find_package(Protobuf REQUIRED)
set(APRILTAG_DEP_REPLACE "find_dependency(apriltag)")
set(CAMERASERVER_DEP_REPLACE_IMPL "find_dependency(cameraserver)")
set(CSCORE_DEP_REPLACE_IMPL "find_dependency(cscore)")
set(HAL_DEP_REPLACE_IMPL "find_dependency(hal)")
set(NTCORE_DEP_REPLACE "find_dependency(ntcore)")
set(WPILIBC_DEP_REPLACE_IMPL "find_dependency(wpilibc)")
set(WPILIBJ_DEP_REPLACE "find_dependency(wpilibj)")
set(WPILIBNEWCOMMANDS_DEP_REPLACE "find_dependency(wpilibNewCommands)")
set(WPIMATH_DEP_REPLACE "find_dependency(wpimath)")
set(WPINET_DEP_REPLACE "find_dependency(wpinet)")
set(WPIUNITS_DEP_REPLACE "find_dependency(wpiunits)")
set(WPIUTIL_DEP_REPLACE "find_dependency(wpiutil)")
set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)")
set(SELF_DIR "$\{SELF_DIR\}")
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(isMultiConfig)
if(NOT "Asan" IN_LIST CMAKE_CONFIGURATION_TYPES)
list(APPEND CMAKE_CONFIGURATION_TYPES Asan)
endif()
if(NOT "Tsan" IN_LIST CMAKE_CONFIGURATION_TYPES)
list(APPEND CMAKE_CONFIGURATION_TYPES Tsan)
endif()
if(NOT "Ubsan" IN_LIST CMAKE_CONFIGURATION_TYPES)
list(APPEND CMAKE_CONFIGURATION_TYPES Ubsan)
endif()
else()
set(allowedBuildTypes
Asan
Tsan
Ubsan
Debug
Release
RelWithDebInfo
MinSizeRel
)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${allowedBuildTypes}")
if(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE IN_LIST allowedBuildTypes)
message(FATAL_ERROR "Invalid build type: ${CMAKE_BUILD_TYPE}")
endif()
endif()
set(CMAKE_C_FLAGS_ASAN
"${CMAKE_C_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer"
CACHE STRING
"Flags used by the C compiler for Asan build type or configuration."
FORCE
)
set(CMAKE_CXX_FLAGS_ASAN
"${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer"
CACHE STRING
"Flags used by the C++ compiler for Asan build type or configuration."
FORCE
)
set(CMAKE_EXE_LINKER_FLAGS_ASAN
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=address"
CACHE STRING
"Linker flags to be used to create executables for Asan build type."
FORCE
)
set(CMAKE_SHARED_LINKER_FLAGS_ASAN
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=address"
CACHE STRING
"Linker lags to be used to create shared libraries for Asan build type."
FORCE
)
set(CMAKE_C_FLAGS_TSAN
"${CMAKE_C_FLAGS_DEBUG} -fsanitize=thread -fno-omit-frame-pointer"
CACHE STRING
"Flags used by the C compiler for Tsan build type or configuration."
FORCE
)
set(CMAKE_CXX_FLAGS_TSAN
"${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=thread -fno-omit-frame-pointer"
CACHE STRING
"Flags used by the C++ compiler for Tsan build type or configuration."
FORCE
)
set(CMAKE_EXE_LINKER_FLAGS_TSAN
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=thread"
CACHE STRING
"Linker flags to be used to create executables for Tsan build type."
FORCE
)
set(CMAKE_SHARED_LINKER_FLAGS_TSAN
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=thread"
CACHE STRING
"Linker lags to be used to create shared libraries for Tsan build type."
FORCE
)
set(CMAKE_C_FLAGS_UBSAN
"${CMAKE_C_FLAGS_DEBUG} -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer"
CACHE STRING
"Flags used by the C compiler for Ubsan build type or configuration."
FORCE
)
set(CMAKE_CXX_FLAGS_UBSAN
"${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer"
CACHE STRING
"Flags used by the C++ compiler for Ubsan build type or configuration."
FORCE
)
set(CMAKE_EXE_LINKER_FLAGS_UBSAN
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=undefined -fno-sanitize-recover=all"
CACHE STRING
"Linker flags to be used to create executables for Ubsan build type."
FORCE
)
set(CMAKE_SHARED_LINKER_FLAGS_UBSAN
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=undefined"
CACHE STRING
"Linker lags to be used to create shared libraries for Ubsan build type."
FORCE
)
if(WITH_TESTS)
enable_testing()
add_subdirectory(googletest)
include(GoogleTest)
endif()
add_subdirectory(wpiutil)
if(WITH_NTCORE)
add_subdirectory(wpinet)
add_subdirectory(ntcore)
endif()
if(WITH_WPIMATH)
if(WITH_JAVA)
add_subdirectory(wpiunits)
endif()
add_subdirectory(wpimath)
endif()
if(WITH_WPIUNITS AND NOT WITH_WPIMATH)
# In case of building wpiunits standalone
add_subdirectory(wpiunits)
endif()
if(WITH_GUI)
add_subdirectory(fieldImages)
add_subdirectory(imgui)
add_subdirectory(wpigui)
add_subdirectory(glass)
add_subdirectory(outlineviewer)
add_subdirectory(sysid)
if(LIBSSH_FOUND)
add_subdirectory(roborioteamnumbersetter)
add_subdirectory(datalogtool)
endif()
endif()
if(WITH_WPILIB OR WITH_SIMULATION_MODULES)
set(HAL_DEP_REPLACE ${HAL_DEP_REPLACE_IMPL})
add_subdirectory(hal)
endif()
if(WITH_CSCORE)
set(CSCORE_DEP_REPLACE ${CSCORE_DEP_REPLACE_IMPL})
set(CAMERASERVER_DEP_REPLACE ${CAMERASERVER_DEP_REPLACE_IMPL})
add_subdirectory(cscore)
add_subdirectory(cameraserver)
endif()
if(WITH_WPILIB)
set(WPILIBC_DEP_REPLACE ${WPILIBC_DEP_REPLACE_IMPL})
add_subdirectory(apriltag)
add_subdirectory(wpilibj)
add_subdirectory(wpilibc)
add_subdirectory(wpilibNewCommands)
add_subdirectory(romiVendordep)
add_subdirectory(xrpVendordep)
if(WITH_EXAMPLES)
add_subdirectory(wpilibcExamples)
endif()
add_subdirectory(myRobot)
endif()
if(WITH_SIMULATION_MODULES AND NOT WITH_EXTERNAL_HAL)
add_subdirectory(simulation)
endif()
configure_file(wpilib-config.cmake.in ${WPILIB_BINARY_DIR}/wpilib-config.cmake)
install(FILES ${WPILIB_BINARY_DIR}/wpilib-config.cmake DESTINATION share/wpilib)
file(GLOB_RECURSE NI_LIBS ni-libraries/*.so*)
get_filename_component(WPILIB_INCLUDES wpilibc/wpilibC++/include REALPATH)
get_filename_component(HAL_API_INCLUDES hal/include REALPATH)
get_filename_component(NWT_API_INCLUDES networktables/cpp/include REALPATH)
add_subdirectory(hal)
add_subdirectory(networktables/cpp)
add_subdirectory(wpilibc)

View File

@@ -1,131 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Exhibiting Gracious Professionalism® at all times. Gracious Professionalism
is a way of doing things that encourages high-quality work, emphasizes the
value of others, and respects individuals and the community.
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[conduct@wpilib.org](mailto:conduct@wpilib.org).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@@ -1,89 +0,0 @@
# Contributing to WPILib
So you want to contribute your changes back to WPILib. Great! We have a few contributing rules that will help you make sure your changes will be accepted into the project. Please remember to follow the rules in the [code of conduct](https://github.com/wpilibsuite/allwpilib/blob/main/CODE_OF_CONDUCT.md), and behave with Gracious Professionalism.
- [General Contribution Rules](#general-contribution-rules)
- [What to Contribute](#what-to-contribute)
- [Coding Guidelines](#coding-guidelines)
- [Submitting Changes](#submitting-changes)
- [Pull Request Format](#pull-request-format)
- [Merge Process](#merge-process)
- [Licensing](#licensing)
## General Contribution Rules
- 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++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 [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), 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! You can create a new issue on our GitHub tracker for feature request/discussion and talk about it with us there.
- 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.
- 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.
## Coding Guidelines
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system.
While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome.
### Math documentation
When writing math expressions in documentation, use https://www.unicodeit.net/ to convert LaTeX to a Unicode equivalent that's easier to read. Not all expressions will translate (e.g., superscripts of superscripts) so focus on making it readable by someone who isn't familiar with LaTeX. If content on multiple lines needs to be aligned in Doxygen/Javadoc comments (e.g., integration/summation limits, matrices packed with square brackets and superscripts for them), put them in @verbatim/@endverbatim blocks in Doxygen or `<pre>` tags in Javadoc so they render with monospace font.
The LaTeX to Unicode conversions can also be done locally via the unicodeit Python package. To install it, execute:
```bash
pip install --user unicodeit
```
Here's example usage:
```bash
$ python -m unicodeit.cli 'x_{k+1} = Ax_k + Bu_k'
xₖ₊₁ = Axₖ + Buₖ
```
On Linux, this process can be streamlined further by adding the following Bash function to your .bashrc (requires `wl-clipboard` on Wayland or `xclip` on X11):
```bash
# Converts LaTeX to Unicode, prints the result, and copies it to the clipboard
uc() {
if [ $WAYLAND_DISPLAY ]; then
python -m unicodeit.cli $@ | tee >(wl-copy -n)
else
python -m unicodeit.cli $@ | tee >(xclip -sel)
fi
}
```
Here's example usage:
```bash
$ uc 'x_{k+1} = Ax_k + Bu_k'
xₖ₊₁ = Axₖ + Buₖ
```
## Submitting Changes
### Pull Request Format
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.
### Merge Process
When you first submit changes, GitHub Actions will attempt to run `./gradlew check` on your change. If this fails, you will need to fix any issues that it sees. Once Actions passes, we will begin the review process in more earnest. One or more WPILib team members will review your change. This will be a back-and-forth process with the WPILib team and the greater community. Once we are satisfied that your change is ready, we will allow our hosted instance to test it. This will run the full gamut of checks, including integration tests on actual hardware. Once all tests have passed and the team is satisfied, we will merge your change into the WPILib repository.
## Licensing
By contributing to WPILib, you agree that your code will be distributed with WPILib, and licensed under the license for the WPILib project. You should not contribute code that you do not have permission to relicense in this manner. This includes code that is licensed under the GPL that you do not have permission to relicense, as WPILib is not released under a copyleft license. Our license is the 3-clause BSD license, which you can find [here](LICENSE.md).

View File

@@ -1,103 +0,0 @@
# Installing Development Builds
This article contains instructions on building projects using a development build and a local WPILib build.
**Note:** This only applies to Java/C++ teams.
> [!WARNING]
> **There are no stability or compatibility guarantees for builds outside of [tagged releases](https://github.com/wpilibsuite/allwpilib/releases). Changes may not be fully documented. Use them at your own risk!**
>
> Development builds may be non-functional between the end of the season and the start of beta testing. Development builds are also likely to be incompatible with vendor libraries during this time.
## Development Build
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 2024 GradleRIO version, ie `2024.0.0-alpha-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.
Java
```groovy
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
}
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = '2024.+'
wpi.versions.wpimathVersion = '2024.+'
```
C++
```groovy
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
}
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = '2024.+'
wpi.versions.wpimathVersion = '2024.+'
```
### Development Build Documentation
* C++: https://github.wpilib.org/allwpilib/docs/development/cpp/
* Java: https://github.wpilib.org/allwpilib/docs/development/java/
## Local Build
Building with a local build is very similar to building with a development build. Ensure you have built and published WPILib by following the instructions attached [here](https://github.com/wpilibsuite/allwpilib#building-wpilib). Next, find the ``build.gradle`` file in your robot project and open it. Then, add the following code below the plugin section and replace ``YEAR`` with the year of the local version.
Java
```groovy
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
}
wpi.maven.useLocal = false
wpi.maven.useFrcMavenLocalDevelopment = true
wpi.versions.wpilibVersion = 'YEAR.424242.+'
wpi.versions.wpimathVersion = 'YEAR.424242.+'
```
C++
```groovy
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2024.0.0-alpha-1"
}
wpi.maven.useLocal = false
wpi.maven.useFrcMavenLocalDevelopment = true
wpi.versions.wpilibVersion = 'YEAR.424242.+'
wpi.versions.wpimathVersion = 'YEAR.424242.+'
```
# roboRIO Development
This repo contains a myRobot project built in way to do full project development without needing to do a full publish into GradleRIO. These also only require building the minimum amount of binaries for the roboRIO, so the builds are much faster as well.
The setup only works if the roboRIO is USB connected. If an alternate IP address is preferred, the `address` block in myRobot\build.gradle can be changed to point to another address.
The following 3 tasks can be used for deployment:
* `:myRobot:deployShared` deploys the C++ project using shared dependencies. Prefer this one for most C++ development.
* `:myRobot:deployStatic` deploys the C++ project with all dependencies statically linked.
* `:myRobot:deployJava` deploys the Java project and all required dependencies. Also installs the JRE if not currently installed.
Deploying any of these to the roboRIO will disable the current startup project until it is redeployed.
From here, ssh into the roboRIO using the `lvuser` account and run `frcRunRobot.sh` (It's in path).

View File

@@ -1,24 +0,0 @@
Copyright (c) 2009-2024 FIRST and other WPILib contributors
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 FIRST, WPILib, nor the names of other WPILib
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY FIRST AND OTHER WPILIB 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 FIRST 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.

View File

@@ -1,27 +0,0 @@
## Publishing Third Party Dependencies
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, 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 (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.
## 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.
Upon pushing a tag, a release will be built, and the files will be uploaded to the releases on GitHub. For publishing to the marketplace, you need a Microsoft account and to be added as a maintainer.
## Publishing GradleRIO
Before publishing, make sure to update the version in build.gradle. Publishing must happen locally, using the command `./gradlew publishPlugin`. This does require your API key for publishing to be set.
## Building the installer
Update the GradleRIO version in gradle.properties, and in the scripts folder in vscode, update the vscode extension. To publish a release build, upload a new tag, and a release will automatically be built and published to artifactory and cloudflare.

View File

@@ -1,164 +0,0 @@
# WPILib Maven Artifacts
WPILib publishes its built artifacts to our Maven server for use by downstream projects. This document explains these locations, and the meanings of artifact names, classifiers, and versions.
## Repositories
We provide two repositories. These repositories are:
* (Release) https://frcmaven.wpi.edu/artifactory/release/
* (Development) https://frcmaven.wpi.edu/artifactory/development/
The release repository is where official WPILib releases are pushed.
The development repository is where development releases of every commit to [main](https://github.com/wpilibsuite/allwpilib/tree/main) is pushed.
## Artifact classifiers
We provide two base types of artifacts.
The first types are Java artifacts. These are usually published as `jar` files. Usually, the actual jar file is published with no classifier. The sources are published with the `-sources` classifier, and the javadocs are published with the `-javadoc` classifier.
The second types are native artifacts. These are usually published as `zip` files (except for the `JNI` artifact types, which are `jar` files. See below for information on this). 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 platform artifact only contains the binaries for a specific platform. In addition, we provide a `-all` classifier. This classifier combines all of the platform artifacts into a single artifact. This is useful for tools that cannot determine what version to use during builds. However, we recommend using the platform specific classifier when possible. Note that the binary artifacts never contain the headers, you always need the `-headers` classifier to get those.
## Artifact Names
WPILib builds four different types of artifacts.
##### C++ Only Libraries
When we publish C++ only libraries, they are published with the base artifact name as their artifact name, with a `-cpp` extension. All dependencies for the library are linked as shared libraries to the binary.
Example:
```
edu.wpi.first.wpilibc:wpilibc-cpp:version:classifier@zip
```
#### Java Only Libraries
When we publish Java only libraries, they are published with the base artifact name as their artifact name, with a `-java` extension.
Example:
```
edu.wpi.first.wpilibj:wpilibj-java:version
```
#### C++/Java Libraries without JNI
For libraries that are both C++ and Java, but without a JNI component, the C++ component is published with the `basename-cpp` artifact name, and the Java component is published with the `basename-java` artifact name.
Example:
```
edu.wpi.first.wpiutil:wpiutil-cpp:version:classifier@zip (C++)
edu.wpi.first.wpiutil:wpiutil-java:version (Java)
```
#### C++/Java Libraries with JNI
For libraries that are both C++ and Java with a JNI component there are three different artifact names. For Java, the component is published as `basename-java`. For C++, the `basename-cpp` artifact contains the C++ artifacts with all dependencies linked as shared libraries to the binary. These binaries DO contain the JNI entry points. The `basename-jni` artifact contains identical C++ binaries to the `-cpp` artifact, however all of its dependencies are statically linked, and only the JNI and C entry points are exported.
The `-jni` artifact should only be used in cases where you want to create a self contained Java application where the native artifacts are embedded in the jar. Note in an extraction scenario, extending off of the library is never supported, which is why the C++ entry points are not exposed. The name of the library is randomly generated during extraction. For pretty much all cases, and if you ever want to extend from a native library, you should use the `-cpp` artifacts. GradleRIO uses the `-cpp` artifacts for all platforms, even desktop, for this reason.
Example:
```
edu.wpi.first.ntcore:ntcore-cpp:version:classifier@zip (C++)
edu.wpi.first.ntcore:ntcore-jni:version:classifier (JNI jar library)
edu.wpi.first.ntcore:ntcore-java:version (Java)
```
## Provided Artifacts
This repository provides the following artifacts. Below each artifact is its dependencies. Note if ever using the `-jni` artifacts, no dependencies are needed for native binaries.
For C++, if building with static dependencies, the listed order should be the link order in your linker.
All artifacts are based at `edu.wpi.first.artifactname` in the repository.
* wpiutil
* wpigui
* imgui
* wpimath
* wpiutil
* wpinet
* wpiutil
* ntcore
* wpiutil
* wpinet
* glass/libglass
* wpiutil
* wpimath
* wpigui
* glass/libglassnt
* wpiutil
* wpinet
* ntcore
* wpimath
* wpigui
* hal
* wpiutil
* halsim
* wpiutil
* wpinet
* ntcore
* wpimath
* wpigui
* libglass
* libglassnt
* cscore
* opencv
* wpinet
* wpiutil
* cameraserver
* ntcore
* cscore
* opencv
* wpinet
* wpiutil
* wpilibj
* hal
* cameraserver
* ntcore
* cscore
* wpinet
* wpiutil
* wpilibc
* hal
* cameraserver
* ntcore
* cscore
* wpimath
* wpinet
* wpiutil
* wpilibNewCommands
* wpilibc
* hal
* cameraserver
* ntcore
* cscore
* wpimath
* wpinet
* wpiutil
* wpiunits
* apriltag
* wpiutil
* wpimath
### Third Party Artifacts
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
* opencv
* libssh

View File

@@ -1,169 +0,0 @@
# 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 it is supported on Windows, these docs will only go over Linux builds.
## Libraries that get built
* wpiutil
* ntcore
* cscore
* cameraserver
* hal
* wpilib
* halsim
* wpigui
* wpimath
* wpiunits
* wpilibNewCommands
By default, all libraries except for the HAL and WPILib 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.
## Prerequisites
The jinja2 pip package is needed to generate classes for NT4's pubsub.
The protobuf library and compiler are needed for protobuf generation. The QuickBuffers protoc-gen package is also required when Java is being built; this can be obtained from https://github.com/HebiRobotics/QuickBuffers/releases/.
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 11 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:
* `WITH_JAVA` (ON Default)
* This option will enable Java and JNI builds. If this is on, `WITH_SHARED_LIBS` must be on. Otherwise CMake will error.
* `WITH_JAVA_SOURCE` (ON 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_SHARED_LIBS` (ON Default)
* 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, the hal and wpilib as well, irrespective of their specific options. If this is off, the OpenCV build requirement is removed.
* `WITH_NTCORE` (ON Default)
* This option will cause ntcore to be built. Turning this off will implicitly disable wpinet and wpilib as well, irrespective of their specific options.
* `WITH_WPIMATH` (ON Default)
* This option will build the wpimath library. This option must be on to build wpilib.
* `WITH_WPIUNITS` (ON 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.
* `WITH_WPILIB` (ON Default)
* This option will build the hal and wpilibc/j during the build. The HAL is the simulator hal, unless the external hal options are used. The cmake build has no capability to build for the RoboRIO.
* `WITH_EXAMPLES` (ON Default)
* This option will build C++ examples.
* `WITH_TESTS` (ON Default)
* This option will build C++ unit tests. These can be run via `make test`.
* `WITH_GUI` (ON Default)
* This option will build GUI items.
* `WITH_SIMULATION_MODULES` (ON Default)
* This option will build simulation modules, including wpigui and the HALSim plugins.
* `WITH_EXTERNAL_HAL` (OFF Default)
* TODO
* `EXTERNAL_HAL_FILE`
* TODO
* `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.
## Build Setup
The WPILib CMake build does not allow in source builds. Because the `build` directory is used by Gradle, we recommend a `build-cmake` directory in the root. This folder is included in the gitignore.
Once you have a build folder, run CMake configuration in that build directory with the following command.
```
cmake path/to/allwpilib/root
```
If you want to change any of the options, add `-DOPTIONHERE=VALUE` to the `cmake` command. This will check for any dependencies. If everything works properly this will succeed. If not, please check out the troubleshooting section for help.
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.
## Building
Once you have cmake setup. run `make` from the directory you configured CMake in. This will build all libraries possible. If you have a multicore system, we recommend running `make` with multiple jobs. The usual rule of thumb is 1.5x the number of cores you have. To run a multiple job build, run the following command with x being the number of jobs you want.
```
make -jx
```
The `ninja` generator is also supported, and can be enabled by passing `-GNinja` to the initial `cmake` command.
## Installing
After build, the easiest way to use the libraries is to install them. Run the following command to install the libraries. This will install them so that they can be used from external cmake projects.
```
sudo make install
```
## Using the installed libraries for C++.
Using the libraries from C++ is the easiest way to use the built libraries.
To do so, create a new folder to contain your project. Add the following code below to a `CMakeLists.txt` file in that directory.
```
cmake_minimum_required(VERSION 3.11)
project(vision_app) # Project Name Here
find_package(wpilib REQUIRED)
add_executable(my_vision_app main.cpp) # executable name as first parameter
target_link_libraries(my_vision_app cameraserver ntcore cscore wpiutil)
```
If you are using them, `wpilibc` and `hal` should be added before the `cameraserver` declaration in the `target_link_libraries` function.
Add a `main.cpp` 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 `make`. 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
TODO
## Troubleshooting
Below are some common issues that are run into when building.
#### Missing OpenCV
If you are missing OpenCV, you will get an error message similar to this.
```
CMake Error at cscore/CMakeLists.txt:3 (find_package):
By not providing "FindOpenCV.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "OpenCV", but
CMake did not find one.
Could not find a package configuration file provided by "OpenCV" with any
of the following names:
OpenCVConfig.cmake
opencv-config.cmake
Add the installation prefix of "OpenCV" to CMAKE_PREFIX_PATH or set
"OpenCV_DIR" to a directory containing one of the above files. If "OpenCV"
provides a separate development package or SDK, be sure it has been
installed.
```
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`.

185
README.md
View File

@@ -1,185 +0,0 @@
# WPILib Project
[![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/)
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)
- [Building WPILib](#building-wpilib)
- [Requirements](#requirements)
- [Setup](#setup)
- [Building](#building)
- [Faster builds](#faster-builds)
- [Using Development Builds](#using-development-builds)
- [Custom toolchain location](#custom-toolchain-location)
- [Formatting/Linting](#formattinglinting)
- [CMake](#cmake)
- [Running examples in simulation](#running-examples-in-simulation)
- [Publishing](#publishing)
- [Structure and Organization](#structure-and-organization)
- [Contributing to WPILib](#contributing-to-wpilib)
## WPILib Mission
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++, 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
Below is a list of instructions that guide you through cloning, building, publishing and using local allwpilib binaries in a robot project. This quick start is not intended as a replacement for the information further listed in this document.
1. Clone the repository with `git clone https://github.com/wpilibsuite/allwpilib.git`
2. Build the repository with `./gradlew build` or `./gradlew build --build-cache` if you have an internet connection
3. Publish the artifacts locally by running `./gradlew publish`
4. [Update your](DevelopmentBuilds.md) `build.gradle` [to use the artifacts](DevelopmentBuilds.md)
# 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 roboRIO binaries.
## Requirements
- [JDK 11](https://adoptium.net/temurin/releases/?version=11)
- Note that the JRE is insufficient; the full JDK is required
- On Ubuntu, run `sudo apt install openjdk-11-jdk`
- On Windows, install the JDK 11 .msi from the link above
- On macOS, install the JDK 11 .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 13 or later is required.
- 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 roboRIO toolchain on ARM.
## Setup
Clone the WPILib repository and follow the instructions above for installing any required tooling.
See the [styleguide README](https://github.com/wpilibsuite/styleguide/blob/main/README.md) for wpiformat setup instructions.
## Building
All build steps are executed using the Gradle wrapper, `gradlew`. Each target that Gradle can build is referred to as a task. The most common Gradle task to use is `build`. This will build all the outputs created by WPILib. To run, open a console and cd into the cloned WPILib directory. Then:
```bash
./gradlew build
```
To build a specific subproject, such as WPILibC, you must access the subproject and run the build task only on that project. Accessing a subproject in Gradle is quite easy. Simply use `:subproject_name:task_name` with the Gradle wrapper. For example, building just WPILibC:
```bash
./gradlew :wpilibc:build
```
The gradlew wrapper only exists in the root of the main project, so be sure to run all commands from there. All of the subprojects have build tasks that can be run. Gradle automatically determines and rebuilds dependencies, so if you make a change in the HAL and then run `./gradlew :wpilibc:build`, the HAL will be rebuilt, then WPILibC.
There are a few tasks other than `build` available. To see them, run the meta-task `tasks`. This will print a list of all available tasks, with a description of each task.
If opening from a fresh clone, generated java dependencies will not exist. Most IDEs will not run the generation tasks, which will cause lots of IDE errors. Manually run `./gradlew compileJava` from a terminal to run all the compile tasks, and then refresh your IDE's configuration (In VS Code open settings.gradle and save).
### Faster builds
`./gradlew build` builds _everything_, which includes debug and release builds for desktop and all installed cross compilers. Many developers don't need or want to build all of this. Therefore, common tasks have shortcuts to only build necessary components for common development and testing tasks.
`./gradlew testDesktopCpp` and `./gradlew testDesktopJava` will build and run the tests for `wpilibc` and `wpilibj` respectively. They will only build the minimum components required to run the tests. `./gradlew testDesktop` will run both `testDesktopJava` and `testDesktopCpp`.
`testDesktopCpp`, `testDesktopJava`, and `testDesktop` tasks also exist for the following projects:
- `apriltag`
- `cameraserver`
- `cscore`
- `hal`
- `ntcore`
- `wpilibNewCommands`
- `wpimath`
- `wpinet`
- `wpiunits`
- `wpiutil`
- `romiVendordep`
- `xrpVendordep`
These can be ran with `./gradlew :projectName:task`.
`./gradlew buildDesktopCpp` and `./gradlew buildDesktopJava` will compile `wpilibcExamples` and `wpilibjExamples` respectively. The results can't be ran, but they can compile.
### Build Cache
Run with `--build-cache` on the command-line to use the shared [build cache](https://docs.gradle.org/current/userguide/build_cache.html) artifacts generated by the continuous integration server. Example:
```bash
./gradlew build --build-cache
```
### Using Development Builds
Please read the documentation available [here](DevelopmentBuilds.md)
### Custom toolchain location
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/frc/toolchain/bin
```
### Formatting/linting
Once a PR has been submitted, formatting can be run in CI by commenting `/format` on the PR. A new commit will be pushed with the formatting changes.
#### wpiformat
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat` on Windows or `python3 -m wpiformat` on other platforms.
#### Java Code Quality Tools
The Java code quality tools Checkstyle, PMD, and Spotless can be run via `./gradlew javaFormat`. SpotBugs can be run via the `spotbugsMain`, `spotbugsTest`, and `spotbugsDev` tasks. These tools will all be run automatically by the `build` task. To disable this behavior, pass the `-PskipJavaFormat` flag.
If you only want to run the Java autoformatter, run `./gradlew spotlessApply`.
### CMake
CMake is also supported for building. See [README-CMAKE.md](README-CMAKE.md).
## Running examples in simulation
Examples can be run in simulation with the following command:
```bash
./gradlew wpilibcExamples:runExample
./gradlew wpilibjExamples:runExample
```
where `Example` is the example's folder name.
## Publishing
If you are building to test with other dependencies or just want to export the build as a Maven-style dependency, simply run the `publish` task. This task will publish all available packages to ~/releases/maven/development. If you need to publish the project to a different repo, you can specify it with `-Prepo=repo_name`. Valid options are:
- development - The default repo.
- beta - Publishes to ~/releases/maven/beta.
- stable - Publishes to ~/releases/maven/stable.
- release - Publishes to ~/releases/maven/release.
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 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 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 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.
The [styleguide repository](https://github.com/wpilibsuite/styleguide) contains our style guides for C++ and Java code. Anything submitted to the WPILib project needs to follow the code style guides outlined in there. For details about the style, please see the contributors document [here](CONTRIBUTING.md#coding-guidelines).
# Contributing to WPILib
See [CONTRIBUTING.md](CONTRIBUTING.md).

File diff suppressed because it is too large Load Diff

View File

@@ -1,150 +0,0 @@
project(apriltag)
include(CompileWarnings)
include(GenResources)
include(FetchContent)
fetchcontent_declare(
apriltaglib
GIT_REPOSITORY https://github.com/wpilibsuite/apriltag.git
GIT_TAG 64be6ab26abf5e995321997fd0752c609a7e30f4
)
# Don't use apriltag's CMakeLists.txt due to conflicting naming and JNI
fetchcontent_getproperties(apriltaglib)
if(NOT apriltaglib_POPULATED)
fetchcontent_populate(apriltaglib)
endif()
aux_source_directory(${apriltaglib_SOURCE_DIR}/common APRILTAGLIB_COMMON_SRC)
file(GLOB TAG_FILES ${apriltaglib_SOURCE_DIR}/tag*.c)
set(APRILTAGLIB_SRCS
${apriltaglib_SOURCE_DIR}/apriltag.c
${apriltaglib_SOURCE_DIR}/apriltag_pose.c
${apriltaglib_SOURCE_DIR}/apriltag_quad_thresh.c
)
file(GLOB apriltag_jni_src src/main/native/cpp/jni/AprilTagJNI.cpp)
if(WITH_JAVA)
find_package(Java REQUIRED)
find_package(JNI REQUIRED)
include(UseJava)
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
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
NO_DEFAULT_PATH
)
set(CMAKE_JAVA_INCLUDE_PATH apriltag.jar ${EJML_JARS} ${JACKSON_JARS})
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 ${EJML_JARS} wpiutil_jar ${OPENCV_JAR_FILE}
OUTPUT_NAME apriltag
GENERATE_NATIVE_HEADERS apriltag_jni_headers
)
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})
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)
endif()
if(WITH_JAVA_SOURCE)
find_package(Java REQUIRED)
include(UseJava)
file(GLOB APRILTAG_SOURCES src/main/java/edu/wpi/first/apriltag/*.java)
add_jar(
apriltag_src_jar
RESOURCES
NAMESPACE "edu/wpi/first/apriltag" ${APRILTAG_SOURCES}
NAMESPACE
"edu/wpi/first/apriltag/jni"
src/main/java/edu/wpi/first/apriltag/jni/AprilTagJNI.java
OUTPUT_NAME apriltag-sources
)
get_property(APRILTAG_SRC_JAR_FILE TARGET apriltag_src_jar PROPERTY JAR_FILE)
install(FILES ${APRILTAG_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
set_property(TARGET apriltag_src_jar PROPERTY FOLDER "java")
endif()
generate_resources(src/main/native/resources/edu/wpi/first/apriltag generated/main/cpp APRILTAG frc apriltag_resources_src)
file(GLOB apriltag_native_src src/main/native/cpp/*.cpp)
add_library(
apriltag
${apriltag_native_src}
${apriltag_resources_src}
${APRILTAGLIB_SRCS}
${APRILTAGLIB_COMMON_SRC}
${TAG_FILES}
)
set_target_properties(apriltag PROPERTIES DEBUG_POSTFIX "d")
set_property(TARGET apriltag PROPERTY FOLDER "libraries")
target_compile_features(apriltag PUBLIC cxx_std_20)
wpilib_target_warnings(apriltag)
# disable warnings that apriltaglib can't handle
if(MSVC)
target_compile_options(apriltag PRIVATE /wd4018 /wd4005 /wd4996)
else()
target_compile_options(
apriltag
PRIVATE -Wno-sign-compare -Wno-gnu-zero-variadic-macro-arguments -Wno-type-limits
)
endif()
target_link_libraries(apriltag wpimath)
target_include_directories(
apriltag
PUBLIC
$<BUILD_INTERFACE:${apriltaglib_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
$<INSTALL_INTERFACE:${include_dest}/apriltag>
)
install(TARGETS apriltag EXPORT apriltag)
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/apriltag")
configure_file(apriltag-config.cmake.in ${WPILIB_BINARY_DIR}/apriltag-config.cmake)
install(FILES ${WPILIB_BINARY_DIR}/apriltag-config.cmake DESTINATION share/apriltag)
install(EXPORT apriltag DESTINATION share/apriltag)
if(WITH_TESTS)
wpilib_add_test(apriltag src/test/native/cpp)
target_include_directories(apriltag_test PRIVATE src/test/native/include)
target_link_libraries(apriltag_test apriltag gmock_main)
endif()

View File

@@ -1,28 +0,0 @@
# AprilTag
## Adding new field to AprilTagFields
### Adding field JSON
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
4. Update the field dimensions at the bottom of the JSON
1. Length should be in meters from alliance wall to alliance wall
2. Width should be in meters from inside guardrail plastic to plastic
### Java updates
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/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/AprilTagFields.cpp`
1. Add resource getter prototype like `std::string_view GetResource_2024_crescendo_json()`
2. Add case for new field to switch in `LoadAprilTagLayoutField()`

View File

@@ -1,10 +0,0 @@
include(CMakeFindDependencyMacro)
@FILENAME_DEP_REPLACE@
@WPIMATH_DEP_REPLACE@
@WPIUTIL_DEP_REPLACE@
@FILENAME_DEP_REPLACE@
include(${SELF_DIR}/apriltag.cmake)
if(@WITH_JAVA@)
include(${SELF_DIR}/apriltag_jar.cmake)
endif()

View File

@@ -1,87 +0,0 @@
apply from: "${rootDir}/shared/resources.gradle"
ext {
nativeName = 'apriltag'
devMain = 'edu.wpi.first.apriltag.DevMain'
useJava = true
useCpp = true
sharedCvConfigs = [
apriltagDev : [],
apriltagTest: []]
staticCvConfigs = []
def generateTask = createGenerateResourcesTask('main', 'APRILTAG', 'frc', project)
tasks.withType(CppCompile) {
dependsOn generateTask
}
splitSetup = {
it.sources {
resourcesCpp(CppSourceSet) {
source {
srcDirs "$buildDir/generated/main/cpp", "$rootDir/shared/singlelib"
include '*.cpp'
}
}
}
}
}
evaluationDependsOn(':wpimath')
apply from: "${rootDir}/shared/jni/setupBuild.gradle"
apply from: "${rootDir}/shared/apriltaglib.gradle"
apply from: "${rootDir}/shared/opencv.gradle"
dependencies {
implementation project(':wpimath')
}
sourceSets {
main {
resources {
srcDirs 'src/main/native/resources'
}
}
}
model {
components {}
binaries {
all {
if (!it.buildable || !(it instanceof NativeBinarySpec)) {
return
}
it.cppCompiler.define 'WPILIB_EXPORTS'
if (it.component.name == "${nativeName}JNI") {
lib project: ':wpimath', library: 'wpimath', linkage: 'static'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
} else {
lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
nativeUtils.useRequiredLibrary(it, 'apriltaglib')
}
}
tasks {
def c = $.components
def found = false
def systemArch = getCurrentArch()
c.each {
if (it in NativeExecutableSpec && it.name == "${nativeName}Dev") {
it.binaries.each {
if (!found) {
def arch = it.targetPlatform.name
if (arch == systemArch) {
def filePath = it.tasks.install.installDirectory.get().toString() + File.separatorChar + 'lib'
found = true
}
}
}
}
}
}
}

View File

@@ -1,89 +0,0 @@
#!/usr/bin/env python3
"""
This script converts all AprilTag field layout CSV files in
src/main/native/resources/edu/wpi/first/apriltag to the JSON format
AprilTagFields expects.
The input CSV has the following format:
* Columns: ID, X, Y, Z, Rotation
* ID is a positive integer
* X, Y, and Z are decimal inches
* Rotation is yaw in degrees
The values come from a table in the layout marking diagram (e.g.,
https://firstfrc.blob.core.windows.net/frc2024/FieldAssets/2024LayoutMarkingDiagram.pdf).
"""
import csv
import json
import os
from wpimath import geometry, units
import numpy as np
def main():
# Find AprilTag field layout CSVs
filenames = [
os.path.join(dp, f)
for dp, dn, fn in os.walk("src/main/native/resources/edu/wpi/first/apriltag")
for f in fn
if f.endswith(".csv")
]
for filename in filenames:
json_data = {"tags": [], "field": {"length": 0.0, "width": 0.0}}
# Read CSV and fill in JSON data
with open(filename, newline="") as csvfile:
reader = csv.reader(csvfile, delimiter=",")
# Skip header
next(reader)
for row in reader:
# Unpack row elements
id = int(row[0])
x = float(row[1])
y = float(row[2])
z = float(row[3])
rotation = float(row[4])
# Turn yaw into quaternion
q = geometry.Rotation3d(
units.radians(0.0),
units.radians(0.0),
units.degreesToRadians(rotation),
).getQuaternion()
json_data["tags"].append(
{
"ID": id,
"pose": {
"translation": {
"x": units.inchesToMeters(x),
"y": units.inchesToMeters(y),
"z": units.inchesToMeters(z),
},
"rotation": {
"quaternion": {
"W": q.W(),
"X": q.X(),
"Y": q.Y(),
"Z": q.Z(),
}
},
},
}
)
# Write JSON
with open(filename.replace(".csv", ".json"), "w") as f:
json.dump(json_data, f, indent=2)
f.write("\n")
if __name__ == "__main__":
main()

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 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,11 +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.
#include "frc/apriltag/AprilTagDetector.h"
int main() {
frc::AprilTagDetector detector;
detector.AddFamily("tag16h5");
detector.SetConfig({.refineEdges = false});
}

View File

@@ -1,82 +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 edu.wpi.first.apriltag;
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;
/** Represents an AprilTag's metadata. */
@SuppressWarnings("MemberName")
public class AprilTag {
/** The tag's ID. */
@JsonProperty(value = "ID")
public int ID;
/** The tag's pose. */
@JsonProperty(value = "pose")
public Pose3d pose;
/**
* Constructs an AprilTag.
*
* @param ID The tag's ID.
* @param pose The tag's pose.
*/
@SuppressWarnings("ParameterName")
@JsonCreator
public AprilTag(
@JsonProperty(required = true, value = "ID") int ID,
@JsonProperty(required = true, value = "pose") Pose3d pose) {
this.ID = ID;
this.pose = pose;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AprilTag) {
var other = (AprilTag) obj;
return ID == other.ID && pose.equals(other.pose);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(ID, pose);
}
@Override
public String toString() {
return "AprilTag(ID: " + ID + ", pose: " + pose + ")";
}
/**
* Generates a RawFrame containing the apriltag with the id with family 16h5 passed in.
*
* @param id id
* @return A RawFrame containing the AprilTag image
*/
public static RawFrame generate16h5AprilTagImage(int id) {
RawFrame frame = new RawFrame();
AprilTagJNI.generate16h5AprilTagImage(frame, frame.getNativeObj(), id);
return frame;
}
/**
* Generates a RawFrame containing the apriltag with the id with family 36h11 passed in.
*
* @param id id
* @return A RawFrame containing the AprilTag image
*/
public static RawFrame generate36h11AprilTagImage(int id) {
RawFrame frame = new RawFrame();
AprilTagJNI.generate36h11AprilTagImage(frame, frame.getNativeObj(), id);
return frame;
}
}

View File

@@ -1,191 +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 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;
/** A detection of an AprilTag tag. */
public class AprilTagDetection {
/**
* Gets the decoded tag's family name.
*
* @return Decoded family name
*/
public String getFamily() {
return m_family;
}
/**
* Gets the decoded ID of the tag.
*
* @return Decoded ID
*/
public int getId() {
return m_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)
*/
public int getHamming() {
return m_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
*/
public float getDecisionMargin() {
return m_decisionMargin;
}
/**
* 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
*/
@SuppressWarnings("PMD.MethodReturnsInternalArray")
public double[] getHomography() {
return m_homography;
}
/**
* 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
*/
public Matrix<N3, N3> getHomographyMatrix() {
return MatBuilder.fill(Nat.N3(), Nat.N3(), m_homography);
}
/**
* Gets the center of the detection in image pixel coordinates.
*
* @return Center point X coordinate
*/
public double getCenterX() {
return m_centerX;
}
/**
* Gets the center of the detection in image pixel coordinates.
*
* @return Center point Y coordinate
*/
public double getCenterY() {
return m_centerY;
}
/**
* 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 X coordinate
*/
public double getCornerX(int ndx) {
return m_corners[ndx * 2];
}
/**
* 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 Y coordinate
*/
public double getCornerY(int ndx) {
return m_corners[ndx * 2 + 1];
}
/**
* 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.
*
* @return Corner point array (X and Y for each corner in order)
*/
@SuppressWarnings("PMD.MethodReturnsInternalArray")
public double[] getCorners() {
return m_corners;
}
private final String m_family;
private final int m_id;
private final int m_hamming;
private final float m_decisionMargin;
private final double[] m_homography;
private final double m_centerX;
private final double m_centerY;
private final double[] m_corners;
/**
* Constructs a new detection result. Used from JNI.
*
* @param family family
* @param id id
* @param hamming hamming
* @param decisionMargin dm
* @param homography homography
* @param centerX centerX
* @param centerY centerY
* @param corners corners
*/
@SuppressWarnings("PMD.ArrayIsStoredDirectly")
public AprilTagDetection(
String family,
int id,
int hamming,
float decisionMargin,
double[] homography,
double centerX,
double centerY,
double[] corners) {
m_family = family;
m_id = id;
m_hamming = hamming;
m_decisionMargin = decisionMargin;
m_homography = homography;
m_centerX = centerX;
m_centerY = centerY;
m_corners = corners;
}
@Override
public String toString() {
return "DetectionResult [centerX="
+ m_centerX
+ ", centerY="
+ m_centerY
+ ", corners="
+ Arrays.toString(m_corners)
+ ", decisionMargin="
+ m_decisionMargin
+ ", hamming="
+ m_hamming
+ ", homography="
+ Arrays.toString(m_homography)
+ ", family="
+ m_family
+ ", id="
+ m_id
+ "]";
}
}

View File

@@ -1,310 +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 edu.wpi.first.apriltag;
import edu.wpi.first.apriltag.jni.AprilTagJNI;
import org.opencv.core.Mat;
/**
* 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().
*/
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
* thread).
*/
public 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.
*/
public 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.
*/
public float quadSigma;
/**
* 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.
*
* <p>Very computationally inexpensive. Option is ignored if quad_decimate = 1.
*/
public boolean 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.
*/
public 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).
*/
public boolean debug;
/** Default constructor. */
public Config() {}
/**
* Constructs a detector configuration.
*
* @param numThreads How many threads should be used for computation.
* @param quadDecimate Quad decimation.
* @param quadSigma What Gaussian blur should be applied to the segmented image (used for quad
* detection).
* @param refineEdges When true, the edges of the each quad are adjusted to "snap to" strong
* gradients nearby.
* @param decodeSharpening How much sharpening should be done to decoded images.
* @param debug Debug mode.
*/
Config(
int numThreads,
float quadDecimate,
float quadSigma,
boolean refineEdges,
double decodeSharpening,
boolean debug) {
this.numThreads = numThreads;
this.quadDecimate = quadDecimate;
this.quadSigma = quadSigma;
this.refineEdges = refineEdges;
this.decodeSharpening = decodeSharpening;
this.debug = debug;
}
@Override
public int hashCode() {
return numThreads
+ Float.hashCode(quadDecimate)
+ Float.hashCode(quadSigma)
+ Boolean.hashCode(refineEdges)
+ Double.hashCode(decodeSharpening)
+ Boolean.hashCode(debug);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Config)) {
return false;
}
Config other = (Config) obj;
return numThreads == other.numThreads
&& quadDecimate == other.quadDecimate
&& quadSigma == other.quadSigma
&& refineEdges == other.refineEdges
&& decodeSharpening == other.decodeSharpening
&& debug == other.debug;
}
}
/** Quad threshold parameters. */
@SuppressWarnings("MemberName")
public static class QuadThresholdParameters {
/** Threshold used to reject quads containing too few pixels. Default is 5 pixels. */
public int minClusterPixels = 5;
/**
* How many corner candidates to consider when segmenting a group of pixels into a quad. Default
* is 10.
*/
public int maxNumMaxima = 10;
/**
* Critical angle, in radians. 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 10 degrees.
*/
public double criticalAngle = 10 * Math.PI / 180.0;
/**
* 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.
*/
public float maxLineFitMSE = 10.0f;
/**
* Minimum brightness offset. When we build our model of black &amp; 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.
*/
public int minWhiteBlackDiff = 5;
/**
* Whether the thresholded image be should be deglitched. Only useful for very noisy images.
* Default is disabled (false).
*/
public boolean deglitch;
/** Default constructor. */
public QuadThresholdParameters() {}
/**
* Constructs quad threshold parameters.
*
* @param minClusterPixels Threshold used to reject quads containing too few pixels.
* @param maxNumMaxima How many corner candidates to consider when segmenting a group of pixels
* into a quad.
* @param criticalAngle Critical angle, in radians.
* @param maxLineFitMSE When fitting lines to the contours, the maximum mean squared error
* allowed.
* @param minWhiteBlackDiff Minimum brightness offset.
* @param deglitch Whether the thresholded image be should be deglitched.
*/
QuadThresholdParameters(
int minClusterPixels,
int maxNumMaxima,
double criticalAngle,
float maxLineFitMSE,
int minWhiteBlackDiff,
boolean deglitch) {
this.minClusterPixels = minClusterPixels;
this.maxNumMaxima = maxNumMaxima;
this.criticalAngle = criticalAngle;
this.maxLineFitMSE = maxLineFitMSE;
this.minWhiteBlackDiff = minWhiteBlackDiff;
this.deglitch = deglitch;
}
@Override
public int hashCode() {
return minClusterPixels
+ maxNumMaxima
+ Double.hashCode(criticalAngle)
+ Float.hashCode(maxLineFitMSE)
+ minWhiteBlackDiff
+ Boolean.hashCode(deglitch);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof QuadThresholdParameters)) {
return false;
}
QuadThresholdParameters other = (QuadThresholdParameters) obj;
return minClusterPixels == other.minClusterPixels
&& maxNumMaxima == other.maxNumMaxima
&& criticalAngle == other.criticalAngle
&& maxLineFitMSE == other.maxLineFitMSE
&& minWhiteBlackDiff == other.minWhiteBlackDiff
&& deglitch == other.deglitch;
}
}
/** Constructs an AprilTagDetector. */
public AprilTagDetector() {
m_native = AprilTagJNI.createDetector();
}
@Override
public void close() {
if (m_native != 0) {
AprilTagJNI.destroyDetector(m_native);
}
m_native = 0;
}
/**
* Sets detector configuration.
*
* @param config Configuration
*/
public void setConfig(Config config) {
AprilTagJNI.setDetectorConfig(m_native, config);
}
/**
* Gets detector configuration.
*
* @return Configuration
*/
public Config getConfig() {
return AprilTagJNI.getDetectorConfig(m_native);
}
/**
* Sets quad threshold parameters.
*
* @param params Parameters
*/
public void setQuadThresholdParameters(QuadThresholdParameters params) {
AprilTagJNI.setDetectorQTP(m_native, params);
}
/**
* Gets quad threshold parameters.
*
* @return Parameters
*/
public QuadThresholdParameters getQuadThresholdParameters() {
return AprilTagJNI.getDetectorQTP(m_native);
}
/**
* Adds a family of tags to be detected.
*
* @param fam Family name, e.g. "tag16h5"
* @throws IllegalArgumentException if family name not recognized
*/
public void addFamily(String fam) {
addFamily(fam, 2);
}
/**
* Adds a family of tags to be detected.
*
* @param fam Family name, e.g. "tag16h5"
* @param bitsCorrected Maximum number of bits to correct
* @throws IllegalArgumentException if family name not recognized
*/
public void addFamily(String fam, int bitsCorrected) {
if (!AprilTagJNI.addFamily(m_native, fam, bitsCorrected)) {
throw new IllegalArgumentException("unknown family name '" + fam + "'");
}
}
/**
* Removes a family of tags from the detector.
*
* @param fam Family name, e.g. "tag16h5"
*/
public void removeFamily(String fam) {
AprilTagJNI.removeFamily(m_native, fam);
}
/** Unregister all families. */
public void clearFamilies() {
AprilTagJNI.clearFamilies(m_native);
}
/**
* Detect tags from an 8-bit image.
*
* <p>The image must be grayscale.
*
* @param img 8-bit OpenCV Mat image
* @return Results (array of AprilTagDetection)
*/
public AprilTagDetection[] detect(Mat img) {
return AprilTagJNI.detect(m_native, img.cols(), img.rows(), img.cols(), img.dataAddr());
}
private long m_native;
}

View File

@@ -1,298 +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 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.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) {
switch (origin) {
case kBlueAllianceWallRightSide:
setOrigin(new Pose3d());
break;
case kRedAllianceWallRightSide:
setOrigin(
new Pose3d(
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
new Rotation3d(0, 0, Math.PI)));
break;
default:
throw new IllegalArgumentException("Unsupported enum value");
}
}
/**
* 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) {
try {
return loadFromResource(field.m_resourceFile);
} catch (IOException e) {
throw new UncheckedIOException(
"Could not load AprilTagFieldLayout from " + field.m_resourceFile, e);
}
}
/**
* 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);
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) {
if (obj instanceof AprilTagFieldLayout) {
var other = (AprilTagFieldLayout) obj;
return m_apriltags.equals(other.m_apriltags) && m_origin.equals(other.m_origin);
}
return false;
}
@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 double fieldLength;
@SuppressWarnings("MemberName")
@JsonProperty(value = "width")
public 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

@@ -1,40 +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 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");
/** Base resource directory. */
public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
/** Alias to the current game. */
public static final AprilTagFields kDefaultField = k2024Crescendo;
/** Resource filename. */
public final String m_resourceFile;
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
*/
public AprilTagFieldLayout loadAprilTagLayoutField() {
return AprilTagFieldLayout.loadField(this);
}
}

View File

@@ -1,55 +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 edu.wpi.first.apriltag;
import edu.wpi.first.math.geometry.Transform3d;
/** A pair of AprilTag pose estimates. */
@SuppressWarnings("MemberName")
public class AprilTagPoseEstimate {
/**
* Constructs a pose estimate.
*
* @param pose1 first pose
* @param pose2 second pose
* @param error1 error of first pose
* @param error2 error of second pose
*/
public AprilTagPoseEstimate(Transform3d pose1, Transform3d pose2, double error1, double error2) {
this.pose1 = pose1;
this.pose2 = pose2;
this.error1 = error1;
this.error2 = error2;
}
/**
* Get the ratio of pose reprojection errors, called ambiguity. Numbers above 0.2 are likely to be
* ambiguous.
*
* @return The ratio of pose reprojection errors.
*/
public double getAmbiguity() {
double min = Math.min(error1, error2);
double max = Math.max(error1, error2);
if (max > 0) {
return min / max;
} else {
return -1;
}
}
/** Pose 1. */
public final Transform3d pose1;
/** Pose 2. */
public final Transform3d pose2;
/** Object-space error of pose 1. */
public final double error1;
/** Object-space error of pose 2. */
public final double error2;
}

View File

@@ -1,199 +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 edu.wpi.first.apriltag;
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.
*
* @param tagSize tag size, in meters
* @param fx camera horizontal focal length, in pixels
* @param fy camera vertical focal length, in pixels
* @param cx camera horizontal focal center, in pixels
* @param cy camera vertical focal center, in pixels
*/
public Config(double tagSize, double fx, double fy, double cx, double cy) {
this.tagSize = tagSize;
this.fx = fx;
this.fy = fy;
this.cx = cx;
this.cy = cy;
}
/** Tag size, in meters. */
public double tagSize;
/** Camera horizontal focal length, in pixels. */
public double fx;
/** Camera vertical focal length, in pixels. */
public double fy;
/** Camera horizontal focal center, in pixels. */
public double cx;
/** Camera vertical focal center, in pixels. */
public double cy;
@Override
public int hashCode() {
return Double.hashCode(tagSize)
+ Double.hashCode(fx)
+ Double.hashCode(fy)
+ Double.hashCode(cx)
+ Double.hashCode(cy);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Config)) {
return false;
}
Config other = (Config) obj;
return tagSize == other.tagSize
&& fx == other.fx
&& fy == other.fy
&& cx == other.cx
&& cy == other.cy;
}
}
/**
* Creates estimator.
*
* @param config Configuration
*/
public AprilTagPoseEstimator(Config config) {
m_config = new Config(config.tagSize, config.fx, config.fy, config.cx, config.cy);
}
/**
* Sets estimator configuration.
*
* @param config Configuration
*/
public void setConfig(Config config) {
m_config.tagSize = config.tagSize;
m_config.fx = config.fx;
m_config.fy = config.fy;
m_config.cx = config.cx;
m_config.cy = config.cy;
}
/**
* Gets estimator configuration.
*
* @return Configuration
*/
public Config getConfig() {
return new Config(m_config.tagSize, m_config.fx, m_config.fy, m_config.cx, m_config.cy);
}
/**
* Estimates the pose of the tag using the homography method described in [1].
*
* @param detection Tag detection
* @return Pose estimate
*/
public Transform3d estimateHomography(AprilTagDetection detection) {
return estimateHomography(detection.getHomography());
}
/**
* Estimates the pose of the tag using the homography method described in [1].
*
* @param homography Homography 3x3 matrix data
* @return Pose estimate
*/
public Transform3d estimateHomography(double[] homography) {
return AprilTagJNI.estimatePoseHomography(
homography, m_config.tagSize, m_config.fx, m_config.fy, m_config.cx, m_config.cy);
}
/**
* Estimates the pose of the tag. This returns one or two possible poses for the tag, along with
* the object-space error of each.
*
* <p>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.
*
* <p>[1]: E. Olson, “Apriltag: A robust and flexible visual fiducial system,” in 2011 IEEE
* International Conference on Robotics and Automation, May 2011, pp. 34003407.
*
* <p>[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
*
* <p>[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
*/
public AprilTagPoseEstimate estimateOrthogonalIteration(AprilTagDetection detection, int nIters) {
return estimateOrthogonalIteration(detection.getHomography(), detection.getCorners(), nIters);
}
/**
* 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
*/
public AprilTagPoseEstimate estimateOrthogonalIteration(
double[] homography, double[] corners, int nIters) {
return AprilTagJNI.estimatePoseOrthogonalIteration(
homography,
corners,
m_config.tagSize,
m_config.fx,
m_config.fy,
m_config.cx,
m_config.cy,
nIters);
}
/**
* 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
*/
public Transform3d estimate(AprilTagDetection detection) {
return estimate(detection.getHomography(), detection.getCorners());
}
/**
* 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
*/
public Transform3d estimate(double[] homography, double[] corners) {
return AprilTagJNI.estimatePose(
homography, corners, m_config.tagSize, m_config.fx, m_config.fy, m_config.cx, m_config.cy);
}
private final Config m_config;
}

View File

@@ -1,229 +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 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;
/** AprilTag JNI. */
public class AprilTagJNI {
static boolean libraryLoaded = false;
static RuntimeLoader<AprilTagJNI> loader = null;
/** Sets whether JNI should be loaded in the static block. */
public static class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
/**
* Returns true if the JNI should be loaded in the static block.
*
* @return True if the JNI should be loaded in the static block.
*/
public static boolean getExtractOnStaticLoad() {
return extractOnStaticLoad.get();
}
/**
* Sets whether the JNI should be loaded in the static block.
*
* @param load Whether the JNI should be loaded in the static block.
*/
public static void setExtractOnStaticLoad(boolean load) {
extractOnStaticLoad.set(load);
}
/** Utility class. */
private Helper() {}
}
static {
if (Helper.getExtractOnStaticLoad()) {
try {
loader =
new RuntimeLoader<>(
"apriltagjni", RuntimeLoader.getDefaultExtractionRoot(), AprilTagJNI.class);
loader.loadLibrary();
} catch (IOException ex) {
ex.printStackTrace();
System.exit(1);
}
libraryLoaded = true;
}
}
/**
* Constructs an AprilTag detector engine.
*
* @return The detector engine handle
*/
public static native long createDetector();
/**
* Destroys an AprilTag detector engine.
*
* @param det The detector engine handle
*/
public static native void destroyDetector(long det);
/**
* Sets the detector engine configuration.
*
* @param det The detector engine handle
* @param config A configuration
*/
public static native void setDetectorConfig(long det, AprilTagDetector.Config config);
/**
* Gets the detector engine configuration.
*
* @param det The detector engine handle
* @return The configuration
*/
public static native AprilTagDetector.Config getDetectorConfig(long det);
/**
* Sets the detector engine quad threshold parameters.
*
* @param det The detector engine handle
* @param params Quad threshold parameters
*/
public static native void setDetectorQTP(
long det, AprilTagDetector.QuadThresholdParameters params);
/**
* Gets the detector engine quad threshold parameters.
*
* @param det The detector engine handle
* @return Quad threshold parameters
*/
public static native AprilTagDetector.QuadThresholdParameters getDetectorQTP(long det);
/**
* Adds a family of tags to be detected by the detector engine.
*
* @param det The detector engine handle
* @param fam Family name, e.g. "tag16h5"
* @param bitsCorrected Maximum number of bits to correct
* @return False if family can't be found
*/
public static native boolean addFamily(long det, String fam, int bitsCorrected);
/**
* Removes a family of tags from the detector.
*
* @param det The detector engine handle
* @param fam Family name, e.g. "tag16h5"
*/
public static native void removeFamily(long det, String fam);
/**
* Unregister all families.
*
* @param det The detector engine handle
*/
public static native void clearFamilies(long det);
/**
* Detect tags from an 8-bit image.
*
* @param det The detector engine handle
* @param width The width of the image
* @param height The height of the image
* @param stride The number of bytes between image rows (often the same as width)
* @param bufAddr The address of the image buffer
* @return The results (array of AprilTagDetection)
*/
public static native AprilTagDetection[] detect(
long det, int width, int height, int stride, long bufAddr);
/**
* Estimates the pose of the tag using the homography method described in [1].
*
* @param homography Homography 3x3 matrix data
* @param tagSize The tag size, in meters
* @param fx The camera horizontal focal length, in pixels
* @param fy The camera vertical focal length, in pixels
* @param cx The camera horizontal focal center, in pixels
* @param cy The camera vertical focal center, in pixels
* @return Pose estimate
*/
public static native Transform3d estimatePoseHomography(
double[] homography, double tagSize, double fx, double fy, double cx, double cy);
/**
* 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 tagSize The tag size, in meters
* @param fx The camera horizontal focal length, in pixels
* @param fy The camera vertical focal length, in pixels
* @param cx The camera horizontal focal center, in pixels
* @param cy The camera vertical focal center, in pixels
* @param nIters Number of iterations
* @return Initial and (possibly) second pose estimates
*/
public static native AprilTagPoseEstimate estimatePoseOrthogonalIteration(
double[] homography,
double[] corners,
double tagSize,
double fx,
double fy,
double cx,
double cy,
int nIters);
/**
* 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)
* @param tagSize The tag size, in meters
* @param fx The camera horizontal focal length, in pixels
* @param fy The camera vertical focal length, in pixels
* @param cx The camera horizontal focal center, in pixels
* @param cy The camera vertical focal center, in pixels
* @return Pose estimate
*/
public static native Transform3d estimatePose(
double[] homography,
double[] corners,
double tagSize,
double fx,
double fy,
double cx,
double cy);
/**
* Generates a RawFrame containing the apriltag with the id with family 16h5 passed in.
*
* @param frameObj generated frame (output parameter).
* @param frame raw frame handle
* @param id id
*/
public static native void generate16h5AprilTagImage(RawFrame frameObj, long frame, int id);
/**
* Generates a RawFrame containing the apriltag with the id with family 36h11 passed in.
*
* @param frameObj generated frame (output parameter).
* @param frame raw frame handle
* @param id id
*/
public static native void generate36h11AprilTagImage(RawFrame frameObj, long frame, int id);
/** Utility class. */
private AprilTagJNI() {}
}

View File

@@ -1,61 +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.
#include "frc/apriltag/AprilTag.h"
#include <cstring>
#include <wpi/json.h>
#ifdef _WIN32
#pragma warning(disable : 4200)
#elif defined(__clang__)
#pragma clang diagnostic ignored "-Wc99-extensions"
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
#include "apriltag.h"
#include "tag16h5.h"
#include "tag36h11.h"
using namespace frc;
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;
bool rv = frame->Reserve(totalDataSize);
std::memcpy(frame->data, image->buf, totalDataSize);
frame->size = totalDataSize;
frame->width = image->width;
frame->height = image->height;
frame->stride = image->stride;
frame->pixelFormat = WPI_PIXFMT_GRAY;
image_u8_destroy(image);
return rv;
}
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::RawFrame* frame, int id) {
apriltag_family_t* tagFamily = tag16h5_create();
bool rv = FamilyToImage(frame, tagFamily, id);
tag16h5_destroy(tagFamily);
return rv;
}
void frc::to_json(wpi::json& json, const AprilTag& apriltag) {
json = wpi::json{{"ID", apriltag.ID}, {"pose", apriltag.pose}};
}
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

@@ -1,37 +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.
#include "frc/apriltag/AprilTagDetection.h"
#include <type_traits>
#ifdef _WIN32
#pragma warning(disable : 4200)
#elif defined(__clang__)
#pragma clang diagnostic ignored "-Wc99-extensions"
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
#include "apriltag.h"
using namespace frc;
static_assert(sizeof(AprilTagDetection) == sizeof(apriltag_detection_t),
"structure sizes don't match");
static_assert(std::is_standard_layout_v<AprilTagDetection>,
"AprilTagDetection is not standard layout?");
std::string_view AprilTagDetection::GetFamily() const {
return static_cast<const apriltag_family_t*>(family)->name;
}
std::span<const double, 9> AprilTagDetection::GetHomography() const {
return std::span<const double, 9>{static_cast<matd_t*>(H)->data, 9};
}
Eigen::Matrix3d AprilTagDetection::GetHomographyMatrix() const {
return Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>{
static_cast<matd_t*>(H)->data};
}

View File

@@ -1,200 +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.
#include "frc/apriltag/AprilTagDetector.h"
#include <cmath>
#include <numbers>
#ifdef _WIN32
#pragma warning(disable : 4200)
#elif defined(__clang__)
#pragma clang diagnostic ignored "-Wc99-extensions"
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
#include "apriltag.h"
#include "tag16h5.h"
#include "tag25h9.h"
#include "tag36h11.h"
#include "tagCircle21h7.h"
#include "tagCircle49h12.h"
#include "tagCustom48h12.h"
#include "tagStandard41h12.h"
#include "tagStandard52h13.h"
using namespace frc;
AprilTagDetector::Results::Results(void* impl, const private_init&)
: span{reinterpret_cast<AprilTagDetection**>(
static_cast<zarray_t*>(impl)->data),
static_cast<size_t>(static_cast<zarray_t*>(impl)->size)},
m_impl{impl} {}
AprilTagDetector::Results& AprilTagDetector::Results::operator=(Results&& rhs) {
Destroy();
m_impl = rhs.m_impl;
rhs.m_impl = nullptr;
return *this;
}
void AprilTagDetector::Results::Destroy() {
if (m_impl) {
apriltag_detections_destroy(static_cast<zarray_t*>(m_impl));
}
}
AprilTagDetector::AprilTagDetector() : m_impl{apriltag_detector_create()} {}
AprilTagDetector& AprilTagDetector::operator=(AprilTagDetector&& rhs) {
Destroy();
m_impl = rhs.m_impl;
rhs.m_impl = nullptr;
m_families = std::move(rhs.m_families);
rhs.m_families.clear();
m_qtpCriticalAngle = rhs.m_qtpCriticalAngle;
return *this;
}
void AprilTagDetector::SetConfig(const Config& config) {
auto& impl = *static_cast<apriltag_detector_t*>(m_impl);
impl.nthreads = config.numThreads;
impl.quad_decimate = config.quadDecimate;
impl.quad_sigma = config.quadSigma;
impl.refine_edges = config.refineEdges;
impl.decode_sharpening = config.decodeSharpening;
impl.debug = config.debug;
}
AprilTagDetector::Config AprilTagDetector::GetConfig() const {
auto& impl = *static_cast<apriltag_detector_t*>(m_impl);
return {
.numThreads = impl.nthreads,
.quadDecimate = impl.quad_decimate,
.quadSigma = impl.quad_sigma,
.refineEdges = impl.refine_edges,
.decodeSharpening = impl.decode_sharpening,
.debug = impl.debug,
};
}
void AprilTagDetector::SetQuadThresholdParameters(
const QuadThresholdParameters& params) {
auto& qtp = static_cast<apriltag_detector_t*>(m_impl)->qtp;
qtp.min_cluster_pixels = params.minClusterPixels;
qtp.max_nmaxima = params.maxNumMaxima;
qtp.critical_rad = params.criticalAngle.value();
qtp.cos_critical_rad = std::cos(params.criticalAngle.value());
qtp.max_line_fit_mse = params.maxLineFitMSE;
qtp.min_white_black_diff = params.minWhiteBlackDiff;
qtp.deglitch = params.deglitch;
m_qtpCriticalAngle = params.criticalAngle;
}
AprilTagDetector::QuadThresholdParameters
AprilTagDetector::GetQuadThresholdParameters() const {
auto& qtp = static_cast<apriltag_detector_t*>(m_impl)->qtp;
return {
.minClusterPixels = qtp.min_cluster_pixels,
.maxNumMaxima = qtp.max_nmaxima,
.criticalAngle = m_qtpCriticalAngle,
.maxLineFitMSE = qtp.max_line_fit_mse,
.minWhiteBlackDiff = qtp.min_white_black_diff,
.deglitch = qtp.deglitch != 0,
};
}
bool AprilTagDetector::AddFamily(std::string_view fam, int bitsCorrected) {
auto& data = m_families[fam];
if (data) {
return true; // already detecting
}
// create the family
if (fam == "tag16h5") {
data = tag16h5_create();
} else if (fam == "tag25h9") {
data = tag25h9_create();
} else if (fam == "tag36h11") {
data = tag36h11_create();
} else if (fam == "tagCircle21h7") {
data = tagCircle21h7_create();
} else if (fam == "tagCircle49h12") {
data = tagCircle49h12_create();
} else if (fam == "tagStandard41h12") {
data = tagStandard41h12_create();
} else if (fam == "tagStandard52h13") {
data = tagStandard52h13_create();
} else if (fam == "tagCustom48h12") {
data = tagCustom48h12_create();
}
if (!data) {
m_families.erase(fam); // don't keep null value
return false; // can't add
}
apriltag_detector_add_family_bits(static_cast<apriltag_detector_t*>(m_impl),
static_cast<apriltag_family_t*>(data),
bitsCorrected);
return true;
}
void AprilTagDetector::RemoveFamily(std::string_view fam) {
auto it = m_families.find(fam);
if (it != m_families.end()) {
apriltag_detector_remove_family(
static_cast<apriltag_detector_t*>(m_impl),
static_cast<apriltag_family_t*>(it->second));
DestroyFamily(it->getKey(), it->second);
m_families.erase(it);
}
}
void AprilTagDetector::ClearFamilies() {
apriltag_detector_clear_families(static_cast<apriltag_detector_t*>(m_impl));
DestroyFamilies();
m_families.clear();
}
AprilTagDetector::Results AprilTagDetector::Detect(int width, int height,
int stride, uint8_t* buf) {
image_u8_t img{width, height, stride, buf};
return {
apriltag_detector_detect(static_cast<apriltag_detector_t*>(m_impl), &img),
Results::private_init{}};
}
void AprilTagDetector::Destroy() {
if (m_impl) {
apriltag_detector_destroy(static_cast<apriltag_detector_t*>(m_impl));
}
DestroyFamilies();
}
void AprilTagDetector::DestroyFamilies() {
for (auto&& entry : m_families) {
DestroyFamily(entry.getKey(), entry.second);
}
}
void AprilTagDetector::DestroyFamily(std::string_view name, void* data) {
auto fam = static_cast<apriltag_family_t*>(data);
if (name == "tag16h5") {
tag16h5_destroy(fam);
} else if (name == "tag25h9") {
tag25h9_destroy(fam);
} else if (name == "tag36h11") {
tag36h11_destroy(fam);
} else if (name == "tagCircle21h7") {
tagCircle21h7_destroy(fam);
} else if (name == "tagCircle49h12") {
tagCircle49h12_destroy(fam);
} else if (name == "tagStandard41h12") {
tagStandard41h12_destroy(fam);
} else if (name == "tagStandard52h13") {
tagStandard52h13_destroy(fam);
} else if (name == "tagCustom48h12") {
tagCustom48h12_destroy(fam);
}
}

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.
#include "frc/apriltag/AprilTagFieldLayout.h"
#include <system_error>
#include <units/angle.h>
#include <units/length.h>
#include <wpi/MemoryBuffer.h>
#include <wpi/json.h>
#include <wpi/raw_ostream.h>
using namespace frc;
AprilTagFieldLayout::AprilTagFieldLayout(std::string_view path) {
std::error_code ec;
std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
wpi::MemoryBuffer::GetFile(path, ec);
if (fileBuffer == nullptr || ec) {
throw std::runtime_error(fmt::format("Cannot open file: {}", path));
}
wpi::json json = wpi::json::parse(fileBuffer->GetCharBuffer());
for (const auto& tag : json.at("tags").get<std::vector<AprilTag>>()) {
m_apriltags[tag.ID] = tag;
}
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,
units::meter_t fieldLength,
units::meter_t fieldWidth)
: m_fieldLength(std::move(fieldLength)),
m_fieldWidth(std::move(fieldWidth)) {
for (const auto& tag : apriltags) {
m_apriltags[tag.ID] = tag;
}
}
units::meter_t AprilTagFieldLayout::GetFieldLength() const {
return m_fieldLength;
}
units::meter_t AprilTagFieldLayout::GetFieldWidth() const {
return m_fieldWidth;
}
std::vector<AprilTag> AprilTagFieldLayout::GetTags() const {
std::vector<AprilTag> tags;
tags.reserve(m_apriltags.size());
for (const auto& tag : m_apriltags) {
tags.emplace_back(tag.second);
}
return tags;
}
void AprilTagFieldLayout::SetOrigin(OriginPosition origin) {
switch (origin) {
case OriginPosition::kBlueAllianceWallRightSide:
SetOrigin(Pose3d{});
break;
case OriginPosition::kRedAllianceWallRightSide:
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 Pose3d& origin) {
m_origin = origin;
}
Pose3d AprilTagFieldLayout::GetOrigin() const {
return m_origin;
}
std::optional<frc::Pose3d> AprilTagFieldLayout::GetTagPose(int ID) const {
const auto& it = m_apriltags.find(ID);
if (it == m_apriltags.end()) {
return std::nullopt;
}
return it->second.pose.RelativeTo(m_origin);
}
void AprilTagFieldLayout::Serialize(std::string_view path) {
std::error_code error_code;
wpi::raw_fd_ostream output{path, error_code};
if (error_code) {
throw std::runtime_error(fmt::format("Cannot open file: {}", path));
}
wpi::json json = *this;
output << json;
output.flush();
}
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);
}
json = wpi::json{{"field",
{{"length", layout.m_fieldLength.value()},
{"width", layout.m_fieldWidth.value()}}},
{"tags", tagVector}};
}
void frc::from_json(const wpi::json& json, AprilTagFieldLayout& layout) {
layout.m_apriltags.clear();
for (const auto& tag : json.at("tags").get<std::vector<AprilTag>>()) {
layout.m_apriltags[tag.ID] = tag;
}
layout.m_fieldLength =
units::meter_t{json.at("field").at("length").get<double>()};
layout.m_fieldWidth =
units::meter_t{json.at("field").at("width").get<double>()};
}
// Use namespace declaration for forward declaration
namespace frc {
// C++ generated from resource files
std::string_view GetResource_2022_rapidreact_json();
std::string_view GetResource_2023_chargedup_json();
std::string_view GetResource_2024_crescendo_json();
} // namespace frc
AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
std::string_view fieldString;
switch (field) {
case AprilTagField::k2022RapidReact:
fieldString = GetResource_2022_rapidreact_json();
break;
case AprilTagField::k2023ChargedUp:
fieldString = GetResource_2023_chargedup_json();
break;
case AprilTagField::k2024Crescendo:
fieldString = GetResource_2024_crescendo_json();
break;
case AprilTagField::kNumFields:
throw std::invalid_argument("Invalid Field");
}
wpi::json json = wpi::json::parse(fieldString);
return json.get<AprilTagFieldLayout>();
}
AprilTagFieldLayout frc::LoadAprilTagLayoutField(AprilTagField field) {
return AprilTagFieldLayout::LoadField(field);
}

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.
#include "frc/apriltag/AprilTagPoseEstimate.h"
#include <algorithm>
using namespace frc;
double AprilTagPoseEstimate::GetAmbiguity() const {
auto min = (std::min)(error1, error2);
auto max = (std::max)(error1, error2);
if (max > 0) {
return min / max;
} else {
return -1;
}
}

View File

@@ -1,154 +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.
#include "frc/apriltag/AprilTagPoseEstimator.h"
#include <Eigen/QR>
#include "frc/apriltag/AprilTagDetection.h"
#ifdef _WIN32
#pragma warning(disable : 4200)
#elif defined(__clang__)
#pragma clang diagnostic ignored "-Wc99-extensions"
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
#include "apriltag.h"
#include "apriltag_pose.h"
using namespace frc;
static Eigen::Matrix3d OrthogonalizeRotationMatrix(
const Eigen::Matrix3d& input) {
Eigen::HouseholderQR<Eigen::Matrix3d> qr{input};
Eigen::Matrix3d Q = qr.householderQ();
Eigen::Matrix3d R = qr.matrixQR().triangularView<Eigen::Upper>();
// Fix signs in R if they're < 0 so it's close to an identity matrix
// (our QR decomposition implementation sometimes flips the signs of
// columns)
for (int colR = 0; colR < 3; ++colR) {
if (R(colR, colR) < 0) {
for (int rowQ = 0; rowQ < 3; ++rowQ) {
Q(rowQ, colR) = -Q(rowQ, colR);
}
}
}
return Q;
}
static Transform3d MakePose(const apriltag_pose_t& pose) {
if (!pose.R || !pose.t) {
return {};
}
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})}};
}
static apriltag_detection_info_t MakeDetectionInfo(
const apriltag_detection_t* det,
const AprilTagPoseEstimator::Config& config) {
return {const_cast<apriltag_detection_t*>(det),
config.tagSize.value(),
config.fx,
config.fy,
config.cx,
config.cy};
}
static apriltag_detection_t MakeBasicDet(
std::span<const double, 9> homography,
const std::span<const double, 8>* corners) {
apriltag_detection_t detection;
detection.H = matd_create(3, 3);
std::memcpy(detection.H->data, homography.data(), 9 * sizeof(double));
if (corners) {
for (int i = 0; i < 4; i++) {
detection.p[i][0] = (*corners)[i * 2];
detection.p[i][1] = (*corners)[i * 2 + 1];
}
}
return detection;
}
static Transform3d DoEstimateHomography(
const apriltag_detection_t* detection,
const AprilTagPoseEstimator::Config& config) {
auto info = MakeDetectionInfo(detection, config);
apriltag_pose_t pose;
estimate_pose_for_tag_homography(&info, &pose);
return MakePose(pose);
}
Transform3d AprilTagPoseEstimator::EstimateHomography(
const AprilTagDetection& detection) const {
return DoEstimateHomography(
reinterpret_cast<const apriltag_detection_t*>(&detection), m_config);
}
Transform3d AprilTagPoseEstimator::EstimateHomography(
std::span<const double, 9> homography) const {
auto detection = MakeBasicDet(homography, nullptr);
auto rv = DoEstimateHomography(&detection, m_config);
matd_destroy(detection.H);
return rv;
}
static AprilTagPoseEstimate DoEstimateOrthogonalIteration(
const apriltag_detection_t* detection,
const AprilTagPoseEstimator::Config& config, int nIters) {
auto info = MakeDetectionInfo(detection, config);
apriltag_pose_t pose1, pose2;
double err1, err2;
estimate_tag_pose_orthogonal_iteration(&info, &err1, &pose1, &err2, &pose2,
nIters, 1e-7);
return {MakePose(pose1), MakePose(pose2), err1, err2};
}
AprilTagPoseEstimate AprilTagPoseEstimator::EstimateOrthogonalIteration(
const AprilTagDetection& detection, int nIters) const {
return DoEstimateOrthogonalIteration(
reinterpret_cast<const apriltag_detection_t*>(&detection), m_config,
nIters);
}
AprilTagPoseEstimate AprilTagPoseEstimator::EstimateOrthogonalIteration(
std::span<const double, 9> homography, std::span<const double, 8> corners,
int nIters) const {
auto detection = MakeBasicDet(homography, &corners);
auto rv = DoEstimateOrthogonalIteration(&detection, m_config, nIters);
matd_destroy(detection.H);
return rv;
}
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);
}
Transform3d AprilTagPoseEstimator::Estimate(
const AprilTagDetection& detection) const {
return DoEstimate(reinterpret_cast<const apriltag_detection_t*>(&detection),
m_config);
}
Transform3d AprilTagPoseEstimator::Estimate(
std::span<const double, 9> homography,
std::span<const double, 8> corners) const {
auto detection = MakeBasicDet(homography, &corners);
auto rv = DoEstimate(&detection, m_config);
matd_destroy(detection.H);
return rv;
}

View File

@@ -1,634 +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.
#include <jni.h>
#include <cstdio>
#include <cstring>
#define WPI_RAWFRAME_JNI
#include <wpi/RawFrame.h>
#include <wpi/jni_util.h>
#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;
static JClass detectionCls;
static JClass detectorConfigCls;
static JClass detectorQTPCls;
static JClass poseEstimateCls;
static JClass quaternionCls;
static JClass rotation3dCls;
static JClass transform3dCls;
static JClass translation3dCls;
static JClass rawFrameCls;
static JException illegalArgEx;
static JException nullPointerEx;
static const JClassInit classes[] = {
{"edu/wpi/first/apriltag/AprilTagDetection", &detectionCls},
{"edu/wpi/first/apriltag/AprilTagDetector$Config", &detectorConfigCls},
{"edu/wpi/first/apriltag/AprilTagDetector$QuadThresholdParameters",
&detectorQTPCls},
{"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},
{"java/lang/NullPointerException", &nullPointerEx}};
extern "C" {
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
jvm = vm;
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
// Cache references to classes
for (auto& c : classes) {
*c.cls = JClass(env, c.name);
if (!*c.cls) {
std::fprintf(stderr, "could not load class %s\n", c.name);
return JNI_ERR;
}
}
for (auto& c : exceptions) {
*c.cls = JException(env, c.name);
if (!*c.cls) {
std::fprintf(stderr, "could not load exception %s\n", c.name);
return JNI_ERR;
}
}
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return;
}
// Delete global references
for (auto& c : classes) {
c.cls->free(env);
}
for (auto& c : exceptions) {
c.cls->free(env);
}
jvm = nullptr;
}
} // extern "C"
//
// Conversions from Java to C++ objects
//
static AprilTagDetector::Config FromJavaDetectorConfig(JNIEnv* env,
jobject jconfig) {
if (!jconfig) {
return {};
}
#define FIELD(name, sig) \
static jfieldID name##Field = nullptr; \
if (!name##Field) { \
name##Field = env->GetFieldID(detectorConfigCls, #name, sig); \
}
FIELD(numThreads, "I");
FIELD(quadDecimate, "F");
FIELD(quadSigma, "F");
FIELD(refineEdges, "Z");
FIELD(decodeSharpening, "D");
FIELD(debug, "Z");
#undef FIELD
#define FIELD(ctype, jtype, name) \
.name = static_cast<ctype>(env->Get##jtype##Field(jconfig, name##Field))
return {
FIELD(int, Int, numThreads),
FIELD(float, Float, quadDecimate),
FIELD(float, Float, quadSigma),
FIELD(bool, Boolean, refineEdges),
FIELD(double, Double, decodeSharpening),
FIELD(bool, Boolean, debug),
};
#undef GET
#undef FIELD
}
static AprilTagDetector::QuadThresholdParameters FromJavaDetectorQTP(
JNIEnv* env, jobject jparams) {
if (!jparams) {
return {};
}
#define FIELD(name, sig) \
static jfieldID name##Field = nullptr; \
if (!name##Field) { \
name##Field = env->GetFieldID(detectorQTPCls, #name, sig); \
}
FIELD(minClusterPixels, "I");
FIELD(maxNumMaxima, "I");
FIELD(criticalAngle, "D");
FIELD(maxLineFitMSE, "F");
FIELD(minWhiteBlackDiff, "I");
FIELD(deglitch, "Z");
#undef FIELD
#define FIELD(ctype, jtype, name) \
.name = static_cast<ctype>(env->Get##jtype##Field(jparams, name##Field))
return {
FIELD(int, Int, minClusterPixels),
FIELD(int, Int, maxNumMaxima),
.criticalAngle = units::radian_t{static_cast<double>(
env->GetDoubleField(jparams, criticalAngleField))},
FIELD(float, Float, maxLineFitMSE),
FIELD(int, Int, minWhiteBlackDiff),
FIELD(bool, Boolean, deglitch),
};
#undef GET
#undef FIELD
}
//
// Conversions from C++ to Java objects
//
static jobject MakeJObject(JNIEnv* env, const AprilTagDetection& detect) {
static jmethodID constructor = env->GetMethodID(
detectionCls, "<init>", "(Ljava/lang/String;IIF[DDD[D)V");
if (!constructor) {
return nullptr;
}
JLocal<jstring> fam{env, MakeJString(env, detect.GetFamily())};
auto homography = detect.GetHomography();
JLocal<jdoubleArray> harr{
env, MakeJDoubleArray(
env, {reinterpret_cast<const jdouble*>(homography.data()),
homography.size()})};
double cornersBuf[8];
auto corners = detect.GetCorners(cornersBuf);
JLocal<jdoubleArray> carr{
env,
MakeJDoubleArray(env, {reinterpret_cast<const jdouble*>(corners.data()),
corners.size()})};
auto center = detect.GetCenter();
return env->NewObject(detectionCls, constructor, fam.obj(),
static_cast<jint>(detect.GetId()),
static_cast<jint>(detect.GetHamming()),
static_cast<jfloat>(detect.GetDecisionMargin()),
harr.obj(), static_cast<jdouble>(center.x),
static_cast<jdouble>(center.y), carr.obj());
}
static jobjectArray MakeJObject(JNIEnv* env,
std::span<const AprilTagDetection* const> arr) {
jobjectArray jarr = env->NewObjectArray(arr.size(), detectionCls, nullptr);
if (!jarr) {
return nullptr;
}
for (size_t i = 0; i < arr.size(); ++i) {
JLocal<jobject> elem{env, MakeJObject(env, *arr[i])};
env->SetObjectArrayElement(jarr, i, elem.obj());
}
return jarr;
}
static jobject MakeJObject(JNIEnv* env,
const AprilTagDetector::Config& config) {
static jmethodID constructor =
env->GetMethodID(detectorConfigCls, "<init>", "(IFFZDZ)V");
if (!constructor) {
return nullptr;
}
return env->NewObject(detectorConfigCls, constructor,
static_cast<jint>(config.numThreads),
static_cast<jfloat>(config.quadDecimate),
static_cast<jfloat>(config.quadSigma),
static_cast<jboolean>(config.refineEdges),
static_cast<jdouble>(config.decodeSharpening),
static_cast<jboolean>(config.debug));
}
static jobject MakeJObject(
JNIEnv* env, const AprilTagDetector::QuadThresholdParameters& params) {
static jmethodID constructor =
env->GetMethodID(detectorQTPCls, "<init>", "(IIDFIZ)V");
if (!constructor) {
return nullptr;
}
return env->NewObject(detectorQTPCls, constructor,
static_cast<jint>(params.minClusterPixels),
static_cast<jint>(params.maxNumMaxima),
static_cast<jdouble>(params.criticalAngle),
static_cast<jfloat>(params.maxLineFitMSE),
static_cast<jint>(params.minWhiteBlackDiff),
static_cast<jboolean>(params.deglitch));
}
static jobject MakeJObject(JNIEnv* env, const Translation3d& xlate) {
static jmethodID constructor =
env->GetMethodID(translation3dCls, "<init>", "(DDD)V");
if (!constructor) {
return nullptr;
}
return env->NewObject(
translation3dCls, constructor, static_cast<jdouble>(xlate.X()),
static_cast<jdouble>(xlate.Y()), static_cast<jdouble>(xlate.Z()));
}
static jobject MakeJObject(JNIEnv* env, const Quaternion& q) {
static jmethodID constructor =
env->GetMethodID(quaternionCls, "<init>", "(DDDD)V");
if (!constructor) {
return nullptr;
}
return env->NewObject(quaternionCls, constructor, static_cast<jdouble>(q.W()),
static_cast<jdouble>(q.X()),
static_cast<jdouble>(q.Y()),
static_cast<jdouble>(q.Z()));
}
static jobject MakeJObject(JNIEnv* env, const Rotation3d& rot) {
static jmethodID constructor = env->GetMethodID(
rotation3dCls, "<init>", "(Ledu/wpi/first/math/geometry/Quaternion;)V");
if (!constructor) {
return nullptr;
}
JLocal<jobject> q{env, MakeJObject(env, rot.GetQuaternion())};
return env->NewObject(rotation3dCls, constructor, q.obj());
}
static jobject MakeJObject(JNIEnv* env, const Transform3d& xform) {
static jmethodID constructor =
env->GetMethodID(transform3dCls, "<init>",
"(Ledu/wpi/first/math/geometry/Translation3d;"
"Ledu/wpi/first/math/geometry/Rotation3d;)V");
if (!constructor) {
return nullptr;
}
JLocal<jobject> xlate{env, MakeJObject(env, xform.Translation())};
JLocal<jobject> rot{env, MakeJObject(env, xform.Rotation())};
return env->NewObject(transform3dCls, constructor, xlate.obj(), rot.obj());
}
static jobject MakeJObject(JNIEnv* env, const AprilTagPoseEstimate& est) {
static jmethodID constructor =
env->GetMethodID(poseEstimateCls, "<init>",
"(Ledu/wpi/first/math/geometry/Transform3d;"
"Ledu/wpi/first/math/geometry/Transform3d;DD)V");
if (!constructor) {
return nullptr;
}
JLocal<jobject> pose1{env, MakeJObject(env, est.pose1)};
JLocal<jobject> pose2{env, MakeJObject(env, est.pose2)};
return env->NewObject(poseEstimateCls, constructor, pose1.obj(), pose2.obj(),
static_cast<jdouble>(est.error1),
static_cast<jdouble>(est.error2));
}
extern "C" {
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: createDetector
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_createDetector
(JNIEnv* env, jclass)
{
return reinterpret_cast<jlong>(new AprilTagDetector);
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: destroyDetector
* Signature: (J)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_destroyDetector
(JNIEnv* env, jclass, jlong det)
{
delete reinterpret_cast<AprilTagDetector*>(det);
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: setDetectorConfig
* Signature: (JLjava/lang/Object;)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_setDetectorConfig
(JNIEnv* env, jclass, jlong det, jobject config)
{
if (det == 0) {
nullPointerEx.Throw(env, "det cannot be null");
return;
}
reinterpret_cast<AprilTagDetector*>(det)->SetConfig(
FromJavaDetectorConfig(env, config));
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: getDetectorConfig
* Signature: (J)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_getDetectorConfig
(JNIEnv* env, jclass, jlong det)
{
if (det == 0) {
nullPointerEx.Throw(env, "det cannot be null");
return nullptr;
}
return MakeJObject(env,
reinterpret_cast<AprilTagDetector*>(det)->GetConfig());
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: setDetectorQTP
* Signature: (JLjava/lang/Object;)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_setDetectorQTP
(JNIEnv* env, jclass, jlong det, jobject params)
{
if (det == 0) {
nullPointerEx.Throw(env, "det cannot be null");
return;
}
reinterpret_cast<AprilTagDetector*>(det)->SetQuadThresholdParameters(
FromJavaDetectorQTP(env, params));
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: getDetectorQTP
* Signature: (J)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_getDetectorQTP
(JNIEnv* env, jclass, jlong det)
{
if (det == 0) {
nullPointerEx.Throw(env, "det cannot be null");
return nullptr;
}
return MakeJObject(
env,
reinterpret_cast<AprilTagDetector*>(det)->GetQuadThresholdParameters());
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: addFamily
* Signature: (JLjava/lang/String;I)Z
*/
JNIEXPORT jboolean JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_addFamily
(JNIEnv* env, jclass, jlong det, jstring fam, jint bitsCorrected)
{
if (det == 0) {
nullPointerEx.Throw(env, "det cannot be null");
return false;
}
if (!fam) {
nullPointerEx.Throw(env, "fam cannot be null");
return false;
}
return reinterpret_cast<AprilTagDetector*>(det)->AddFamily(
JStringRef{env, fam}, bitsCorrected);
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: removeFamily
* Signature: (JLjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_removeFamily
(JNIEnv* env, jclass, jlong det, jstring fam)
{
if (det == 0) {
nullPointerEx.Throw(env, "det cannot be null");
return;
}
if (!fam) {
nullPointerEx.Throw(env, "fam cannot be null");
return;
}
reinterpret_cast<AprilTagDetector*>(det)->RemoveFamily(JStringRef{env, fam});
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: clearFamilies
* Signature: (J)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_clearFamilies
(JNIEnv* env, jclass, jlong det)
{
if (det == 0) {
nullPointerEx.Throw(env, "det cannot be null");
return;
}
reinterpret_cast<AprilTagDetector*>(det)->ClearFamilies();
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: detect
* Signature: (JIIIJ)[Ljava/lang/Object;
*/
JNIEXPORT jobjectArray JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_detect
(JNIEnv* env, jclass, jlong det, jint width, jint height, jint stride,
jlong bufAddr)
{
if (det == 0) {
nullPointerEx.Throw(env, "det cannot be null");
return nullptr;
}
if (bufAddr == 0) {
nullPointerEx.Throw(env, "bufAddr cannot be null");
return nullptr;
}
return MakeJObject(
env, reinterpret_cast<AprilTagDetector*>(det)->Detect(
width, height, stride, reinterpret_cast<uint8_t*>(bufAddr)));
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: estimatePoseHomography
* Signature: ([DDDDDD)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePoseHomography
(JNIEnv* env, jclass, jdoubleArray homography, jdouble tagSize, jdouble fx,
jdouble fy, jdouble cx, jdouble cy)
{
if (!homography) {
nullPointerEx.Throw(env, "homography cannot be null");
return nullptr;
}
JSpan<const jdouble, 9> harr{env, homography};
if (harr.size() != 9) {
illegalArgEx.Throw(env, "homography array must be size 9");
return nullptr;
}
AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
return MakeJObject(env, estimator.EstimateHomography(harr));
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: estimatePoseOrthogonalIteration
* Signature: ([D[DDDDDDI)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
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)
{
// homography
if (!homography) {
nullPointerEx.Throw(env, "homography cannot be null");
return nullptr;
}
JSpan<const jdouble, 9> harr{env, homography};
if (harr.size() != 9) {
illegalArgEx.Throw(env, "homography array must be size 9");
return nullptr;
}
// corners
if (!corners) {
nullPointerEx.Throw(env, "corners cannot be null");
return nullptr;
}
JSpan<const jdouble, 8> carr{env, corners};
if (carr.size() != 8) {
illegalArgEx.Throw(env, "corners array must be size 8");
return nullptr;
}
AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
return MakeJObject(env,
estimator.EstimateOrthogonalIteration(harr, carr, nIters));
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: estimatePose
* Signature: ([D[DDDDDD)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
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)
{
// homography
if (!homography) {
nullPointerEx.Throw(env, "homography cannot be null");
return nullptr;
}
JSpan<const jdouble, 9> harr{env, homography};
if (harr.size() != 9) {
illegalArgEx.Throw(env, "homography array must be size 9");
return nullptr;
}
// corners
if (!corners) {
nullPointerEx.Throw(env, "corners cannot be null");
return nullptr;
}
JSpan<const jdouble, 8> carr{env, corners};
if (carr.size() != 8) {
illegalArgEx.Throw(env, "corners array must be size 8");
return nullptr;
}
AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
return MakeJObject(env, estimator.Estimate(harr, carr));
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: generate16h5AprilTagImage
* Signature: (Ljava/lang/Object;JI)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_generate16h5AprilTagImage
(JNIEnv* env, jclass, jobject frameObj, jlong framePtr, jint id)
{
auto* frame = reinterpret_cast<wpi::RawFrame*>(framePtr);
if (!frame) {
nullPointerEx.Throw(env, "frame is null");
return;
}
bool newData = AprilTag::Generate16h5AprilTagImage(frame, id);
wpi::SetFrameData(env, rawFrameCls, frameObj, *frame, newData);
}
/*
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
* Method: generate36h11AprilTagImage
* Signature: (Ljava/lang/Object;JI)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_generate36h11AprilTagImage
(JNIEnv* env, jclass, jobject frameObj, jlong framePtr, jint id)
{
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::SetFrameData(env, rawFrameCls, frameObj, *frame, newData);
}
} // extern "C"

View File

@@ -1,37 +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/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

@@ -1,160 +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/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

@@ -1,262 +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 <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 5
* pixels.
*/
int minClusterPixels = 5;
/**
* 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 10 degrees.
*/
units::radian_t criticalAngle = 10_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

@@ -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 "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

@@ -1,173 +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 <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
*/
WPILIB_DLLEXPORT AprilTagFieldLayout
LoadAprilTagLayoutField(AprilTagField field);
} // namespace frc

View File

@@ -1,29 +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 <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,
// 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

@@ -1,36 +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/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

@@ -1,145 +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 <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,440 +0,0 @@
{
"tags": [
{
"ID": 0,
"pose": {
"translation": {
"x": -0.0035306,
"y": 7.578928199999999,
"z": 0.8858503999999999
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 1,
"pose": {
"translation": {
"x": 3.2327088,
"y": 5.486654,
"z": 1.7254728
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 3.067812,
"y": 5.3305202,
"z": 1.3762228
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": -0.7071067811865475
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 0.0039878,
"y": 5.058536999999999,
"z": 0.80645
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 0.0039878,
"y": 3.5124898,
"z": 0.80645
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 0.12110719999999998,
"y": 1.7178274,
"z": 0.8906002000000001
},
"rotation": {
"quaternion": {
"W": 0.9196502204050923,
"X": 0.0,
"Y": 0.0,
"Z": 0.39273842708457407
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 0.8733027999999999,
"y": 0.9412985999999999,
"z": 0.8906002000000001
},
"rotation": {
"quaternion": {
"W": 0.9196502204050923,
"X": 0.0,
"Y": 0.0,
"Z": 0.39273842708457407
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": 1.6150844,
"y": 0.15725139999999999,
"z": 0.8906002000000001
},
"rotation": {
"quaternion": {
"W": 0.9196502204050923,
"X": 0.0,
"Y": 0.0,
"Z": 0.39273842708457407
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 16.4627306,
"y": 0.6506718,
"z": 0.8858503999999999
},
"rotation": {
"quaternion": {
"W": 6.123233995736766E-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 13.2350002,
"y": 2.743454,
"z": 1.7254728
},
"rotation": {
"quaternion": {
"W": 6.123233995736766E-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 13.391388000000001,
"y": 2.8998418,
"z": 1.3762228
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865475
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 16.4552122,
"y": 3.1755079999999998,
"z": 0.80645
},
"rotation": {
"quaternion": {
"W": 6.123233995736766E-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 16.4552122,
"y": 4.7171356,
"z": 0.80645
},
"rotation": {
"quaternion": {
"W": 6.123233995736766E-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 16.3350194,
"y": 6.5149729999999995,
"z": 0.8937752
},
"rotation": {
"quaternion": {
"W": -0.37298778257580906,
"X": -0.0,
"Y": 0.0,
"Z": 0.9278362538989199
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 15.5904946,
"y": 7.292695599999999,
"z": 0.8906002000000001
},
"rotation": {
"quaternion": {
"W": -0.37298778257580906,
"X": -0.0,
"Y": 0.0,
"Z": 0.9278362538989199
}
}
}
},
{
"ID": 17,
"pose": {
"translation": {
"x": 14.847188999999998,
"y": 8.0691228,
"z": 0.8906002000000001
},
"rotation": {
"quaternion": {
"W": -0.37298778257580906,
"X": -0.0,
"Y": 0.0,
"Z": 0.9278362538989199
}
}
}
},
{
"ID": 40,
"pose": {
"translation": {
"x": 7.874127,
"y": 4.9131728,
"z": 0.7032752
},
"rotation": {
"quaternion": {
"W": 0.5446390350150271,
"X": 0.0,
"Y": 0.0,
"Z": 0.838670567945424
}
}
}
},
{
"ID": 41,
"pose": {
"translation": {
"x": 7.4312271999999995,
"y": 3.759327,
"z": 0.7032752
},
"rotation": {
"quaternion": {
"W": -0.20791169081775934,
"X": -0.0,
"Y": 0.0,
"Z": 0.9781476007338057
}
}
}
},
{
"ID": 42,
"pose": {
"translation": {
"x": 8.585073,
"y": 3.3164272,
"z": 0.7032752
},
"rotation": {
"quaternion": {
"W": 0.838670567945424,
"X": 0.0,
"Y": 0.0,
"Z": -0.5446390350150271
}
}
}
},
{
"ID": 43,
"pose": {
"translation": {
"x": 9.0279728,
"y": 4.470273,
"z": 0.7032752
},
"rotation": {
"quaternion": {
"W": 0.9781476007338057,
"X": 0.0,
"Y": 0.0,
"Z": 0.20791169081775934
}
}
}
},
{
"ID": 50,
"pose": {
"translation": {
"x": 7.6790296,
"y": 4.3261534,
"z": 2.4177244
},
"rotation": {
"quaternion": {
"W": 0.17729273396782605,
"X": -0.22744989571511945,
"Y": 0.04215534644161733,
"Z": 0.9565859910053995
}
}
}
},
{
"ID": 51,
"pose": {
"translation": {
"x": 8.0182466,
"y": 3.5642296,
"z": 2.4177244
},
"rotation": {
"quaternion": {
"W": -0.5510435465842192,
"X": -0.19063969497246985,
"Y": -0.13102303230819815,
"Z": 0.8017733354717242
}
}
}
},
{
"ID": 52,
"pose": {
"translation": {
"x": 8.7801704,
"y": 3.9034466,
"z": 2.4177244
},
"rotation": {
"quaternion": {
"W": -0.9565859910053994,
"X": -0.04215534644161739,
"Y": -0.22744989571511942,
"Z": 0.17729273396782633
}
}
}
},
{
"ID": 53,
"pose": {
"translation": {
"x": 8.4409534,
"y": 4.6653704,
"z": 2.4177244
},
"rotation": {
"quaternion": {
"W": 0.8017733354717241,
"X": -0.1310230323081982,
"Y": 0.19063969497246983,
"Z": 0.5510435465842194
}
}
}
}
],
"field": {
"length": 16.4592,
"width": 8.2296
}
}

View File

@@ -1,152 +0,0 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 15.513558,
"y": 1.071626,
"z": 0.462788
},
"rotation": {
"quaternion": {
"W": 0.0,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 15.513558,
"y": 2.748026,
"z": 0.462788
},
"rotation": {
"quaternion": {
"W": 0.0,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 15.513558,
"y": 4.424426,
"z": 0.462788
},
"rotation": {
"quaternion": {
"W": 0.0,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 16.178784,
"y": 6.749796,
"z": 0.695452
},
"rotation": {
"quaternion": {
"W": 0.0,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 0.36195,
"y": 6.749796,
"z": 0.695452
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 1.02743,
"y": 4.424426,
"z": 0.462788
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": 1.02743,
"y": 2.748026,
"z": 0.462788
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": 1.02743,
"y": 1.071626,
"z": 0.462788
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
}
],
"field": {
"length": 16.54175,
"width": 8.0137
}
}

View File

@@ -1,17 +0,0 @@
ID,X,Y,Z,Rotation
1,593.68,9.68,53.38,120
2,637.21,34.79,53.38,120
3,652.73,196.17,57.13,180
4,652.73,218.42,57.13,180
5,578.77,323.00,53.38,270
6,72.5,323.00,53.38,270
7,-1.50,218.42,57.13,0
8,-1.50,196.17,57.13,0
9,14.02,34.79,53.38,60
10,57.54,9.68,53.38,60
11,468.69,146.19,52.00,300
12,468.69,177.10,52.00,60
13,441.74,161.62,52.00,180
14,209.48,161.62,52.00,0
15,182.73,177.10,52.00,120
16,182.73,146.19,52.00,240
1 ID X Y Z Rotation
2 1 593.68 9.68 53.38 120
3 2 637.21 34.79 53.38 120
4 3 652.73 196.17 57.13 180
5 4 652.73 218.42 57.13 180
6 5 578.77 323.00 53.38 270
7 6 72.5 323.00 53.38 270
8 7 -1.50 218.42 57.13 0
9 8 -1.50 196.17 57.13 0
10 9 14.02 34.79 53.38 60
11 10 57.54 9.68 53.38 60
12 11 468.69 146.19 52.00 300
13 12 468.69 177.10 52.00 60
14 13 441.74 161.62 52.00 180
15 14 209.48 161.62 52.00 0
16 15 182.73 177.10 52.00 120
17 16 182.73 146.19 52.00 240

View File

@@ -1,296 +0,0 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 15.079471999999997,
"y": 0.24587199999999998,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 16.185134,
"y": 0.883666,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 16.579342,
"y": 4.982717999999999,
"z": 1.4511020000000001
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 16.579342,
"y": 5.547867999999999,
"z": 1.4511020000000001
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 14.700757999999999,
"y": 8.2042,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 1.8415,
"y": 8.2042,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": -0.038099999999999995,
"y": 5.547867999999999,
"z": 1.4511020000000001
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": -0.038099999999999995,
"y": 4.982717999999999,
"z": 1.4511020000000001
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 9,
"pose": {
"translation": {
"x": 0.356108,
"y": 0.883666,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 1.4615159999999998,
"y": 0.24587199999999998,
"z": 1.355852
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 11.904726,
"y": 3.7132259999999997,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": -0.8660254037844387,
"X": -0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 11.904726,
"y": 4.49834,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 11.220196,
"y": 4.105148,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 5.320792,
"y": 4.105148,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 4.641342,
"y": 4.49834,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 4.641342,
"y": 3.7132259999999997,
"z": 1.3208
},
"rotation": {
"quaternion": {
"W": -0.4999999999999998,
"X": -0.0,
"Y": 0.0,
"Z": 0.8660254037844387
}
}
}
}
],
"field": {
"length": 16.541,
"width": 8.211
}
}

View File

@@ -1,264 +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 edu.wpi.first.apriltag;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import edu.wpi.first.math.geometry.Transform3d;
import edu.wpi.first.math.util.Units;
import edu.wpi.first.util.RuntimeLoader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
@SuppressWarnings("PMD.MutableStaticState")
class AprilTagDetectorTest {
@SuppressWarnings("MemberName")
AprilTagDetector detector;
static RuntimeLoader<Core> loader;
@BeforeAll
static void beforeAll() {
try {
loader =
new RuntimeLoader<>(
Core.NATIVE_LIBRARY_NAME, RuntimeLoader.getDefaultExtractionRoot(), Core.class);
loader.loadLibrary();
} catch (IOException ex) {
fail(ex);
}
}
@BeforeEach
void beforeEach() {
detector = new AprilTagDetector();
}
@AfterEach
void afterEach() {
detector.close();
}
@Test
void testConfigDefaults() {
var config = detector.getConfig();
assertEquals(new AprilTagDetector.Config(), config);
}
@Test
void testQtpDefaults() {
var params = detector.getQuadThresholdParameters();
assertEquals(new AprilTagDetector.QuadThresholdParameters(), params);
}
@Test
void testSetConfigNumThreads() {
var newConfig = new AprilTagDetector.Config();
newConfig.numThreads = 2;
detector.setConfig(newConfig);
var config = detector.getConfig();
assertEquals(2, config.numThreads);
}
@Test
void testQtpMinClusterPixels() {
var newParams = new AprilTagDetector.QuadThresholdParameters();
newParams.minClusterPixels = 8;
detector.setQuadThresholdParameters(newParams);
var params = detector.getQuadThresholdParameters();
assertEquals(8, params.minClusterPixels);
}
@Test
void testAdd16h5() {
assertDoesNotThrow(() -> detector.addFamily("tag16h5"));
// duplicate addition is also okay
assertDoesNotThrow(() -> detector.addFamily("tag16h5"));
}
@Test
void testAdd25h9() {
assertDoesNotThrow(() -> detector.addFamily("tag25h9"));
}
@Test
void testAdd36h11() {
assertDoesNotThrow(() -> detector.addFamily("tag36h11"));
}
@Test
void testAddMultiple() {
assertDoesNotThrow(() -> detector.addFamily("tag16h5"));
assertDoesNotThrow(() -> detector.addFamily("tag36h11"));
}
@Test
void testRemoveFamily() {
// okay to remove non-existent family
detector.removeFamily("tag16h5");
// add and remove
detector.addFamily("tag16h5");
detector.removeFamily("tag16h5");
}
@SuppressWarnings("PMD.AssignmentInOperand")
public Mat loadImage(String resource) throws IOException {
Mat encoded;
try (InputStream is = getClass().getResource(resource).openStream()) {
try (ByteArrayOutputStream os = new ByteArrayOutputStream(is.available())) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
encoded = new Mat(1, os.size(), CvType.CV_8U);
encoded.put(0, 0, os.toByteArray());
}
}
Mat image = Imgcodecs.imdecode(encoded, Imgcodecs.IMREAD_COLOR);
encoded.release();
Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2GRAY);
return image;
}
@Test
void testDecodeAndPose() {
detector.addFamily("tag16h5");
detector.addFamily("tag36h11");
Mat image;
try {
image = loadImage("tag1_640_480.jpg");
} catch (IOException ex) {
fail(ex);
return;
}
try {
AprilTagDetection[] results = detector.detect(image);
assertEquals(1, results.length);
assertEquals("tag36h11", results[0].getFamily());
assertEquals(1, results[0].getId());
assertEquals(0, results[0].getHamming());
var estimator =
new AprilTagPoseEstimator(new AprilTagPoseEstimator.Config(0.2, 500, 500, 320, 240));
AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 200);
assertEquals(new Transform3d(), est.pose2);
Transform3d pose = estimator.estimate(results[0]);
assertEquals(est.pose1, pose);
} finally {
image.release();
}
}
/**
* This tag is rotated such that the top is closer to the camera than the bottom. In the camera
* frame, with +x to the right, this is a rotation about +X by 45 degrees.
*/
@Test
void testPoseRotatedX() {
detector.addFamily("tag16h5");
Mat image;
try {
image = loadImage("tag2_45deg_X.png");
} catch (IOException ex) {
fail(ex);
return;
}
try {
AprilTagDetection[] results = detector.detect(image);
assertEquals(1, results.length);
var estimator =
new AprilTagPoseEstimator(
new AprilTagPoseEstimator.Config(
0.2, 500, 500, image.cols() / 2.0, image.rows() / 2.0));
AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 200);
assertEquals(Units.degreesToRadians(45), est.pose1.getRotation().getX(), 0.1);
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getY(), 0.1);
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getZ(), 0.1);
} finally {
image.release();
}
}
/**
* This tag is rotated such that the right is closer to the camera than the left. In the camera
* frame, with +y down, this is a rotation of 45 degrees about +y.
*/
@Test
void testPoseRotatedY() {
detector.addFamily("tag16h5");
Mat image;
try {
image = loadImage("tag2_45deg_y.png");
} catch (IOException ex) {
fail(ex);
return;
}
try {
AprilTagDetection[] results = detector.detect(image);
assertEquals(1, results.length);
var estimator =
new AprilTagPoseEstimator(
new AprilTagPoseEstimator.Config(
0.2, 500, 500, image.cols() / 2.0, image.rows() / 2.0));
AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 200);
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getX(), 0.1);
assertEquals(Units.degreesToRadians(45), est.pose1.getRotation().getY(), 0.1);
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getZ(), 0.1);
} finally {
image.release();
}
}
/** This tag is facing right at the camera -- no rotation should be observed. */
@Test
void testPoseStraightOn() {
detector.addFamily("tag16h5");
Mat image;
try {
image = loadImage("tag2_16h5_straight.png");
} catch (IOException ex) {
fail(ex);
return;
}
try {
AprilTagDetection[] results = detector.detect(image);
assertEquals(1, results.length);
var estimator =
new AprilTagPoseEstimator(
new AprilTagPoseEstimator.Config(
0.2, 500, 500, image.cols() / 2.0, image.rows() / 2.0));
AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 200);
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getX(), 0.1);
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getY(), 0.1);
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getZ(), 0.1);
} finally {
image.release();
}
}
}

View File

@@ -1,34 +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 edu.wpi.first.apriltag;
import static org.junit.jupiter.api.Assertions.assertEquals;
import edu.wpi.first.util.PixelFormat;
import org.junit.jupiter.api.Test;
class AprilTagGenerationTest {
@Test
void test36h11() {
var frame = AprilTag.generate36h11AprilTagImage(1);
assertEquals(PixelFormat.kGray, frame.getPixelFormat());
assertEquals(10, frame.getWidth());
assertEquals(10, frame.getHeight());
int stride = frame.getStride();
assertEquals(stride * 10, frame.getSize());
// check the diagonal values
var data = frame.getData();
assertEquals(-1, data.get(stride * 0 + 0)); // outer border is white
assertEquals(0, data.get(stride * 1 + 1)); // inner border is black
assertEquals(-1, data.get(stride * 2 + 2));
assertEquals(-1, data.get(stride * 3 + 3));
assertEquals(-1, data.get(stride * 4 + 4));
assertEquals(0, data.get(stride * 5 + 5));
assertEquals(0, data.get(stride * 6 + 6));
assertEquals(-1, data.get(stride * 7 + 7));
assertEquals(0, data.get(stride * 8 + 8)); // inner border
assertEquals(-1, data.get(stride * 9 + 9)); // outer border
}
}

View File

@@ -1,46 +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 edu.wpi.first.apriltag;
import static org.junit.jupiter.api.Assertions.assertEquals;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Translation3d;
import edu.wpi.first.math.util.Units;
import java.util.List;
import org.junit.jupiter.api.Test;
class AprilTagPoseSetOriginTest {
@Test
void transformationMatches() {
var layout =
new AprilTagFieldLayout(
List.of(
new AprilTag(1, new Pose3d(new Translation3d(0, 0, 0), new Rotation3d(0, 0, 0))),
new AprilTag(
2,
new Pose3d(
new Translation3d(
Units.feetToMeters(4.0), Units.feetToMeters(4), Units.feetToMeters(4)),
new Rotation3d(0, 0, Units.degreesToRadians(180))))),
Units.feetToMeters(54.0),
Units.feetToMeters(27.0));
layout.setOrigin(AprilTagFieldLayout.OriginPosition.kRedAllianceWallRightSide);
assertEquals(
new Pose3d(
new Translation3d(Units.feetToMeters(54.0), Units.feetToMeters(27.0), 0.0),
new Rotation3d(0.0, 0.0, Units.degreesToRadians(180.0))),
layout.getTagPose(1).orElse(null));
assertEquals(
new Pose3d(
new Translation3d(
Units.feetToMeters(50.0), Units.feetToMeters(23.0), Units.feetToMeters(4)),
new Rotation3d(0.0, 0.0, 0)),
layout.getTagPose(2).orElse(null));
}
}

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 edu.wpi.first.apriltag;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
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.util.Units;
import java.util.List;
import org.junit.jupiter.api.Test;
class AprilTagSerializationTest {
@Test
void deserializeMatches() {
var layout =
new AprilTagFieldLayout(
List.of(
new AprilTag(1, new Pose3d(0, 0, 0, new Rotation3d(0, 0, 0))),
new AprilTag(3, new Pose3d(0, 1, 0, new Rotation3d(0, 0, 0)))),
Units.feetToMeters(54.0),
Units.feetToMeters(27.0));
var objectMapper = new ObjectMapper();
var deserialized =
assertDoesNotThrow(
() ->
objectMapper.readValue(
objectMapper.writeValueAsString(layout), AprilTagFieldLayout.class));
assertEquals(layout, deserialized);
}
}

View File

@@ -1,71 +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 edu.wpi.first.apriltag;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.util.Units;
import java.util.Optional;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
class LoadConfigTest {
@ParameterizedTest
@EnumSource(AprilTagFields.class)
void testLoad(AprilTagFields field) {
AprilTagFieldLayout layout =
Assertions.assertDoesNotThrow(() -> AprilTagFieldLayout.loadField(field));
assertNotNull(layout);
}
@Test
void test2022RapidReact() {
AprilTagFieldLayout layout = AprilTagFieldLayout.loadField(AprilTagFields.k2022RapidReact);
// Blue Hangar Truss - Hub
Pose3d expectedPose =
new Pose3d(
Units.inchesToMeters(127.272),
Units.inchesToMeters(216.01),
Units.inchesToMeters(67.932),
new Rotation3d(0, 0, 0));
Optional<Pose3d> maybePose = layout.getTagPose(1);
assertTrue(maybePose.isPresent());
assertEquals(expectedPose, maybePose.get());
// Blue Terminal Near Station
expectedPose =
new Pose3d(
Units.inchesToMeters(4.768),
Units.inchesToMeters(67.631),
Units.inchesToMeters(35.063),
new Rotation3d(0, 0, Math.toRadians(46.25)));
maybePose = layout.getTagPose(5);
assertTrue(maybePose.isPresent());
assertEquals(expectedPose, maybePose.get());
// Upper Hub Blue-Near
expectedPose =
new Pose3d(
Units.inchesToMeters(332.321),
Units.inchesToMeters(183.676),
Units.inchesToMeters(95.186),
new Rotation3d(0, Math.toRadians(26.75), Math.toRadians(69)));
maybePose = layout.getTagPose(53);
assertTrue(maybePose.isPresent());
assertEquals(expectedPose, maybePose.get());
// Doesn't exist
maybePose = layout.getTagPose(54);
assertFalse(maybePose.isPresent());
}
}

View File

@@ -1,68 +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.
#include <gtest/gtest.h>
#include "frc/apriltag/AprilTagDetector.h"
using namespace frc;
TEST(AprilTagDetectorTest, ConfigDefaults) {
AprilTagDetector detector;
auto config = detector.GetConfig();
ASSERT_EQ(config, AprilTagDetector::Config{});
}
TEST(AprilTagDetectorTest, QtpDefaults) {
AprilTagDetector detector;
auto params = detector.GetQuadThresholdParameters();
ASSERT_EQ(params, AprilTagDetector::QuadThresholdParameters{});
}
TEST(AprilTagDetectorTest, SetConfigNumThreads) {
AprilTagDetector detector;
detector.SetConfig({.numThreads = 2});
auto config = detector.GetConfig();
ASSERT_EQ(config.numThreads, 2);
}
TEST(AprilTagDetectorTest, QtpMinClusterPixels) {
AprilTagDetector detector;
detector.SetQuadThresholdParameters({.minClusterPixels = 8});
auto params = detector.GetQuadThresholdParameters();
ASSERT_EQ(params.minClusterPixels, 8);
}
TEST(AprilTagDetectorTest, Add16h5) {
AprilTagDetector detector;
ASSERT_TRUE(detector.AddFamily("tag16h5"));
// duplicate addition is also okay
ASSERT_TRUE(detector.AddFamily("tag16h5"));
}
TEST(AprilTagDetectorTest, Add25h9) {
AprilTagDetector detector;
ASSERT_TRUE(detector.AddFamily("tag25h9"));
}
TEST(AprilTagDetectorTest, Add36h11) {
AprilTagDetector detector;
ASSERT_TRUE(detector.AddFamily("tag36h11"));
}
TEST(AprilTagDetectorTest, AddMultiple) {
AprilTagDetector detector;
ASSERT_TRUE(detector.AddFamily("tag16h5"));
ASSERT_TRUE(detector.AddFamily("tag36h11"));
}
TEST(AprilTagDetectorTest, RemoveFamily) {
AprilTagDetector detector;
// okay to remove non-existent family
detector.RemoveFamily("tag16h5");
// add and remove
detector.AddFamily("tag16h5");
detector.RemoveFamily("tag16h5");
}

View File

@@ -1,27 +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.
#include <vector>
#include <gtest/gtest.h>
#include <wpi/json.h>
#include "frc/apriltag/AprilTag.h"
#include "frc/apriltag/AprilTagFieldLayout.h"
#include "frc/geometry/Pose3d.h"
using namespace frc;
TEST(AprilTagJsonTest, DeserializeMatches) {
auto layout = AprilTagFieldLayout{
std::vector{
AprilTag{1, Pose3d{}},
AprilTag{3, Pose3d{0_m, 1_m, 0_m, Rotation3d{0_deg, 0_deg, 0_deg}}}},
54_ft, 27_ft};
AprilTagFieldLayout deserialized;
wpi::json json = layout;
EXPECT_NO_THROW(deserialized = json.get<AprilTagFieldLayout>());
EXPECT_EQ(layout, deserialized);
}

View File

@@ -1,33 +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.
#include <vector>
#include <gtest/gtest.h>
#include <wpi/json.h>
#include "frc/apriltag/AprilTag.h"
#include "frc/apriltag/AprilTagFieldLayout.h"
#include "frc/geometry/Pose3d.h"
using namespace frc;
TEST(AprilTagPoseSetOriginTest, TransformationMatches) {
auto layout = AprilTagFieldLayout{
std::vector<AprilTag>{
AprilTag{1,
Pose3d{0_ft, 0_ft, 0_ft, Rotation3d{0_deg, 0_deg, 0_deg}}},
AprilTag{
2, Pose3d{4_ft, 4_ft, 4_ft, Rotation3d{0_deg, 0_deg, 180_deg}}}},
54_ft, 27_ft};
layout.SetOrigin(
AprilTagFieldLayout::OriginPosition::kRedAllianceWallRightSide);
auto mirrorPose =
Pose3d{54_ft, 27_ft, 0_ft, Rotation3d{0_deg, 0_deg, 180_deg}};
EXPECT_EQ(mirrorPose, *layout.GetTagPose(1));
mirrorPose = Pose3d{50_ft, 23_ft, 4_ft, Rotation3d{0_deg, 0_deg, 0_deg}};
EXPECT_EQ(mirrorPose, *layout.GetTagPose(2));
}

View File

@@ -1,63 +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.
#include <gtest/gtest.h>
#include "frc/apriltag/AprilTagFieldLayout.h"
#include "frc/apriltag/AprilTagFields.h"
namespace frc {
std::vector<AprilTagField> GetAllFields() {
std::vector<AprilTagField> output;
for (int i = 0; i < static_cast<int>(AprilTagField::kNumFields); ++i) {
output.push_back(static_cast<AprilTagField>(i));
}
return output;
}
TEST(AprilTagFieldsTest, TestLoad2022RapidReact) {
AprilTagFieldLayout layout =
AprilTagFieldLayout::LoadField(AprilTagField::k2022RapidReact);
// Blue Hangar Truss - Hub
auto expectedPose =
Pose3d{127.272_in, 216.01_in, 67.932_in, Rotation3d{0_deg, 0_deg, 0_deg}};
auto maybePose = layout.GetTagPose(1);
EXPECT_TRUE(maybePose);
EXPECT_EQ(expectedPose, *maybePose);
// Blue Terminal Near Station
expectedPose = Pose3d{4.768_in, 67.631_in, 35.063_in,
Rotation3d{0_deg, 0_deg, 46.25_deg}};
maybePose = layout.GetTagPose(5);
EXPECT_TRUE(maybePose);
EXPECT_EQ(expectedPose, *maybePose);
// Upper Hub Blue-Near
expectedPose = Pose3d{332.321_in, 183.676_in, 95.186_in,
Rotation3d{0_deg, 26.75_deg, 69_deg}};
maybePose = layout.GetTagPose(53);
EXPECT_TRUE(maybePose);
EXPECT_EQ(expectedPose, *maybePose);
// Doesn't exist
maybePose = layout.GetTagPose(54);
EXPECT_FALSE(maybePose);
}
// Test all of the fields in the enum
class AllFieldsFixtureTest : public ::testing::TestWithParam<AprilTagField> {};
TEST_P(AllFieldsFixtureTest, CheckEntireEnum) {
AprilTagField field = GetParam();
EXPECT_NO_THROW(AprilTagFieldLayout::LoadField(field));
}
INSTANTIATE_TEST_SUITE_P(ValuesEnumTestInstTests, AllFieldsFixtureTest,
::testing::ValuesIn(GetAllFields()));
} // namespace frc

View File

@@ -1,11 +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.
#include <gtest/gtest.h>
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
return ret;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

10
arm-toolchain.cmake Normal file
View File

@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 2.8)
INCLUDE(CMakeForceCompiler)
set(ARM_PREFIX arm-none-linux-gnueabi)
set(CMAKE_SYSTEM_NAME Linux)
CMAKE_FORCE_CXX_COMPILER(${ARM_PREFIX}-g++ GNU)
CMAKE_FORCE_C_COMPILER(${ARM_PREFIX}-gcc GNU)
set(CMAKE_CXX_FLAGS "-march=armv7-a -mcpu=cortex-a9 -mfloat-abi=softfp -Wall -Wno-psabi" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -g" CACHE STRING "" FORCE) # still want debugging for release?
SET(CMAKE_FIND_ROOT_PATH $ENV{USER_HOME}/wpilib/toolchains/arm-none-linux-gnueabi-4.4.1/arm-none-linux-gnueabi/libc)

View File

@@ -1,92 +0,0 @@
# Testing steps for real hardware
trigger:
batch: true
branches:
include:
- master
stages:
- stage: Build
jobs:
- job: IntegrationTests
displayName: Integration Tests
pool:
vmImage: 'ubuntu-latest'
container:
image: wpilib/roborio-cross-ubuntu:2023-22.04
timeoutInMinutes: 0
steps:
- task: Gradle@2
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
inputs:
workingDirectory: ""
gradleWrapperFile: "gradlew"
gradleOptions: "-Xmx3072m"
publishJUnitResults: false
testResultsFiles: "**/TEST-*.xml"
tasks: "copyWpilibJIntegrationTestJarToOutput copyWpilibCTestLibrariesToOutput"
options: "-Ponlylinuxathena -PbuildServer -PskipJavaFormat"
- task: PublishPipelineArtifact@0
inputs:
artifactName: "Integration Tests"
targetPath: "build/integrationTestFiles"
- stage: TestBench
displayName: Test Bench
condition: false
jobs:
- job: Cpp
displayName: C++
pool: RoboRioConnections
timeoutInMinutes: 30
workspace:
clean: all
steps:
- task: DownloadPipelineArtifact@0
inputs:
artifactName: "Integration Tests"
targetPath: "build/integrationTestFiles"
- task: ShellScript@2
displayName: Run C++ Tests
inputs:
scriptPath: test-scripts/deploy-and-run-test-on-robot.sh
args: 'cpp -A "--gtest_output=xml:/home/admin/testResults/cppreport.xml"'
- task: PublishTestResults@2
displayName: Publish C++ Test Results
inputs:
testResultsFormat: "JUnit"
testResultsFiles: "*.xml"
testRunTitle: "C++ Test Report"
searchFolder: "$(System.DefaultWorkingDirectory)/test-reports"
- job: Java
pool: RoboRioConnections
timeoutInMinutes: 30
workspace:
clean: all
steps:
- task: DownloadPipelineArtifact@0
inputs:
artifactName: "Integration Tests"
targetPath: "build/integrationTestFiles"
- task: ShellScript@2
displayName: Run Java Tests
inputs:
scriptPath: test-scripts/deploy-and-run-test-on-robot.sh
args: "java"
- task: PublishTestResults@2
displayName: Publish Java Test Results
inputs:
testResultsFormat: "JUnit"
testResultsFiles: "*.xml"
testRunTitle: "Java Test Report"
searchFolder: "$(System.DefaultWorkingDirectory)/test-reports"

View File

@@ -1,178 +0,0 @@
import edu.wpi.first.toolchain.*
buildscript {
repositories {
maven {
url = 'https://frcmaven.wpi.edu/artifactory/ex-mvn'
}
}
dependencies {
classpath 'com.hubspot.jinjava:jinjava:2.7.1'
}
}
plugins {
id 'base'
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '2023.0.1'
id 'edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin' version '2020.2'
id 'edu.wpi.first.NativeUtils' apply false
id 'edu.wpi.first.GradleJni' version '1.1.0'
id 'edu.wpi.first.GradleVsCode'
id 'idea'
id 'visual-studio'
id 'net.ltgt.errorprone' version '3.1.0' apply false
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'com.diffplug.spotless' version '6.20.0' apply false
id 'com.github.spotbugs' version '6.0.2' apply false
id 'com.google.protobuf' version '0.9.3' apply false
}
wpilibVersioning.buildServerMode = project.hasProperty('buildServer')
wpilibVersioning.releaseMode = project.hasProperty('releaseMode')
allprojects {
repositories {
maven {
url = 'https://frcmaven.wpi.edu/artifactory/ex-mvn'
}
}
if (project.hasProperty('releaseMode')) {
wpilibRepositories.addAllReleaseRepositories(it)
} else {
wpilibRepositories.addAllDevelopmentRepositories(it)
}
}
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
publishAlways()
}
import com.github.spotbugs.snom.Effort
ext.spotbugsEffort = Effort.MAX
ext.licenseFile = files("$rootDir/LICENSE.md", "$rootDir/ThirdPartyNotices.txt")
if (project.hasProperty("publishVersion")) {
wpilibVersioning.version.set(project.publishVersion)
}
wpilibVersioning.version.finalizeValue()
def outputsFolder = file("$buildDir/allOutputs")
def versionFile = file("$outputsFolder/version.txt")
task outputVersions() {
description = 'Prints the versions of wpilib to a file for use by the downstream packaging project'
group = 'Build'
outputs.files(versionFile)
doFirst {
buildDir.mkdir()
outputsFolder.mkdir()
}
doLast {
versionFile.write wpilibVersioning.version.get()
}
}
task libraryBuild() {}
build.dependsOn outputVersions
task copyAllOutputs(type: Copy) {
destinationDir outputsFolder
}
build.dependsOn copyAllOutputs
copyAllOutputs.dependsOn outputVersions
def copyReleaseOnly = project.hasProperty('ciReleaseOnly')
ext.addTaskToCopyAllOutputs = { task ->
if (copyReleaseOnly && task.name.contains('debug')) {
return
}
copyAllOutputs.dependsOn task
copyAllOutputs.inputs.file task.archiveFile
copyAllOutputs.from task.archiveFile
}
subprojects {
apply plugin: 'eclipse'
apply plugin: 'idea'
def subproj = it
plugins.withType(NativeComponentPlugin) {
subproj.apply plugin: MultiBuilds
}
plugins.withType(JavaPlugin) {
java {
sourceCompatibility = 11
targetCompatibility = 11
}
}
apply from: "${rootDir}/shared/java/javastyle.gradle"
// Disables doclint in java 8.
if (JavaVersion.current().isJava8Compatible()) {
tasks.withType(Javadoc) {
if (project.name != "docs") {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
}
tasks.withType(JavaCompile) {
options.compilerArgs.add '-XDstringConcat=inline'
options.encoding = 'UTF-8'
}
// Enables UTF-8 support in Javadoc
tasks.withType(Javadoc) {
options.addStringOption("charset", "utf-8")
options.addStringOption("docencoding", "utf-8")
options.addStringOption("encoding", "utf-8")
}
// Sign outputs with Developer ID
tasks.withType(AbstractLinkTask) { task ->
task.inputs.property "HasDeveloperId", project.hasProperty("developerID")
if (project.hasProperty("developerID")) {
// Don't sign any executables because codesign complains
// about relative rpath.
if (!(task instanceof LinkExecutable)) {
doLast {
// Get path to binary.
String path = task.getLinkedFile().getAsFile().get().getAbsolutePath()
exec {
workingDir rootDir
def args = [
"sh",
"-c",
"codesign --force --strict --timestamp --options=runtime " +
"--verbose -s ${project.findProperty("developerID")} ${path}"
]
commandLine args
}
}
}
}
}
}
ext.getCurrentArch = {
return NativePlatforms.desktop
}
wrapper {
gradleVersion = '8.5'
}

View File

@@ -1,13 +0,0 @@
repositories {
maven {
mavenLocal()
maven {
url = 'https://frcmaven.wpi.edu/artifactory/ex-gradle'
}
mavenCentral()
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
implementation "edu.wpi.first:native-utils:2024.5.2"
}

View File

@@ -1,70 +0,0 @@
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.FileTree;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.language.base.internal.ProjectLayout;
import org.gradle.language.base.plugins.ComponentModelBasePlugin;
import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask;
import org.gradle.model.ModelMap;
import edu.wpi.first.toolchain.ToolchainExtension
import org.gradle.model.Mutate;
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec;
import org.gradle.model.RuleSource;
import org.gradle.model.Validate;
import org.gradle.nativeplatform.NativeExecutableBinarySpec
import org.gradle.nativeplatform.NativeBinarySpec;
import org.gradle.nativeplatform.NativeComponentSpec;
import org.gradle.nativeplatform.NativeLibrarySpec;
import org.gradle.nativeplatform.SharedLibraryBinarySpec;
import org.gradle.nativeplatform.StaticLibraryBinarySpec;
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
import org.gradle.nativeplatform.toolchain.NativeToolChain;
import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
import org.gradle.nativeplatform.toolchain.internal.ToolType;
import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
import org.gradle.platform.base.BinarySpec;
import org.gradle.platform.base.ComponentSpec;
import org.gradle.platform.base.ComponentSpecContainer;
import org.gradle.platform.base.BinaryContainer;
import org.gradle.platform.base.ComponentType;
import org.gradle.platform.base.TypeBuilder;
import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
import groovy.transform.CompileStatic;
import groovy.transform.CompileDynamic
import org.gradle.nativeplatform.BuildTypeContainer
@CompileStatic
class DisableBuildingGTest implements Plugin<Project> {
@CompileStatic
public void apply(Project project) {
}
@CompileStatic
static class Rules extends RuleSource {
@CompileDynamic
private static void setBuildableFalseDynamically(NativeBinarySpec binary) {
binary.buildable = false
}
@Validate
@CompileStatic
// TODO: Move this to tc plugin
void disableCrossTests(BinaryContainer binaries, ExtensionContainer extContainer) {
final ToolchainExtension ext = extContainer.getByType(ToolchainExtension.class);
for (GoogleTestTestSuiteBinarySpec binary : binaries.withType(GoogleTestTestSuiteBinarySpec.class)) {
if (ext.getCrossCompilers().findByName(binary.getTargetPlatform().getName()) != null) {
setBuildableFalseDynamically(binary)
}
}
}
}
}

View File

@@ -1,88 +0,0 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.FileTree;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.language.base.internal.ProjectLayout;
import org.gradle.language.base.plugins.ComponentModelBasePlugin;
import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask;
import org.gradle.model.ModelMap;
import org.gradle.model.Mutate;
import org.gradle.model.RuleSource;
import org.gradle.model.Validate;
import org.gradle.nativeplatform.NativeBinarySpec;
import org.gradle.nativeplatform.NativeComponentSpec;
import org.gradle.nativeplatform.NativeLibrarySpec;
import org.gradle.nativeplatform.SharedLibraryBinarySpec;
import org.gradle.nativeplatform.StaticLibraryBinarySpec;
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
import org.gradle.nativeplatform.toolchain.NativeToolChain;
import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
import org.gradle.nativeplatform.toolchain.internal.ToolType;
import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec;
import org.gradle.platform.base.BinarySpec;
import org.gradle.platform.base.ComponentSpec;
import org.gradle.platform.base.ComponentSpecContainer;
import org.gradle.platform.base.ComponentType;
import org.gradle.platform.base.TypeBuilder;
import org.gradle.nativeplatform.test.tasks.RunTestExecutable;
import org.gradle.platform.base.BinaryContainer;
import groovy.transform.CompileStatic;
@CompileStatic
class ExtraTasks implements Plugin<Project> {
@CompileStatic
public void apply(Project project) {
}
@CompileStatic
static class Rules extends RuleSource {
@Mutate
@CompileStatic
void createNativeCompileTask(ModelMap<Task> tasks, BinaryContainer binaries) {
tasks.create('compileCpp', Task) { oTask ->
def task = (Task) oTask
task.group = 'build'
task.description = 'Uber task to compile all native code for this project'
binaries.each { binary ->
if (binary instanceof NativeBinarySpec && binary.buildable) {
binary.tasks.withType(AbstractNativeSourceCompileTask) { compileTask ->
task.dependsOn compileTask
}
}
}
}
}
@Mutate
@CompileStatic
void createNativeTestTask(ModelMap<Task> tasks, BinaryContainer binaries) {
tasks.create('testCpp', Task) { oTask ->
def task = (Task) oTask
task.group = 'build'
task.description = 'Uber task to run all native tests for project'
binaries.each { binary ->
if (binary instanceof GoogleTestTestSuiteBinarySpec && binary.buildable) {
binary.tasks.withType(RunTestExecutable) { testTask ->
task.dependsOn testTask
}
}
}
}
}
}
}

View File

@@ -1,79 +0,0 @@
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.FileTree;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.language.base.internal.ProjectLayout;
import org.gradle.language.base.plugins.ComponentModelBasePlugin;
import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask;
import org.gradle.model.ModelMap;
import org.gradle.internal.os.OperatingSystem
import edu.wpi.first.toolchain.ToolchainExtension
import org.gradle.model.Mutate;
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec;
import org.gradle.model.RuleSource;
import org.gradle.model.Validate;
import org.gradle.nativeplatform.test.tasks.RunTestExecutable
import org.gradle.nativeplatform.NativeExecutableBinarySpec
import org.gradle.nativeplatform.NativeBinarySpec;
import org.gradle.nativeplatform.NativeComponentSpec;
import org.gradle.nativeplatform.NativeLibrarySpec;
import org.gradle.nativeplatform.SharedLibraryBinarySpec;
import org.gradle.nativeplatform.StaticLibraryBinarySpec;
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
import org.gradle.nativeplatform.toolchain.NativeToolChain;
import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
import org.gradle.nativeplatform.toolchain.internal.ToolType;
import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
import org.gradle.platform.base.BinarySpec;
import org.gradle.platform.base.ComponentSpec;
import org.gradle.platform.base.ComponentSpecContainer;
import org.gradle.platform.base.BinaryContainer;
import org.gradle.platform.base.ComponentType;
import org.gradle.platform.base.TypeBuilder;
import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
import groovy.transform.CompileStatic;
import groovy.transform.CompileDynamic
import org.gradle.nativeplatform.BuildTypeContainer
@CompileStatic
class MultiBuilds implements Plugin<Project> {
@CompileStatic
public void apply(Project project) {
}
@CompileStatic
static class Rules extends RuleSource {
@CompileDynamic
private static void setBuildableFalseDynamically(NativeBinarySpec binary) {
binary.buildable = false
}
@Mutate
@CompileStatic
void disableReleaseGoogleTest(BinaryContainer binaries, ProjectLayout projectLayout) {
def project = (Project) projectLayout.projectIdentifier
if (project.hasProperty('testOther')) {
return
}
def check_string = 'release'
if (project.hasProperty('buildServer') && !OperatingSystem.current().isWindows()) {
check_string = 'debug'
}
binaries.withType(GoogleTestTestSuiteBinarySpec) { oSpec ->
GoogleTestTestSuiteBinarySpec spec = (GoogleTestTestSuiteBinarySpec) oSpec
if (spec.buildType.name == check_string) {
Rules.setBuildableFalseDynamically(spec)
}
}
}
}
}

View File

@@ -1,172 +0,0 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import org.gradle.api.tasks.Delete
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.tasks.Copy;
import org.gradle.api.file.CopySpec;
import org.gradle.api.file.FileTree;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.language.base.internal.ProjectLayout;
import org.gradle.language.base.plugins.ComponentModelBasePlugin;
import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask;
import org.gradle.model.ModelMap;
import org.gradle.model.Mutate;
import org.gradle.model.RuleSource;
import org.gradle.model.Validate;
import org.gradle.nativeplatform.NativeBinarySpec;
import org.gradle.nativeplatform.NativeComponentSpec;
import org.gradle.nativeplatform.NativeLibrarySpec;
import org.gradle.nativeplatform.SharedLibraryBinarySpec;
import org.gradle.nativeplatform.StaticLibraryBinarySpec;
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
import org.gradle.nativeplatform.toolchain.NativeToolChain;
import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
import org.gradle.nativeplatform.toolchain.internal.ToolType;
import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
import org.gradle.nativeplatform.tasks.CreateStaticLibrary;
import org.gradle.nativeplatform.tasks.AbstractLinkTask;
import org.gradle.platform.base.BinarySpec;
import org.gradle.platform.base.ComponentSpec;
import org.gradle.platform.base.ComponentSpecContainer;
import org.gradle.platform.base.BinaryContainer;
import org.gradle.platform.base.ComponentType;
import org.gradle.platform.base.TypeBuilder;
import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
import groovy.transform.CompileStatic;
import edu.wpi.first.nativeutils.exports.ExportsGenerationTask
@CompileStatic
class SingleNativeBuild implements Plugin<Project> {
@CompileStatic
public void apply(Project project) {
}
@CompileStatic
static class Rules extends RuleSource {
@Mutate
@CompileStatic
void removeMacSystemIncludes(ModelMap<Task> tasks, BinaryContainer binaries) {
binaries.each {
if (!(it instanceof NativeBinarySpec)) {
return
}
NativeBinarySpec nativeBin = (NativeBinarySpec)it
if (nativeBin.targetPlatform.operatingSystem.isMacOsX()) {
nativeBin.tasks.withType(AbstractNativeSourceCompileTask) { AbstractNativeSourceCompileTask compileTask->
compileTask.getSystemIncludes().setFrom()
}
}
}
}
@Mutate
@CompileStatic
void setupSingleNativeBuild(ModelMap<Task> tasks, ComponentSpecContainer components, BinaryContainer binaryContainer, ProjectLayout projectLayout) {
Project project = (Project) projectLayout.projectIdentifier;
def nativeName = project.extensions.extraProperties.get('nativeName')
NativeLibrarySpec base = null
def subs = []
components.each { component ->
if (component.name == "${nativeName}Base") {
base = (NativeLibrarySpec) component
} else if (component.name == "${nativeName}" || component.name == "${nativeName}JNI" || component.name == "${nativeName}JNICvStatic") {
subs << component
}
}
Delete deleteObjects = null
if (project.hasProperty('buildServer')) {
deleteObjects = project.tasks.create('deleteObjects', Delete)
project.tasks.named('build').configure { Task t ->
t.dependsOn deleteObjects
return
}
}
subs.each {
((NativeLibrarySpec) it).binaries.each { oBinary ->
if (oBinary.buildable == false) {
return
}
NativeBinarySpec binary = (NativeBinarySpec) oBinary
NativeBinarySpec baseBin = null
base.binaries.each { oTmpBaseBin ->
if (oTmpBaseBin.buildable == false) {
return
}
def tmpBaseBin = (NativeBinarySpec) oTmpBaseBin
if (tmpBaseBin.targetPlatform.name == binary.targetPlatform.name &&
tmpBaseBin.buildType == binary.buildType) {
baseBin = tmpBaseBin
}
}
if (binary instanceof StaticLibraryBinarySpec) {
File intoDir = ((CreateStaticLibrary)((StaticLibraryBinarySpec)binary).tasks.createStaticLib).outputFile.get().asFile.parentFile
File fromDir = ((CreateStaticLibrary)((StaticLibraryBinarySpec)baseBin).tasks.createStaticLib).outputFile.get().asFile.parentFile
def copyBasePdbName = "copyBasePdbFor" + binary.buildTask.name
def copyTask = project.tasks.register(copyBasePdbName, Copy) { Copy t ->
t.from (fromDir)
t.include '*.pdb'
t.into intoDir
t.dependsOn (((StaticLibraryBinarySpec)baseBin).tasks.createStaticLib)
}
((CreateStaticLibrary)((StaticLibraryBinarySpec)binary).tasks.createStaticLib).dependsOn(copyTask)
}
baseBin.tasks.withType(AbstractNativeSourceCompileTask) { oCompileTask ->
def compileTask = (AbstractNativeSourceCompileTask) oCompileTask
if (binary instanceof SharedLibraryBinarySpec) {
def sBinary = (SharedLibraryBinarySpec) binary
ObjectFilesToBinary link = (ObjectFilesToBinary) sBinary.tasks.link
ExportsGenerationTask exportsTask = binary.tasks.withType(ExportsGenerationTask)[0]
if (exportsTask != null) {
exportsTask.dependsOn compileTask
}
link.dependsOn compileTask
link.inputs.dir compileTask.objectFileDir
def tree = project.fileTree(compileTask.objectFileDir)
tree.include '**/*.o'
tree.include '**/*.obj'
link.source tree
if (project.hasProperty('buildServer')) {
deleteObjects.dependsOn link
deleteObjects.delete tree
}
} else if (binary instanceof StaticLibraryBinarySpec) {
def sBinary = (StaticLibraryBinarySpec) binary
ObjectFilesToBinary assemble = (ObjectFilesToBinary) sBinary.tasks.createStaticLib
assemble.dependsOn compileTask
assemble.inputs.dir compileTask.objectFileDir
def tree = project.fileTree(compileTask.objectFileDir)
tree.include '**/*.o'
tree.include '**/*.obj'
assemble.source tree
if (project.hasProperty('buildServer')) {
deleteObjects.dependsOn assemble
deleteObjects.delete tree
}
}
}
}
}
}
}
}

View File

@@ -1,77 +0,0 @@
import groovy.transform.CompileStatic;
import javax.inject.Inject;
import org.gradle.api.Project;
import edu.wpi.first.deployutils.deploy.CommandDeployResult;
import edu.wpi.first.deployutils.deploy.artifact.MavenArtifact;
import edu.wpi.first.deployutils.deploy.context.DeployContext;
import edu.wpi.first.deployutils.deploy.target.RemoteTarget;
import edu.wpi.first.deployutils.PredicateWrapper;
import edu.wpi.first.deployutils.ActionWrapper;
@CompileStatic
public class WPIJREArtifact extends MavenArtifact {
private final String configName;
public String getConfigName() {
return configName;
}
public boolean isCheckJreVersion() {
return checkJreVersion;
}
public void setCheckJreVersion(boolean checkJreVersion) {
this.checkJreVersion = checkJreVersion;
}
private boolean checkJreVersion = true;
private final String artifactLocation = "edu.wpi.first.jdk:roborio-2024:17.0.9u7-1"
@Inject
public WPIJREArtifact(String name, RemoteTarget target) {
super(name, target);
String configName = name + "frcjre";
this.configName = configName;
Project project = target.getProject();
getConfiguration().set(project.getConfigurations().create(configName));
getDependency().set(project.getDependencies().add(configName, artifactLocation));
setOnlyIf(new PredicateWrapper({ DeployContext ctx ->
return jreMissing(ctx) || jreOutOfDate(ctx) || project.hasProperty("force-redeploy-jre");
}));
getDirectory().set("/tmp");
getFilename().set("frcjre.ipk");
getPostdeploy().add(new ActionWrapper({ DeployContext ctx ->
ctx.getLogger().log("Installing JRE...");
ctx.execute("opkg remove frc*-openjdk*; opkg install /tmp/frcjre.ipk; rm /tmp/frcjre.ipk");
ctx.getLogger().log("JRE Deployed!");
}));
}
private boolean jreMissing(DeployContext ctx) {
return ctx.execute("if [[ -f \"/usr/local/frc/JRE/bin/java\" ]]; then echo OK; else echo MISSING; fi").getResult().contains("MISSING");
}
private boolean jreOutOfDate(DeployContext ctx) {
if (!checkJreVersion) {
return false;
}
String version = getDependency().get().getVersion();
CommandDeployResult cmdResult = ctx.execute("opkg list-installed | grep openjdk");
if (cmdResult.getExitCode() != 0) {
ctx.getLogger().log("JRE not found");
return false;
}
String result = cmdResult.getResult().trim();
ctx.getLogger().log("Searching for JRE " + version);
ctx.getLogger().log("Found JRE " + result);
boolean matches = result.contains(version);
ctx.getLogger().log(matches ? "JRE Is Correct Version" : "JRE is mismatched. Reinstalling");
return !matches;
}
}

View File

@@ -1,26 +0,0 @@
cppHeaderFileInclude {
\.h$
\.inc$
}
cppSrcFileInclude {
\.cpp$
}
repoRootNameOverride {
cameraserver
}
includeOtherLibs {
^fmt/
^gtest/
^hal/
^networktables/
^opencv2/
^support/
^wpi/
}
includeGuardRoots {
cameraserver/src/main/native/include/
}

View File

@@ -1,92 +0,0 @@
project(cameraserver)
include(CompileWarnings)
include(AddTest)
find_package(OpenCV REQUIRED)
# Java bindings
if(WITH_JAVA)
find_package(Java REQUIRED)
include(UseJava)
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
#find java files, copy them locally
set(OPENCV_JAVA_INSTALL_DIR ${OpenCV_INSTALL_PATH}/share/OpenCV/java/)
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
NO_DEFAULT_PATH
)
file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
add_jar(
cameraserver_jar
${JAVA_SOURCES}
INCLUDE_JARS wpiutil_jar cscore_jar ntcore_jar ${OPENCV_JAR_FILE}
OUTPUT_NAME cameraserver
)
install_jar(cameraserver_jar DESTINATION ${java_lib_dest})
install_jar_exports(
TARGETS cameraserver_jar
FILE cameraserver_jar.cmake
DESTINATION share/cameraserver
)
endif()
if(WITH_JAVA_SOURCE)
find_package(Java REQUIRED)
include(UseJava)
file(GLOB CAMERASERVER_SOURCES src/main/java/edu/wpi/first/cameraserver/*.java)
file(GLOB VISION_SOURCES src/main/java/edu/wpi/first/vision/*.java)
add_jar(
cameraserver_src_jar
RESOURCES
NAMESPACE "edu/wpi/first/cameraserver" ${CAMERASERVER_SOURCES}
NAMESPACE "edu/wpi/first/vision" ${VISION_SOURCES}
OUTPUT_NAME cameraserver-sources
)
get_property(CAMERASERVER_SRC_JAR_FILE TARGET cameraserver_src_jar PROPERTY JAR_FILE)
install(FILES ${CAMERASERVER_SRC_JAR_FILE} DESTINATION "${java_lib_dest}")
set_property(TARGET cameraserver_src_jar PROPERTY FOLDER "java")
endif()
file(GLOB_RECURSE cameraserver_native_src src/main/native/cpp/*.cpp)
add_library(cameraserver ${cameraserver_native_src})
set_target_properties(cameraserver PROPERTIES DEBUG_POSTFIX "d")
target_include_directories(
cameraserver
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
$<INSTALL_INTERFACE:${include_dest}/cameraserver>
)
wpilib_target_warnings(cameraserver)
target_link_libraries(cameraserver PUBLIC ntcore cscore wpiutil ${OpenCV_LIBS})
set_property(TARGET cameraserver PROPERTY FOLDER "libraries")
install(TARGETS cameraserver EXPORT cameraserver)
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/cameraserver")
configure_file(cameraserver-config.cmake.in ${WPILIB_BINARY_DIR}/cameraserver-config.cmake)
install(FILES ${WPILIB_BINARY_DIR}/cameraserver-config.cmake DESTINATION share/cameraserver)
install(EXPORT cameraserver DESTINATION share/cameraserver)
file(GLOB multiCameraServer_src multiCameraServer/src/main/native/cpp/*.cpp)
add_executable(multiCameraServer ${multiCameraServer_src})
wpilib_target_warnings(multiCameraServer)
target_link_libraries(multiCameraServer cameraserver)
set_property(TARGET multiCameraServer PROPERTY FOLDER "examples")
if(WITH_TESTS)
wpilib_add_test(cameraserver src/test/native/cpp)
target_link_libraries(cameraserver_test cameraserver gtest)
endif()

View File

@@ -1,81 +0,0 @@
ext {
nativeName = 'cameraserver'
devMain = 'edu.wpi.first.cameraserver.DevMain'
}
evaluationDependsOn(':ntcore')
evaluationDependsOn(':cscore')
evaluationDependsOn(':hal')
apply from: "${rootDir}/shared/javacpp/setupBuild.gradle"
dependencies {
implementation project(':wpiutil')
implementation project(':wpinet')
implementation project(':ntcore')
implementation project(':cscore')
}
ext {
sharedCvConfigs = [cameraserver : [],
cameraserverBase: [],
cameraserverDev : [],
cameraserverTest: []]
staticCvConfigs = [:]
useJava = true
useCpp = true
}
apply from: "${rootDir}/shared/opencv.gradle"
nativeUtils.exportsConfigs {
cameraserver {
x64ExcludeSymbols = [
'_CT??_R0?AV_System_error',
'_CT??_R0?AVexception',
'_CT??_R0?AVfailure',
'_CT??_R0?AVruntime_error',
'_CT??_R0?AVsystem_error',
'_CTA5?AVfailure',
'_TI5?AVfailure',
'_CT??_R0?AVout_of_range',
'_CTA3?AVout_of_range',
'_TI3?AVout_of_range',
'_CT??_R0?AVbad_cast'
]
}
}
model {
components {}
binaries {
all {
if (!it.buildable || !(it instanceof NativeBinarySpec)) {
return
}
project(':ntcore').addNtcoreDependency(it, 'shared')
lib project: ':cscore', library: 'cscore', linkage: 'shared'
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
}
tasks {
def c = $.components
def found = false
def systemArch = getCurrentArch()
c.each {
if (it in NativeExecutableSpec && it.name == "${nativeName}Dev") {
it.binaries.each {
if (!found) {
def arch = it.targetPlatform.name
if (arch == systemArch) {
def filePath = it.tasks.install.installDirectory.get().toString() + File.separatorChar + 'lib'
found = true
}
}
}
}
}
}
}

View File

@@ -1,12 +0,0 @@
include(CMakeFindDependencyMacro)
@FILENAME_DEP_REPLACE@
@WPIUTIL_DEP_REPLACE@
@NTCORE_DEP_REPLACE@
@CSCORE_DEP_REPLACE@
find_dependency(OpenCV)
@FILENAME_DEP_REPLACE@
include(${SELF_DIR}/cameraserver.cmake)
if(@WITH_JAVA@)
include(${SELF_DIR}/cameraserver_jar.cmake)
endif()

View File

@@ -1,70 +0,0 @@
plugins {
id 'java'
id 'application'
id 'cpp'
id 'visual-studio'
}
apply plugin: 'edu.wpi.first.NativeUtils'
apply from: "${rootDir}/shared/config.gradle"
ext {
staticCvConfigs = [multiCameraServerCpp: []]
useJava = true
useCpp = true
skipDev = true
}
apply from: "${rootDir}/shared/opencv.gradle"
application {
mainClass = 'edu.wpi.Main'
}
apply plugin: 'com.github.johnrengelman.shadow'
repositories {
maven {
url = 'https://frcmaven.wpi.edu/artifactory/ex-mvn'
}
}
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
implementation project(':wpiutil')
implementation project(':wpinet')
implementation project(':ntcore')
implementation project(':cscore')
implementation project(':cameraserver')
}
model {
components {
multiCameraServerCpp(NativeExecutableSpec) {
sources {
cpp {
source {
srcDirs = ['src/main/native/cpp']
includes = ['**/*.cpp']
}
exportedHeaders {
srcDirs = ['src/main/native/include']
includes = ['**/*.h']
}
}
}
binaries.all { binary ->
lib project: ':cameraserver', library: 'cameraserver', linkage: 'static'
project(':ntcore').addNtcoreDependency(binary, 'static')
lib project: ':cscore', library: 'cscore', linkage: 'static'
lib project: ':wpinet', library: 'wpinet', linkage: 'static'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
nativeUtils.useRequiredLibrary(binary, 'ni_link_libraries', 'ni_runtime_libraries')
}
}
}
}
}

View File

@@ -1,196 +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 edu.wpi;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import edu.wpi.first.cameraserver.CameraServer;
import edu.wpi.first.cscore.VideoSource;
import edu.wpi.first.networktables.NetworkTableInstance;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
/*
JSON format:
{
"team": <team number>,
"ntmode": <"client" or "server", "client" if unspecified>
"cameras": [
{
"name": <camera name>
"path": <path, e.g. "/dev/video0">
"pixel format": <"MJPEG", "YUYV", etc> // optional
"width": <video mode width> // optional
"height": <video mode height> // optional
"fps": <video mode fps> // optional
"brightness": <percentage brightness> // optional
"white balance": <"auto", "hold", value> // optional
"exposure": <"auto", "hold", value> // optional
"properties": [ // optional
{
"name": <property name>
"value": <property value>
}
]
}
]
}
*/
public final class Main {
private static String configFile = "/boot/frc.json";
@SuppressWarnings("MemberName")
public static class CameraConfig {
public String name;
public String path;
public JsonObject config;
}
private static int team;
private static boolean server;
private static List<CameraConfig> cameras = new ArrayList<>();
private Main() {}
/** Report parse error. */
public static void parseError(String str) {
System.err.println("config error in '" + configFile + "': " + str);
}
/** Read single camera configuration. */
public static boolean readCameraConfig(JsonObject config) {
CameraConfig cam = new CameraConfig();
// name
JsonElement nameElement = config.get("name");
if (nameElement == null) {
parseError("could not read camera name");
return false;
}
cam.name = nameElement.getAsString();
// path
JsonElement pathElement = config.get("path");
if (pathElement == null) {
parseError("camera '" + cam.name + "': could not read path");
return false;
}
cam.path = pathElement.getAsString();
cam.config = config;
cameras.add(cam);
return true;
}
/** Read configuration file. */
public static boolean readConfig() {
// parse file
JsonElement top;
try {
top = JsonParser.parseReader(Files.newBufferedReader(Paths.get(configFile)));
} catch (IOException ex) {
System.err.println("could not open '" + configFile + "': " + ex);
return false;
}
// top level must be an object
if (!top.isJsonObject()) {
parseError("must be JSON object");
return false;
}
JsonObject obj = top.getAsJsonObject();
// team number
JsonElement teamElement = obj.get("team");
if (teamElement == null) {
parseError("could not read team number");
return false;
}
team = teamElement.getAsInt();
// ntmode (optional)
if (obj.has("ntmode")) {
String str = obj.get("ntmode").getAsString();
if ("client".equalsIgnoreCase(str)) {
server = false;
} else if ("server".equalsIgnoreCase(str)) {
server = true;
} else {
parseError("could not understand ntmode value '" + str + "'");
}
}
// cameras
JsonElement camerasElement = obj.get("cameras");
if (camerasElement == null) {
parseError("could not read cameras");
return false;
}
JsonArray cameras = camerasElement.getAsJsonArray();
for (JsonElement camera : cameras) {
if (!readCameraConfig(camera.getAsJsonObject())) {
return false;
}
}
return true;
}
/** Start running the camera. */
public static void startCamera(CameraConfig config) {
System.out.println("Starting camera '" + config.name + "' on " + config.path);
VideoSource camera = CameraServer.startAutomaticCapture(config.name, config.path);
Gson gson = new GsonBuilder().create();
camera.setConfigJson(gson.toJson(config.config));
}
/** Main. */
public static void main(String... args) {
if (args.length > 0) {
configFile = args[0];
}
// read configuration
if (!readConfig()) {
return;
}
// start NetworkTables
NetworkTableInstance ntinst = NetworkTableInstance.getDefault();
if (server) {
System.out.println("Setting up NetworkTables server");
ntinst.startServer();
} else {
System.out.println("Setting up NetworkTables client for team " + team);
ntinst.setServerTeam(team);
ntinst.startClient4("multicameraserver");
}
// start cameras
for (CameraConfig camera : cameras) {
startCamera(camera);
}
// loop forever
for (; ; ) {
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
return;
}
}
}
}

View File

@@ -1,206 +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.
#include <cstdio>
#include <string>
#include <string_view>
#include <thread>
#include <vector>
#include <fmt/core.h>
#include <networktables/NetworkTableInstance.h>
#include <wpi/MemoryBuffer.h>
#include <wpi/StringExtras.h>
#include <wpi/fmt/raw_ostream.h>
#include <wpi/json.h>
#include <wpi/raw_ostream.h>
#include "cameraserver/CameraServer.h"
/*
JSON format:
{
"team": <team number>,
"ntmode": <"client" or "server", "client" if unspecified>
"cameras": [
{
"name": <camera name>
"path": <path, e.g. "/dev/video0">
"pixel format": <"MJPEG", "YUYV", etc> // optional
"width": <video mode width> // optional
"height": <video mode height> // optional
"fps": <video mode fps> // optional
"brightness": <percentage brightness> // optional
"white balance": <"auto", "hold", value> // optional
"exposure": <"auto", "hold", value> // optional
"properties": [ // optional
{
"name": <property name>
"value": <property value>
}
]
}
]
}
*/
#ifdef __RASPBIAN__
static const char* configFile = "/boot/frc.json";
#else
static const char* configFile = "frc.json";
#endif
namespace {
unsigned int team;
bool server = false;
struct CameraConfig {
std::string name;
std::string path;
wpi::json config;
};
std::vector<CameraConfig> cameras;
bool ReadCameraConfig(const wpi::json& config) {
CameraConfig c;
// name
try {
c.name = config.at("name").get<std::string>();
} catch (const wpi::json::exception& e) {
fmt::print(stderr, "config error in '{}': could not read camera name: {}\n",
configFile, e.what());
return false;
}
// path
try {
c.path = config.at("path").get<std::string>();
} catch (const wpi::json::exception& e) {
fmt::print(stderr,
"config error in '{}': camera '{}': could not read path: {}\n",
configFile, c.name, e.what());
return false;
}
c.config = config;
cameras.emplace_back(std::move(c));
return true;
}
bool ReadConfig() {
// open config file
std::error_code ec;
std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
wpi::MemoryBuffer::GetFile(configFile, ec);
if (fileBuffer == nullptr || ec) {
fmt::print(stderr, "could not open '{}': {}\n", configFile, ec.message());
return false;
}
// parse file
wpi::json j;
try {
j = wpi::json::parse(fileBuffer->GetCharBuffer());
} catch (const wpi::json::parse_error& e) {
fmt::print(stderr, "config error in '{}': byte {}: {}\n", configFile,
e.byte, e.what());
return false;
}
// top level must be an object
if (!j.is_object()) {
fmt::print(stderr, "config error in '{}': must be JSON object\n",
configFile);
return false;
}
// team number
try {
team = j.at("team").get<unsigned int>();
} catch (const wpi::json::exception& e) {
fmt::print(stderr, "config error in '{}': could not read team number: {}\n",
configFile, e.what());
return false;
}
// ntmode (optional)
if (j.count("ntmode") != 0) {
try {
auto str = j.at("ntmode").get<std::string>();
if (wpi::equals_lower(str, "client")) {
server = false;
} else if (wpi::equals_lower(str, "server")) {
server = true;
} else {
fmt::print(
stderr,
"config error in '{}': could not understand ntmode value '{}'\n",
configFile, str);
}
} catch (const wpi::json::exception& e) {
fmt::print(stderr, "config error in '{}': could not read ntmode: {}\n",
configFile, e.what());
}
}
// cameras
try {
for (auto&& camera : j.at("cameras")) {
if (!ReadCameraConfig(camera)) {
return false;
}
}
} catch (const wpi::json::exception& e) {
fmt::print(stderr, "config error in '{}': could not read cameras: {}\n",
configFile, e.what());
return false;
}
return true;
}
void StartCamera(const CameraConfig& config) {
fmt::print("Starting camera '{}' on {}\n", config.name, config.path);
auto camera =
frc::CameraServer::StartAutomaticCapture(config.name, config.path);
camera.SetConfigJson(config.config);
}
} // namespace
int main(int argc, char* argv[]) {
if (argc >= 2) {
configFile = argv[1];
}
// read configuration
if (!ReadConfig()) {
return EXIT_FAILURE;
}
// start NetworkTables
auto ntinst = nt::NetworkTableInstance::GetDefault();
if (server) {
std::puts("Setting up NetworkTables server");
ntinst.StartServer();
} else {
fmt::print("Setting up NetworkTables client for team {}\n", team);
ntinst.StartClient4("multicameraserver");
ntinst.SetServerTeam(team);
}
// start cameras
for (auto&& camera : cameras) {
StartCamera(camera);
}
// loop forever
for (;;) {
std::this_thread::sleep_for(std::chrono::seconds(10));
}
}

View File

@@ -1,11 +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 edu.wpi.first.cameraserver;
public final class DevMain {
public static void main(String[] args) {}
private DevMain() {}
}

View File

@@ -1,5 +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.
int main() {}

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