diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 662df347bc..b080592564 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -9,8 +9,8 @@ jobs: build-pdf: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: 3.9 cache: 'pip' @@ -25,7 +25,7 @@ jobs: run: | make latexpdf - name: Archive PDF - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: frc-docs-pdf path: build/latex/firstroboticscompetition.pdf @@ -34,8 +34,8 @@ jobs: build-html: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: 3.9 cache: 'pip' @@ -50,7 +50,7 @@ jobs: run: | make html - name: Archive HTML - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: frc-docs-html path: build/html/ @@ -59,16 +59,16 @@ jobs: build-html-translation: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: wpilibsuite/frc-docs-translations - name: Delete old submodule run: | rm -rf ./frc-docs - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: frc-docs - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: 3.9 cache: 'pip' @@ -89,13 +89,13 @@ jobs: # Changes have been made. runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get changed files id: changed-files - uses: tj-actions/changed-files@v35 - - uses: actions/setup-python@v4 + uses: tj-actions/changed-files@v41 + - uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install Dependencies @@ -118,8 +118,8 @@ jobs: check-linting: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install Dependencies @@ -132,8 +132,8 @@ jobs: check-image-size: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install Dependencies @@ -146,7 +146,7 @@ jobs: check-spelling: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: misspell uses: reviewdog/action-misspell@v1 with: @@ -156,11 +156,11 @@ jobs: check-redirects: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Checkout main run: | git fetch origin main --depth=1 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install Dependencies @@ -176,8 +176,8 @@ jobs: check-formatting: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install Python Dependencies diff --git a/.github/workflows/base-branch.yml b/.github/workflows/base-branch.yml index 3b4837dd99..f16217d08c 100644 --- a/.github/workflows/base-branch.yml +++ b/.github/workflows/base-branch.yml @@ -10,9 +10,8 @@ jobs: check-base-branch: runs-on: ubuntu-latest steps: - - uses: wpilibsuite/check-base-branch-action@v1.1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - protected-branches: stable - default-branch: main - update-branch: true + - name: Base Branch + run: | + echo "::error ::Base branch of PR is stable. It should be main. Please retarget the base branch of the PR." + exit 1 + diff --git a/.github/workflows/inspector.json b/.github/workflows/inspector.json index 9ede255d2b..591a55aef5 100644 --- a/.github/workflows/inspector.json +++ b/.github/workflows/inspector.json @@ -3,28 +3,28 @@ { "baseUrl": "https://raw.githubusercontent.com/wpilibsuite/allwpilib/", "versionScheme": "v\\d{4}\\.\\d\\.\\d(?:-(?:alpha|beta)-\\d)?|[0-9a-f]{40}", - "latestVersion": "v2024.1.1-beta-3" + "latestVersion": "v2024.3.2" }, { "baseUrl": "https://github.com/wpilibsuite/allwpilib/raw/", "versionScheme": "v\\d{4}\\.\\d\\.\\d(?:-(?:alpha|beta)-\\d)?|[0-9a-f]{40}", - "latestVersion": "v2024.1.1-beta-3" + "latestVersion": "v2024.3.2" }, { "baseUrl": "https://raw.githubusercontent.com/robotpy/examples/", "versionScheme": "\\d{4}\\.\\d\\.\\d\\.\\d(?:-(?:alpha|beta)-\\d)?|[0-9a-f]{40}", - "latestVersion": "2023.4.3.0" + "latestVersion": "d89b0587a1e1111239728140466c7dc4324d4005" }, { "baseUrl": "https://raw.githubusercontent.com/wpilibsuite/vscode-wpilib/", "versionScheme": "v\\d{4}\\.\\d\\.\\d(?:-(?:alpha|beta)-\\d)?|[0-9a-f]{40}", - "latestVersion":"v2024.1.1-beta-2" + "latestVersion":"v2024.3.2" }, { "baseUrl": "https://raw.githubusercontent.com/wpilibsuite/StandaloneAppSamples/", "versionScheme": "v\\d{4}\\.\\d\\.\\d(?:-(?:alpha|beta)-\\d)?|[0-9a-f]{40}", - "latestVersion":"3b64aadee717c9f0566497a40fd0be7d0eaed96d" + "latestVersion":"6a5b6352807a8759bd0f012e57695c47f7ef7324" } ], - "ignoredFiles": ["source/docs/software/commandbased/command-scheduler.rst"] + "ignoredFiles": ["source/docs/software/commandbased/command-scheduler.rst", "source/docs/software/hardware-apis/pneumatics/pressure.rst", "source/docs/software/hardware-apis/pneumatics/solenoids.rst", "source/docs/software/advanced-controls/state-space/state-space-pose-estimators.rst", "source/docs/software/commandbased/profilepid-subsystems-commands.rst", "source/docs/software/commandbased/subsystems.rst", "source/docs/software/telemetry/writing-sendable-classes.rst", "source/docs/software/advanced-controls/trajectories/troubleshooting.rst", "source/docs/software/hardware-apis/motors/wpi-drive-classes.rst", "source/docs/software/pathplanning/trajectory-tutorial/creating-drive-subsystem.rst", "source/docs/software/pathplanning/trajectory-tutorial/creating-following-trajectory.rst", "source/docs/software/pathplanning/trajectory-tutorial/entering-constants.rst"] } diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml index 5d5b554d2e..3f1ea260f3 100644 --- a/.github/workflows/link-check.yml +++ b/.github/workflows/link-check.yml @@ -10,13 +10,13 @@ env: jobs: check-links: - if: github.repository_owner == 'wpilibsuite' + if: github.repository_owner == 'wpilibsuite' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: 3.11 - name: Install Dependencies run: pip install -r source/requirements.txt - name: Check Links diff --git a/.github/workflows/publish_stable.yml b/.github/workflows/publish_stable.yml index b499f98038..f4610d7098 100644 --- a/.github/workflows/publish_stable.yml +++ b/.github/workflows/publish_stable.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest if: ${{ github.repository_owner == 'wpilibsuite' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Merge @@ -33,8 +33,5 @@ jobs: git merge --ff-only main - name: Push - if: env.PUBLISH_STABLE == 'true' || github.event.action == 'workflow_dispatch' - env: - PUBLISH_STABLE: ${{ secrets.PUBLISH_STABLE }} run: | git push origin stable diff --git a/.github/workflows/update-rli-commands.yml b/.github/workflows/update-rli-commands.yml index 67bb1db9ea..1a41a00cf8 100644 --- a/.github/workflows/update-rli-commands.yml +++ b/.github/workflows/update-rli-commands.yml @@ -11,7 +11,7 @@ jobs: # ---- Find Trigger States ---- # Run Inspector autofix on everything - name: Trigger-Check - uses: shanegenschaw/pull-request-comment-trigger@v2 + uses: shanegenschaw/pull-request-comment-trigger@v3.0.0 id: trigger-check-all with: trigger: '\inspector check all' @@ -22,7 +22,7 @@ jobs: # Run Inspector autofix only on files changed by the PR - name: Trigger-Diff - uses: shanegenschaw/pull-request-comment-trigger@v2 + uses: shanegenschaw/pull-request-comment-trigger@v3.0.0 id: trigger-diff with: trigger: '\inspector fix diff' @@ -33,7 +33,7 @@ jobs: # Run Inspector autofix on everything - name: Trigger-All - uses: shanegenschaw/pull-request-comment-trigger@v2 + uses: shanegenschaw/pull-request-comment-trigger@v3.0.0 id: trigger-all with: trigger: '\inspector fix all' @@ -48,7 +48,7 @@ jobs: run: exit 1 - name: Checkout [Common] - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Checkout PR [Comment] run: gh pr checkout $NUMBER @@ -100,7 +100,7 @@ jobs: # ---- Common - Post Report ---- - name: Find Comment - uses: peter-evans/find-comment@v2 + uses: peter-evans/find-comment@v3 id: fc with: issue-number: ${{ github.event.issue.number }} @@ -110,14 +110,14 @@ jobs: - name: Create comment if: ${{ steps.fc.outputs.comment-id == 0 }} - uses: peter-evans/create-or-update-comment@v2 + uses: peter-evans/create-or-update-comment@v4 with: body: ${{ env.REPORT }} issue-number: ${{ github.event.issue.number }} - name: Update comment if: ${{ steps.fc.outputs.comment-id != 0 }} - uses: peter-evans/create-or-update-comment@v2 + uses: peter-evans/create-or-update-comment@v4 with: body: | ${{ env.REPORT }} diff --git a/Makefile b/Makefile index 5a12527fbe..74b0e64099 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ SPHINXOPTS = -W --keep-going -q -T SPHINXBUILD = sphinx-build SOURCEDIR = source BUILDDIR = build -LINTER = doc8 +LINTER = python3 scripts/doc8_redown.py LINTEROPTS = --ignore D001 # D001 is linelength SIZECHECKER = python3 -m scripts.imagesizechecker CONFEXCLUDE = --exclude-file source/conf.py diff --git a/make.bat b/make.bat index 5f3389dcf2..fe1c304f13 100644 --- a/make.bat +++ b/make.bat @@ -10,7 +10,7 @@ if "%SPHINXBUILD%" == "" ( set SOURCEDIR=source set BUILDDIR=build set SPHINXOPTS=-W --keep-going -q -T -set LINTER=doc8 +set LINTER=python scripts/doc8_redown.py set LINTEROPTS=--ignore D001 --ignore D004 set SIZECHECKER=python -m scripts.imagesizechecker set CONFEXCLUDE=--exclude-file source/conf.py diff --git a/poetry.lock b/poetry.lock index 61441bb691..60b38128cb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -72,6 +72,9 @@ files = [ {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"}, ] +[package.dependencies] +setuptools = {version = "*", markers = "python_version >= \"3.12\""} + [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] @@ -95,36 +98,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "23.1.0" +version = "24.4.2" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "black-23.1.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221"}, - {file = "black-23.1.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26"}, - {file = "black-23.1.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b"}, - {file = "black-23.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104"}, - {file = "black-23.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074"}, - {file = "black-23.1.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27"}, - {file = "black-23.1.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648"}, - {file = "black-23.1.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958"}, - {file = "black-23.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a"}, - {file = "black-23.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481"}, - {file = "black-23.1.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad"}, - {file = "black-23.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8"}, - {file = "black-23.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24"}, - {file = "black-23.1.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6"}, - {file = "black-23.1.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd"}, - {file = "black-23.1.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580"}, - {file = "black-23.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468"}, - {file = "black-23.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753"}, - {file = "black-23.1.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651"}, - {file = "black-23.1.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06"}, - {file = "black-23.1.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739"}, - {file = "black-23.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9"}, - {file = "black-23.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555"}, - {file = "black-23.1.0-py3-none-any.whl", hash = "sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32"}, - {file = "black-23.1.0.tar.gz", hash = "sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac"}, + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, ] [package.dependencies] @@ -134,11 +134,11 @@ packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -296,13 +296,13 @@ redis = ["redis (>=2.10.5)"] [[package]] name = "certifi" -version = "2023.7.22" +version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] @@ -692,13 +692,53 @@ typing = ["typing-extensions (>=4.8)"] [[package]] name = "fonttools" -version = "4.33.3" +version = "4.43.0" description = "Tools to manipulate font files" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "fonttools-4.33.3-py3-none-any.whl", hash = "sha256:f829c579a8678fa939a1d9e9894d01941db869de44390adb49ce67055a06cc2a"}, - {file = "fonttools-4.33.3.zip", hash = "sha256:c0fdcfa8ceebd7c1b2021240bd46ef77aa8e7408cf10434be55df52384865f8e"}, + {file = "fonttools-4.43.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ab80e7d6bb01316d5fc8161a2660ca2e9e597d0880db4927bc866c76474472ef"}, + {file = "fonttools-4.43.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82d8e687a42799df5325e7ee12977b74738f34bf7fde1c296f8140efd699a213"}, + {file = "fonttools-4.43.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d08a694b280d615460563a6b4e2afb0b1b9df708c799ec212bf966652b94fc84"}, + {file = "fonttools-4.43.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d654d3e780e0ceabb1f4eff5a3c042c67d4428d0fe1ea3afd238a721cf171b3"}, + {file = "fonttools-4.43.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:20fc43783c432862071fa76da6fa714902ae587bc68441e12ff4099b94b1fcef"}, + {file = "fonttools-4.43.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:33c40a657fb87ff83185828c0323032d63a4df1279d5c1c38e21f3ec56327803"}, + {file = "fonttools-4.43.0-cp310-cp310-win32.whl", hash = "sha256:b3813f57f85bbc0e4011a0e1e9211f9ee52f87f402e41dc05bc5135f03fa51c1"}, + {file = "fonttools-4.43.0-cp310-cp310-win_amd64.whl", hash = "sha256:05056a8c9af048381fdb17e89b17d45f6c8394176d01e8c6fef5ac96ea950d38"}, + {file = "fonttools-4.43.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da78f39b601ed0b4262929403186d65cf7a016f91ff349ab18fdc5a7080af465"}, + {file = "fonttools-4.43.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5056f69a18f3f28ab5283202d1efcfe011585d31de09d8560f91c6c88f041e92"}, + {file = "fonttools-4.43.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcc01cea0a121fb0c009993497bad93cae25e77db7dee5345fec9cce1aaa09cd"}, + {file = "fonttools-4.43.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee728d5af70f117581712966a21e2e07031e92c687ef1fdc457ac8d281016f64"}, + {file = "fonttools-4.43.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b5e760198f0b87e42478bb35a6eae385c636208f6f0d413e100b9c9c5efafb6a"}, + {file = "fonttools-4.43.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af38f5145258e9866da5881580507e6d17ff7756beef175d13213a43a84244e9"}, + {file = "fonttools-4.43.0-cp311-cp311-win32.whl", hash = "sha256:25620b738d4533cfc21fd2a4f4b667e481f7cb60e86b609799f7d98af657854e"}, + {file = "fonttools-4.43.0-cp311-cp311-win_amd64.whl", hash = "sha256:635658464dccff6fa5c3b43fe8f818ae2c386ee6a9e1abc27359d1e255528186"}, + {file = "fonttools-4.43.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a682fb5cbf8837d1822b80acc0be5ff2ea0c49ca836e468a21ffd388ef280fd3"}, + {file = "fonttools-4.43.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3d7adfa342e6b3a2b36960981f23f480969f833d565a4eba259c2e6f59d2674f"}, + {file = "fonttools-4.43.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa67d1e720fdd902fde4a59d0880854ae9f19fc958f3e1538bceb36f7f4dc92"}, + {file = "fonttools-4.43.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e5113233a2df07af9dbf493468ce526784c3b179c0e8b9c7838ced37c98b69"}, + {file = "fonttools-4.43.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:57c22e5f9f53630d458830f710424dce4f43c5f0d95cb3368c0f5178541e4db7"}, + {file = "fonttools-4.43.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:206808f9717c9b19117f461246372a2c160fa12b9b8dbdfb904ab50ca235ba0a"}, + {file = "fonttools-4.43.0-cp312-cp312-win32.whl", hash = "sha256:f19c2b1c65d57cbea25cabb80941fea3fbf2625ff0cdcae8900b5fb1c145704f"}, + {file = "fonttools-4.43.0-cp312-cp312-win_amd64.whl", hash = "sha256:7c76f32051159f8284f1a5f5b605152b5a530736fb8b55b09957db38dcae5348"}, + {file = "fonttools-4.43.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e3f8acc6ef4a627394021246e099faee4b343afd3ffe2e517d8195b4ebf20289"}, + {file = "fonttools-4.43.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a68b71adc3b3a90346e4ac92f0a69ab9caeba391f3b04ab6f1e98f2c8ebe88e3"}, + {file = "fonttools-4.43.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ace0fd5afb79849f599f76af5c6aa5e865bd042c811e4e047bbaa7752cc26126"}, + {file = "fonttools-4.43.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f9660e70a2430780e23830476332bc3391c3c8694769e2c0032a5038702a662"}, + {file = "fonttools-4.43.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:48078357984214ccd22d7fe0340cd6ff7286b2f74f173603a1a9a40b5dc25afe"}, + {file = "fonttools-4.43.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d27d960e10cf7617d70cf3104c32a69b008dde56f2d55a9bed4ba6e3df611544"}, + {file = "fonttools-4.43.0-cp38-cp38-win32.whl", hash = "sha256:a6a2e99bb9ea51e0974bbe71768df42c6dd189308c22f3f00560c3341b345646"}, + {file = "fonttools-4.43.0-cp38-cp38-win_amd64.whl", hash = "sha256:030355fbb0cea59cf75d076d04d3852900583d1258574ff2d7d719abf4513836"}, + {file = "fonttools-4.43.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52e77f23a9c059f8be01a07300ba4c4d23dc271d33eed502aea5a01ab5d2f4c1"}, + {file = "fonttools-4.43.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a530fa28c155538d32214eafa0964989098a662bd63e91e790e6a7a4e9c02da"}, + {file = "fonttools-4.43.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70f021a6b9eb10dfe7a411b78e63a503a06955dd6d2a4e130906d8760474f77c"}, + {file = "fonttools-4.43.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:812142a0e53cc853964d487e6b40963df62f522b1b571e19d1ff8467d7880ceb"}, + {file = "fonttools-4.43.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ace51902ab67ef5fe225e8b361039e996db153e467e24a28d35f74849b37b7ce"}, + {file = "fonttools-4.43.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8dfd8edfce34ad135bd69de20c77449c06e2c92b38f2a8358d0987737f82b49e"}, + {file = "fonttools-4.43.0-cp39-cp39-win32.whl", hash = "sha256:e5d53eddaf436fa131042f44a76ea1ead0a17c354ab9de0d80e818f0cb1629f1"}, + {file = "fonttools-4.43.0-cp39-cp39-win_amd64.whl", hash = "sha256:93c5b6d77baf28f306bc13fa987b0b13edca6a39dc2324eaca299a74ccc6316f"}, + {file = "fonttools-4.43.0-py3-none-any.whl", hash = "sha256:e4bc589d8da09267c7c4ceaaaa4fc01a7908ac5b43b286ac9279afe76407c384"}, + {file = "fonttools-4.43.0.tar.gz", hash = "sha256:b62a53a4ca83c32c6b78cac64464f88d02929779373c716f738af6968c8c821e"}, ] [package.dependencies] @@ -707,7 +747,7 @@ brotlicffi = {version = ">=0.8.0", optional = true, markers = "platform_python_i zopfli = {version = ">=0.1.4", optional = true, markers = "extra == \"woff\""} [package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=14.0.0)", "xattr", "zopfli (>=0.1.4)"] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"] graphite = ["lz4 (>=1.7.4.2)"] interpolatable = ["munkres", "scipy"] lxml = ["lxml (>=4.0,<5)"] @@ -717,7 +757,7 @@ repacker = ["uharfbuzz (>=0.23.0)"] symfont = ["sympy"] type1 = ["xattr"] ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=14.0.0)"] +unicode = ["unicodedata2 (>=15.0.0)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] @@ -773,20 +813,20 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.40" +version = "3.1.41" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.40-py3-none-any.whl", hash = "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a"}, - {file = "GitPython-3.1.40.tar.gz", hash = "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4"}, + {file = "GitPython-3.1.41-py3-none-any.whl", hash = "sha256:c36b6634d069b3f719610175020a9aed919421c87552185b085e04fbbdb10b7c"}, + {file = "GitPython-3.1.41.tar.gz", hash = "sha256:ed66e624884f76df22c8e16066d567aaa5a37d5b5fa19db2c6df6f7156db9048"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" [package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-sugar"] +test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "sumtypes"] [[package]] name = "html5lib" @@ -811,13 +851,13 @@ lxml = ["lxml"] [[package]] name = "idna" -version = "3.4" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] @@ -870,13 +910,13 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -1160,13 +1200,12 @@ python-dateutil = ">=2.7" [[package]] name = "modern-sphinx-version-warning" -version = "1.1.4" +version = "1.1.6" description = "Sphinx extension to add a warning banner" optional = false python-versions = "*" files = [ - {file = "modern-sphinx-version-warning-1.1.4.tar.gz", hash = "sha256:946e364b8544d85be8f7182468e06ce153cd9b16404cfb5c5e0804b52a2f1816"}, - {file = "modern_sphinx_version_warning-1.1.4-py3-none-any.whl", hash = "sha256:4503501f7d174966bc196b1326bd7824ba950a431ce535ea0c3f649d91ec399a"}, + {file = "modern_sphinx_version_warning-1.1.6-py3-none-any.whl", hash = "sha256:59e7b47e7af929363f7395c1de88f305c09d93bdc2c874d9a654adbd74d788c2"}, ] [package.dependencies] @@ -1343,70 +1382,89 @@ files = [ [[package]] name = "pillow" -version = "10.1.0" +version = "10.3.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ - {file = "Pillow-10.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106"}, - {file = "Pillow-10.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db"}, - {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f"}, - {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818"}, - {file = "Pillow-10.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57"}, - {file = "Pillow-10.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7"}, - {file = "Pillow-10.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061"}, - {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262"}, - {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992"}, - {file = "Pillow-10.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a"}, - {file = "Pillow-10.1.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b"}, - {file = "Pillow-10.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651"}, - {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b"}, - {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f"}, - {file = "Pillow-10.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996"}, - {file = "Pillow-10.1.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793"}, - {file = "Pillow-10.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a7e3daa202beb61821c06d2517428e8e7c1aab08943e92ec9e5755c2fc9ba5e"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fadc71218ad2b8ffe437b54876c9382b4a29e030a05a9879f615091f42ffc2"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:912e3812a1dbbc834da2b32299b124b5ddcb664ed354916fd1ed6f193f0e2d01"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7dbaa3c7de82ef37e7708521be41db5565004258ca76945ad74a8e998c30af8d"}, - {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9d7bc666bd8c5a4225e7ac71f2f9d12466ec555e89092728ea0f5c0c2422ea80"}, - {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baada14941c83079bf84c037e2d8b7506ce201e92e3d2fa0d1303507a8538212"}, - {file = "Pillow-10.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:2ef6721c97894a7aa77723740a09547197533146fba8355e86d6d9a4a1056b14"}, - {file = "Pillow-10.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099"}, - {file = "Pillow-10.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd"}, - {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28"}, - {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2"}, - {file = "Pillow-10.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f"}, - {file = "Pillow-10.1.0.tar.gz", hash = "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, ] [package.extras] docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] [[package]] name = "platformdirs" @@ -1478,13 +1536,13 @@ six = ">=1.5" [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -1537,24 +1595,24 @@ python-versions = ">=3.6" files = [ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, @@ -1562,7 +1620,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, @@ -1570,7 +1628,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, @@ -1578,7 +1636,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, @@ -1629,6 +1687,22 @@ dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyl doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +[[package]] +name = "setuptools" +version = "72.1.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, + {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, +] + +[package.extras] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "shutilwhich" version = "1.1.0" @@ -1737,6 +1811,24 @@ docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)"] numpy = ["nptyping (>=2.5)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "sphobjinv (>=2.3.1)", "typing-extensions (>=4.7.1)"] +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +description = "Add a copy button to each of your code cells." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, + {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, +] + +[package.dependencies] +sphinx = ">=1.8" + +[package.extras] +code-style = ["pre-commit (==2.12.1)"] +rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] + [[package]] name = "sphinx-design" version = "0.5.0" @@ -1830,13 +1922,13 @@ Sphinx = ">=7.0.0,<8.0.0" [[package]] name = "sphinx-rtd-theme" -version = "2.0.0rc2" +version = "2.0.0" description = "Read the Docs theme for Sphinx" optional = false python-versions = ">=3.6" files = [ - {file = "sphinx_rtd_theme-2.0.0rc2-py2.py3-none-any.whl", hash = "sha256:f04df9213acf421c3b42f4f39005c8bc68fc4696c5b4ed4ef13d1678369713f7"}, - {file = "sphinx_rtd_theme-2.0.0rc2.tar.gz", hash = "sha256:d1270effe620df9164b1cd2d617909472a63531e21a716fd22d0fbcedf9d24ff"}, + {file = "sphinx_rtd_theme-2.0.0-py2.py3-none-any.whl", hash = "sha256:ec93d0856dc280cf3aee9a4c9807c60e027c7f7b461b77aeffed682e68f0e586"}, + {file = "sphinx_rtd_theme-2.0.0.tar.gz", hash = "sha256:bd5d7b80622406762073a04ef8fadc5f9151261563d47027de09910ce03afe6b"}, ] [package.dependencies] @@ -2096,18 +2188,18 @@ sphinx = ">=4.0" [[package]] name = "sphinxext-photofinish" -version = "0.1.9" +version = "0.1.11" description = "Sphinx Extension that creates responsive images." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "sphinxext-photofinish-0.1.9.tar.gz", hash = "sha256:d84b98b9b255e73ce7e421222877c791a12aa60a176ca93bcabadb3e0da4f278"}, - {file = "sphinxext_photofinish-0.1.9-py3-none-any.whl", hash = "sha256:e0fe74c935b3ebaaed25b7d77987cd45b021322252798535632d2b6acec3aff9"}, + {file = "sphinxext_photofinish-0.1.11-py3-none-any.whl", hash = "sha256:25c1098ce9e7a0c17ceee27d6432940f4c2f32b8efa7f391384023a4364f7578"}, + {file = "sphinxext_photofinish-0.1.11.tar.gz", hash = "sha256:175f8a1522f2decc59b34daa974c7ff92d037b0820f4af4f6218df967b68ebfa"}, ] [package.dependencies] beautifulsoup4 = ">=4" -pillow = ">=8" +pillow = ">=10.0.1" sphinx = ">=2.0" tinycss2 = ">=1.1.1" @@ -2249,13 +2341,13 @@ files = [ [[package]] name = "urllib3" -version = "1.26.18" +version = "1.26.19" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, - {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, + {file = "urllib3-1.26.19-py2.py3-none-any.whl", hash = "sha256:37a0344459b199fce0e80b0d3569837ec6b6937435c5244e7fd73fa6006830f3"}, + {file = "urllib3-1.26.19.tar.gz", hash = "sha256:3e3d753a8618b86d7de333b4223005f68720bcd6a7d2bcb9fbd2229ec7c1e429"}, ] [package.extras] @@ -2276,18 +2368,18 @@ files = [ [[package]] name = "zipp" -version = "3.17.0" +version = "3.19.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [[package]] name = "zopfli" @@ -2364,5 +2456,5 @@ test = ["pytest"] [metadata] lock-version = "2.0" -python-versions = ">=3.9,<3.12" -content-hash = "10f5d132b57f924a957628ef5fe60b58fe5bad424aa4f195f60972cc4315f8aa" +python-versions = ">=3.9,<3.13" +content-hash = "cb130333d0f1d710b95e9dbab883bd4a5bb84797d1727a901330a7de6bd299de" diff --git a/pyproject.toml b/pyproject.toml index 6b6594b65c..5d701fb146 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,11 +5,11 @@ description = "" authors = [] [tool.poetry.dependencies] -black = "==23.1.0" -python = ">=3.9,<3.12" +black = "==24.4.2" +python = ">=3.9,<3.13" doc8 = "==0.11.2" docutils = "==0.18.1" -fonttools = {version = "==4.33.3", extras = ["woff"]} +fonttools = {version = "==4.43.0", extras = ["woff"]} jsmin = "==3.0.1" latex = "==0.7.0" markdown2 = "==2.4.6" @@ -17,12 +17,13 @@ packaging = "==22.0" scipy = ">=1.9.0, <2.0.0" sphinx = "==7.2.6" sphinx_design= "==0.5.0" +sphinx_copybutton = "==0.5.2" sphinx-hoverxref = "==1.3.0" sphinx-notfound-page = "==1.0.0" -sphinx-rtd-theme = "2.0.0rc2" +sphinx-rtd-theme = "2.0.0" sphinx-tabs = "==3.4.4" sphinx-toolbox = "==3.5.0" -modern-sphinx-version-warning = "==1.1.4" +modern-sphinx-version-warning = "==1.1.6" sphinxcontrib-htmlhelp = "==2.0.4" sphinxcontrib-serializinghtml = "==1.1.9" sphinxcontrib-svg2pdfconverter = "==1.2.2" @@ -30,7 +31,7 @@ sphinxext-delta = "==0.2.0" sphinxext-linkcheckdiff = "==0.1.0" sphinxext-mimic = "==0.1.1" sphinxext-opengraph = "==0.9.0" -sphinxext-photofinish = "==0.1.9" +sphinxext-photofinish = "==0.1.11" sphinxext-presentations = "==0.2.2" sphinxext-rediraffe = "==0.2.7" sphinxext-remoteliteralinclude = "==0.4.0" diff --git a/scripts/doc8_redown.py b/scripts/doc8_redown.py new file mode 100644 index 0000000000..15fcce7d6d --- /dev/null +++ b/scripts/doc8_redown.py @@ -0,0 +1,21 @@ +from doc8 import main, parser +import sys +from pathlib import Path + +sys.path.append((Path(__file__).parent / "../source/_extensions").as_posix()) + +from redown import redown + +old_setattr = parser.ParsedFile.__setattr__ + + +def new_setattr(self, name, value): + if name == "_raw_content" and value is not None: + value = redown(value.decode("utf-8").replace("\r", "")).encode("utf-8") + # Path(self.filename).with_suffix(".rd").write_text(value.decode("utf-8")) + old_setattr(self, name, value) + + +parser.ParsedFile.__setattr__ = new_setattr + +sys.exit(main.main()) diff --git a/source/404.rst b/source/404.rst index d33115dbc1..184979446b 100644 --- a/source/404.rst +++ b/source/404.rst @@ -1,12 +1,11 @@ :orphan: -The Requested Page Was Not Found -================================ +# The Requested Page Was Not Found -The page you were searching for does not exist. If you feel like this may be an issue, open a request at the `frc-docs repository `__. +The page you were searching for does not exist. If you feel like this may be an issue, open a request at the [frc-docs repository](https://github.com/wpilibsuite/frc-docs/issues). .. image:: assets/first_design.png :align: center :alt: https://xkcd.com/689/ -Image credit to `XKCD `__. +Image credit to [XKCD](https://xkcd.com/689/). diff --git a/source/_extensions/controls_js_sim/__init__.py b/source/_extensions/controls_js_sim/__init__.py index cc9a1b109c..dae67826e6 100644 --- a/source/_extensions/controls_js_sim/__init__.py +++ b/source/_extensions/controls_js_sim/__init__.py @@ -6,7 +6,7 @@ from sphinx.application import Sphinx # Handle custom javascript -# Groups, sorts, merges, and minifies the JS files assocated with +# Groups, sorts, merges, and minifies the JS files associated with # the controls documentation debugJS = False # flip to true to make the output js more readable diff --git a/source/_extensions/default_latex_image_settings.py b/source/_extensions/default_latex_image_settings.py new file mode 100644 index 0000000000..1025792343 --- /dev/null +++ b/source/_extensions/default_latex_image_settings.py @@ -0,0 +1,43 @@ +from typing import Any, Dict + +from docutils import nodes + +from sphinx.transforms.post_transforms import SphinxPostTransform +from sphinx.application import Sphinx + + +class DefaultLaTeXImageSettingsTransform(SphinxPostTransform): + """ + Set a default image width for images which do not have a width attribute + manually set. Also center images which are not centered. This only applies + to LaTeX builds. + """ + + default_priority = 100 # chosen arbitrarily + + def run(self, **kwargs: Any) -> None: + if not self.app.tags.has("latex"): + return + + width = self.app.config["default_latex_image_width"] + + for node in self.document.findall(nodes.image): + if "width" not in node.attributes: + node.attributes["width"] = width + + if ( + self.app.config["default_latex_image_centered"] + and "align" not in node.attributes + ): + node.attributes["align"] = "center" + + +def setup(app: Sphinx) -> Dict[str, Any]: + app.add_config_value("default_latex_image_width", "25em", True) + app.add_config_value("default_latex_image_centered", True, True) + app.add_post_transform(DefaultLaTeXImageSettingsTransform) + + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/source/_extensions/redown.py b/source/_extensions/redown.py new file mode 100644 index 0000000000..de40c94a51 --- /dev/null +++ b/source/_extensions/redown.py @@ -0,0 +1,170 @@ +""" +Cai, a noite sobre o nosso amor +Cai, e agora só restou do amor + +Uma palavra +Adeus +Adeus +Adeus +Mas tendo de ir embora +""" + +import re + +from pathlib import Path +from sphinx.application import Sphinx +from dataclasses import dataclass + + +LINK_CORE = r""" + \[ (?P[^\[\]]*?) \] # link brackets + text w/o brackets - allows spaces in text + \( + (?P + \S+? # link start + (?: + \( [^()\s]*? \) # nested parens + text w/o parens - matches `initialize(boolean)` + [^()\s]*? # more text - matches `initialize(boolean)abc` + )*? # allow none (or multiple?) + ) + \) + """ + +ROLE_LINK_RE = re.compile( + r""" + (? + :(?:.\w+?:)+? # role(s) - matches :py:func: or :mod: or :class: + ) + """ + + LINK_CORE, + re.VERBOSE, # whitespace and comments are ignored +) + +LINK_RE = re.compile( + r""" + (? str: + """21""" + + # replace md code blocks with reST code blocks + "redown, redown, redown, redown" + find = r"(?P^|\n)(?P *?)(?P```+)(?P\S+) *?(?:\n\s*?)+(?P *?)(?P.*?)(?P=ticks) *?(?P\n|$|\Z)" + + def replace(match: re.Match) -> str: + start = match.group("start") + btindent = match.group("btindent") + lang = match.group("lang") + cindent = match.group("cindent") + code = match.group("code") + end = match.group("end") + + ret = "" + ret += start + ret += btindent + f".. code-block:: {lang}\n\n" + cindent = 3 * " " + + for line in code.splitlines(keepends=True): + if line.strip() == "": + ret += "\n" + else: + ret += cindent + line + + return ret + + code = lambda: re.sub(find, replace, text, flags=re.DOTALL) + text = code() + + # find rst code block ranges + "redown, redown, redown, redown" + + @dataclass + class Chunk: + is_code: bool + text: str = "" + + chunks: list[Chunk] = [] + + for line in text.splitlines(keepends=True): + in_code_block = chunks and chunks[-1].is_code + is_code_block_directive = re.match(r"^\s*\.\. code-block::", line) + + if not in_code_block and is_code_block_directive: + chunks.append(Chunk(is_code=True)) + elif in_code_block: + indent = len(re.match(r"^(\s*)", line).group(1).rstrip("\n")) + code_block_indent = len( + re.match(r"^(\s*)", chunks[-1].text).group(1).rstrip("\n") + ) + if len(line.strip()) and indent <= code_block_indent: + if is_code_block_directive: + chunks.append(Chunk(is_code=True)) + else: + chunks.append(Chunk(is_code=False)) + + if not chunks: + chunks.append(Chunk(is_code=False)) + chunks[-1].text += line # existing block + + # dont operate on code blocks + for chunk in chunks: + if chunk.is_code: + continue + text = chunk.text + + "redown, redown, redown, redown" + heading = lambda prefix, underfix: re.sub( + rf"(^|\n){prefix} +(.+?)(?:$|\n|\Z)", + lambda m: f"{m.group(1)}{m.group(2)}\n{underfix * len(m.group(2))}\n", + text, + ) + + text = heading("#", "=") + text = heading("##", "-") + text = heading("###", "^") + text = heading("####", "~") + + "redown, redown, redown, redown" + role_links = lambda: ROLE_LINK_RE.sub( + lambda m: f"{m.group('role')}`{(t:=m.group('text'))}{' ' if len(t) else ''}<{m.group('link')}>`", + text, + ) + text = role_links() + + "redown, redown, redown, redown" + links = lambda: LINK_RE.sub( + lambda m: f"`{(t:=m.group('text'))}{' ' if len(t) else ''}<{m.group('link')}>`__", + text, + ) + text = links() + + "redown, redown, redown, redown" + math = lambda: re.sub( + r"(\b|\s|^)\$([^$\n]+)\$(\b|\s|[^\w]|$)", r"\1:math:`\2`\3", text + ) + text = math() + + chunk.text = text + + text = "".join(chunk.text for chunk in chunks) + + return text + + +def setup(app: Sphinx): + @(lambda breadcrumb: app.connect("source-read", breadcrumb)) + def _(app, docname, content): + content[0] = redown(content[0]) + # Path(app.srcdir, docname).with_suffix(".rd").write_text(content[0], encoding="utf8") + + return { + "version": "builtin", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/source/_extensions/wpilib_release.py b/source/_extensions/wpilib_release.py index 0ffd2f951a..e0949c061e 100644 --- a/source/_extensions/wpilib_release.py +++ b/source/_extensions/wpilib_release.py @@ -3,6 +3,7 @@ from typing import Any, Dict, Iterable, List import markdown2 +from bs4 import BeautifulSoup from docutils import nodes from docutils.parsers.rst import directives from docutils.statemachine import StringList @@ -25,31 +26,26 @@ def run(self) -> List[nodes.Node]: release_json = requests.get(release_url) release: Dict = release_json.json() - artifactory_folder = f"https://frcmaven.wpi.edu/api/storage/installer/{version}" - - win_folder = f"{artifactory_folder}/Win64" - win_file = requests.get(win_folder).json()["children"][0]["uri"] - win = requests.get(win_folder + win_file).json() - - win_size = int(win["size"]) - win_download_url = win["uri"].replace("/storage/", "/download/") - - mac_intel_folder = f"{artifactory_folder}/macOS" - mac_intel_file = requests.get(mac_intel_folder).json()["children"][0]["uri"] - mac_intel = requests.get(mac_intel_folder + mac_intel_file).json() - - mac_intel_size = int(mac_intel["size"]) - mac_intel_download_url = mac_intel["uri"].replace("/storage/", "/download/") + cf_folder = f"https://packages.wpilib.workers.dev/installer/{version}" + artifactory_folder = ( + f"https://frcmaven.wpi.edu/api/download/installer/{version}" + ) - mac_arm_folder = f"{artifactory_folder}/macOSArm" - mac_arm_file = requests.get(mac_arm_folder).json()["children"][0]["uri"] - mac_arm = requests.get(mac_arm_folder + mac_arm_file).json() + def get_url_size(osname): + soup = BeautifulSoup( + requests.get(f"{cf_folder}/{osname}/").text, "html.parser" + ) + file = str(soup.find_all("tr")[-1].contents[0].string) + size = str(soup.find_all("tr")[-1].contents[2].string) + url_part = f"/{osname}/{file}" + return url_part, size - mac_arm_size = int(mac_arm["size"]) - mac_arm_download_url = mac_arm["uri"].replace("/storage/", "/download/") + win_download_url_part, win_size = get_url_size("Win64") + mac_intel_download_url_part, mac_intel_size = get_url_size("macOS") + mac_arm_download_url_part, mac_arm_size = get_url_size("macOSArm") # There's something weird going where the hashes are all printed on one line. - # This works aroung that. + # This works around that. release_notes = release["body"].replace("\r", "") r2 = release_notes.split("```") @@ -87,16 +83,23 @@ def run(self) -> List[nodes.Node]: addEventListener('DOMContentLoaded', async (event) => {{ let dlbutton = document.getElementsByClassName("wpilibrelease-dl-button")[0]; let ua = await navigator.userAgentData.getHighEntropyValues(['architecture', 'bitness', 'mobile', 'platform', 'platformVersion']); + let baseUrl; + try {{ + await fetch("https://packages.wpilib.workers.dev/", {{ mode: "no-cors" }}); + baseUrl = "{cf_folder}"; + }} catch (e) {{ + baseUrl = "{artifactory_folder}"; + }} if (ua['platform'] == 'Windows') {{ - dlbutton.href = '{win_download_url}'; - dlbutton.text = 'Download for Windows - {win_size / 1e9 : .2f} GB'; + dlbutton.href = `${{baseUrl}}{win_download_url_part}`; + dlbutton.text = 'Download for Windows - {win_size}'; }} else if (ua['platform'] == 'macOS') {{ if (ua['architecture'] == 'x86') {{ - dlbutton.href = '{mac_intel_download_url}'; - dlbutton.text = 'Download for macOS Intel - {mac_intel_size / 1e9 : .2f} GB'; + dlbutton.href = `${{baseUrl}}{mac_intel_download_url_part}`; + dlbutton.text = 'Download for macOS Intel - {mac_intel_size}'; }} else if (ua['architecture'].includes('arm')) {{ - dlbutton.href = '{mac_arm_download_url}'; - dlbutton.text = 'Download for macOS Arm | Apple Silicon - {mac_arm_size / 1e9 : .2f} GB'; + dlbutton.href = `${{baseUrl}}{mac_arm_download_url_part}`; + dlbutton.text = 'Download for macOS Arm | Apple Silicon - {mac_arm_size}'; }} }} }}); diff --git a/source/_static/css/frc-rtd.css b/source/_static/css/frc-rtd.css index f82d64a946..a68c7a53df 100644 --- a/source/_static/css/frc-rtd.css +++ b/source/_static/css/frc-rtd.css @@ -96,6 +96,10 @@ background: #003974; } +.wy-menu-vertical li button.toctree-expand { + color: #d9d9d9 +} + /* Hide color bar on mobile */ @media screen and (max-width: 768px) { .header-bar { @@ -154,4 +158,8 @@ summary { font-weight: bold; margin: -.5em -.5em 0; padding: .5em; +} + +code.py-class, code.py-func, code.py-meth { + all: unset!important; } \ No newline at end of file diff --git a/source/_static/wpilibDocsLogo_1050.png b/source/_static/wpilibDocsLogo_1050.png index d59dade52e..95f5667edf 100644 Binary files a/source/_static/wpilibDocsLogo_1050.png and b/source/_static/wpilibDocsLogo_1050.png differ diff --git a/source/_static/wpilibDocsLogo_1050.webp b/source/_static/wpilibDocsLogo_1050.webp index 3c3982d7c4..b9fd009eb8 100644 Binary files a/source/_static/wpilibDocsLogo_1050.webp and b/source/_static/wpilibDocsLogo_1050.webp differ diff --git a/source/_static/wpilibDocsLogo_300.png b/source/_static/wpilibDocsLogo_300.png index 4f7d6aadf8..38362b9aed 100644 Binary files a/source/_static/wpilibDocsLogo_300.png and b/source/_static/wpilibDocsLogo_300.png differ diff --git a/source/_static/wpilibDocsLogo_300.webp b/source/_static/wpilibDocsLogo_300.webp index 8c3472da7b..ca1486f7ce 100644 Binary files a/source/_static/wpilibDocsLogo_300.webp and b/source/_static/wpilibDocsLogo_300.webp differ diff --git a/source/_static/wpilibDocsLogo_500.png b/source/_static/wpilibDocsLogo_500.png index b278113681..86426bfad6 100644 Binary files a/source/_static/wpilibDocsLogo_500.png and b/source/_static/wpilibDocsLogo_500.png differ diff --git a/source/_static/wpilibDocsLogo_500.webp b/source/_static/wpilibDocsLogo_500.webp index 9401318243..1f62159fd0 100644 Binary files a/source/_static/wpilibDocsLogo_500.webp and b/source/_static/wpilibDocsLogo_500.webp differ diff --git a/source/_static/wpilibDocsLogo_700.png b/source/_static/wpilibDocsLogo_700.png index f2aa9d11d7..d48b3f5292 100644 Binary files a/source/_static/wpilibDocsLogo_700.png and b/source/_static/wpilibDocsLogo_700.png differ diff --git a/source/_static/wpilibDocsLogo_700.webp b/source/_static/wpilibDocsLogo_700.webp index 38e94b5d18..9161430198 100644 Binary files a/source/_static/wpilibDocsLogo_700.webp and b/source/_static/wpilibDocsLogo_700.webp differ diff --git a/source/assets/FIRSTRobotics_IconVert_RGB_reverse.png b/source/assets/FIRSTRobotics_IconVert_RGB_reverse.png index 11c6914f86..f9f896f142 100644 Binary files a/source/assets/FIRSTRobotics_IconVert_RGB_reverse.png and b/source/assets/FIRSTRobotics_IconVert_RGB_reverse.png differ diff --git a/source/assets/first_design.png b/source/assets/first_design.png index c6af805ef4..52c9cc98eb 100644 Binary files a/source/assets/first_design.png and b/source/assets/first_design.png differ diff --git a/source/assets/wpilibDocsLogo.png b/source/assets/wpilibDocsLogo.png index e1fe0aeeaf..95f5667edf 100644 Binary files a/source/assets/wpilibDocsLogo.png and b/source/assets/wpilibDocsLogo.png differ diff --git a/source/conf.py b/source/conf.py index af43621a6f..35b884c475 100644 --- a/source/conf.py +++ b/source/conf.py @@ -24,7 +24,7 @@ # -- Project information ----------------------------------------------------- project = "FIRST Robotics Competition" -copyright = "2023, FIRST and other WPILib Contributors. This work is licensed under a Creative Commons Attribution 4.0 International License" +copyright = "2024, FIRST and other WPILib Contributors. This work is licensed under a Creative Commons Attribution 4.0 International License" author = "WPILib" version = "2024" @@ -39,6 +39,7 @@ "sphinx.ext.mathjax", "sphinx.ext.todo", "sphinx.ext.autosectionlabel", + "sphinx.ext.intersphinx", "sphinxcontrib.rsvgconverter", "sphinxext.delta", "sphinxext.opengraph", @@ -55,6 +56,7 @@ "sphinx.ext.viewcode", "sphinx-prompt", "sphinx_toolbox.collapse", + "sphinx_copybutton", ] local_extensions = [ @@ -63,6 +65,8 @@ "_extensions.localization", "_extensions.controls_js_sim", "_extensions.wpilib_release", + "_extensions.default_latex_image_settings", + "_extensions.redown", ] extensions += local_extensions @@ -108,9 +112,7 @@ # Configure OpenGraph support ogp_site_url = "https://docs.wpilib.org/en/stable/" ogp_site_name = "FIRST Robotics Competition Documentation" -ogp_image = ( - "https://raw.githubusercontent.com/wpilibsuite/branding/main/png/wpilib-128.png" -) +ogp_image = "https://raw.githubusercontent.com/wpilibsuite/branding/main/export/png/wpilib-icon-256.png" # Configure photofinish ci mode photofinish_ci_only = True @@ -127,6 +129,7 @@ # Enable hover content on glossary term hoverxref_roles = ["term"] +hoverxref_role_types = {"term": "tooltip"} # TODO Directives omit a warning todo_emit_warnings = False @@ -135,21 +138,29 @@ todo_include_todos = False # Disable following anchors in URLS for linkcheck -linkcheck_anchors = False +linkcheck_anchors = True # Linkcheck Exclusions linkcheck_ignore = [ - r".*kauailabs.com.*", + # r".*kauailabs.com.*", r".*wpilibpi.local.*", - r".*andymark.com.*", - r".*ti.com/lit/an/spma033a/spma033a.pdf.*", - r".*java.com/en/download/help/locale.xml.*", + # r".*andymark.com.*", + # r".*ti.com/lit/an/spma033a/spma033a.pdf.*", + r".*java.com.*", r".*playingwithfusion.com/contactus.php.*", - r".*github.com/wpilibsuite/BetaTest.*", - r".*vexrobotics.com/docs/.*", + # r".*vexrobotics.com/docs/.*", r".*forums.firstinspires.org.*", r".*digikey.com.*", r".*chiefdelphi.com.*", + r".*raspberrypi.com.*", + r".*stackoverflow.com.*", + r".*allaboutcircuits.com.*", + r".*knowledge.ni.com.*", +] + +linkcheck_anchors_ignore_for_url = [ + r".*github.com.*", + r".*ni.com/en/support/downloads/drivers/download.frc-game-tools.html.*", ] # Sets linkcheck timeout in seconds @@ -158,7 +169,7 @@ linkcheck_workers = 1 # Specify a standard user agent, as Sphinx default is blocked on some sites -user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" +user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" # Autosection labels prefix document path and filename autosectionlabel_prefix_document = True @@ -171,8 +182,7 @@ # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [ "docs/yearly-overview/2020-Game-Data.rst", - "docs/software/wpilib-tools/axon/**", - "docs/software/vision-processing/grip/**", + "docs/beta/*", ] # Specify the master doc file, AKA our homepage @@ -217,7 +227,7 @@ html_baseurl = "https://docs.wpilib.org/en/stable/" html_theme_options = { - "collapse_navigation": True, + "collapse_navigation": False, "sticky_navigation": False, "titles_only": True, } @@ -249,6 +259,14 @@ def setup(app): app.add_js_file("js/version-2014.js") +html_context = { + "display_github": True, # Integrate GitHub + "github_user": "wpilibsuite", # Username + "github_repo": "frc-docs", # Repo name + "github_version": "main", # Version + "conf_py_path": "/source/", # Path in the checkout to the docs root +} + # -- Options for latex generation -------------------------------------------- latex_engine = "xelatex" @@ -323,3 +341,19 @@ def new_send(self, data): http.client.HTTPConnection.send = new_send + +intersphinx_mapping = { + "robotpy": ("https://robotpy.readthedocs.io/projects/robotpy/en/stable/", None), + "commands2": ( + "https://robotpy.readthedocs.io/projects/commands-v2/en/stable/", + None, + ), +} + +# We recommend adding the following config value. +# Sphinx defaults to automatically resolve *unresolved* labels using all your Intersphinx mappings. +# This behavior has unintended side-effects, namely that documentations local references can +# suddenly resolve to an external location. +# See also: +# https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#confval-intersphinx_disabled_reftypes +intersphinx_disabled_reftypes = ["*"] diff --git a/source/docs/beta/beta-getting-started/images/accessing-the-beta-project/login.png b/source/docs/beta/beta-getting-started/images/accessing-the-beta-project/login.png index 5b7796ebf4..68dbd02dbf 100644 Binary files a/source/docs/beta/beta-getting-started/images/accessing-the-beta-project/login.png and b/source/docs/beta/beta-getting-started/images/accessing-the-beta-project/login.png differ diff --git a/source/docs/beta/beta-getting-started/images/accessing-the-beta-project/project-home.png b/source/docs/beta/beta-getting-started/images/accessing-the-beta-project/project-home.png index ab26c7b3f0..a4ac087ba8 100644 Binary files a/source/docs/beta/beta-getting-started/images/accessing-the-beta-project/project-home.png and b/source/docs/beta/beta-getting-started/images/accessing-the-beta-project/project-home.png differ diff --git a/source/docs/beta/beta-getting-started/images/monitoring-via-email-notifications/subscribe.png b/source/docs/beta/beta-getting-started/images/monitoring-via-email-notifications/subscribe.png index a5a6ea6e63..0d610525d4 100644 Binary files a/source/docs/beta/beta-getting-started/images/monitoring-via-email-notifications/subscribe.png and b/source/docs/beta/beta-getting-started/images/monitoring-via-email-notifications/subscribe.png differ diff --git a/source/docs/beta/beta-getting-started/images/monitoring-via-email-notifications/watch.png b/source/docs/beta/beta-getting-started/images/monitoring-via-email-notifications/watch.png index 42a3c6b087..bc11f6a263 100644 Binary files a/source/docs/beta/beta-getting-started/images/monitoring-via-email-notifications/watch.png and b/source/docs/beta/beta-getting-started/images/monitoring-via-email-notifications/watch.png differ diff --git a/source/docs/beta/beta-getting-started/images/reporting-progress/forum-summary.png b/source/docs/beta/beta-getting-started/images/reporting-progress/forum-summary.png index 75b7c3e491..3b4430429f 100644 Binary files a/source/docs/beta/beta-getting-started/images/reporting-progress/forum-summary.png and b/source/docs/beta/beta-getting-started/images/reporting-progress/forum-summary.png differ diff --git a/source/docs/beta/beta-getting-started/images/reporting-progress/post-report.png b/source/docs/beta/beta-getting-started/images/reporting-progress/post-report.png index 7252fa9513..dc6c1fa274 100644 Binary files a/source/docs/beta/beta-getting-started/images/reporting-progress/post-report.png and b/source/docs/beta/beta-getting-started/images/reporting-progress/post-report.png differ diff --git a/source/docs/beta/beta-getting-started/images/reporting-progress/task-reports.png b/source/docs/beta/beta-getting-started/images/reporting-progress/task-reports.png index 723a93a0ad..c31335e7c7 100644 Binary files a/source/docs/beta/beta-getting-started/images/reporting-progress/task-reports.png and b/source/docs/beta/beta-getting-started/images/reporting-progress/task-reports.png differ diff --git a/source/docs/beta/beta-getting-started/images/trackers-reporting-bugs/github-issues.png b/source/docs/beta/beta-getting-started/images/trackers-reporting-bugs/github-issues.png index d2eb45899b..97bf18f839 100644 Binary files a/source/docs/beta/beta-getting-started/images/trackers-reporting-bugs/github-issues.png and b/source/docs/beta/beta-getting-started/images/trackers-reporting-bugs/github-issues.png differ diff --git a/source/docs/beta/beta-getting-started/images/trackers-reporting-bugs/report-bug.png b/source/docs/beta/beta-getting-started/images/trackers-reporting-bugs/report-bug.png index 0204623a09..30f8651bdd 100644 Binary files a/source/docs/beta/beta-getting-started/images/trackers-reporting-bugs/report-bug.png and b/source/docs/beta/beta-getting-started/images/trackers-reporting-bugs/report-bug.png differ diff --git a/source/docs/contributing/frc-docs/build-instructions.rst b/source/docs/contributing/frc-docs/build-instructions.rst index 910db8401f..4753a57b2d 100644 --- a/source/docs/contributing/frc-docs/build-instructions.rst +++ b/source/docs/contributing/frc-docs/build-instructions.rst @@ -1,35 +1,31 @@ -Build Instructions -================== +# Build Instructions -This document contains information on how to build the HTML, PDF, and EPUB versions of the frc-docs site. frc-docs uses Sphinx as the documentation generator. This document also assumes you have basic knowledge of `Git `__ and console commands. +This document contains information on how to build the HTML, PDF, and EPUB versions of the frc-docs site. frc-docs uses Sphinx as the documentation generator. This document also assumes you have basic knowledge of [Git](https://git-scm.com/) and console commands. -Prerequisites -------------- +## Prerequisites -Ensure that `Git `__ is installed and that the frc-docs repository is cloned by using ``git clone https://github.com/wpilibsuite/frc-docs.git``. +Ensure that [Git](https://git-scm.com/) is installed and that the frc-docs repository is cloned by using ``git clone https://github.com/wpilibsuite/frc-docs.git``. -Text Editors / IDE -^^^^^^^^^^^^^^^^^^ +### Text Editors / IDE -For development, we recommend that you use VS Code along with the `reStructuredText extension `_. However, any text editor will work. +For development, we recommend that you use VS Code along with the [reStructuredText extension](https://marketplace.visualstudio.com/items?itemName=lextudio.restructuredtext). However, any text editor will work. By default, the reStructuredText extension enables linting with all doc8 features enabled. As frc-docs does not follow the line length lint, add the following to your VS Code ``settings.json`` to disable line length linting. -.. code-block:: json +```json +"restructuredtext.linter.doc8.extraArgs": [ + "--ignore D001" +] +``` - "restructuredtext.linter.doc8.extraArgs": [ - "--ignore D001" - ] - -Windows -^^^^^^^ +### Windows .. note:: MikTeX and ``rsvg-convert`` are not required for building HTML, they are only required for Windows PDF builds. -- `Python 3.9 `__ -- `Perl `__ -- `MiKTeX `__ (Only needed for PDF builds) -- `rsvg-convert `__ (Only needed for PDF builds) +- [Python 3.9](https://www.python.org/downloads/) +- [Perl](https://strawberryperl.com/) +- [MiKTeX](https://miktex.org/download) (Only needed for PDF builds) +- [rsvg-convert](https://community.chocolatey.org/packages/rsvg-convert) (Only needed for PDF builds) Ensure that Python is in your Path by selecting the **Add Python to PATH** toggle when installing Python. @@ -40,56 +36,49 @@ Once Python is installed, open up Powershell. Then navigate to the frc-docs dire Install the missing MikTex packages by navigating to the frc-docs directory, then running the following command from Powershell: ``miktex --verbose packages require --package-id-file miktex-packages.txt`` -Linux (Ubuntu) -^^^^^^^^^^^^^^ - -.. code-block:: console +### Linux (Ubuntu) - $ sudo apt update - $ sudo apt install python3 python3-pip - $ python3 -m pip install -U pip setuptools wheel - $ python3 -m pip install -r source/requirements.txt - $ sudo apt install -y texlive-latex-recommended texlive-fonts-recommended texlive-latex-extra latexmk texlive-lang-greek texlive-luatex texlive-xetex texlive-fonts-extra dvipng librsvg2-bin +```console +$ sudo apt update +$ sudo apt install python3 python3-pip +$ python3 -m pip install -U pip setuptools wheel +$ python3 -m pip install -r source/requirements.txt +$ sudo apt install -y texlive-latex-recommended texlive-fonts-recommended texlive-latex-extra latexmk texlive-lang-greek texlive-luatex texlive-xetex texlive-fonts-extra dvipng librsvg2-bin +``` -Building --------- +## Building Open up a Powershell Window or terminal and navigate to the frc-docs directory that was cloned. -.. code-block:: console - - PS > cd "%USERPROFILE%\Documents" - PS C:\Users\Example\Documents> git clone https://github.com/wpilibsuite/frc-docs.git - Cloning into 'frc-docs'... - remote: Enumerating objects: 217, done. - remote: Counting objects: 100% (217/217), done. - remote: Compressing objects: 100% (196/196), done. - remote: Total 2587 (delta 50), reused 68 (delta 21), pack-reused 2370 - Receiving objects: 100% (2587/2587), 42.68MiB | 20.32 MiB/s, done. - Receiving deltas: 100% (1138/1138), done/ - PS C:\Users\Example\Documents> cd frc-docs - PS C:\Users\Example\Documents\frc-docs> - +```console +PS > cd "%USERPROFILE%\Documents" +PS C:\Users\Example\Documents> git clone https://github.com/wpilibsuite/frc-docs.git +Cloning into 'frc-docs'... +remote: Enumerating objects: 217, done. +remote: Counting objects: 100% (217/217), done. +remote: Compressing objects: 100% (196/196), done. +remote: Total 2587 (delta 50), reused 68 (delta 21), pack-reused 2370 +Receiving objects: 100% (2587/2587), 42.68MiB | 20.32 MiB/s, done. +Receiving deltas: 100% (1138/1138), done/ +PS C:\Users\Example\Documents> cd frc-docs +PS C:\Users\Example\Documents\frc-docs> +``` -Lint Check -^^^^^^^^^^ +### Lint Check -.. note:: Lint Check will not check line endings on Windows due to a bug with line endings. See `this issue `__ for more information. +.. note:: Lint Check will not check line endings on Windows due to a bug with line endings. See [this issue](https://bugs.launchpad.net/doc8/+bug/1756704) for more information. It's encouraged to check any changes you make with the linter. This **will** fail the buildbot if it does not pass. To check, run ``.\make lint`` -Link Check -^^^^^^^^^^ +### Link Check The link checker makes sure that all links in the documentation resolve. This **will** fail the buildbot if it does not pass. To check, run ``.\make linkcheck`` -Image Size Check -^^^^^^^^^^^^^^^^ +### Image Size Check Please run ``.\make sizecheck`` to verify that all images are below 500KB. This check **will** fail CI if it fails. Exclusions are allowed on a case by case basis and are added to the ``IMAGE_SIZE_EXCLUSIONS`` list in the configuration file. -Redirect Check -^^^^^^^^^^^^^^ +### Redirect Check Files that have been moved or renamed must have their new location (or replaced with 404) in the ``redirects.txt`` file in ``source``. @@ -99,48 +88,40 @@ The redirect writer will automatically add renamed/moved files to the redirects The redirect checker makes sure that there are valid redirects for all files. This **will** fail the buildbot if it does not pass. To check, run ``.\make rediraffecheckdiff`` to verify all files are redirected. Additionally, an HTML build may need to be ran to ensure that all files redirect properly. -Building HTML -^^^^^^^^^^^^^ +### Building HTML Type the command ``.\make html`` to generate HTML content. The content is located in the ``build/html`` directory at the root of the repository. -Building PDF ------------- +## Building PDF .. warning:: Please note that PDF build on Windows may result in distorted images for SVG content. This is due to a lack of librsvg2-bin support on Windows. Type the command ``.\make latexpdf`` to generate PDF content. The PDF is located in the ``build/latex`` directory at the root of the repository. -Building EPUB -------------- +## Building EPUB Type the command ``.\make epub`` to generate EPUB content. The EPUB is located in the ``build/epub`` directory at the root of the repository. -Adding Python Third-Party libraries ------------------------------------ +## Adding Python Third-Party libraries .. important:: After modifying frc-docs dependencies in any way, ``requirements.txt`` must be regenerated by running ``poetry export -f requirements.txt --output source/requirements.txt --without-hashes`` from the root of the repo. -frc-docs uses `Poetry `__ to manage its dependencies to make sure builds are reproducible. +frc-docs uses [Poetry](https://python-poetry.org/) to manage its dependencies to make sure builds are reproducible. .. note:: Poetry is **not** required to build and contribute to frc-docs content. It is *only* used for dependency management. -Installing Poetry -^^^^^^^^^^^^^^^^^ +### Installing Poetry Ensure that Poetry is installed. Run the following command: ``pip install poetry``. -Adding a Dependency -^^^^^^^^^^^^^^^^^^^ +### Adding a Dependency Add the dependency to the ``[tool.poetry.dependencies]`` section of ``pyproject.toml``. Make sure to specify an exact version. Then, run the following command: ``poetry lock --no-update``. -Updating a Top-Level Dependency -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Updating a Top-Level Dependency Update the dependency's version in the ``[tool.poetry.dependencies]`` section of ``pyproject.toml``. Then, run the following command: ``poetry lock --no-update``. -Updating Hidden Dependencies -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Updating Hidden Dependencies Run the following command: ``poetry lock``. diff --git a/source/docs/contributing/frc-docs/contribution-guidelines.rst b/source/docs/contributing/frc-docs/contribution-guidelines.rst index fa82af88d0..a1c67bc2f9 100644 --- a/source/docs/contributing/frc-docs/contribution-guidelines.rst +++ b/source/docs/contributing/frc-docs/contribution-guidelines.rst @@ -1,14 +1,12 @@ .. include:: -Contribution Guidelines -======================= +# Contribution Guidelines -Welcome to the contribution guidelines for the frc-docs project. If you are unfamiliar to writing in the reStructuredText format, please read up on it `here `__. +Welcome to the contribution guidelines for the frc-docs project. If you are unfamiliar to writing in the reStructuredText format, please read up on it [here](https://sphinx-tutorial.readthedocs.io/cheatsheet/). -.. important:: *FIRST*\ |reg| retains all rights to documentation and images provided. Credit for articles/updates will be in the `GitHub commit history. `_ +.. important:: *FIRST*\ |reg| retains all rights to documentation and images provided. Credit for articles/updates will be in the [GitHub commit history.](https://github.com/wpilibsuite/frc-docs/graphs/commit-activity) -Mission Statement ------------------ +## Mission Statement 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. @@ -20,8 +18,7 @@ These docs serve to provide a learning ground for all *FIRST* Robotics Competiti Please see the :ref:`docs/contributing/frc-docs/style-guide:Style Guide` for information on styling your documentation. -Release Process ---------------- +## Release Process frc-docs uses a special release process for handling the main site ``/stable/`` and the development site ``/latest/``. This flow is detailed below. @@ -42,37 +39,32 @@ Off-Season: - Only updates ``/latest/`` on the documentation site -Creating a PR -------------- +## Creating a PR -PRs should be made to the `frc-docs `__ repo on GitHub. They should point to the ``main`` branch and *not* ``stable``. +PRs should be made to the [frc-docs](https://github.com/wpilibsuite/frc-docs) repo on GitHub. They should point to the ``main`` branch and *not* ``stable``. -Creating New Content --------------------- +## Creating New Content -Thanks for contributing to the `frc-docs `__ project! There are a couple things you should know before getting started! +Thanks for contributing to the [frc-docs](https://github.com/wpilibsuite/frc-docs) project! There are a couple things you should know before getting started! -Where to place articles? -^^^^^^^^^^^^^^^^^^^^^^^^ +### Where to place articles? The location for new articles can be a pretty opinionated subject. Standalone articles that fall well into an already subject category should be placed into mentioned subject category (documentation on something about simulation should be placed into the simulation section). However, things can get pretty complicated when an article combines or references two separate existing sections. In this situation, we advise the author to open an issue on the repository to get discussion going before opening the PR. .. note:: All new articles will undergo a review process before being merged into the repository. This review process will be done by members of the WPILib team. New Articles must be on official *FIRST* supported Software and Hardware. Documentation on unofficial libraries or sensors *will not* be accepted. This process may take some time to review, please be patient. -Where to place sections? -^^^^^^^^^^^^^^^^^^^^^^^^ +### Where to place sections? -Sections are quite tricky, as they contain a large amount of content. We advise the author to open an `issue `__ to gather discussion before opening up a PR. +Sections are quite tricky, as they contain a large amount of content. We advise the author to open an [issue](https://github.com/wpilibsuite/frc-docs/issues) to gather discussion before opening up a PR. -Linking Other Articles -^^^^^^^^^^^^^^^^^^^^^^ +### Linking Other Articles In the instance that the article references content that is described in another article, the author should make best effort to link to that article upon the first reference. Imagine we have the following content in a drivetrain tutorial: -.. code-block:: text - - Teams may often need to test their robot code outside of a competition. :ref:`Simulation ` is a means to achieve this. Simulation offers teams a way to unit test and test their robot code without ever needing a robot. +```text +Teams may often need to test their robot code outside of a competition. :ref:`Simulation ` is a means to achieve this. Simulation offers teams a way to unit test and test their robot code without ever needing a robot. +``` Notice how only the first instance of Simulation is linked. This is the structure the author should follow. There are times where a linked article has different topics of content. If you reference the different types of content in the article, you should link to each new reference once (except in situations where the author has deemed it appropriate otherwise). diff --git a/source/docs/contributing/frc-docs/drawio-saving-instructions.rst b/source/docs/contributing/frc-docs/drawio-saving-instructions.rst index 22a0842292..325ad1c56c 100644 --- a/source/docs/contributing/frc-docs/drawio-saving-instructions.rst +++ b/source/docs/contributing/frc-docs/drawio-saving-instructions.rst @@ -1,14 +1,13 @@ -Draw.io Saving Instructions -=========================== +# Draw.io Saving Instructions .. warning:: Make sure you don't modify any file that is in a ``diagrams`` folder, or ends in ``.drawio.svg`` in any program other than draw.io; otherwise you might risk breaking the metadata of the file, making it uneditable. -Draw.io (also known as `diagrams.net `__) are supported when saved as ``svg`` files, with embedded XML metadata for the draw.io source file (normally stored as ``.drawio``). +Draw.io (also known as [diagrams.net](https://app.diagrams.net/)) are supported when saved as ``svg`` files, with embedded XML metadata for the draw.io source file (normally stored as ``.drawio``). This allows these images to both act as source files for the diagrams that can be edited in the future, and be rendered as normal ``svg`` files. There are a few methods to save a diagram with the embedded metadata, but using the export menu is preferred because it allows us to embed any images in the diagram; otherwise they might not render properly on the docs. -This method is applicable to both draw.io desktop and the web version at `diagrams.net `__. +This method is applicable to both draw.io desktop and the web version at [diagrams.net](https://app.diagrams.net/). To export go to ``File - Export as - SVG...``. Make sure ``Include a copy of my diagram`` is enabled to embed the diagram metadata, and that ``Embed Images`` is enabled so image files in the diagram are embedded so they render in the docs. Additionally, mark the ``Transparent Background`` option to make sure the background is displayed correctly. diff --git a/source/docs/contributing/frc-docs/images/python-path.png b/source/docs/contributing/frc-docs/images/python-path.png index a5e32e1755..a1fd9972ed 100644 Binary files a/source/docs/contributing/frc-docs/images/python-path.png and b/source/docs/contributing/frc-docs/images/python-path.png differ diff --git a/source/docs/contributing/frc-docs/images/svg-export.png b/source/docs/contributing/frc-docs/images/svg-export.png index 0f781297c1..54cfd75c56 100644 Binary files a/source/docs/contributing/frc-docs/images/svg-export.png and b/source/docs/contributing/frc-docs/images/svg-export.png differ diff --git a/source/docs/contributing/frc-docs/index.rst b/source/docs/contributing/frc-docs/index.rst index fccb12eb19..85baee1e81 100644 --- a/source/docs/contributing/frc-docs/index.rst +++ b/source/docs/contributing/frc-docs/index.rst @@ -1,5 +1,4 @@ -Contributing to frc-docs -======================== +# Contributing to frc-docs .. toctree:: :maxdepth: 1 diff --git a/source/docs/contributing/frc-docs/style-guide.rst b/source/docs/contributing/frc-docs/style-guide.rst index b3ab0c01db..ae42c302cc 100644 --- a/source/docs/contributing/frc-docs/style-guide.rst +++ b/source/docs/contributing/frc-docs/style-guide.rst @@ -1,12 +1,10 @@ .. include:: -Style Guide -=========== +# Style Guide -This document contains the various RST/Sphinx specific guidelines for the frc-docs project. For guidelines related to the various WPILib code projects, see `the WPILib GitHub `__ +This document contains the various MD and RST/Sphinx specific guidelines for the frc-docs project. For guidelines related to the various WPILib code projects, see [the WPILib GitHub](https://github.com/wpilibsuite/styleguide) -Filenames ---------- +## Filenames Use only lowercase alphanumeric characters and ``-`` (minus) symbol. @@ -14,10 +12,9 @@ For documents that will have an identical software/hardware name, append "Hardwa Suffix filenames with the ``.rst`` extension. -.. note:: If you are having issues editing files with the ``.rst`` extension, the recommended text editor is VS Code with the rST extension. +.. note:: If you are having issues editing files with the ``.rst`` extension, the recommended text editor is VS Code with the [reStructuredText extension](https://marketplace.visualstudio.com/items?itemName=lextudio.restructuredtext). -Text ----- +## Text All text content should be on the same line. If you need readability, use the word-wrap function of your editor. @@ -32,163 +29,150 @@ Use the following case for these terms: - Linux (not linux) - Java (not java) -Use the ASCII character set for English text. For special characters (e.g. Greek symbols) use the `standard character entity sets `_. +Use the ASCII character set for English text. For special characters (e.g. Greek symbols) use the [standard character entity sets](https://docutils.sourceforge.io/docs/ref/rst/definitions.html#character-entity-sets). -Use ``.. math::`` for standalone equations and ``:math:`` for inline equations. A useful LaTeX equation cheat sheet can be found `here `_. +Use ``.. math::`` for standalone equations and ``:math:`` for inline equations. A useful LaTeX equation cheat sheet can be found [here](https://www.reed.edu/academic_support/pdfs/qskills/latexcheatsheet.pdf). Use literals for filenames, function, and variable names. -Use of the registered trademarks *FIRST*\ |reg| and FRC\ |reg| should follow the Policy from `this page `__. Specifically, where possible (i.e. not nested inside other markup or in a document title), the first use of the trademarks should have the |reg| symbol and all instances of *FIRST* should be italicized. The |reg| symbol can be added by using ``.. include:: `` at the top of the document and then using ``*FIRST*\ |reg|`` or ``FRC\ |reg|``. +Use of the registered trademarks *FIRST*\ |reg| and FRC\ |reg| should follow the Policy from [this page](https://www.firstinspires.org/brand). Specifically, where possible (i.e. not nested inside other markup or in a document title), the first use of the trademarks should have the |reg| symbol and all instances of *FIRST* should be italicized. The |reg| symbol can be added by using ``.. include:: `` at the top of the document and then using ``*FIRST*\ |reg|`` or ``FRC\ |reg|``. Commonly used terms should be added to the :ref:`docs/software/frc-glossary:FRC Glossary`. You can reference items in the glossary by using ``:term:`deprecated```. -Whitespace ----------- +## Whitespace -Indentation -^^^^^^^^^^^ +### Indentation Indentation should *always* match the previous level of indentation *unless* you are creating a new content block. Indentation of content directives as new line ``.. toctree::`` should be `3` spaces. -Blank Lines -^^^^^^^^^^^ +### Blank Lines There should be ``1`` blank line separating basic text blocks and section titles. There *should* be ``1`` blank line separating text blocks *and* content directives. -Interior Whitespace -^^^^^^^^^^^^^^^^^^^ +### Interior Whitespace Use one space between sentences. -Headings --------- +## Headings -Headings should be in the following structure. Heading underlines should match the same number of characters as the heading itself. +Headings should be in the following structure. -1. ``=`` for document titles. *Do not* use this more than *once* per article. -2. ``-`` for document sections -3. ``^`` for document sub-sections -4. ``~`` for document sub-sub-sections +1. ``#`` for document titles. *Do not* use this more than *once* per article. +2. ``##`` for document sections +3. ``###`` for document sub-sections +4. ``####`` for document sub-sub-sections 5. If you need to use any lower levels of structure, you're doing things wrong. Use title case for headings. -Lists ------ +## Lists Lists should have a new line in between each indent level. The highest indent should have ``0`` indentation, and subsequent sublists should have an indentation starting at the first character of the previous indentation. -.. code-block:: ReST +```ReST +- Block one +- Block two +- Block three + - Sub 1 + - Sub 2 +- Block four +``` - - Block one - - Block two - - Block three +## Code blocks - - Sub 1 - - Sub 2 - - - Block four +All code blocks should have a language specified. -Code blocks ------------ +````md +```python +print("Hello!") -All code blocks should have a language specified. +import antigravity +``` +```` 1. Exception: Content where formatting must be preserved and has no language. Instead use ``text``. -Follow the `WPILib style guide `_ for C++ and Java example code. For example, use two spaces for indentation in C++ and Java. +Follow the [WPILib style guide](https://github.com/wpilibsuite/styleguide/) for C++ and Java example code. For example, use two spaces for indentation in C++ and Java. -RLI (Remote Literal Include) ----------------------------- +## RLI (Remote Literal Include) When possible, instead of using code blocks, an RLI should be used. This pulls code lines directly from GitHub, most commonly using the example programs. This automatically keeps the code up to date with any changes that are made. The format of an RLI is: -.. code-block:: ReST - - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java - :language: java - :lines: 44-61 - :linenos: - :lineno-start: 44 - - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/RamseteController/cpp/Robot.cpp - :language: cpp - :lines: 18-30 - :linenos: - :lineno-start: 18 +```ReST +.. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java + :language: java + :lines: 44-61 + :linenos: + :lineno-start: 44 +.. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/RamseteController/cpp/Robot.cpp + :language: c++ + :lines: 18-30 + :linenos: + :lineno-start: 18 +``` Make sure to link to the raw version of the file on GitHub. There is a handy ``Raw`` button in the top right corner of the page. -Tabs ----- -To create code tabs in an article, you can use the ``.. tab-set-code::`` directive. You can use ``code-block`` and ``rli`` directives inside. The format is: +.. note:: RLIs should use a tag instead of main to ensure the documentation isn't broken the next time there is a change to the RLIed code. If a tag hasn't been created, use the full (40 character) commit hash. -.. code-block:: ReST - - .. tab-set-code:: - - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java - :language: java - :lines: 44-61 - :linenos: - :lineno-start: 44 - - - .. code-block:: cpp - // Start the timer. - m_timer.Start(); - - // Send Field2d to SmartDashboard. - frc::SmartDashboard::PutData(&m_field); - - // Reset the drivetrain's odometry to the starting pose of the trajectory. - m_drive.ResetOdometry(m_trajectory.InitialPose()); - - // Send our generated trajectory to Field2d. - m_field.GetObject("traj")->SetTrajectory(m_trajectory); - } +## Tabs +To create code tabs in an article, you can use the ``.. tab-set-code::`` directive. You can use md style \`\`\` codeblocks and ``rli`` directives inside. The format is: +````ReST +.. tab-set-code:: + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java + :language: java + :lines: 44-61 + :linenos: + :lineno-start: 44 + + ```c++ + // Start the timer. + m_timer.Start(); + // Send Field2d to SmartDashboard. + frc::SmartDashboard::PutData(&m_field); + // Reset the drivetrain's odometry to the starting pose of the trajectory. + m_drive.ResetOdometry(m_trajectory.InitialPose()); + // Send our generated trajectory to Field2d. + m_field.GetObject("traj")->SetTrajectory(m_trajectory); + ``` +```` If you need to use more than one tab per language, multiple RLIs per language, or text tabs, you can use the ``.. tab-set::`` and ``.. tab-item::`` directive. The format is: -.. code-block:: ReST - - .. tab-set:: - - ..tab-item:: Title - :sync: sync-id - - Content +```ReST +.. tab-set:: + .. tab-item:: Title + :sync: sync-id + Content +``` This example uses the sync argument to allow all of the tabs with the same key to be synced together. This means that when you click on a tab, all of the tabs with the same key will open. If you have a mix of ``tab-set`` and ``tab-set-code`` directives on a page, you can sync them by setting the sync id on the ``tab-item`` directives to ``tabcode-LANGUAGE``. For example, a java tab would have a sync id of ``tabcode-java``. -Admonitions ------------ +## Admonitions -Admonitions (list `here `__) should have their text on the same line as the admonition itself. There are exceptions to this rule, however, when having multiple sections of content inside of an admonition. Generally having multiple sections of content inside of an admonition is not recommended. +Admonitions (list [here](https://docutils.sourceforge.io/docs/ref/rst/directives.html#admonitions)) should have their text on the same line as the admonition itself. There are exceptions to this rule, however, when having multiple sections of content inside of an admonition. Generally having multiple sections of content inside of an admonition is not recommended. Use -.. code-block:: ReST - - .. warning:: This is a warning! +```ReST +.. warning:: This is a warning! +``` NOT -.. code-block:: ReST +```ReST +.. warning:: + This is a warning! +``` - .. warning:: - This is a warning! +## Links -Links ------ - -Internal Links -^^^^^^^^^^^^^^ +### Internal Links Internal Links will be auto-generated based on the ReStructuredText filename and section title. @@ -202,37 +186,47 @@ Use this format to reference the top-level of a document. You can use relative p When using ``:ref:`` or ``:doc:`` you may customize the displayed text by surrounding the actual link with angle brackets ``<>`` and adding the custom text between the first backtick ````` and the first angle bracket ``<``. For example ``:ref:`custom text ``` renders to :ref:`custom text `. -External Links -^^^^^^^^^^^^^^ - -It is preferred to format external links as anonymous hyperlinks. The important thing to note is the **two** underscores appending the text. In the situation that only one underscore is used, issues may arise when compiling the document. +### External Links -.. code-block:: ReST +It is preferred to format external links as md style hyperlinks. - Hi there, `this is a link `__ and it's pretty cool! +```md +Hi there, [this is a link](https://example.com) and it's pretty cool! +``` However, in some cases where the same link must be referenced multiple times, the syntax below is accepted. -.. code-block:: ReST +```ReST +Hi there, `this is a link`_ and it's pretty cool! +.. _this is a link: https://example.com +``` + +### Python API Links + +Links to the RobotPY API documentation should use the following sphinx [python syntax](https://www.sphinx-doc.org/en/master/usage/domains/python.html) (example linking to the DriverStation API docs). + +```ReST +:external:py:class:`Python ` +``` - Hi there, `this is a link`_ and it's pretty cool! +This expands to be equivalent to: - .. _this is a link: https://example.com +```ReST +[Python](https://robotpy.readthedocs.io/projects/robotpy/en/stable/wpilib/DriverStation.html#wpilib.DriverStation) +``` -Images ------- +## Images Images should be created with ``1`` new line separating content and directive. All images (including vectors) should be less than ``500`` kilobytes in size. Please make use of a smaller resolution and more efficient compression algorithms. -.. code-block:: ReST +```ReST +.. image:: images/my-article/my-image.png + :alt: Always add alt text here describing the image. +``` - .. image:: images/my-article/my-image.png - :alt: Always add alt text here describing the image. - -Image Files -^^^^^^^^^^^ +### Image Files Image files should be stored in the document directory, sub-directory of ``document-name/images``. @@ -242,13 +236,12 @@ They should be of the ``.png`` or ``.jpg`` image extension. ``.gif`` is unaccept .. note:: Accessibility is important! Images should be marked with a ``:alt:`` directive. - .. code-block:: ReST - - .. image:: images/my-document/my-image.png - :alt: An example image + ```ReST + .. image:: images/my-document/my-image.png + :alt: An example image + ``` -Vector Images -^^^^^^^^^^^^^ +### Vector Images SVG files are supported through the ``svg2pdfconverter`` Sphinx extension. @@ -256,25 +249,23 @@ Simply use them as you would with any other image. .. note:: Ensure that any embedded images in the vector do not bloat the vector to exceed the 500KB limit. -.. code-block:: ReST - - .. image:: images/my-document/my-image.svg - :alt: Always add alt text here describing the image. +```ReST +.. image:: images/my-document/my-image.svg + :alt: Always add alt text here describing the image. +``` -Draw.io Diagrams -^^^^^^^^^^^^^^^^ +### Draw.io Diagrams -Draw.io (also known as `diagrams.net `__) diagrams are supported through ``svg`` files with embedded ``.drawio`` metadata, allowing the ``svg`` file to act as a source file of the diagrams, and to be rendered like a normal vector graphics file. +Draw.io (also known as [diagrams.net](https://app.diagrams.net/)) diagrams are supported through ``svg`` files with embedded ``.drawio`` metadata, allowing the ``svg`` file to act as a source file of the diagrams, and to be rendered like a normal vector graphics file. Simply use them like you would any other vector image, or any other image. -.. code-block:: ReST +```ReST +.. image:: diagrams/my-document/diagram-1.drawio.svg + :alt: Always add alt text here describing the image. +``` - .. image:: diagrams/my-document/diagram-1.drawio.svg - :alt: Always add alt text here describing the image. - -Draw.io Files -~~~~~~~~~~~~~ +#### Draw.io Files Draw.io files follow almost the same naming scheme as normal images. To keep track of files that have the embedded ``.drawio`` metadata, append a ``.drawio`` to the end of the file name, before the extension, meaning the name of the file should be ``document-title-1.drawio.svg`` and so on. Additionally, diagrams should be stored in the document directory in a sub-folder named ``diagrams``. @@ -282,48 +273,40 @@ For the specifics of saving a diagram as a ``.svg`` with metadata, take a look a .. warning:: Make sure you don't modify any file that is in a ``diagrams`` folder, or ends in ``.drawio.svg`` in any program other than draw.io, otherwise you might risk breaking the metadata of the file, making it uneditable. -File Extensions ---------------- +## File Extensions File extensions should use code formatting. For example, use: -.. code-block:: text - - ``.png`` +```text +``.png`` +``` instead of: -.. code-block:: text +```text +.png +".png" +"``.png``" +``` - .png - ".png" - "``.png``" - -Table of Contents (TOC) ------------------------ +## Table of Contents (TOC) Each category should contain an ``index.rst``. This index file should contain a ``maxdepth`` of ``1``. Sub-categories are acceptable, with a ``maxdepth`` of 1. The category ``index.rst`` file can then be added to the root index file located at ``source/index.rst``. -Examples --------- - -.. code-block:: ReST - - Title - ===== - This is an example article - - .. code-block:: java - - System.out.println("Hello World"); +## Examples - Section - ------- - This is a section! +````ReST +# Title +This is an example article +```java +System.out.println("Hello World"); +``` +## Section +This is a section! +```` -Important Note! ---------------- +## Important Note! This list is not exhaustive and administrators reserve the right to make changes. Changes will be reflected in this document. diff --git a/source/docs/contributing/frc-docs/top-translators.rst b/source/docs/contributing/frc-docs/top-translators.rst index 1bb2ed4569..88ba468eb7 100644 --- a/source/docs/contributing/frc-docs/top-translators.rst +++ b/source/docs/contributing/frc-docs/top-translators.rst @@ -1,40 +1,34 @@ -Top Translators -=============== +# Top Translators -Chinese -^^^^^^^ +### Chinese .. toptranslators:: wpilibsuite/frc-docs-translations :locale: zh_CN :limit: 20 :hide_contributions: true -French -^^^^^^ +### French .. toptranslators:: wpilibsuite/frc-docs-translations :locale: fr :limit: 20 :hide_contributions: true -Portuguese -^^^^^^^^^^ +### Portuguese .. toptranslators:: wpilibsuite/frc-docs-translations :locale: pt :limit: 20 :hide_contributions: true -Spanish -^^^^^^^ +### Spanish .. toptranslators:: wpilibsuite/frc-docs-translations :locale: es :limit: 20 :hide_contributions: true -Turkish -^^^^^^^ +### Turkish .. toptranslators:: wpilibsuite/frc-docs-translations :locale: tr @@ -42,8 +36,7 @@ Turkish :order: numerical :hide_contributions: true -Hebrew -^^^^^^ +### Hebrew .. toptranslators:: wpilibsuite/frc-docs-translations :locale: he diff --git a/source/docs/contributing/frc-docs/translations.rst b/source/docs/contributing/frc-docs/translations.rst index 4363d3689f..e9267fb9c5 100644 --- a/source/docs/contributing/frc-docs/translations.rst +++ b/source/docs/contributing/frc-docs/translations.rst @@ -1,47 +1,42 @@ -Translations -============ +# Translations -frc-docs supports translations using the web-based `Transifex `__ utility. frc-docs has been translated into Spanish - Mexico (es_MX), French - Canada (fr_CA) and Turkish - Turkey (tr_TR). Chinese - China (zh_CN), Hebrew - Israel (he_IL), and Portuguese - Brazil (pt_BR) have translations in progress. Translators that are fluent in *both* English and one of the specified languages would be greatly appreciated to contribute to the translations. Even once a translation is complete, it needs to be updated to keep up with changes in frc-docs. +frc-docs supports translations using the web-based [Transifex](https://www.transifex.com) utility. frc-docs has been translated into Spanish - Mexico (es_MX), French - Canada (fr_CA) and Turkish - Turkey (tr_TR). Chinese - China (zh_CN), Hebrew - Israel (he_IL), and Portuguese - Brazil (pt_BR) have translations in progress. Translators that are fluent in *both* English and one of the specified languages would be greatly appreciated to contribute to the translations. Even once a translation is complete, it needs to be updated to keep up with changes in frc-docs. -Workflow --------- +## Workflow Here are some steps to follow for translating frc-docs. -1. Sign up for `Transifex `__ and ask to join the `frc-docs project `__, and request access to the language you'd like to contribute to. -2. Join GitHub `discussions `__! This is a direct means of communication with the WPILib team. You can use this to ask us questions in a fast and streamlined fashion. +1. Sign up for [Transifex](https://www.transifex.com/) and ask to join the [frc-docs project](https://www.transifex.com/wpilib/frc-docs), and request access to the language you'd like to contribute to. +2. Join GitHub [discussions](https://github.com/wpilibsuite/allwpilib/discussions)! This is a direct means of communication with the WPILib team. You can use this to ask us questions in a fast and streamlined fashion. 3. You may be contacted and asked questions involving contributing languages before being granted access to the frc-docs translation project. 4. Translate your language! -Links ------ +## Links Links must be preserved in their original syntax. To translate a link, you can replace the TRANSLATE ME text (this will be replaced with the English title) with the appropriate translation. An example of the original text may be -.. code-block:: text - - For complete wiring instructions/diagrams, please see the :doc:`Wiring the FRC Control System Document `. +```text +For complete wiring instructions/diagrams, please see the :doc:`Wiring the FRC Control System Document `. +``` where the ``Wiring the FRC Control System Document`` then gets translated. -.. code-block:: text - - For complete wiring instructions/diagrams, please see the :doc:`TRANSLATED TEXT `. +```text +For complete wiring instructions/diagrams, please see the :doc:`TRANSLATED TEXT `. +``` Another example is below -.. code-block:: text - - For complete wiring instructions/diagrams, please see the :ref:`TRANSLATED TEXT ` +```text +For complete wiring instructions/diagrams, please see the :ref:`TRANSLATED TEXT ` +``` -Publishing Translations ------------------------ +## Publishing Translations Translations are pulled from Transifex and published automatically each day. -Accuracy --------- +## Accuracy -Translations should be accurate to the original text. If improvements to the English text can be made, open a PR or issue on the `frc-docs `__ repository. These can then get translated on merge. +Translations should be accurate to the original text. If improvements to the English text can be made, open a PR or issue on the [frc-docs](https://github.com/wpilibsuite/frc-docs) repository. These can then get translated on merge. diff --git a/source/docs/contributing/wpilib/index.rst b/source/docs/contributing/wpilib/index.rst index e12cdc7621..dcf4de2af3 100644 --- a/source/docs/contributing/wpilib/index.rst +++ b/source/docs/contributing/wpilib/index.rst @@ -1,24 +1,21 @@ .. include:: -Developing with allwpilib -========================= +# Developing with allwpilib .. important:: This document contains information for developers *of* WPILib. This is not for programming FRC\ |reg| robots. -This is a list of links to the various documentation for the `allwpilib `__ repository. +This is a list of links to the various documentation for the [allwpilib](https://github.com/wpilibsuite/allwpilib) repository. -Quick Start ------------ +## 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 that is further listed in this document. * Clone the repository with ``git clone https://github.com/wpilibsuite/allwpilib.git`` * Build the repository with ``./gradlew build`` or ``./gradlew build --build-cache`` if you have an internet connection * Publish the artifacts locally by running ``./gradlew publish`` -* `Update your robot project's `__ ``build.gradle`` `to use the artifacts `__ +* [Update your robot project's](https://github.com/wpilibsuite/allwpilib/blob/main/DevelopmentBuilds.md) ``build.gradle`` [to use the artifacts](https://github.com/wpilibsuite/allwpilib/blob/main/DevelopmentBuilds.md) -Core Repository ---------------- +## Core Repository .. toctree:: :maxdepth: 1 @@ -29,11 +26,9 @@ Core Repository Maven Artifacts Contributor Guidelines -NetworkTables -------------- +## NetworkTables .. toctree:: :maxdepth: 1 NetworkTables 4 Protocol Spec - NetworkTables 3 Protocol Spec diff --git a/source/docs/controls-overviews/control-system-hardware.rst b/source/docs/controls-overviews/control-system-hardware.rst index 7dc16e0162..97875016f0 100644 --- a/source/docs/controls-overviews/control-system-hardware.rst +++ b/source/docs/controls-overviews/control-system-hardware.rst @@ -1,14 +1,12 @@ .. include:: -Hardware Component Overview -=========================== +# Hardware Component Overview The goal of this document is to provide a brief overview of the hardware components that make up the FRC\ |reg| Control System. Each component will contain a brief description of the component function and a link to more documentation. .. note:: For wiring instructions/diagrams, please see the :doc:`Wiring the FRC Control System ` document. -Overview of Control System --------------------------- +## Overview of Control System .. tab-set:: .. tab-item:: REV @@ -29,8 +27,7 @@ Overview of Control System Diagram courtesy of FRC\ |reg| Team 3161 and Stefen Acepcion. -NI roboRIO ----------- +## NI roboRIO .. image:: images/control-system-hardware/roborio.png :alt: NI roboRIO @@ -38,73 +35,65 @@ NI roboRIO The :ref:`NI-roboRIO ` is the main robot controller used for FRC. The roboRIO serves as the "brain" for the robot running team-generated code that commands all of the other hardware. -CTRE Power Distribution Panel ------------------------------ +## CTRE Power Distribution Panel .. image:: images/control-system-hardware/power-distribution-panel.png :alt: CTRE Power Distribution Panel :width: 500 -The :ref:`CTRE Power Distribution Panel ` (PDP) is designed to distribute power from a 12VDC battery to various robot components through auto-resetting circuit breakers and a small number of special function fused connections. The PDP provides 8 output pairs rated for 40A continuous current and 8 pairs rated for 30A continuous current. The PDP provides dedicated 12V connectors for the roboRIO, as well as connectors for the Voltage Regulator Module and Pneumatics Control Module. It also includes a CAN interface for logging current, temperature, and battery voltage. For more detailed information, see the `PDP User Manual `__. +The :ref:`CTRE Power Distribution Panel ` (PDP) is designed to distribute power from a 12VDC battery to various robot components through auto-resetting circuit breakers and a small number of special function fused connections. The PDP provides 8 output pairs rated for 40A continuous current and 8 pairs rated for 30A continuous current. The PDP provides dedicated 12V connectors for the roboRIO, as well as connectors for the Voltage Regulator Module and Pneumatics Control Module. It also includes a CAN interface for logging current, temperature, and battery voltage. For more detailed information, see the [PDP User Manual](https://store.ctr-electronics.com/content/user-manual/PDP%20User%27s%20Guide.pdf). -REV Power Distribution Hub --------------------------- +## REV Power Distribution Hub .. image:: images/control-system-hardware/power-distribution-hub.png :alt: REV Power Distribution Hub :width: 500 -The `REV Power Distribution Hub `__ (PDH) is designed to distribute power from a 12VDC battery to various robot components. The PDH features 20 high-current (40A max) channels, 3 low-current (15A max), and 1 switchable low-current channel. The Power Distribution Hub features toolless latching WAGO terminals, an LED voltage display, and the ability to connect over CAN or USB-C to the REV Hardware Client for real-time telemetry. +The [REV Power Distribution Hub](https://docs.revrobotics.com/rev-11-1850/) (PDH) is designed to distribute power from a 12VDC battery to various robot components. The PDH features 20 high-current (40A max) channels, 3 low-current (15A max), and 1 switchable low-current channel. The Power Distribution Hub features toolless latching WAGO terminals, an LED voltage display, and the ability to connect over CAN or USB-C to the REV Hardware Client for real-time telemetry. -CTRE Voltage Regulator Module ------------------------------ +## CTRE Voltage Regulator Module .. image:: images/control-system-hardware/voltage-regulator-module.png :alt: CTRE Voltage Regulator Module :width: 500 -The CTRE Voltage Regulator Module (VRM) is an independent module that is powered by 12 volts. The device is wired to a dedicated connector on the PDP. The module has multiple regulated 12V and 5V outputs. The purpose of the VRM is to provide regulated power for the robot radio, custom circuits, and IP vision cameras. For more information, see the `VRM User Manual `__. +The CTRE Voltage Regulator Module (VRM) is an independent module that is powered by 12 volts. The device is wired to a dedicated connector on the PDP. The module has multiple regulated 12V and 5V outputs. The purpose of the VRM is to provide regulated power for the robot radio, custom circuits, and IP vision cameras. For more information, see the [VRM User Manual](https://store.ctr-electronics.com/content/user-manual/VRM%20User%27s%20Guide.pdf). -REV Radio Power Module ----------------------- +## REV Radio Power Module .. image:: images/control-system-hardware/radio-power-module.png :alt: REV Radio Power Module :width: 500 -The `REV Radio Power Module `__ is designed to keep one of the most critical system components, the OpenMesh WiFi radio, powered in the toughest moments of the competition. The Radio Power Module eliminates the need for powering the radio through a traditional barrel power jack. Utilizing 18V Passive POE with two socketed RJ45 connectors, the Radio Power Module passes signal between the radio and roboRIO while providing power directly to the radio. After connecting the radio and roboRIO, easily add power to the Radio Power Module by wiring it to the low-current channels on the Power Distribution Hub utilizing the color coded push button WAGO terminals. +The [REV Radio Power Module](https://docs.revrobotics.com/rev-11-1856/) is designed to keep one of the most critical system components, the OpenMesh WiFi radio, powered in the toughest moments of the competition. The Radio Power Module eliminates the need for powering the radio through a traditional barrel power jack. Utilizing 18V Passive POE with two socketed RJ45 connectors, the Radio Power Module passes signal between the radio and roboRIO while providing power directly to the radio. After connecting the radio and roboRIO, easily add power to the Radio Power Module by wiring it to the low-current channels on the Power Distribution Hub utilizing the color coded push button WAGO terminals. -OpenMesh OM5P-AN or OM5P-AC Radio ---------------------------------- +## OpenMesh OM5P-AN or OM5P-AC Radio .. image:: images/control-system-hardware/openmesh-radio.png :alt: OpenMesh OM5P-AN or OM5P-AC Radio :width: 500 -Either the OpenMesh OM5P-AN or `OpenMesh OM5P-AC `__ wireless radio is used as the robot radio to provide wireless communication functionality to the robot. The device can be configured as an Access Point for direct connection of a laptop for use at home. It can also be configured as a bridge for use on the field. The robot radio should be powered by one of the 12V/2A outputs on the VRM and connected to the roboRIO controller over Ethernet. For more information, see :ref:`Programming your Radio `. +Either the OpenMesh OM5P-AN or [OpenMesh OM5P-AC](https://www.andymark.com/products/open-mesh-om5p-ac-dual-band-1-17-gbps-access-point-radio) wireless radio is used as the robot radio to provide wireless communication functionality to the robot. The device can be configured as an Access Point for direct connection of a laptop for use at home. It can also be configured as a bridge for use on the field. The robot radio should be powered by one of the 12V/2A outputs on the VRM and connected to the roboRIO controller over Ethernet. For more information, see :ref:`Programming your Radio `. -The OM5P-AN `is no longer available for purchase `__. The OM5P-AC is slightly heavier, has more cooling grates, and has a rough surface texture compared to the OM5P-AN. +The OM5P-AN [is no longer available for purchase](https://www.firstinspires.org/robotics/frc/blog/radio-silence). The OM5P-AC is slightly heavier, has more cooling grates, and has a rough surface texture compared to the OM5P-AN. -120A Circuit Breaker --------------------- +## 120A Circuit Breaker .. image:: images/control-system-hardware/circuit-breaker.png :alt: 120A Circuit Breaker :width: 500 -The 120A Main Circuit Breaker serves two roles on the robot: the main robot power switch and a protection device for downstream robot wiring and components. The 120A circuit breaker is wired to the positive terminals of the robot battery and Power Distribution boards. For more information, please see the `Cooper Bussmann 18X Series Datasheet (PN: 185120F) `__ +The 120A Main Circuit Breaker serves two roles on the robot: the main robot power switch and a protection device for downstream robot wiring and components. The 120A circuit breaker is wired to the positive terminals of the robot battery and Power Distribution boards. For more information, please see the [Cooper Bussmann 18X Series Datasheet (PN: 185120F)](https://www.mouser.com/datasheet/2/87/BUS_Tns_DS_18X_CIRCUITBREAKER-515519.pdf) -Snap Action Circuit Breakers ----------------------------- +## Snap Action Circuit Breakers .. image:: images/control-system-hardware/snap-action-circuit-breaker.png :alt: Snap Action Circuit Breakers to be inserted in the PDP. :width: 500 -The Snap Action circuit breakers, `MX5 series `__ and `VB3 Series `__, are used with the Power Distribution Panel to limit current to branch circuits. The ratings on these circuit breakers are for continuous current, temporary peak values can be considerably higher. +The Snap Action circuit breakers, [MX5 series](https://www.snapaction.net/assets/img/MX5-Spec-Sheet-Revision-2023.pdf) and [VB3 Series](https://www.snapaction.net/assets/img/VB3-Spec-Sheet-Revision-2023.pdf), are used with the Power Distribution Panel to limit current to branch circuits. The ratings on these circuit breakers are for continuous current, temporary peak values can be considerably higher. -Robot Battery -------------- +## Robot Battery .. image:: images/control-system-hardware/robot-battery.png :alt: Robot Battery @@ -112,16 +101,15 @@ Robot Battery The power supply for an FRC robot is a single 12V 18Ah Sealed Lead Acid (SLA) battery, capable of meeting the high current demands of an FRC robot. For more information, see the :ref:`Robot Battery page. ` -.. note:: Multiple battery part numbers may be legal, consult the `FRC Manual `__ for a complete list. +.. note:: Multiple battery part numbers may be legal, consult the [FRC Manual](https://www.firstinspires.org/resource-library/frc/competition-manual-qa-system) for a complete list. -Robot Signal Light ------------------- +## Robot Signal Light .. tab-set:: .. tab-item:: Allen-Bradley - .. figure:: images/control-system-hardware/rsl-allenbradley.png + .. figure:: images/control-system-hardware/rsl-allenbradley.jpg :alt: Orange Robot Signal Light (Allen-Bradley) :width: 500 @@ -137,71 +125,63 @@ Robot Signal Light The Robot Signal Light (RSL) is required to be either Allen-Bradley 855PB-B12ME522 or AndyMark am-3583. It is directly controlled by the roboRIO and will flash when enabled and stay solid while disabled. -CTRE Pneumatics Control Module ------------------------------- +## CTRE Pneumatics Control Module .. image:: images/control-system-hardware/pneumatics-control-module.png :alt: CTRE Pneumatics Control Module :width: 500 -The :ref:`CTRE Pneumatics Control Module ` (PCM) contains all of the inputs and outputs required to operate 12V or 24V pneumatic solenoids and the on board compressor. The PCM contains an input for the pressure sensor and will control the compressor automatically when the robot is enabled and a solenoid has been created in the code. For more information see the `PCM User Manual `__. +The :ref:`CTRE Pneumatics Control Module ` (PCM) contains all of the inputs and outputs required to operate 12V or 24V pneumatic solenoids and the on board compressor. The PCM contains an input for the pressure sensor and will control the compressor automatically when the robot is enabled and a solenoid has been created in the code. For more information see the [PCM User Manual](https://store.ctr-electronics.com/content/user-manual/PCM%20User%27s%20Guide.pdf). -REV Pneumatic Hub ------------------ +## REV Pneumatic Hub .. image:: images/control-system-hardware/pneumatic-hub.png :alt: REV Pneumatic Hub :width: 500 -The `REV Pneumatic Hub `__ is a standalone module that is capable of switching both 12V and 24V pneumatic solenoid valves. The Pneumatic Hub features 16 solenoid channels which allow for up to 16 single-acting solenoids, 8 double-acting solenoids, or a combination of the two types. The user selectable output voltage is fully regulated, allowing even 12V solenoids to stay active when the robot battery drops as low as 4.75V. +The [REV Pneumatic Hub](https://docs.revrobotics.com/rev-11-1852/) is a standalone module that is capable of switching both 12V and 24V pneumatic solenoid valves. The Pneumatic Hub features 16 solenoid channels which allow for up to 16 single-acting solenoids, 8 double-acting solenoids, or a combination of the two types. The user selectable output voltage is fully regulated, allowing even 12V solenoids to stay active when the robot battery drops as low as 4.75V. Digital and analog pressure sensor ports are built into the device, increasing the flexibility and feedback functionality of the pneumatic system. The USB-C connection on the Hub works with the REV Hardware Client, allowing users to test pneumatic systems without a need for an additional robot controller. -Motor Controllers ------------------ +## Motor Controllers -There are a variety of different :ref:`motor controllers ` which work with the FRC Control System and are approved for use. These devices are used to provide variable voltage control of the brushed and brushless DC motors used in FRC. They are listed here in order of `usage `__. +There are a variety of different :ref:`motor controllers ` which work with the FRC Control System and are approved for use. These devices are used to provide variable voltage control of the brushed and brushless DC motors used in FRC. They are listed here in order of [usage](https://www.firstinspires.org/robotics/frc/blog/2021-beta-testing-usage-report). .. note:: 3rd Party CAN control is not supported from WPILib. See this section on :ref:`docs/software/can-devices/third-party-devices:Third-Party CAN Devices` for more information. -Talon SRX -^^^^^^^^^ +### Talon SRX .. image:: images/control-system-hardware/talonsrx-motor-controller.png :alt: Talon SRX :width: 500 -The `Talon SRX Motor Controller `__ is a "smart motor controller" from Cross The Road Electronics/VEX Robotics. The Talon SRX can be controlled over the CAN bus or PWM interface. When using the CAN bus control, this device can take inputs from limit switches and potentiometers, encoders, or similar sensors in order to perform advanced control. For more information see the `Talon SRX User's Guide `__. +The [Talon SRX Motor Controller](https://store.ctr-electronics.com/talon-srx/) is a "smart motor controller" from Cross The Road Electronics/VEX Robotics. The Talon SRX can be controlled over the CAN bus or :term:`PWM` interface. When using the CAN bus control, this device can take inputs from limit switches and potentiometers, encoders, or similar sensors in order to perform advanced control. For more information see the [Talon SRX User's Guide](https://store.ctr-electronics.com/content/user-manual/Talon%20SRX%20User's%20Guide.pdf). -Victor SPX -^^^^^^^^^^ +### Victor SPX .. image:: images/control-system-hardware/victor-spx-motor-controller.png :alt: Victor SPX :width: 500 -The `Victor SPX Motor Controller `__ is a CAN or PWM controlled motor controller from Cross The Road Electronics/VEX Robotics. The device is connectorized to allow easy connection to the roboRIO PWM connectors or a CAN bus. The case is sealed to prevent debris from entering the controller. For more information, see the `Victor SPX User Guide `__. +The [Victor SPX Motor Controller](https://store.ctr-electronics.com/victor-spx/) is a CAN or PWM controlled motor controller from Cross The Road Electronics/VEX Robotics. The device is connectorized to allow easy connection to the roboRIO PWM connectors or a CAN bus. The case is sealed to prevent debris from entering the controller. For more information, see the [Victor SPX User Guide](https://store.ctr-electronics.com/content/user-manual/Victor%20SPX%20User's%20Guide.pdf). -SPARK MAX Motor Controller -^^^^^^^^^^^^^^^^^^^^^^^^^^ +### SPARK MAX Motor Controller .. image:: images/control-system-hardware/spark-max-motor-controller.png :alt: SPARK MAX Motor Controller :width: 400 -The `SPARK MAX Motor Controller `__ is an advanced brushed and brushless DC motor controller from REV Robotics. When using CAN bus or USB control, the SPARK MAX uses input from limit switches, encoders, and other sensors, including the integrated encoder of the REV NEO Brushless Motor, to perform advanced control modes. The SPARK MAX can be controlled over PWM, CAN or USB (for configuration/testing only). For more information, see the `SPARK MAX User's Manual `__. +The [SPARK MAX Motor Controller](https://www.revrobotics.com/rev-11-2158/) is an advanced brushed and brushless DC motor controller from REV Robotics. When using CAN bus or USB control, the SPARK MAX uses input from limit switches, encoders, and other sensors, including the integrated encoder of the REV NEO Brushless Motor, to perform advanced control modes. The SPARK MAX can be controlled over PWM, CAN or USB (for configuration/testing only). For more information, see the [SPARK MAX User's Manual](https://docs.revrobotics.com/sparkmax/). -TalonFX Motor Controller -^^^^^^^^^^^^^^^^^^^^^^^^ +### TalonFX Motor Controller .. image:: images/control-system-hardware/talonfx.png :alt: TalonFX Motor Controller :width: 500 -The `TalonFX Motor Controller `__ is integrated into the Falcon 500 brushless motor. It features an integrated encoder and all of the smart features of the Talon SRX and more! For more information see the `Falcon 500 User Guide `__. +The [TalonFX Motor Controller](https://store.ctr-electronics.com/falcon-500-powered-by-talon-fx/) is integrated into the Falcon 500 brushless motor. It features an integrated encoder and all of the smart features of the Talon SRX and more! For more information see the [Falcon 500 User Guide](https://store.ctr-electronics.com/content/user-manual/Falcon%20500%20User%20Guide.pdf). -SPARK Motor Controller -^^^^^^^^^^^^^^^^^^^^^^ +### SPARK Motor Controller .. image:: images/control-system-hardware/spark-motor-controller.png :alt: SPARK Motor Controller @@ -209,10 +189,9 @@ SPARK Motor Controller .. warning:: While this motor controller is still legal for FRC use, the manufacturer has discontinued this product. -The `SPARK Motor Controller `__ from REV Robotics is an inexpensive brushed DC motor controller. The SPARK is controlled using the PWM interface. Limit switches may be wired directly to the SPARK to limit motor travel in one or both directions. For more information, see the `SPARK User's Manual `__. +The [SPARK Motor Controller](https://www.revrobotics.com/content/docs/REV-11-1200-UM.pdf) from REV Robotics is an inexpensive brushed DC motor controller. The SPARK is controlled using the PWM interface. Limit switches may be wired directly to the SPARK to limit motor travel in one or both directions. -Victor SP -^^^^^^^^^ +### Victor SP .. image:: images/control-system-hardware/victor-sp-motor-controller.png :alt: Victor SP @@ -220,20 +199,18 @@ Victor SP .. warning:: While this motor controller is still legal for FRC use, the manufacturer has discontinued this product. -The `Victor SP Motor Controller `__ is a PWM motor controller from Cross The Road Electronics/VEX Robotics. The Victor SP has an electrically isolated metal housing for heat dissipation, making the use of the fan optional. The case is sealed to prevent debris from entering the controller. The controller is approximately half the size of previous models. +The [Victor SP Motor Controller](https://store.ctr-electronics.com/content/user-manual/Victor-SP-Quick-Start-Guide.pdf) is a PWM motor controller from Cross The Road Electronics/VEX Robotics. The Victor SP has an electrically isolated metal housing for heat dissipation, making the use of the fan optional. The case is sealed to prevent debris from entering the controller. The controller is approximately half the size of previous models. -Talon Motor Controller -^^^^^^^^^^^^^^^^^^^^^^ +### Talon Motor Controller .. image:: images/control-system-hardware/talon-motor-controller.png :alt: Talon Motor Controller .. warning:: While this motor controller is still legal for FRC use, the manufacturer has discontinued this product. -The `Talon Motor Controller `__ from Cross the Road Electronics is a PWM controlled brushed DC motor controller with passive cooling. +The [Talon Motor Controller](https://files.andymark.com/Talon_User_Manual_1_3.pdf) from Cross the Road Electronics is a PWM controlled brushed DC motor controller with passive cooling. -Victor 888 Motor Controller / Victor 884 Motor Controller -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Victor 888 Motor Controller / Victor 884 Motor Controller .. image:: images/control-system-hardware/victor-888-motor-controller.png :alt: Victor 888 Motor Controller @@ -241,10 +218,9 @@ Victor 888 Motor Controller / Victor 884 Motor Controller .. warning:: While this motor controller is still legal for FRC use, the manufacturer has discontinued this product. -The `Victor 884 `__ and `Victor 888 `__ motor controllers from VEX Robotics are variable speed PWM motor controllers for use in FRC. The Victor 888 replaces the Victor 884, which is also usable in FRC. +The [Victor 884](https://content.vexrobotics.com/docs/ifi-v884-users-manual-9-25-06.pdf) and [Victor 888](https://content.vexrobotics.com/docs/217-2769-Victor888UserManual.pdf) motor controllers from VEX Robotics are variable speed PWM motor controllers for use in FRC. The Victor 888 replaces the Victor 884, which is also usable in FRC. -Jaguar Motor Controller -^^^^^^^^^^^^^^^^^^^^^^^ +### Jaguar Motor Controller .. image:: images/control-system-hardware/jaguar-motor-controller.png :alt: Jaguar Motor Controller @@ -252,10 +228,9 @@ Jaguar Motor Controller .. warning:: While this motor controller is still legal for FRC use, the manufacturer has discontinued this product. -The `Jaguar Motor Controller `__ from VEX Robotics (formerly made by Luminary Micro and Texas Instruments) is a variable speed motor controller for use in FRC. For FRC, the Jaguar may only be controlled using the PWM interface. +The [Jaguar Motor Controller](https://www.ti.com/lit/an/spma033a/spma033a.pdf?ts=1607574399581) from VEX Robotics (formerly made by Luminary Micro and Texas Instruments) is a variable speed motor controller for use in FRC. For FRC, the Jaguar may only be controlled using the PWM interface. -DMC-60 and DMC-60C Motor Controller -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### DMC-60 and DMC-60C Motor Controller .. image:: images/control-system-hardware/dmc-60c-motor-controller.png :alt: DMC-60C Motor Controller @@ -263,39 +238,35 @@ DMC-60 and DMC-60C Motor Controller .. warning:: While this motor controller is still legal for FRC use, the manufacturer has discontinued this product. -The DMC-60 is a PWM motor controller from Digilent. The DMC-60 features integrated thermal sensing and protection including current-foldback to prevent overheating and damage, and four multi-color LEDs to indicate speed, direction, and status for easier debugging. For more information, see the `DMC-60 reference manual `__ +The DMC-60 is a PWM motor controller from Digilent. The DMC-60 features integrated thermal sensing and protection including current-foldback to prevent overheating and damage, and four multi-color LEDs to indicate speed, direction, and status for easier debugging. For more information, see the [DMC-60 reference manual](https://reference.digilentinc.com/_media/dmc-60/dmc60_rm.pdf) -The DMC-60C adds CAN smart controller capabilities to the DMC-60 controller. Due to the manufacturer discontinuing this product, the DMC-60C is only usable with PWM. For more information see the `DMC-60C Product Page `__ +The DMC-60C adds CAN smart controller capabilities to the DMC-60 controller. Due to the manufacturer discontinuing this product, the DMC-60C is only usable with PWM. For more information see the [DMC-60C Product Page](https://reference.digilentinc.com/dmc-60c/start/) -Venom Motor Controller -^^^^^^^^^^^^^^^^^^^^^^ +### Venom Motor Controller -.. image:: images/control-system-hardware/venom.png +.. image:: images/control-system-hardware/venom.jpg :alt: Venom Motor Controller :width: 500 -The `Venom Motor Controller `__ from Playing With Fusion is integrated into a motor based on the original CIM. Speed, current, temperature, and position are all measured onboard, enabling advanced control modes without complicated sensing and wiring schemes. +The [Venom Motor Controller](https://www.playingwithfusion.com/productview.php?pdid=99) from Playing With Fusion is integrated into a motor based on the original :term:`CIM`. Speed, current, temperature, and position are all measured onboard, enabling advanced control modes without complicated sensing and wiring schemes. -Nidec Dynamo BLDC Motor with Controller -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Nidec Dynamo BLDC Motor with Controller -.. image:: images/control-system-hardware/nidec-dynamo.png +.. image:: images/control-system-hardware/nidec-dynamo.jpg :alt: Nidec Dynamo BLDC Motor with Controller :width: 500 -The `Nidec Dynamo BLDC Motor with Controller `__ is the first brushless motor and controller legal in FRC. This motor's controller is integrated into the back of the motor. The `motor data sheet `__ provides more device specifics. +The [Nidec Dynamo BLDC Motor with Controller](https://www.andymark.com/products/dynamo-brushless-motor-controller) is the first brushless motor and controller legal in FRC. This motor's controller is integrated into the back of the motor. The [motor data sheet](https://cdn.andymark.com/media/W1siZiIsIjIwMTkvMDUvMDkvMDkvNTEvNDQvZjQwYjliZDctYzdkOC00MWFlLWIzZmYtZTQyNTJhYjRkNmIyL2FtLTM3NDAgTmlkZWMgRHluYW1vIERNMzAxMi0xMDYzLUIgU3BlYy5wZGYiXV0/am-3740%20Nidec%20Dynamo%20DM3012-1063-B%20Spec.pdf?sha=eb03d3f578fe782e) provides more device specifics. -SD540B and SD540C Motor Controllers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### SD540B and SD540C Motor Controllers .. image:: images/control-system-hardware/sd540b-pwm.png :alt: SD540B Motor Controller :width: 500 -The SD540B and SD540C Motor Controllers from Mindsensors are controlled using PWM. CAN control is no longer available for the SD540C due to lack of manufacturer support. Limit switches may be wired directly to the SD540 to limit motor travel in one or both directions. For more information see the `Mindsensors FRC page `__ +The SD540B and SD540C Motor Controllers from Mindsensors are controlled using PWM. CAN control is no longer available for the SD540C due to lack of manufacturer support. Limit switches may be wired directly to the SD540 to limit motor travel in one or both directions. For more information see the [Mindsensors FRC page](http://www.mindsensors.com/68-frc) -Spike H-Bridge Relay --------------------- +## Spike H-Bridge Relay .. image:: images/control-system-hardware/spike-relay.png :alt: Spike H-Bridge Relay @@ -303,27 +274,24 @@ Spike H-Bridge Relay .. warning:: While this relay is still legal for FRC use, the manufacturer has discontinued this product. -The Spike H-Bridge Relay from VEX Robotics is a device used for controlling power to motors or other custom robot electronics. When connected to a motor, the Spike provides On/Off control in both the forward and reverse directions. The Spike outputs are independently controlled so it can also be used to provide power to up to 2 custom electronic circuits. The Spike H-Bridge Relay should be connected to a relay output of the roboRIO and powered from the Power Distribution Panel. For more information, see the `Spike User’s Guide `__. +The Spike H-Bridge Relay from VEX Robotics is a device used for controlling power to motors or other custom robot electronics. When connected to a motor, the Spike provides On/Off control in both the forward and reverse directions. The Spike outputs are independently controlled so it can also be used to provide power to up to 2 custom electronic circuits. The Spike H-Bridge Relay should be connected to a relay output of the roboRIO and powered from the Power Distribution Panel. For more information, see the [Spike User’s Guide](https://content.vexrobotics.com/docs/spike-blue-guide-sep05.pdf). -Servo Power Module ------------------- +## Servo Power Module .. image:: images/control-system-hardware/servo-power-module.png :alt: Servo Power Module :width: 300 -The Servo Power Module from Rev Robotics is capable of expanding the power available to servos beyond what the roboRIO integrated power supply is capable of. The Servo Power Module provides up to 90W of 6V power across 6 channels. All control signals are passed through directly from the roboRIO. For more information, see the `Servo Power Module webpage `__. +The Servo Power Module from Rev Robotics is capable of expanding the power available to servos beyond what the roboRIO integrated power supply is capable of. The Servo Power Module provides up to 90W of 6V power across 6 channels. All control signals are passed through directly from the roboRIO. For more information, see the [Servo Power Module webpage](https://www.revrobotics.com/rev-11-1144/). -Microsoft Lifecam HD3000 ------------------------- +## Microsoft Lifecam HD3000 .. image:: images/control-system-hardware/microsoft-lifecam.png :alt: Microsoft Lifecam HD3000 :width: 300 -The Microsoft Lifecam HD3000 is a USB webcam that can be plugged directly into the roboRIO. The camera is capable of capturing up to 1280x720 video at 30 FPS. For more information about the camera, see the `Microsoft product page `__. For more information about using the camera with the roboRIO, see the :ref:`Vision Processing ` section of this documentation. +The Microsoft Lifecam HD3000 is a USB webcam that can be plugged directly into the roboRIO. The camera is capable of capturing up to 1280x720 video at 30 FPS. For more information about the camera, see the [Microsoft product page](https://www.microsoft.com/en/accessories/business/lifecam-hd-3000-for-business). For more information about using the camera with the roboRIO, see the :ref:`Vision Processing ` section of this documentation. -Image Credits -------------- +## Image Credits Image of roboRIO courtesy of National Instruments. Image of DMC-60 courtesy of Digilent. Image of SD540 courtesy of Mindsensors. Images of Jaguar Motor Controller, Talon SRX, Talon FX, Victor 888, Victor SP, Victor SPX, and Spike H-Bridge Relay courtesy of VEX Robotics, Inc. Image of SPARK MAX, Power Distribution Hub, Radio Power Module, and Pneumatic Hub courtesy of REV Robotics. Lifecam, PDP, PCM, SPARK, and VRM photos courtesy of *FIRST*\ |reg|. All other photos courtesy of AndyMark Inc. diff --git a/source/docs/controls-overviews/control-system-software.rst b/source/docs/controls-overviews/control-system-software.rst index 3c878cd8c0..dfeffaabbe 100644 --- a/source/docs/controls-overviews/control-system-software.rst +++ b/source/docs/controls-overviews/control-system-software.rst @@ -1,152 +1,139 @@ .. include:: -Software Component Overview -=========================== +# Software Component Overview The FRC\ |reg| software consists of a wide variety of mandatory and optional components. These elements are designed to assist you in the design, development, and debugging of your robot code as well as assist with control robot operation and to provide feedback when troubleshooting. For each software component this document will provide a brief overview of its purpose, a link to the package download, if appropriate, and a link to further documentation where available. -Operating System Compatibility ------------------------------- +## Operating System Compatibility The primary supported OS for FRC components is Windows. All required FRC software components have been tested on Windows 10 & 11. -Many of the tools for C++/Java programming are also supported and tested on macOS and Linux. Teams programming in C++/Java should be able to develop using these systems, using a Windows system for the Windows-only operations such as the Driver Station, Radio Configuration Utility, and roboRIO Imaging Tool. +Many of the tools for C++/Java/Python programming are also supported and tested on macOS and Linux. Teams programming in C++/Java/Python should be able to develop using these systems, using a Windows system for the Windows-only operations such as the Driver Station, Radio Configuration Utility, and roboRIO Imaging Tool. -LabVIEW FRC (Windows Only) --------------------------- +## LabVIEW FRC (Windows Only) .. image:: /docs/zero-to-robot/step-4/images/creating-test-program-labview/creating-a-project.png :alt: LabVIEW FRC Getting Started screen. LabVIEW FRC, based on a recent version of LabVIEW Professional, is one of the three officially supported languages for programming an FRC robot. LabVIEW is a graphical, dataflow-driven language. LabVIEW programs consist of a collection of icons, called VIs, wired together with wires which pass data between the VIs. The LabVIEW FRC installer is distributed on a DVD found in the Kickoff Kit of Parts and is also available for download. A guide to getting started with the LabVIEW FRC software, including installation instructions can be found :ref:`here `. -Visual Studio Code ------------------- +## Visual Studio Code .. image:: images/control-system-software/visual-studio-code.png :alt: Welcome screen of Visual Studio Code. -Visual Studio Code is the supported development environment for C++ and Java (the other two supported languages). Both are object-oriented text based programming languages. A guide to getting started with C++ or Java for FRC, including the installation and configuration of Visual Studio Code can be found :ref:`here `. +Visual Studio Code is the supported development environment for C++, Java. A guide to getting started with Java and C++ for FRC, including the installation and configuration of Visual Studio Code can be found :ref:`here `. -FRC Driver Station Powered by NI LabVIEW (Windows Only) -------------------------------------------------------- +## FRC Driver Station Powered by NI LabVIEW (Windows Only) .. image:: images/control-system-software/frc-driver-station.png :alt: Driver Station on the first tab with the robot disabled and disconnected. This is the only software allowed to be used for the purpose of controlling the state of the robot during competition. This software sends data to your robot from a variety of input devices. It also contains a number of tools used to help troubleshoot robot issues. More information about the FRC Driver Station Powered by NI LabVIEW can be found :ref:`here `. -Dashboard Options ------------------ +## Dashboard Options -LabVIEW Dashboard (Windows Only) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### LabVIEW Dashboard (Windows Only) .. image:: images/control-system-software/frc-labview-dashboard.png :alt: The default LabVIEW Dashboard on the Drive tab. The LabVIEW Dashboard is automatically launched by the FRC Driver Station by default. The purpose of the Dashboard is to provide feedback about the operation of the robot using tabbed display with a variety of built in features. More information about the FRC Default Dashboard software can be found :ref:`here `. -SmartDashboard -^^^^^^^^^^^^^^ +### SmartDashboard .. image:: images/control-system-software/smartdashboard.png :alt: SmartDashboard with 3 widgets added. SmartDashboard allows you to view your robot data by automatically creating customizable indicators specifically for each piece of data sent from your robot. Additional documentation on SmartDashboard can be found :ref:`here `. -Shuffleboard -^^^^^^^^^^^^ +### Shuffleboard .. image:: images/control-system-software/shuffleboard.png :alt: Shuffleboard with 3 widgets from their NetworkTables entries added. Shuffleboard has the same features as SmartDashboard. It also improves on the setup and visualization of your data with new features and a modern design at the cost of being less resource efficient. Additional documentation on Shuffleboard can be found :ref:`here `. -Glass -^^^^^ +### Glass .. image:: images/control-system-software/glass.png :alt: Glass connected and showing NetworkTables, a Field2D window, and a plot of a couple signals. :ref:`Glass ` is a Dashboard focused on being a programmer's tool for debugging. The primary advantages are the field view, pose visualization and advanced signal plotting tools. -LiveWindow ----------- +## LiveWindow .. image:: images/control-system-software/livewindow-smartdashboard.png :alt: LiveWindow showing two different subsystems. LiveWindow is a feature of SmartDashboard and Shuffleboard, designed for use with the Test Mode of the Driver Station. LiveWindow allows the user to see feedback from sensors on the robot and control actuators independent of the written user code. More information about LiveWindow can be found :ref:`here `. -FRC roboRIO Imaging Tool (Windows Only) ---------------------------------------- +## FRC roboRIO Imaging Tool (Windows Only) .. image:: /docs/zero-to-robot/step-3/images/imaging-your-roborio/roborio-imaging-tool.png :alt: roboRIO Imaging Tool after it has found a connected roboRIO. This tool is used to format and setup a roboRIO for use in FRC. Installation instructions can be found :ref:`here `. Additional instructions on imaging your roboRIO using this tool can be found :doc:`here `. -FRC Radio Configuration Utility (Windows Only) ----------------------------------------------- +## FRC Radio Configuration Utility (Windows Only) .. image:: images/control-system-software/frc-radio-configuration-utility.png :alt: Initial screen of the FRC Radio Configuration Utility. The FRC Radio Configuration Utility is a tool used to configure the standard radio for practice use at home. This tool sets the appropriate network settings to mimic the experience of the FRC playing field. The FRC Radio Configuration Utility is installed by a standalone installer that can be found :ref:`here `. -FRC Driver Station Log Viewer (Windows Only) --------------------------------------------- +## FRC Driver Station Log Viewer (Windows Only) .. image:: images/control-system-software/frc-log-viewer.png :alt: Driver Station Log Viewer showing a logged practice session. The FRC Driver Station Log Viewer is used to view logs created by the FRC Driver Station. These logs contain a variety of information important for understanding what happened during a practice session or FRC match. More information about the FRC Driver Station Log Viewer and understanding the logs can be found :ref:`here ` -RobotBuilder ------------- +## RobotBuilder .. image:: images/control-system-software/robot-builder.png :alt: RobotBuilder building a robot with two subsystems. -RobotBuilder is a tool designed to aid in setup and structuring of a Command Based robot project for C++ or Java. RobotBuilder allows you to enter in the various components of your robot subsystems and operator interface and define what your commands are in a graphical tree structure. RobotBuilder will then generate structural template code to get you started. More information about RobotBuilder can be found :ref:`here `. More information about the Command Based programming architecture can be found :ref:`here `. +RobotBuilder is a tool designed to aid in setup and structuring of a Command Based robot project for C++ or Java (Python not currently supported). RobotBuilder allows you to enter in the various components of your robot subsystems and operator interface and define what your commands are in a graphical tree structure. RobotBuilder will then generate structural template code to get you started. More information about RobotBuilder can be found :ref:`here `. More information about the Command Based programming architecture can be found :ref:`here `. -Robot Simulation ----------------- +## Robot Simulation .. image:: images/control-system-software/sim-gui.png :alt: The Simulation GUI similar to Glass but also has Joysticks and control over the robot state and a few other features. -Robot Simulation offers a way for Java and C++ teams to verify their actual robot code is working in a simulated environment. This simulation can be launched directly from VS Code and includes a 2D field that users can visualize their robot's movement on. For more information see the :ref:`Robot Simulation section `. +Robot Simulation offers a way for Java, C++, and Python teams to verify their actual robot code is working in a simulated environment. This simulation can be launched directly from VS Code and includes a 2D field that users can visualize their robot's movement on. For more information see the :ref:`Robot Simulation section `. -FRC LabVIEW Robot Simulator (Windows Only) ------------------------------------------- +## FRC LabVIEW Robot Simulator (Windows Only) .. image:: images/control-system-software/robot-simulator.png :alt: FRC LabVIEW Robot Simulator -The FRC Robot Simulator is a component of the LabVIEW programming environment that allows you to operate a predefined robot in a simulated environment to test code and/or Driver Station functions. Information on using the FRC Robot Simulator can be found `here `__ or by opening the Robot Simulation Readme.html file in the LabVIEW Project Explorer. +The FRC Robot Simulator is a component of the LabVIEW programming environment that allows you to operate a predefined robot in a simulated environment to test code and/or Driver Station functions. Information on using the FRC Robot Simulator can be found [here](https://forums.ni.com/t5/FIRST-Robotics-Competition/LabVIEW-Tutorial-10-Robot-Simulation/ta-p/3739702?profile.language=en) or by opening the Robot Simulation Readme.html file in the LabVIEW Project Explorer. -PathWeaver ----------- +## PathWeaver .. image:: images/control-system-software/pathweaver.png :alt: PathWeaver UI with a project for FRC Deep Space plotting a trajectory to the back of the rocket. PathWeaver allows teams to quickly generate and configure paths for advanced autonomous routines. These paths have smooth curves allowing the team to quickly navigate their robot between points on the field. For more information see the :ref:`PathWeaver section `. -System Identification ----------------------- +## System Identification -.. image:: /docs/software/pathplanning/system-identification/images/analysis-type.png - :alt: System Identification new project screen. +.. image:: images/control-system-software/sysid.png + :alt: SysId UI showing diagnostics and analysis for a flywheel. -This tool helps teams automatically calculate constants that can be used to describe the physical properties of your robot for use in features like robot simulation, trajectory following, and PID control. For more information see the :ref:`System Identification section `. +This tool helps teams automatically calculate constants that can be used to describe the physical properties of your robot for use in features like robot simulation, trajectory following, and PID control. For more information see the :ref:`System Identification section `. -OutlineViewer -------------- +## OutlineViewer .. image:: images/control-system-software/outline-viewer.png :alt: OutlineViewer with the preferences dialog box. OutlineViewer is a utility used to view, modify and add to all of the contents of the NetworkTables for debugging purposes. LabVIEW teams can use the Variables tab of the LabVIEW Dashboard to accomplish this functionality. For more information see the :ref:`Outline Viewer section `. + +## roboRIO Team Number Setter + +.. image:: /docs/software/wpilib-tools/roborio-team-number-setter/images/roborioteamnumbersetter.png + :alt: roboRIO Team Number Setter tool. + +The roboRIO Team Number Setter is a cross-platform utility that can be used to set the team number on the roboRIO. It is an alternative to the roboRIO imaging tool for setting the team number. For more information see the :ref:`roboRIO Team Number Setter section `. diff --git a/source/docs/controls-overviews/images/control-system-hardware/axis-camera.png b/source/docs/controls-overviews/images/control-system-hardware/axis-camera.png index 5108db4727..f216eca8c5 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/axis-camera.png and b/source/docs/controls-overviews/images/control-system-hardware/axis-camera.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/circuit-breaker.png b/source/docs/controls-overviews/images/control-system-hardware/circuit-breaker.png index 8389a9d7ae..9042c552dc 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/circuit-breaker.png and b/source/docs/controls-overviews/images/control-system-hardware/circuit-breaker.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/dmc-60c-motor-controller.png b/source/docs/controls-overviews/images/control-system-hardware/dmc-60c-motor-controller.png index f8a976147e..91ea5246fb 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/dmc-60c-motor-controller.png and b/source/docs/controls-overviews/images/control-system-hardware/dmc-60c-motor-controller.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/jaguar-motor-controller.png b/source/docs/controls-overviews/images/control-system-hardware/jaguar-motor-controller.png index d3c7ded44c..eb96075622 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/jaguar-motor-controller.png and b/source/docs/controls-overviews/images/control-system-hardware/jaguar-motor-controller.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/microsoft-lifecam.png b/source/docs/controls-overviews/images/control-system-hardware/microsoft-lifecam.png index 3cf9a425e9..1dc4868e67 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/microsoft-lifecam.png and b/source/docs/controls-overviews/images/control-system-hardware/microsoft-lifecam.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/nidec-dynamo.png b/source/docs/controls-overviews/images/control-system-hardware/nidec-dynamo.jpg similarity index 100% rename from source/docs/controls-overviews/images/control-system-hardware/nidec-dynamo.png rename to source/docs/controls-overviews/images/control-system-hardware/nidec-dynamo.jpg diff --git a/source/docs/controls-overviews/images/control-system-hardware/openmesh-radio.png b/source/docs/controls-overviews/images/control-system-hardware/openmesh-radio.png index 49245ae8a3..af3d9761a7 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/openmesh-radio.png and b/source/docs/controls-overviews/images/control-system-hardware/openmesh-radio.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/pneumatic-hub.png b/source/docs/controls-overviews/images/control-system-hardware/pneumatic-hub.png index 052113920e..9916788214 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/pneumatic-hub.png and b/source/docs/controls-overviews/images/control-system-hardware/pneumatic-hub.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/pneumatics-control-module.png b/source/docs/controls-overviews/images/control-system-hardware/pneumatics-control-module.png index 95ce48b9c8..b292a58a0a 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/pneumatics-control-module.png and b/source/docs/controls-overviews/images/control-system-hardware/pneumatics-control-module.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/power-distribution-hub.png b/source/docs/controls-overviews/images/control-system-hardware/power-distribution-hub.png index 908c10a453..f40593cf9f 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/power-distribution-hub.png and b/source/docs/controls-overviews/images/control-system-hardware/power-distribution-hub.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/power-distribution-panel.png b/source/docs/controls-overviews/images/control-system-hardware/power-distribution-panel.png index ee22d5ae2b..8e6613ef56 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/power-distribution-panel.png and b/source/docs/controls-overviews/images/control-system-hardware/power-distribution-panel.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/radio-power-module.png b/source/docs/controls-overviews/images/control-system-hardware/radio-power-module.png index 5b8a7ffa62..87087f84a1 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/radio-power-module.png and b/source/docs/controls-overviews/images/control-system-hardware/radio-power-module.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/roborio.png b/source/docs/controls-overviews/images/control-system-hardware/roborio.png index c22b0a99a1..aa5db973ad 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/roborio.png and b/source/docs/controls-overviews/images/control-system-hardware/roborio.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/robot-battery.png b/source/docs/controls-overviews/images/control-system-hardware/robot-battery.png index 1f36a59a50..3541d11a02 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/robot-battery.png and b/source/docs/controls-overviews/images/control-system-hardware/robot-battery.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/rsl-allenbradley.png b/source/docs/controls-overviews/images/control-system-hardware/rsl-allenbradley.jpg similarity index 100% rename from source/docs/controls-overviews/images/control-system-hardware/rsl-allenbradley.png rename to source/docs/controls-overviews/images/control-system-hardware/rsl-allenbradley.jpg diff --git a/source/docs/controls-overviews/images/control-system-hardware/rsl-andymark.png b/source/docs/controls-overviews/images/control-system-hardware/rsl-andymark.png index 7d3baa8ed5..60a9aaa2b5 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/rsl-andymark.png and b/source/docs/controls-overviews/images/control-system-hardware/rsl-andymark.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/sd540b-pwm.png b/source/docs/controls-overviews/images/control-system-hardware/sd540b-pwm.png index 5d9685eb1d..7dac0ed1e9 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/sd540b-pwm.png and b/source/docs/controls-overviews/images/control-system-hardware/sd540b-pwm.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/servo-power-module.png b/source/docs/controls-overviews/images/control-system-hardware/servo-power-module.png index e064b9c9f4..2c5092d751 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/servo-power-module.png and b/source/docs/controls-overviews/images/control-system-hardware/servo-power-module.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/snap-action-circuit-breaker.png b/source/docs/controls-overviews/images/control-system-hardware/snap-action-circuit-breaker.png index 08f6b5eca6..fd8a2b5501 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/snap-action-circuit-breaker.png and b/source/docs/controls-overviews/images/control-system-hardware/snap-action-circuit-breaker.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/spark-max-motor-controller.png b/source/docs/controls-overviews/images/control-system-hardware/spark-max-motor-controller.png index 8e9a8d82c1..06fa9e7789 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/spark-max-motor-controller.png and b/source/docs/controls-overviews/images/control-system-hardware/spark-max-motor-controller.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/spark-motor-controller.png b/source/docs/controls-overviews/images/control-system-hardware/spark-motor-controller.png index bf039dc42e..6fbe3c1da2 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/spark-motor-controller.png and b/source/docs/controls-overviews/images/control-system-hardware/spark-motor-controller.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/spike-relay.png b/source/docs/controls-overviews/images/control-system-hardware/spike-relay.png index b7ee5e0942..35d06f3061 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/spike-relay.png and b/source/docs/controls-overviews/images/control-system-hardware/spike-relay.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/talon-motor-controller.png b/source/docs/controls-overviews/images/control-system-hardware/talon-motor-controller.png index ab62547187..50bb7a1127 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/talon-motor-controller.png and b/source/docs/controls-overviews/images/control-system-hardware/talon-motor-controller.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/talonfx.png b/source/docs/controls-overviews/images/control-system-hardware/talonfx.png index cb5f0c8d2e..5b972dcfb9 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/talonfx.png and b/source/docs/controls-overviews/images/control-system-hardware/talonfx.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/talonsrx-motor-controller.png b/source/docs/controls-overviews/images/control-system-hardware/talonsrx-motor-controller.png index bdd5cb7acb..b76f3c7223 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/talonsrx-motor-controller.png and b/source/docs/controls-overviews/images/control-system-hardware/talonsrx-motor-controller.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/venom.png b/source/docs/controls-overviews/images/control-system-hardware/venom.jpg similarity index 100% rename from source/docs/controls-overviews/images/control-system-hardware/venom.png rename to source/docs/controls-overviews/images/control-system-hardware/venom.jpg diff --git a/source/docs/controls-overviews/images/control-system-hardware/victor-888-motor-controller.png b/source/docs/controls-overviews/images/control-system-hardware/victor-888-motor-controller.png index f1bc88f0c1..7ee27b8466 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/victor-888-motor-controller.png and b/source/docs/controls-overviews/images/control-system-hardware/victor-888-motor-controller.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/victor-sp-motor-controller.png b/source/docs/controls-overviews/images/control-system-hardware/victor-sp-motor-controller.png index b942670351..8fd7b4d2a2 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/victor-sp-motor-controller.png and b/source/docs/controls-overviews/images/control-system-hardware/victor-sp-motor-controller.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/victor-spx-motor-controller.png b/source/docs/controls-overviews/images/control-system-hardware/victor-spx-motor-controller.png index 9d9566d917..21df1b5f03 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/victor-spx-motor-controller.png and b/source/docs/controls-overviews/images/control-system-hardware/victor-spx-motor-controller.png differ diff --git a/source/docs/controls-overviews/images/control-system-hardware/voltage-regulator-module.png b/source/docs/controls-overviews/images/control-system-hardware/voltage-regulator-module.png index 4d1603172a..1cb5d59cd2 100644 Binary files a/source/docs/controls-overviews/images/control-system-hardware/voltage-regulator-module.png and b/source/docs/controls-overviews/images/control-system-hardware/voltage-regulator-module.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/axis-camera-setup.png b/source/docs/controls-overviews/images/control-system-software/axis-camera-setup.png index 2a5091bdaf..e0cfa58666 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/axis-camera-setup.png and b/source/docs/controls-overviews/images/control-system-software/axis-camera-setup.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/frc-driver-station.png b/source/docs/controls-overviews/images/control-system-software/frc-driver-station.png index d89e73d739..29b392cdf6 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/frc-driver-station.png and b/source/docs/controls-overviews/images/control-system-software/frc-driver-station.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/frc-labview-dashboard.png b/source/docs/controls-overviews/images/control-system-software/frc-labview-dashboard.png index 22fec34a41..e26776cc2a 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/frc-labview-dashboard.png and b/source/docs/controls-overviews/images/control-system-software/frc-labview-dashboard.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/frc-log-viewer.png b/source/docs/controls-overviews/images/control-system-software/frc-log-viewer.png index a04c92e12f..72db2f45f9 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/frc-log-viewer.png and b/source/docs/controls-overviews/images/control-system-software/frc-log-viewer.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/frc-radio-configuration-utility.png b/source/docs/controls-overviews/images/control-system-software/frc-radio-configuration-utility.png index de8b8d0f25..9ff7a203db 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/frc-radio-configuration-utility.png and b/source/docs/controls-overviews/images/control-system-software/frc-radio-configuration-utility.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/glass.png b/source/docs/controls-overviews/images/control-system-software/glass.png index 8d3f7508a2..ee52be83a0 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/glass.png and b/source/docs/controls-overviews/images/control-system-software/glass.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/livewindow-smartdashboard.png b/source/docs/controls-overviews/images/control-system-software/livewindow-smartdashboard.png index 29bf415abf..eec8490de5 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/livewindow-smartdashboard.png and b/source/docs/controls-overviews/images/control-system-software/livewindow-smartdashboard.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/outline-viewer.png b/source/docs/controls-overviews/images/control-system-software/outline-viewer.png index 1f840c2df0..b3e82be904 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/outline-viewer.png and b/source/docs/controls-overviews/images/control-system-software/outline-viewer.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/pathweaver.png b/source/docs/controls-overviews/images/control-system-software/pathweaver.png index b5c8099e77..5d53f989bb 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/pathweaver.png and b/source/docs/controls-overviews/images/control-system-software/pathweaver.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/robot-builder.png b/source/docs/controls-overviews/images/control-system-software/robot-builder.png index 2b79797e12..db37f6197d 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/robot-builder.png and b/source/docs/controls-overviews/images/control-system-software/robot-builder.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/robot-simulator.png b/source/docs/controls-overviews/images/control-system-software/robot-simulator.png index c319b1a5c2..30fe06e160 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/robot-simulator.png and b/source/docs/controls-overviews/images/control-system-software/robot-simulator.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/shuffleboard.png b/source/docs/controls-overviews/images/control-system-software/shuffleboard.png index df9d939f84..cd76577325 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/shuffleboard.png and b/source/docs/controls-overviews/images/control-system-software/shuffleboard.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/sim-gui.png b/source/docs/controls-overviews/images/control-system-software/sim-gui.png index 33116344bf..2f31300f32 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/sim-gui.png and b/source/docs/controls-overviews/images/control-system-software/sim-gui.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/smartdashboard.png b/source/docs/controls-overviews/images/control-system-software/smartdashboard.png index 845b125590..a1b055c4ca 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/smartdashboard.png and b/source/docs/controls-overviews/images/control-system-software/smartdashboard.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/sysid.png b/source/docs/controls-overviews/images/control-system-software/sysid.png new file mode 100644 index 0000000000..980812cbb1 Binary files /dev/null and b/source/docs/controls-overviews/images/control-system-software/sysid.png differ diff --git a/source/docs/controls-overviews/images/control-system-software/visual-studio-code.png b/source/docs/controls-overviews/images/control-system-software/visual-studio-code.png index 31b671c858..edc5027d63 100644 Binary files a/source/docs/controls-overviews/images/control-system-software/visual-studio-code.png and b/source/docs/controls-overviews/images/control-system-software/visual-studio-code.png differ diff --git a/source/docs/hardware/hardware-basics/can-wiring-basics.rst b/source/docs/hardware/hardware-basics/can-wiring-basics.rst index a178d6a2a1..b5b44d68df 100644 --- a/source/docs/hardware/hardware-basics/can-wiring-basics.rst +++ b/source/docs/hardware/hardware-basics/can-wiring-basics.rst @@ -1,12 +1,10 @@ -CAN Wiring Basics -================= +# CAN Wiring Basics -CAN is a two wire network that is designed to facilitate communication between multiple devices on your robot. It is recommended that CAN on your robot follow a "daisy-chain" topology. This means that the CAN wiring should usually start at your roboRIO and go into and out of each device successively until finally ending at the PDP. +:term:`CAN` is a two wire network that is designed to facilitate communication between multiple devices on your robot. It is recommended that CAN on your robot follow a "daisy-chain" topology. This means that the CAN wiring should usually start at your roboRIO and go into and out of each device successively until finally ending at the :term:`PDP`. .. image:: images/can-wiring-basics/daisychain.png -Standard Wiring ---------------- +## Standard Wiring CAN is generally wired with yellow and green wire with yellow acting as the CAN-High and green as the CAN-Low signals. Many devices show this yellow and green color scheme to indicate how the wires should be plugged in. @@ -18,7 +16,6 @@ CAN wiring from the PCM to the PDP. .. image:: /docs/zero-to-robot/step-1/images/how-to-wire-a-simple-robot/pdp-can.jpg -Termination ------------ +## Termination It is recommended that the wiring starts at the roboRIO and ends at the PDP because the CAN network is required to be terminated by 120 :math:`\Omega` resistors and these are built into these two devices. The PDP ships with the CAN bus terminating resistor jumper in the "ON" position. It is recommended to leave the jumper in this position and place any additional CAN nodes between the roboRIO and the PDP (leaving the PDP as the end of the bus). If you wish to place the PDP in the middle of the bus (utilizing both pairs of PDP CAN terminals) move the jumper to the "OFF" position and place your own 120 :math:`\Omega` terminating resistor at the end of your CAN bus chain. diff --git a/source/docs/hardware/hardware-basics/images/can-wiring-basics/daisychain.png b/source/docs/hardware/hardware-basics/images/can-wiring-basics/daisychain.png index 37ecf21fd3..00299b5095 100644 Binary files a/source/docs/hardware/hardware-basics/images/can-wiring-basics/daisychain.png and b/source/docs/hardware/hardware-basics/images/can-wiring-basics/daisychain.png differ diff --git a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/blade-fuses.png b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/blade-fuses.png index 3a620262e1..3fdb7ed899 100644 Binary files a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/blade-fuses.png and b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/blade-fuses.png differ diff --git a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/pdp-bolts.png b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/pdp-bolts.png index 350b15efbb..a8928230b7 100644 Binary files a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/pdp-bolts.png and b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/pdp-bolts.png differ diff --git a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBatt.png b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBatt.png index 3ff27b2843..0a5c828622 100644 Binary files a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBatt.png and b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBatt.png differ diff --git a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBreaker-1.png b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBreaker-1.png index 9f9d5939ea..8581b72502 100644 Binary files a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBreaker-1.png and b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBreaker-1.png differ diff --git a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBreaker-2.png b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBreaker-2.png index 0ac7cdd988..4f2a5c219a 100644 Binary files a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBreaker-2.png and b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckBreaker-2.png differ diff --git a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckConnecc.png b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckConnecc.png index 7e12d122bd..610eb71d86 100644 Binary files a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckConnecc.png and b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckConnecc.png differ diff --git a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckTug-1.png b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckTug-1.png index c5dd76a13b..e0d890cf8f 100644 Binary files a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckTug-1.png and b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckTug-1.png differ diff --git a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckTug-2.png b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckTug-2.png index 9c97b47816..6b7031912c 100644 Binary files a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckTug-2.png and b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/preCheckTug-2.png differ diff --git a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/tools.png b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/tools.png index aa1c43544c..1d75db6b5a 100644 Binary files a/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/tools.png and b/source/docs/hardware/hardware-basics/images/preemptive-troubleshooting/tools.png differ diff --git a/source/docs/hardware/hardware-basics/images/robot-battery/battery-strap.png b/source/docs/hardware/hardware-basics/images/robot-battery/battery-strap.png index 2d6439650f..9396e1f056 100644 Binary files a/source/docs/hardware/hardware-basics/images/robot-battery/battery-strap.png and b/source/docs/hardware/hardware-basics/images/robot-battery/battery-strap.png differ diff --git a/source/docs/hardware/hardware-basics/images/robot-battery/beak.png b/source/docs/hardware/hardware-basics/images/robot-battery/beak.png index 392a6789ff..ead346316d 100644 Binary files a/source/docs/hardware/hardware-basics/images/robot-battery/beak.png and b/source/docs/hardware/hardware-basics/images/robot-battery/beak.png differ diff --git a/source/docs/hardware/hardware-basics/images/robot-battery/broken-terminal.png b/source/docs/hardware/hardware-basics/images/robot-battery/broken-terminal.png index 541b09842a..9bd6dfc370 100644 Binary files a/source/docs/hardware/hardware-basics/images/robot-battery/broken-terminal.png and b/source/docs/hardware/hardware-basics/images/robot-battery/broken-terminal.png differ diff --git a/source/docs/hardware/hardware-basics/images/robot-battery/edge-clip.png b/source/docs/hardware/hardware-basics/images/robot-battery/edge-clip.png index 10bd7003a7..fe681fb2f3 100644 Binary files a/source/docs/hardware/hardware-basics/images/robot-battery/edge-clip.png and b/source/docs/hardware/hardware-basics/images/robot-battery/edge-clip.png differ diff --git a/source/docs/hardware/hardware-basics/images/robot-battery/heatshrink.png b/source/docs/hardware/hardware-basics/images/robot-battery/heatshrink.png index 187fd1ad30..441510ff65 100644 Binary files a/source/docs/hardware/hardware-basics/images/robot-battery/heatshrink.png and b/source/docs/hardware/hardware-basics/images/robot-battery/heatshrink.png differ diff --git a/source/docs/hardware/hardware-basics/images/robot-battery/sb50-handle.png b/source/docs/hardware/hardware-basics/images/robot-battery/sb50-handle.png index 2a6e4b3bae..edfa8c02ea 100644 Binary files a/source/docs/hardware/hardware-basics/images/robot-battery/sb50-handle.png and b/source/docs/hardware/hardware-basics/images/robot-battery/sb50-handle.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/cancoder-status-lights.png b/source/docs/hardware/hardware-basics/images/status-lights/cancoder-status-lights.png index 7cf02fbbde..0adeb4248d 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/cancoder-status-lights.png and b/source/docs/hardware/hardware-basics/images/status-lights/cancoder-status-lights.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/dmc60c-status-lights.png b/source/docs/hardware/hardware-basics/images/status-lights/dmc60c-status-lights.png index 1bfd278e4f..c24c2619c7 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/dmc60c-status-lights.png and b/source/docs/hardware/hardware-basics/images/status-lights/dmc60c-status-lights.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/jaguar-status-light.png b/source/docs/hardware/hardware-basics/images/status-lights/jaguar-status-light.png index 5b2a160b5e..1645f11c69 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/jaguar-status-light.png and b/source/docs/hardware/hardware-basics/images/status-lights/jaguar-status-light.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/openmesh-radio-status-lights.png b/source/docs/hardware/hardware-basics/images/status-lights/openmesh-radio-status-lights.png index 18a0d175b5..3bc3de76cb 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/openmesh-radio-status-lights.png and b/source/docs/hardware/hardware-basics/images/status-lights/openmesh-radio-status-lights.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/pneumatic-hub.png b/source/docs/hardware/hardware-basics/images/status-lights/pneumatic-hub.png index 5bf410e01b..c55c68ae56 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/pneumatic-hub.png and b/source/docs/hardware/hardware-basics/images/status-lights/pneumatic-hub.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/power-distribution-hub.png b/source/docs/hardware/hardware-basics/images/status-lights/power-distribution-hub.png index 14f6ec0daa..c73f4159c6 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/power-distribution-hub.png and b/source/docs/hardware/hardware-basics/images/status-lights/power-distribution-hub.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/rev-robotics-servo-power-module.png b/source/docs/hardware/hardware-basics/images/status-lights/rev-robotics-servo-power-module.png index 586a24e2b6..de7fb2c758 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/rev-robotics-servo-power-module.png and b/source/docs/hardware/hardware-basics/images/status-lights/rev-robotics-servo-power-module.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/sd540b-status-lights.png b/source/docs/hardware/hardware-basics/images/status-lights/sd540b-status-lights.png index 2608071597..80ba509dee 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/sd540b-status-lights.png and b/source/docs/hardware/hardware-basics/images/status-lights/sd540b-status-lights.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/sd540c-status-lights.png b/source/docs/hardware/hardware-basics/images/status-lights/sd540c-status-lights.png index 072e9d7546..e22c428f33 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/sd540c-status-lights.png and b/source/docs/hardware/hardware-basics/images/status-lights/sd540c-status-lights.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/sparkLight.png b/source/docs/hardware/hardware-basics/images/status-lights/sparkLight.png index 20eeae71c9..b360abca2c 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/sparkLight.png and b/source/docs/hardware/hardware-basics/images/status-lights/sparkLight.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/sparkMAXLight.png b/source/docs/hardware/hardware-basics/images/status-lights/sparkMAXLight.png index c417c0fa04..941530f8ee 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/sparkMAXLight.png and b/source/docs/hardware/hardware-basics/images/status-lights/sparkMAXLight.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/spikeRelay1Light.png b/source/docs/hardware/hardware-basics/images/status-lights/spikeRelay1Light.png index e260ecafb9..d0951c0a6b 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/spikeRelay1Light.png and b/source/docs/hardware/hardware-basics/images/status-lights/spikeRelay1Light.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/spikeRelay2Light.png b/source/docs/hardware/hardware-basics/images/status-lights/spikeRelay2Light.png index 241db4bbdb..b0934158cf 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/spikeRelay2Light.png and b/source/docs/hardware/hardware-basics/images/status-lights/spikeRelay2Light.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/talon-srx-status-lights.png b/source/docs/hardware/hardware-basics/images/status-lights/talon-srx-status-lights.png index 1ad2d1e65f..353f1a3c8b 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/talon-srx-status-lights.png and b/source/docs/hardware/hardware-basics/images/status-lights/talon-srx-status-lights.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/talonsr-status-light.png b/source/docs/hardware/hardware-basics/images/status-lights/talonsr-status-light.png index 8bf84eae1c..1c0e9071d0 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/talonsr-status-light.png and b/source/docs/hardware/hardware-basics/images/status-lights/talonsr-status-light.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/venom.png b/source/docs/hardware/hardware-basics/images/status-lights/venom.png index 688aef18eb..4862a3098b 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/venom.png and b/source/docs/hardware/hardware-basics/images/status-lights/venom.png differ diff --git a/source/docs/hardware/hardware-basics/images/status-lights/victorSPLight.png b/source/docs/hardware/hardware-basics/images/status-lights/victorSPLight.png index a86d6eb109..a5ef5f75fa 100644 Binary files a/source/docs/hardware/hardware-basics/images/status-lights/victorSPLight.png and b/source/docs/hardware/hardware-basics/images/status-lights/victorSPLight.png differ diff --git a/source/docs/hardware/hardware-basics/images/wiring-pneumatics-ph/ph-subsystem.png b/source/docs/hardware/hardware-basics/images/wiring-pneumatics-ph/ph-subsystem.png index 346a3856a8..800e2514d7 100644 Binary files a/source/docs/hardware/hardware-basics/images/wiring-pneumatics-ph/ph-subsystem.png and b/source/docs/hardware/hardware-basics/images/wiring-pneumatics-ph/ph-subsystem.png differ diff --git a/source/docs/hardware/hardware-basics/index.rst b/source/docs/hardware/hardware-basics/index.rst index 3418223b49..2e60b3e6aa 100644 --- a/source/docs/hardware/hardware-basics/index.rst +++ b/source/docs/hardware/hardware-basics/index.rst @@ -1,5 +1,4 @@ -Hardware - Basics -================= +# Hardware - Basics .. toctree:: :maxdepth: 1 diff --git a/source/docs/hardware/hardware-basics/preemptive-troubleshooting.rst b/source/docs/hardware/hardware-basics/preemptive-troubleshooting.rst index 22b3352a5b..d096bd1918 100644 --- a/source/docs/hardware/hardware-basics/preemptive-troubleshooting.rst +++ b/source/docs/hardware/hardware-basics/preemptive-troubleshooting.rst @@ -1,14 +1,12 @@ .. include:: -Robot Preemptive Troubleshooting -================================ +# Robot Preemptive Troubleshooting .. note:: In *FIRST*\ |reg| Robotics Competition, robots take a lot of stress while driving around the field. It is important to make sure that connections are tight, parts are bolted securely in place and that everything is mounted so that a robot bouncing around the field does not break. -Check Battery Connections -------------------------- +## Check Battery Connections .. image:: images/preemptive-troubleshooting/preCheckBatt.png :alt: Trying to wiggle the battery terminal by hand. @@ -20,22 +18,19 @@ Wiggle battery harness connector. Often these are loose because the screws loose Apply considerable force onto the battery cable at 90 degrees to try to move the direction of the cable leaving the battery, if successful the connection was not tight enough to begin with and it should be redone. This :ref:`article ` has more detailed battery information. -Securing the Battery to the Robot ---------------------------------- +## Securing the Battery to the Robot .. image:: images/preemptive-troubleshooting/preCheckConnecc.png :alt: Disconnected battery of a robot mid match. :width: 350 -In almost every event we see at least one robot where a not properly secured battery connector (the large Anderson) comes apart and disconnects power from the robot. This has happened in championship matches on the Einstein and everywhere else. Its an easy to ensure that this doesn't happen to you by securing the two connectors by wrapping a tie wrap around the connection. 10 or 12 tie wraps for the piece of mind during an event is not a high price to pay to guarantee that you will not have the problem of this robot from an actual event after a bumpy ride over a defense. Also, secure your battery to the chassis with hook and loop tape or another method, especially in games with rough defense, obstacles or climbing. +In almost every event we see at least one robot where a not properly secured battery connector (the large Anderson) comes apart and disconnects power from the robot. This has happened in championship matches on the Einstein and everywhere else. Its an easy to ensure that this doesn't happen to you by securing the two connectors by wrapping a tie wrap around the connection. 10 or 12 tie wraps for the peace of mind during an event is not a high price to pay to guarantee that you will not have the problem of this robot from an actual event after a bumpy ride over a defense. Also, secure your battery to the chassis with hook and loop tape or another method, especially in games with rough defense, obstacles or climbing. -Securing the Battery Connector & Main Power Leads -------------------------------------------------- +## Securing the Battery Connector & Main Power Leads A loose robot-side battery connector (the large Anderson SB) can allow the main power leads to be tugged when the battery is replaced. If the main power leads are loose, that "tug" can get all the way back to the crimp lugs attached to the 120 Amp Circuit Breaker or Power Distribution Panel (PDP), bend the lug, and over time cause the lug end to break from fatigue. Putting a couple tie wraps attaching the main power leads to the chassis and bolting down the robot-side battery connector can prevent this, as well as make it easier to connect the battery. -Main Breaker (120 Amp Circuit Breaker) ---------------------------------------- +## Main Breaker (120 Amp Circuit Breaker) .. note:: Ensure nuts are tightened firmly and the breaker is attached to a rigid element. @@ -57,8 +52,7 @@ Because the metal stud is just molded into the case, every once in awhile you ma When subjected to multiple competition seasons, the Main Breaker is susceptible to fatigue damage from vibration and use, and can start opening under impact. Each time the thermal fuse function is triggered, it can become progressively easier to trip. Many veteran teams start each season with a fresh main breaker, and carry spares. -Power Distribution Panel (PDP) ------------------------------- +## Power Distribution Panel (PDP) .. image:: images/preemptive-troubleshooting/pdp-bolts.png :alt: Battery terminals on the PDP. @@ -67,8 +61,7 @@ Power Distribution Panel (PDP) Make sure that split washers were placed under the PDP screws, but it is not easy to visually confirm, and sometimes you can’t. You can check by removing the case. Also if you squeeze the red and black wires together, sometimes you can catch the really lose connections. -Tug Testing ------------ +## Tug Testing .. image:: images/preemptive-troubleshooting/preCheckTug-1.png :alt: Tug test on the roboRIO power. @@ -84,8 +77,7 @@ Look for possible or impending shorts with Weidmuller connections that are close Spade connectors can also fail due to improper crimps, so tug-test those as well. -Blade Fuses ------------ +## Blade Fuses Be sure to place the 20A fuse (yellow) on the left and the 10A fuse (red) on the right. @@ -101,42 +93,35 @@ Be sure to place the 20A fuse (yellow) on the left and the 10A fuse (red) on the If you can remove the blade fuses by hand then they are not in completely. Make sure that they are completely seated in the PDP so that they don't pop out during robot operation. -roboRIO swarf -------------- +## roboRIO swarf Swarf is fine chips or filings of stone, metal, or other material produced by a machining operation. Often modifications must be made to a robot while the control system parts are in place. The circuit board for the roboRIO is conformally coated, but that doesn't absolutely guarantee that metal chips won't short out traces or components inside the case. In this case, you must exercise care in making sure that none of the chips end up in the roboRIO or any of the other components. In particular, the exposed 3 pin headers are a place where chips can enter the case. A quick sweep through each of the four sides with a flashlight is usually sufficient to find the really bad areas of infiltration. -Radio Barrel Jack ------------------ +## Radio Barrel Jack -Make sure the correct barrel jack is used, not one that is too small and falls out for no reason. This isn’t common, but ask an FTA and every once in awhile a team will use some random barrel jack that is not sized correctly, and it falls out in a match on first contact. +Make sure the correct barrel jack is used, not one that is too small and falls out for no reason. This isn’t common, but ask an :term:`FTA` and every once in awhile a team will use some random barrel jack that is not sized correctly, and it falls out in a match on first contact. -Ethernet Cable --------------- +## Ethernet Cable If the RIO to radio ethernet cable is missing the clip that locks the connector in, get another cable. This is a common problem that will happen several times in every competition. Make sure that your cables are secure. The clip often breaks off, especially when pulling it through a tight path, it snags on something then breaks. -Loose Cables ------------- +## Loose Cables Cables must be tightened down, particularly the radio power and ethernet cable. The radio power cables don’t have a lot of friction force and will fall out (even if it is the correct barrel) if the weight of the cable-slack is allowed to swing freely. Ethernet cable is also pretty heavy, if it’s allowed to swing freely, the plastic clip may not be enough to hold the ethernet pin connectors in circuit. -Reproducing Problems in the Pit -------------------------------- +## Reproducing Problems in the Pit Beyond the normal shaking of cables whilst the robot is powered and tethered, it is suggested that one side of the robot be picked up and dropped. Driving on the field, especially against defenders, will often be very violent, and this helps makes sure nothing falls out. It is better for the robot to fail in the pits rather than in the middle of a match. When doing this test it’s important to be ethernet tethered and not USB tethered, otherwise you are not testing all of the critical paths. -Check Firmware and Versions ---------------------------- +## Check Firmware and Versions Robot inspectors do this, but you should do it as well, it helps robot inspectors out and they appreciate it. And it guarantees that you are running with the most recent, bug fixed code. You wouldn't want to lose a match because of an out of date piece of control system software on your robot. -Driver Station Checks ---------------------- +## Driver Station Checks We often see problems with the Drivers Station. You should: @@ -149,8 +134,7 @@ We often see problems with the Drivers Station. You should: - Close all apps except for DS/Dashboard when out on the field. - Verify that there is nothing unnecessary running in the application tray in the start menu (bottom right side) -Handy Tools ------------ +## Handy Tools .. image:: images/preemptive-troubleshooting/tools.png :alt: A Wago screwdriver and flashlight. diff --git a/source/docs/hardware/hardware-basics/robot-battery.rst b/source/docs/hardware/hardware-basics/robot-battery.rst index b0a91ffb85..68a545cecf 100644 --- a/source/docs/hardware/hardware-basics/robot-battery.rst +++ b/source/docs/hardware/hardware-basics/robot-battery.rst @@ -1,17 +1,14 @@ .. include:: -Robot Battery Basics -==================== +# Robot Battery Basics The power supply for an FRC\ |reg| robot is a single 12V 18Ah SLA (Sealed Lead Acid) non-spillable battery, capable of briefly supplying over 180A and arcing over 500A when fully charged. The Robot Battery assembly includes the :term:`COTS` battery, lead cables with contacts, and Anderson SB connector. Teams are encouraged to have multiple Robot Batteries. -COTS Battery ------------- +## COTS Battery The Robot Rules in the Game Manual specify a COTS non-spillable sealed lead acid battery meeting specific criteria, and gives examples of legal part numbers from a variety of vendors. -Battery Safety & Handling -------------------------- +## Battery Safety & Handling A healthy battery is **always** "On" and the terminals are **always** energized. If the polarities short together - for example, a wrench or aerosol can falls and bridges the gap between two bare terminals - all the stored energy will be released in a dangerous arc. This risk drives a wide range of best practices, such as covering terminals in storage, only uncovering and working on one terminal or polarity at a time, keeping SB contacts fully inserted in connectors, etc. @@ -31,16 +28,13 @@ Damaged batteries should be safely disposed of as soon as possible. All retailer .. danger:: **DO NOT** attempt to "repair" damaged or non-functional batteries. -Battery Construction & Tools ------------------------------ +## Battery Construction & Tools -Battery Leads -^^^^^^^^^^^^^ +### Battery Leads Battery leads must be copper, minimum size (cross section) 6 AWG (16mm2, 7 SWG) and maximum length 12", color coded for polarity, with an Anderson SB connector. Standard 6AWG copper leads with Pink/Red SB50 battery leads often come in the Kit of Parts and are sold by FRC vendors. -Lead Cables -~~~~~~~~~~~ +#### Lead Cables Tinned, annealed, or coated copper is allowed. Do not use CCA (copper clad aluminum), aluminum, or other non-copper base metal. The conductor metal is normally printed on the outside of the insulation with the other cable ratings. @@ -48,24 +42,21 @@ Wire size 6AWG is sufficient for almost all robots and fits standard SB50 contac Higher strand count wire (sometimes sold as "Flex" or "welding wire") has a smaller bend radius, which makes it easier to route, and a higher fatigue limit. There is no strand count requirement, but 84/25 (84 strand "flex" hookup wire) and 259/30 (259 strand "welding wire") will both be *much* easier to work with than 19/0.0372 (19 strand hookup wire). -The insulation must be color-coded per the Game Manual: as of 2021, the +12Vdc wire must be red, white, brown, yellow, or black w/stripe and the ground wire (return wire) must be black or blue. There is no explicit insulation temperature rating requirement, but any blackened or damaged insulation means the wire needs to be replaced: off hand, 105C is plenty and lower will work for almost all robots. There is no insulation voltage rating requirement, lower is better for thinner insulation. +The insulation must be color-coded per the Game Manual: the +12Vdc wire must be red, white, brown, yellow, or black w/stripe and the ground wire (return wire) must be black or blue. There is no explicit insulation temperature rating requirement, but any blackened or damaged insulation means the wire needs to be replaced: off hand, 105C is plenty and lower will work for almost all robots. There is no insulation voltage rating requirement, lower is better for thinner insulation. -SB Connector -~~~~~~~~~~~~~ +#### SB Connector The Anderson SB Connector may be the standard Pink/Red SB50, or another Anderson SB connector. Teams are *STRONGLY* recommended to use the Pink/Red SB50 for interoperability: the other colors and sizes of housings will not intermate, and you will be unable to borrow batteries or chargers. Follow manufacturer's instructions to crimp contacts and assemble the leads into Anderson SB connectors. A small flathead screwdriver can help to insert the contacts (push on the contact, not on the wire insulation), or it can help to disengage the internal latch if the contact is in the wrong slot or upside down. -Battery Lugs -^^^^^^^^^^^^^ +### Battery Lugs Compression lugs ("crimp lugs") for #10 bolt (or M5) battery tabs (~0.2" or ~5mm hole diameter) are available online and through electrical supply houses, sold by the accepted wire sizes in AWG (or mm2) and post diameter ("bolt size", "hole diameter"). Higher end vendors will also distinguish between Standard (~19) and Flex (>80) strand counts in their lug catalogs. Some vendors also offer right angle lugs, in addition to more common straight styles. Follow manufacturer's instructions to crimp the lugs. Screw terminal lugs are legal, but not recommended. If using screw terminal lugs, use the correct tip size screwdriver to tighten the terminal. Check the terminal tightness frequently because they may loosen over time. -Battery Lead Lug To Post Connection -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Battery Lead Lug To Post Connection A #10 or M5 nut & bolt connect the battery lead lug to the battery tab. @@ -86,8 +77,7 @@ This connection must also be completely covered for electrical safety; electrica .. image:: images/robot-battery/heatshrink.png :alt: One terminal of an FRC battery fully covered in heatshrink. -Battery Chargers -^^^^^^^^^^^^^^^^^^^ +### Battery Chargers There are many good COTS "smart" battery chargers designed for 12V SLA batteries, rated for 6A or less per battery, with 'maintenance mode' features. Chargers rated over 6A are not allowed in FRC pits. @@ -97,8 +87,7 @@ Chargers used at competition are required to use Anderson SB connectors. Attachi Some FRC vendors sell chargers with red SB50 connectors pre-attached. -Battery Evaluation Tools -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Battery Evaluation Tools **Battery Charger** @@ -125,8 +114,7 @@ Ideal internal resistance should be less than 0.015 Ohms. The manufacturer speci If a battery shows significantly lower voltages at the higher test current loads, it may not be done charging, or it may need to be retired. -Understanding Battery Voltages ------------------------------- +## Understanding Battery Voltages A "12V battery" is anything but 12.0V. @@ -138,8 +126,7 @@ Batteries reading 12.5V on an idle robot should be swapped and charged before a Battery voltage and current also depends on temperature: cool batteries are happy batteries. -Battery Characterization -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Battery Characterization A battery analyzer can be used to give a detailed inspection and comparison of battery performance. @@ -150,8 +137,7 @@ It will provide graphs of battery performance over time. This test takes signifi At the standard 7.5 amps test load, competition batteries should have at least a 11.5 amp hour rating. Anything less than that should only be used for practice or other less demanding use cases. -Battery Longevity -^^^^^^^^^^^^^^^^^^^ +### Battery Longevity A battery is rated for about 1200 normal charge/recharge cycles. The high currents required for an FRC match reduce that lifespan to about 400 cycles. These cycles are intended to be relatively low discharge, from around 13.5 down to 12 or 12.5 volts. Deep cycling the battery (running it all the way down) will damage it. @@ -159,8 +145,7 @@ Batteries last the longest if they are kept fully charged when not in use, eithe Batteries need to be kept away from both extreme heat and cold. This generally means storing the batteries in a climate controlled area: a classroom closet is usually fine, a parking lot shipping container is more risky. -Battery Best Practices ----------------------- +## Battery Best Practices - Only use a charged battery for competition matches. If you are in a situation where you have run out of charged batteries, please ask a veteran team for help! Nobody wants to see a robot dead on the field (:ref:`brownout `) due to a bad or uncharged battery. diff --git a/source/docs/hardware/hardware-basics/status-lights-ref.rst b/source/docs/hardware/hardware-basics/status-lights-ref.rst index 6e608805c3..6a886d08d5 100644 --- a/source/docs/hardware/hardware-basics/status-lights-ref.rst +++ b/source/docs/hardware/hardware-basics/status-lights-ref.rst @@ -1,12 +1,12 @@ .. include:: -Status Light Quick Reference -============================ +# Status Light Quick Reference Many of the components of the FRC\ |reg| Control System have indicator lights that can be used to quickly diagnose problems with your robot. This guide shows each of the hardware components and describes the meaning of the indicators. Photos and information from Innovation FIRST and Cross the Road Electronics. -Robot Signal Light (RSL) ------------------------- +A compact and printable `Status Light Quick Reference `_ is available. + +## Robot Signal Light (RSL) .. image:: images/status-lights/rsl.svg :alt: Robot Signal Light with wiring. @@ -20,8 +20,7 @@ Robot Signal Light (RSL) | Off | Robot Off, roboRIO not powered or RSL not wired properly | +----------+----------------------------------------------------------+ -roboRIO -------- +## roboRIO .. image:: images/status-lights/roborio-status-lights.svg :alt: roboRIO status lights highlighted. @@ -65,8 +64,7 @@ roboRIO | **RSL** | `See above <#robot-signal-light-rsl>`_ | +------------+----------------------------------------------------------------------------------------------------+ -OpenMesh Radio --------------- +## OpenMesh Radio .. image:: images/status-lights/openmesh-radio-status-lights.png :alt: Radio with the Wifi, Eth Link (2), and Power ports labeled. @@ -91,15 +89,13 @@ OpenMesh Radio | | Green | Bridge mode, Linked | +----------+---------------+------------------------------+ -Power Distribution Panel ------------------------- +## Power Distribution Panel .. image:: images/status-lights/pdp-status-lights.svg :alt: The location of the "STAT" and "COMM" lights on the PDP. :width: 600 -PDP Status/Comm LEDs -^^^^^^^^^^^^^^^^^^^^ +### PDP Status/Comm LEDs +--------+--------------------------+---------------------------+ | LED | Strobe | Slow | @@ -113,10 +109,9 @@ PDP Status/Comm LEDs .. tip:: If a PDP LED is showing more than one color, see the PDP LED special states table below. For more information on resolving PDP faults see the PDP User Manual. -.. note:: Note that the No CAN Comm fault will occur if the PDP cannot communicate with the roboRIO via CAN Bus. +.. note:: Note that the No :term:`CAN` Comm fault will occur if the PDP cannot communicate with the roboRIO via CAN Bus. -PDP Special States -^^^^^^^^^^^^^^^^^^ +### PDP Special States +--------------+------------------------------+ | LED Colors | Problem | @@ -128,8 +123,7 @@ PDP Special States | No LED | No Power/ Incorrect Polarity | +--------------+------------------------------+ -Power Distribution Hub ----------------------- +## Power Distribution Hub .. image:: images/status-lights/power-distribution-hub.png :alt: REV Power Distribution Hub with Status LED, and Channel LEDs Highlighted @@ -137,8 +131,7 @@ Power Distribution Hub .. note:: These led patterns only apply to firmware version 21.1.7 and later -PDH Status LED --------------- +## PDH Status LED +-------------------------+--------------------------------------------------------+ | LED Color | Status | @@ -162,8 +155,7 @@ PDH Status LED | Orange/Magenta Blinking | Device Over Current | +-------------------------+--------------------------------------------------------+ -Channel LEDs -^^^^^^^^^^^^ +### Channel LEDs +--------------+----------------------------------------------------------------------------------------------------------+ | LED Color | Status | @@ -175,8 +167,7 @@ Channel LEDs | Red Blinking | Sticky fault on the channel. Check for tripped circuit breaker / fuse. | +--------------+----------------------------------------------------------------------------------------------------------+ -Voltage Regulator Module ------------------------- +## Voltage Regulator Module .. image:: images/status-lights/vrm-status-lights.svg :alt: Highlights the position of the 12V and 5V status lights. @@ -184,15 +175,13 @@ Voltage Regulator Module The status LEDs on the VRM indicate the state of the two power supplies. If the supply is functioning properly the LED should be lit bright green. If the LED is not lit or is dim, the output may be shorted or drawing too much current. -Pneumatics Control Module (PCM) -------------------------------- +## Pneumatics Control Module (PCM) .. image:: images/status-lights/pcm-status-lights.svg :alt: Highlights both the "Status" and "Comp" lights in the middle and the individual channel lights on the sides. :width: 400 -PCM Status LED -^^^^^^^^^^^^^^ +### PCM Status LED +--------+---------------+-------------------------------+------------------+ | LED | Strobe | Slow | Long | @@ -210,8 +199,7 @@ PCM Status LED .. note:: Note that the No CAN Comm fault will not occur only if the device cannot communicate with any other device, if the PCM and PDP can communicate with each other, but not the roboRIO. -PCM LED Special States Table -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### PCM LED Special States Table +--------------+-----------------------------+ | LED | Problems | @@ -223,18 +211,15 @@ PCM LED Special States Table | No LED | No Power/Incorrect Polarity | +--------------+-----------------------------+ -PCM Comp LED -^^^^^^^^^^^^ +### PCM Comp LED This is the Compressor LED. This LED is green when the compressor output is active (compressor is currently on) and off when the compressor output is not active. -PCM Solenoid Channel LEDs -^^^^^^^^^^^^^^^^^^^^^^^^^ +### PCM Solenoid Channel LEDs These LEDs are lit red if the Solenoid channel is enabled and not lit if it is disabled. -Pneumatic Hub -------------- +## Pneumatic Hub .. image:: images/status-lights/pneumatic-hub.png :alt: REV Pneumatic Hub with Status LED, Compressor LED, and Solenoid LEDs highlighted @@ -242,8 +227,7 @@ Pneumatic Hub .. note:: These led patterns only apply to firmware version 21.1.7 and later -PH Status LED -^^^^^^^^^^^^^ +### PH Status LED +-------------------------+--------------------------------------------------+ | LED Color | Status | @@ -267,8 +251,7 @@ PH Status LED | Orange/Green Blinking | Orange/Green Blinking | +-------------------------+--------------------------------------------------+ -Compressor LED -^^^^^^^^^^^^^^ +### Compressor LED +-------------+----------------+ | LED Color | Status | @@ -278,8 +261,7 @@ Compressor LED | Black Solid | Compressor Off | +-------------+----------------+ -Solenoid LEDs -^^^^^^^^^^^^^ +### Solenoid LEDs +-------------+--------------+ | LED Color | Status | @@ -289,15 +271,13 @@ Solenoid LEDs | Black Solid | Solenoid Off | +-------------+--------------+ -Talon SRX & Victor SPX & Talon FX Motor Controllers ---------------------------------------------------- +## Talon SRX & Victor SPX & Talon FX Motor Controllers .. image:: images/status-lights/talon-srx-status-lights.png :alt: Status LEDs on either side of the center of the Talon SRX. :width: 600 -Status LEDs During Normal Operation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Status LEDs During Normal Operation +------------------------------+----------------+--------------------------------------------+ | LEDs | Colors | Talon SRX State | @@ -327,8 +307,7 @@ Status LEDs During Normal Operation | LEDs Strobe towards (M+) | Off/Orange | Thermal Fault / Shutoff (Talon FX Only) | +------------------------------+----------------+--------------------------------------------+ -Status LEDs During Calibration -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Status LEDs During Calibration +------------------------+------------------------+ | Status LEDs Blink Code | Talon SRX State | @@ -340,8 +319,7 @@ Status LEDs During Calibration | Blinking Red | Failed Calibration | +------------------------+------------------------+ -B/C CAL Blink Codes -^^^^^^^^^^^^^^^^^^^ +### B/C CAL Blink Codes +----------------------+-----------------+ | B/C CAL Button Color | Talon SRX State | @@ -351,20 +329,17 @@ B/C CAL Blink Codes | Off | Coast Mode | +----------------------+-----------------+ -SPARK-MAX Motor Controller --------------------------- +## SPARK-MAX Motor Controller .. image:: images/status-lights/sparkMAXLight.png :alt: Table listing the SPARKMAX blink codes. -REV Robotics SPARK ------------------- +## REV Robotics SPARK .. image:: images/status-lights/sparkLight.png :alt: Table of the SPARK blink codes. -Victor-SP Motor Controller --------------------------- +## Victor-SP Motor Controller .. image:: images/status-lights/victorSPLight.png :alt: Status LEDs on either side of the center of the Victor-SP. @@ -372,8 +347,7 @@ Victor-SP Motor Controller Brake/Coast/Cal Button/LED - Red if the controller is in brake mode, off if the controller is in coast mode -Status -^^^^^^ +### Status +-----------+----------+------------------------------------------------------------------------+ | Green | Solid | Full forward output | @@ -391,8 +365,7 @@ Status | | | calibration, and red several times indicates unsuccessful calibration. | +-----------+----------+------------------------------------------------------------------------+ -Talon Motor Controller ----------------------- +## Talon Motor Controller .. image:: images/status-lights/talonsr-status-light.png :alt: Talon motor controller with a single multicolor LED in the bottom right corner. @@ -417,8 +390,7 @@ Talon Motor Controller | | | calibration, and red several times indicates unsuccessful calibration. | +-----------+----------+------------------------------------------------------------------------+ -Victor888 Motor Controller --------------------------- +## Victor888 Motor Controller .. image:: images/status-lights/victor888-status-light.svg :alt: Victor888 motor controller with a single multicolor LED in the bottom right corner. @@ -438,8 +410,7 @@ Victor888 Motor Controller | Red/Green | Blinking | Calibration mode | +-----------+----------+--------------------------+ -Jaguar Motor Controller ------------------------ +## Jaguar Motor Controller .. image:: images/status-lights/jaguar-status-light.png :alt: Jaguar motor controller with a single multicolor LED in the bottom center. @@ -487,8 +458,7 @@ Jaguar Motor Controller | | awaiting valid ID assignment | +------------------------------+------------------------------------------------+ -Digilent DMC-60 ---------------- +## Digilent DMC-60 .. image:: images/status-lights/dmc60c-status-lights.png :alt: The 5 LEDs in each of the corners plus the center. @@ -532,8 +502,7 @@ At power-on, the RGB LEDs illuminate Blue, continually getting brighter. This la | The specified control mode resulted in a Negative Duty Cycle being Motor Output | The LED update rate is proportional to the duty cycle of the output and increases with increased duty cycle. At 100% duty cycle, all 4 LEDs are illuminated Red. | +-------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -Fault Color Indicators -^^^^^^^^^^^^^^^^^^^^^^ +### Fault Color Indicators When a fault condition is detected, the output duty cycle is reduced to 0% and a fault is signaled. The output then remains disabled for 3 seconds. During this time the onboard LEDs (LED1-4) are used to indicate the fault condition. The fault condition is indicated by toggling between the top (LED1 and LED2) and bottom (LED3 and LED4) LEDs being illuminated Red and off. The color of the bottom LEDs depends on which faults are presently active. The table below describes how the color of the bottom LEDs maps to the presently active faults. @@ -547,19 +516,16 @@ When a fault condition is detected, the output duty cycle is reduced to 0% and a | Cyan / Aqua | On | On | +-------------+------------------+---------------+ -Break/Coast Mode -^^^^^^^^^^^^^^^^ +### Break/Coast Mode When the center LED is off the device is operating in coast mode. When the center LED is illuminated the device is operating in brake mode. The Brake/Coast mode can be toggled by pressing down on the center of the triangle and then releasing the button. -Venom Motor Controller ----------------------- +## Venom Motor Controller .. image:: images/status-lights/venom.png :alt: The LED blink codes for the Venom. -Mindsensors SD540B (PWM) ------------------------- +## Mindsensors SD540B (PWM) .. image:: images/status-lights/sd540b-status-lights.png :alt: The two LEDs on the top one at each end and the LED on the side next to the connector. @@ -579,8 +545,7 @@ Mindsensors SD540B (PWM) | | Green | Valid PWM signal is detected | +----------------+-------+---------------------------------+ -Mindsensors SD540C (CAN Bus) ----------------------------- +## Mindsensors SD540C (CAN Bus) .. image:: images/status-lights/sd540c-status-lights.png :alt: The two LEDs on the top one at each end and the LED on the side next to the connector. @@ -600,17 +565,15 @@ Mindsensors SD540C (CAN Bus) | | Off | Connected to the roboRIO and the driver station is open | +----------------+------------------+---------------------------------------------------------+ -REV Robotics Servo Power Module -------------------------------- +## REV Robotics Servo Power Module .. image:: images/status-lights/rev-robotics-servo-power-module.png :alt: One power LED and a status LED for each channel all down the center. :width: 400 -Status LEDs -^^^^^^^^^^^ +### Status LEDs -Each channel has a corresponding status LED that will indicate the sensed state of the connected PWM signal. The table below describes each state’s corresponding LED pattern. +Each channel has a corresponding status LED that will indicate the sensed state of the connected :term:`PWM` signal. The table below describes each state’s corresponding LED pattern. +-----------------------+----------------+ | State | Pattern | @@ -626,8 +589,7 @@ Each channel has a corresponding status LED that will indicate the sensed state - 6V Power LED off, dim or flickering with power applied = Over-current shutdown -Spike relay configured as a motor, light, or solenoid switch ------------------------------------------------------------- +## Spike relay configured as a motor, light, or solenoid switch .. image:: images/status-lights/spikeRelay1Light.png :alt: Single LED in the corner. @@ -649,8 +611,7 @@ Spike relay configured as a motor, light, or solenoid switch .. note:: 'Brake Condition' refers to the dynamic stopping of the motor due to the shorting of the motor inputs. This condition is not optional when going to an off state. -Spike relay configured as for one or two solenoids --------------------------------------------------- +## Spike relay configured as for one or two solenoids .. image:: images/status-lights/spikeRelay2Light.png :alt: Single LED in the corner. @@ -670,8 +631,7 @@ Spike relay configured as for one or two solenoids | On | On | +12v | +12v | Off | Both Solenoids ON | +---------------------+-------------------+--------+--------+---------------+--------------------------------+ -CANCoder Encoder ----------------- +## CANCoder Encoder .. image:: images/status-lights/cancoder-status-lights.png :alt: LED at the top of the encoder. diff --git a/source/docs/hardware/hardware-basics/wiring-best-practices.rst b/source/docs/hardware/hardware-basics/wiring-best-practices.rst index 8c6665a85e..ce0262e120 100644 --- a/source/docs/hardware/hardware-basics/wiring-best-practices.rst +++ b/source/docs/hardware/hardware-basics/wiring-best-practices.rst @@ -1,12 +1,10 @@ .. include:: -Wiring Best Practices -======================== +# Wiring Best Practices .. tip:: The article :doc:`Intro to FRC Robot Wiring ` walks through the details of what connects where to wire up the FRC Control System and this article provides some additional "Best Practices" that may increase reliability and make maintenance easier. Take a look at :doc:`Preemptive Troubleshooting ` for more tips and tricks. -Vibration/Shock ------------------- +## Vibration/Shock An FRC\ |reg| Robot is an incredibly rough environment when it comes to vibrations and shock loads. While many of the FRC specific electronics are extensively tested for mechanical robustness in these conditions, a few components, such as the radio, are not specifically designed for use on a mobile platform. Taking steps to reduce the shock and vibration these components are exposed to may help reduce failures. Some suggestions that may reduce mechanical failures: @@ -15,18 +13,15 @@ An FRC\ |reg| Robot is an incredibly rough environment when it comes to vibratio - Bumpers - Use Bumpers to cover as much of the robot as possible for your design. While the rules require specific bumper coverage around the corners of your robot, maximizing the use of bumpers increases the likelihood that all collisions will be damped by your bumpers. Bumpers significantly reduce the g-forces experienced in a collision compared to hitting directly on a hard robot surface, reducing the shock experienced by the electronics and decreasing the chance of a shock related failure. - Shock Mounting - You may choose to shock mount some or all of your electronic components to further reduce the forces they see in robot collisions. This is especially helpful for the robot radio and other electronics such as co-processors, which may not be designed for use on mobile platforms. Vibration isolators, springs, foams, or mounting to flexible materials all may reduce the shock forces seen by these components. -Redundancy ------------ +## Redundancy Unfortunately there are few places in the FRC Control System where redundancy is feasible. Taking advantage of opportunities for redundancy can increase reliability. The primary example of this is wiring the barrel connector to the radio in addition to the provided PoE connection. This ensures that if one of the cables becomes damaged or dislodged, the other will maintain power to the radio. Keep an eye out for other potential areas to provide redundancy when wiring and programming your robot. -Port Savers ------------ +## Port Savers For any connections on the Robot or Driver station that may be frequently plugged and unplugged (such as DS joysticks, DS Ethernet, roboRIO USB tether, and Ethernet tether) using a "Port Saver" or "pigtail" can substantially reduce the potential for damaging the port. This type of device can serve double duty, both reducing the number of cycles that the port on the electronic device sees, as well as relocating the connection to a more convenient location. Make sure to secure the port saver (see the next item) to avoid port damage. -Wire Management and Strain Relief ---------------------------------- +## Wire Management and Strain Relief One of the most critical components to robot reliability and maintenance is good wire management and strain relief. Good wire management is comprised of a few components: @@ -35,20 +30,17 @@ One of the most critical components to robot reliability and maintenance is good - Secure cables near any moving components. Make sure that all wire runs are secure and protected from moving components, even if the moving components were to bend or over-travel. - Secure cables at additional points as necessary to keep wiring neat and clean. Take care to not over secure wires; if wires are secured in too many locations, it may actually make troubleshooting and maintenance more difficult. -Documentation ------------------------ +## Documentation A great way to make maintenance easier is to create documentation describing what is connected where on the robot. There are a number of ways of creating this type of documentation which range from complete wiring diagrams to excel charts to a quick list of what functions are attached to which channels. Many teams also integrate these lists with labeling (see the next bullet). When a wire is accidentally cut, or a mechanism is malfunctioning, or a component burns out, it will be much easier to repair if you have some documentation to tell you what is connected where without having to trace the wiring all the way through (even if your wiring is neat!) -Labeling --------- +## Labeling Labeling is a great way to supplement the wiring documentation described above. There are many different strategies to labeling wiring and electronics, all with their own pros and cons. Labels for electronics and flags for wires can be made by hand, or using a label maker (some can also do heat-shrink labels), or you can use different colors of electrical tape or labeling flags to indicate different things. Whatever system you choose, make sure you understand how it complements your documentation and make sure everyone on your team is familiar with it. -Check all wiring and connections ----------------------------------- +## Check all wiring and connections After all wiring on the robot is complete, make sure to check each connection, pulling on each, to ensure that everything is secure. Additionally, ensure that no stray wire "whiskers" are sticking out of any connection point and that no uninsulated connections are exposed. If any connections come loose while testing, or any "whiskers" are discovered, re-make the connection and make sure to have a second person check it when complete. @@ -58,15 +50,13 @@ Another common source of failures is the fuses at the end of the PDP. Ensure the Snap-in connections such as the SB-50 connector should be secured using clips or cable ties to ensure they do not pop loose during impacts. -Re-Check Early and Often ----------------------------------- +## Re-Check Early and Often Re-check the entire electrical system as thoroughly as possible after playing the first match or two (or doing very vigorous testing). The first few impacts the robot sees may loosen fasteners or expose issues. Create a checklist for re-checking electrical connections on a regular basis. As a very rough starting point, rotational fasteners such as battery and PDP connections should be checked every 1-3 matches. Spring type connections such as the WAGO and Weidmuller connectors likely only need to be checked once per event. Ensure that the team knows who is responsible for completing the checklist and how they will document that it has been done. -Battery Maintenance ----------------------- +## Battery Maintenance Take good care of your batteries! A bad battery can easily cause a robot to functional poorly, or not at all, during a match. Label all of your batteries @@ -78,7 +68,6 @@ information such as the age of the battery on this label. - Rotate batteries evenly. This helps ensure that batteries have the most time to charge and rest and that they wear evenly (equal number of charge/discharge cycles) - Load test batteries if possible to monitor health. There are a number of commercially available products teams use to load test batteries, including at least one designed specifically for FRC. A load test can provide an indicator of battery health by measuring internal resistance. This measurement is much more meaningful when it comes to match performance than a simple no-load voltage number provided by a multimeter. -Check DS Logs ----------------------- +## Check DS Logs After each match, review the DS logs to see what the battery voltage and current usage looks like. Once you have established what the normal range of these items is for your robot, you may be able to spot potential issues (bad batteries, failing motors, mechanical binding) before they become critical failures. diff --git a/source/docs/hardware/hardware-basics/wiring-pneumatics-pcm.rst b/source/docs/hardware/hardware-basics/wiring-pneumatics-pcm.rst index daffcf67bc..6809968b66 100644 --- a/source/docs/hardware/hardware-basics/wiring-pneumatics-pcm.rst +++ b/source/docs/hardware/hardware-basics/wiring-pneumatics-pcm.rst @@ -1,19 +1,16 @@ -Wiring Pneumatics - CTRE Pneumatic Control Module -================================================= +# Wiring Pneumatics - CTRE Pneumatic Control Module This page describes wiring pneumatics with the CTRE Pneumatic Control Module (PCM). For instructions on wiring pneumatics with the REV Pneumatic Hub (PH) see :doc:`this page `. -.. hint:: For pneumatics safety & mechanical requirements, consult this year's Robot Construction rules. For mechanical design guidelines, the FIRST Pneumatics Manual is located `here `__ +.. hint:: For pneumatics safety & mechanical requirements, consult this year's Robot Construction rules. For mechanical design guidelines, the FIRST Pneumatics Manual is located [here](https://www.firstinspires.org/sites/default/files/uploads/resource_library/frc/technical-resources/frc_pneumatics_manual.pdf) -Wiring Overview ---------------- +## Wiring Overview -A single PCM will support most pneumatics applications, providing an output for the compressor, input for the pressure switch, and outputs for up to 8 solenoid channels (12V or 24V selectable). The module is connected to the roboRIO over the CAN bus and powered via 12V from the PDP or PDH. +A single PCM will support most pneumatics applications, providing an output for the compressor, input for the pressure switch, and outputs for up to 8 solenoid channels (12V or 24V selectable). The module is connected to the roboRIO over the :term:`CAN` bus and powered via 12V from the PDP or PDH. For complicated robot designs requiring more channels or multiple solenoid voltages, additional PCMs or PHs can be added to the control system. -PCM Power and Control Wiring ----------------------------- +## PCM Power and Control Wiring .. image:: images/wiring-pneumatics-pcm/pcm-subsystem.svg :alt: Pneumatics wiring diagram showing all of the connections to the PCM. @@ -22,23 +19,19 @@ The first PCM on your robot can be wired from the PDP VRM/PCM connectors on the Additional PCMs can be wired to a standard WAGO connector on the side of the PDP and protected with a 20A or smaller circuit breaker. Additional PCMs should also be placed anywhere in the middle of the CAN chain. -The Compressor --------------- +## The Compressor The compressor can be wired directly to the Compressor Out connectors on the PCM. If additional length is required, make sure to use 18 AWG wire or larger for the extension. -The Pressure Switch -------------------- +## The Pressure Switch The pressure switch should be connected directly to the pressure switch input terminals on the PCM. There is no polarity on the input terminals or on the pressure switch itself, either terminal on the PCM can be connected to either terminal on the switch. Ring or spade terminals are recommended for the connection to the switch screws (note that the screws are slightly larger than #6, but can be threaded through a ring terminal with a hole for a #6 screw such as the terminals shown in the image). -Solenoids ---------- +## Solenoids Each solenoid channel should be wired directly to a numbered pair of terminals on the PCM. A single acting solenoid will use one numbered terminal pair. A double acting solenoid will use two pairs. If your solenoid does not come with color coded wiring, check the datasheet to make sure to wire with the proper polarity. -Solenoid Voltage Jumper ------------------------ +## Solenoid Voltage Jumper .. image:: images/wiring-pneumatics-pcm/pcm-jumper.svg :alt: The "VSOL" jumper in the center of the PCM with the 12V and 24V showing the left and right positions of the jumper respectively. diff --git a/source/docs/hardware/hardware-basics/wiring-pneumatics-ph.rst b/source/docs/hardware/hardware-basics/wiring-pneumatics-ph.rst index f6c5bc19c9..26b71d637f 100644 --- a/source/docs/hardware/hardware-basics/wiring-pneumatics-ph.rst +++ b/source/docs/hardware/hardware-basics/wiring-pneumatics-ph.rst @@ -1,9 +1,8 @@ -Wiring Pneumatics - REV Pneumatic Hub -===================================== +# Wiring Pneumatics - REV Pneumatic Hub -This page describes wiring pneumatics with the REV Pneumatic Hub (PH). For instructions on wiring pneumatics with the CTRE Pneumatic Control Module (PCM) see :doc:`this page `. +This page describes wiring pneumatics with the REV Pneumatic Hub (:term:`PH`). For instructions on wiring pneumatics with the CTRE Pneumatic Control Module (:term:`PCM`) see :doc:`this page `. -.. hint:: For pneumatics safety & mechanical requirements, consult this year's Robot Construction rules. For mechanical design guidelines, the FIRST Pneumatics Manual is located `here `__ +.. hint:: For pneumatics safety & mechanical requirements, consult this year's Robot Construction rules. For mechanical design guidelines, the FIRST Pneumatics Manual is located [here](https://www.firstinspires.org/sites/default/files/uploads/resource_library/frc/technical-resources/frc_pneumatics_manual.pdf) .. raw:: html @@ -13,15 +12,13 @@ This page describes wiring pneumatics with the REV Pneumatic Hub (PH). For instr ---- -Wiring Overview ---------------- +## Wiring Overview -A single PH will support most pneumatics applications, providing an output for the compressor, input for a pressure switch, and outputs for up to 16 solenoid channels (12V or 24V selectable). The module is connected to the roboRIO over the CAN bus and powered via 12V from the PDP/PDH. +A single PH will support most pneumatics applications, providing an output for the compressor, input for a pressure switch, and outputs for up to 16 solenoid channels (12V or 24V selectable). The module is connected to the roboRIO over the :term:`CAN` bus and powered via 12V from the PDP/PDH. For complicated robot designs requiring more channels or multiple solenoid voltages, additional PHs or PCMs can be added to the control system. -PCM Power and Control Wiring ----------------------------- +## PCM Power and Control Wiring .. image:: images/wiring-pneumatics-ph/ph-subsystem.png :alt: Pneumatics wiring diagram showing all of the connections to the PH. @@ -30,34 +27,28 @@ The first PH on your robot can be wired from the PDP VRM/PCM connectors on the e Additional PHs can be wired to a standard WAGO connector on the side of the PDP and protected with a 20A or smaller circuit breaker or to a 15A port on the PDH. Additional PHs should also be placed anywhere in the middle of the CAN chain. -The Compressor --------------- +## The Compressor The compressor can be wired directly to the Compressor connectors on the PH. If additional length is required, make sure to use 18 AWG wire or larger for the extension. -The Pressure Switch -------------------- +## The Pressure Switch The PH has two options for detecting pressure, a digital pressure switch, or an analog pressure switch. -Digital -~~~~~~~ +#### Digital A digital pressure switch should be connected directly to the digital pressure sensor input terminals on the PCM. There is no polarity on the input terminals or on the pressure switch itself, either terminal on the PH can be connected to either terminal on the switch. Ring or spade terminals are recommended for the connection to the switch screws (note that the screws are slightly larger than #6, but can be threaded through a ring terminal with a hole for a #6 screw such as the terminals shown in the image). -Analog -~~~~~~ +#### Analog -An analog pressure switch (`REV-11-1107 `__ can be connected directly to the analog pressure sensor port 0 input terminals. Using an analog pressure sensor allows reading the pressure in the pneumatic system through code and setting custom trigger thresholds for turning on and off the compressor. +An analog pressure switch ([REV-11-1107](https://www.revrobotics.com/rev-11-1107/) can be connected directly to the analog pressure sensor port 0 input terminals. Using an analog pressure sensor allows reading the pressure in the pneumatic system through code and setting custom trigger thresholds for turning on and off the compressor. -.. warning:: The Analog Pressure Sensor port is a very tight fit and requires special attention. See `REV Wiring an Analog Pressure Sensor `__ for more tips +.. warning:: The Analog Pressure Sensor port is a very tight fit and requires special attention. See [REV Wiring an Analog Pressure Sensor](https://docs.revrobotics.com/ion-control-system/ph/gs/wiring#wiring-an-analog-pressure-sensor) for more tips -Solenoids ---------- +## Solenoids Each solenoid channel should be wired directly to a numbered pair of terminals on the PH. A single acting solenoid will use one numbered terminal pair. A double acting solenoid will use two pairs. If your solenoid does not come with color coded wiring, check the datasheet to make sure to wire with the proper polarity. -Solenoid Voltage Switch ------------------------- +## Solenoid Voltage Switch The PH is capable of powering either 12V or 24V solenoids, but all solenoids connected to a single PH must be the same voltage. Set the voltage switch to the appropriate voltage for solenoids prior to use. diff --git a/source/docs/hardware/hardware-tutorials/index.rst b/source/docs/hardware/hardware-tutorials/index.rst index 2dc4994dea..ff401a47ce 100644 --- a/source/docs/hardware/hardware-tutorials/index.rst +++ b/source/docs/hardware/hardware-tutorials/index.rst @@ -1,5 +1,4 @@ -Hardware Tutorials -================== +# Hardware Tutorials .. note:: The pages in this section of the documentation contain media which is only viewable from the web version of the documentation diff --git a/source/docs/hardware/hardware-tutorials/motors.rst b/source/docs/hardware/hardware-tutorials/motors.rst index 6b33eaaa80..1358323cae 100644 --- a/source/docs/hardware/hardware-tutorials/motors.rst +++ b/source/docs/hardware/hardware-tutorials/motors.rst @@ -1,5 +1,4 @@ -Motors for Robotics Applications -================================ +# Motors for Robotics Applications One of the most important design decisions that teams have to deal with is selecting and designing the motor driven systems on their robot. So often the incorrect motor is chosen for a particular design yielding reduced performance and, sometimes even worse, motors failing from excessive current draw. In this series of videos, WPI Professor Ken Stafford walks through how motors work, how to design systems to operate at maximum performance, and a sample design for a robot system. .. raw:: html diff --git a/source/docs/hardware/hardware-tutorials/pneumatics.rst b/source/docs/hardware/hardware-tutorials/pneumatics.rst index fec6c64ea1..1cf0a240a5 100644 --- a/source/docs/hardware/hardware-tutorials/pneumatics.rst +++ b/source/docs/hardware/hardware-tutorials/pneumatics.rst @@ -1,5 +1,4 @@ -Pneumatics -========== +# Pneumatics Pneumatics is an often underused actuation device that can be used on robots. There are many advantages to pneumatics over using motors. In this video Professor Ken Stafford describes the characteristics of pneumatics, applications with robots, and calculating the right sized system for an application. .. raw:: html diff --git a/source/docs/hardware/hardware-tutorials/sensors.rst b/source/docs/hardware/hardware-tutorials/sensors.rst index 1f2fac285b..93620e4ec6 100644 --- a/source/docs/hardware/hardware-tutorials/sensors.rst +++ b/source/docs/hardware/hardware-tutorials/sensors.rst @@ -1,5 +1,4 @@ -Sensing and Sensors -=================== +# Sensing and Sensors Without sensors and sensing robots are really radio controlled vehicles. Sensors allow the robots to understand the internal operation of the robots mechanical systems as well as the ability to interact with the environment around the robot. In these videos, WPI Professor Craig Putnam describes a number of classes of sensors, how they are used, and provides guidance on what sensors are best for your applications. .. raw:: html diff --git a/source/docs/hardware/hardware-tutorials/transmissions.rst b/source/docs/hardware/hardware-tutorials/transmissions.rst index 2bf8734adf..9d14f61203 100644 --- a/source/docs/hardware/hardware-tutorials/transmissions.rst +++ b/source/docs/hardware/hardware-tutorials/transmissions.rst @@ -1,5 +1,4 @@ -Power Transmission -================== +# Power Transmission Hand in hand with choosing the correct motors for an application is transmitting that motor power to the place it's needed. Using gears or chains and sprockets are two effective ways of matching the motor power to the application being driven. In this video, WPI Robotics Engineering PhD student Michael Delph talks about power transmission, including choosing correct gear or chain and sprocket ratios to get the maximum performance from your robot design. .. raw:: html diff --git a/source/docs/hardware/sensors/accelerometers-hardware.rst b/source/docs/hardware/sensors/accelerometers-hardware.rst index b776e242bc..19edef55da 100644 --- a/source/docs/hardware/sensors/accelerometers-hardware.rst +++ b/source/docs/hardware/sensors/accelerometers-hardware.rst @@ -1,7 +1,6 @@ .. include:: -Accelerometers - Hardware -========================== +# Accelerometers - Hardware Accelerometers are common sensors used to measure acceleration. @@ -10,45 +9,40 @@ In principle, precise measurements of acceleration can be double-integrated and The roboRIO comes with a :ref:`built-in three-axis accelerometer ` that all teams can use, however teams seeking more-precise measurements may purchase and use a peripheral accelerometer, as well. -Types of accelerometers ------------------------ +## Types of accelerometers There are three types of accelerometers commonly-used in FRC: single-axis accelerometers, multi-axis accelerometers, and IMUs. -Single-axis accelerometers -^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Single-axis accelerometers .. image:: images/accelerometers-hardware/adxl193-single-axis-accelerometer-to-roborio.svg :alt: The three wire connection from the Accelerometer to the Analog In port of the roboRIO. As per their name, single-axis accelerometers measure acceleration along a single axis. This axis is generally specified on the physical device, and mounting the device in the proper orientation so that the desired axis is measured is highly important. Single-axis accelerometers generally output an analog voltage corresponding to the measured acceleration, and so connect to the roboRIO's :doc:`analog input ` ports. -Multi-axis accelerometers -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Multi-axis accelerometers .. image:: images/analog-inputs-hardware/triple-axis-accelerometer-to-roborio.svg :alt: The triple axis accelerometer hooked up to three different Analog In channels. -Multi-axis accelerometers measure acceleration along multiple spacial axes. The roboRIO's built-in accelerometer is a three-axis accelerometer. +Multi-axis accelerometers measure acceleration along multiple spatial axes. The roboRIO's built-in accelerometer is a three-axis accelerometer. Peripheral multi-axis accelerometers may simply output multiple analog voltages (and thus connect to the :ref:`analog input ports `, or (more commonly) they may communicate with one of the roboRIO's :doc:`serial buses `. -roboRIO built-in accelerometer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### roboRIO built-in accelerometer .. image:: images/roborio/roborio-accelerometer.svg :alt: The details of this accelerometer are printed on the roboRIO to the right of the NI logo. The roboRIO has a built-in accelerometer, which does not need any external connections. You can find more details about how to use it in the :ref:`Built-in Accelerometer section ` of the software documentation. -IMUs (Inertial Measurement Units) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### IMUs (Inertial Measurement Units) .. image:: images/accelerometers-hardware/navx-imu-to-roborio-mxp.svg :alt: Show the NavX plugged in to the MXP port on the roboRIO. Several popular FRC devices (known as "inertial measurement units," or "IMUs") combine both an accelerometer and a gyroscope. Popular FRC example include: - - `Analog Devices ADIS16448 and ADIS 16470 IMUs `__ - - `CTRE Pigeon IMU `__ - - `Kauai Labs NavX `__ + - [Analog Devices ADIS16448 and ADIS 16470 IMUs](https://www.analog.com/en/landing-pages/001/first.html) + - [CTRE Pigeon IMU](https://store.ctr-electronics.com/gadgeteer-pigeon-imu/) + - [Kauai Labs NavX](https://pdocs.kauailabs.com/navx-mxp/) diff --git a/source/docs/hardware/sensors/analog-inputs-hardware.rst b/source/docs/hardware/sensors/analog-inputs-hardware.rst index 8400f1e3a0..8b458ac5b4 100644 --- a/source/docs/hardware/sensors/analog-inputs-hardware.rst +++ b/source/docs/hardware/sensors/analog-inputs-hardware.rst @@ -1,16 +1,14 @@ -Analog Inputs - Hardware -======================== +# Analog Inputs - Hardware .. note:: This section covers analog input hardware. For a software guide to analog inputs, see :ref:`docs/software/hardware-apis/sensors/analog-inputs-software:Analog Inputs - Software`. -An `analog signal `__ is a signal whose value can lie anywhere in a continuous interval. This lies in stark contrast to a :doc:`digital signal `, which can take only one of several discrete values. The roboRIO's analog input ports allow the measurement of analog signals with values from 0V to 5V. +An [analog signal](https://en.wikipedia.org/wiki/Analog_signal) is a signal whose value can lie anywhere in a continuous interval. This lies in stark contrast to a :doc:`digital signal `, which can take only one of several discrete values. The roboRIO's analog input ports allow the measurement of analog signals with values from 0V to 5V. In practice, there is no way to measure a "true" analog signal with a digital device such as a computer (like the roboRIO). Accordingly, the analog inputs are actually measured as a 12-bit digital signal - however, this is quite a high resolution [1]_. Analog inputs are typically (but not always!) used for sensors whose measurements vary continuously over a range, such as :doc:`ultrasonic rangefinders ` and :doc:`potentiometers `, as they can communicate by outputting a voltage proportional to their measurements. -Connecting to roboRIO analog input ports ----------------------------------------- +## Connecting to roboRIO analog input ports .. note:: An additional four analog inputs are available via the "MXP" expansion port. To use these, a breakout board of some sort that connects to the MXP is needed. @@ -23,8 +21,7 @@ Connecting to roboRIO analog input ports The roboRIO has 4 built-in analog input ports (numbered 0-3), as seen in the image above. Each port has three pins - signal ("S"), power ("V"), and ground ("|ground|"). The "power" and "ground" pins are used to power the peripheral sensors that connect to the analog input ports - there is a constant 5V potential difference between the "power" and the "ground" pins [2]_. The signal pin is the pin on which the signal is actually measured. -Connecting a sensor to a single analog input port -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Connecting a sensor to a single analog input port .. note:: Some sensors (such as :doc:`potentiometers `) may have interchangeable power and ground connections. @@ -33,8 +30,7 @@ Most sensors that connect to analog input ports will have three wires - signal, .. image:: images/accelerometers-hardware/adxl193-single-axis-accelerometer-to-roborio.svg :alt: Hooking a ADXL193 single axis accelerometer to an analog input on the roboRIO. -Connecting a sensor to multiple analog input ports -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Connecting a sensor to multiple analog input ports Some sensors may need to connect to multiple analog input ports in order to function. In general, these sensors will only ever require a single power and a single ground pin - only the signal pin of the additional port(s) will be needed. The image below is shows an analog accelerometer that requires three analog input ports, but similar wiring can be used for analog sensors requiring two analog input ports. @@ -43,8 +39,7 @@ Some sensors may need to connect to multiple analog input ports in order to func .. |ground| unicode:: 0x23DA -Footnotes ---------- +## Footnotes -.. [1] A 12-bit resolution yields :math:`2^{12}`, or 4096 different values. For a 5V range, that's an effective resolution of approximately 1.2 mV, or .0012V. The actual accuracy specification is plus-or-minus 50mV, so the discretization is not the limiting factor in the measurement accuracy. +.. [1] A 12-bit resolution yields $2^{12}$, or 4096 different values. For a 5V range, that's an effective resolution of approximately 1.2 mV, or .0012V. The actual accuracy specification is plus-or-minus 50mV, so the discretization is not the limiting factor in the measurement accuracy. .. [2] All power pins are actually connected to a single rail, as are all ground pins - there is no need to use the power/ground pins corresponding to a given signal pin. diff --git a/source/docs/hardware/sensors/analog-potentiometers-hardware.rst b/source/docs/hardware/sensors/analog-potentiometers-hardware.rst index 39b3104be4..68b04bbdfc 100644 --- a/source/docs/hardware/sensors/analog-potentiometers-hardware.rst +++ b/source/docs/hardware/sensors/analog-potentiometers-hardware.rst @@ -1,16 +1,14 @@ .. include:: -Analog Potentiometers - Hardware -================================ +# Analog Potentiometers - Hardware .. note:: This section covers analog potentiometer hardware. For a software guide to analog potentiometers, see :ref:`docs/software/hardware-apis/sensors/analog-potentiometers-software:Analog Potentiometers - Software`. .. warning:: Potentiometers generally have a mechanically-limited travel range. Users should be careful that their mechanisms do not turn their potentiometers past their maximum travel, as this will damage or destroy the potentiometer. -Apart from :doc:`quadrature encoders `, another common way of measuring rotation on FRC\ |reg| robots is with analog potentiometers. A potentiometer is simply a variable resistor - as the shaft of the potentiometer turns, the resistance changes (usually linearly). Placing this resistor in a `voltage divider `__ allows the user to easily measure the resistance by measuring the voltage across the potentiometer, which can then be used to calculate the rotational position of the shaft. +Apart from :doc:`quadrature encoders `, another common way of measuring rotation on FRC\ |reg| robots is with analog potentiometers. A potentiometer is simply a variable resistor - as the shaft of the potentiometer turns, the resistance changes (usually linearly). Placing this resistor in a [voltage divider](https://en.wikipedia.org/wiki/Voltage_divider) allows the user to easily measure the resistance by measuring the voltage across the potentiometer, which can then be used to calculate the rotational position of the shaft. -Wiring an analog potentiometer ------------------------------- +## Wiring an analog potentiometer As suggested by the names, analog potentiometers connect to the roboRIO's :doc:`analog input ` ports. To understand how exactly to wire potentiometers, however, it is important to understand their internal circuitry. @@ -23,7 +21,6 @@ As mentioned before, a potentiometer is a voltage divider, as shown in the circu Since the circuit is symmetric, it is reversible - this allows the user to choose at which end of the travel the measured voltage is zero, and at which end it is 5 volts. To reverse the directionality of the sensor, it can simply be wired backwards! Be sure to check the directionality of your potentiometer with a multimeter to be sure it is in the desired orientation before soldering your wires to the contacts. -Footnotes ---------- +## Footnotes .. [1] The way this actually works is generally by having the shaft control the position of a contact along a resistive "wiper" of fixed length along which the current flows - the resistance is proportional to the length of wiper between the contact and the end of the wiper. diff --git a/source/docs/hardware/sensors/digital-inputs-hardware.rst b/source/docs/hardware/sensors/digital-inputs-hardware.rst index 4cc5e21f4b..7921a42333 100644 --- a/source/docs/hardware/sensors/digital-inputs-hardware.rst +++ b/source/docs/hardware/sensors/digital-inputs-hardware.rst @@ -1,14 +1,12 @@ -Digital Inputs - Hardware -========================= +# Digital Inputs - Hardware .. note:: This section covers digital input hardware. For a software guide to digital inputs, see :ref:`docs/software/hardware-apis/sensors/digital-inputs-software:Digital Inputs - Software`. -A `digital signal `__ is a signal that can be in one of several discrete states. In the vast majority of cases, the signal is the voltage in a wire, and there are only two states for a digital signal - high, or low (also denoted 1 and 0, or true and false, respectively). +A [digital signal](https://en.wikipedia.org/wiki/Digital_signal) is a signal that can be in one of several discrete states. In the vast majority of cases, the signal is the voltage in a wire, and there are only two states for a digital signal - high, or low (also denoted 1 and 0, or true and false, respectively). The roboRIO's built-in digital input-output ports (or "DIO") ports function on 5V, so "high" corresponds to a signal of 5V, and "low" to a signal of 0V [1]_ [2]_. -Connecting to the roboRIO DIO ports ------------------------------------ +## Connecting to the roboRIO DIO ports .. note:: Additional DIO ports are available through the "MXP" expansion port. To use these, a breakout board of some sort that connects to the MXP is needed. @@ -23,8 +21,7 @@ The roboRIO has 10 built-in DIO ports (numbered 0-9), as seen in the image above All DIO ports have built-in "pull-up" resistors between the power pins and the signal pins - these ensure that when the signal pin is "floating" (i.e. is not connected to any circuit), they consistently remain in a "high" state. -Connecting a simple switch to a DIO port -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Connecting a simple switch to a DIO port The simplest device that can be connected to a DIO port is a switch (such as a :ref:`limit switch `). When a switch is connected correctly to a DIO port, the port will read "high" when the circuit is open, and "low" when the circuit is closed. @@ -33,16 +30,14 @@ A simple switch does not need to be powered, and thus only has two wires. Switc .. image:: images/digital-inputs-hardware/limit-switch-to-roborio.svg :alt: Connecting a normally open limit switch to a DIO channel of the roboRIO. -Connecting a powered sensor to a DIO port -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Connecting a powered sensor to a DIO port Many digital sensors (such as most no-contact proximity switches) require power in order to work. A powered sensor will generally have three wires - signal, power, and ground. These should be connected to the corresponding pins of the DIO port. .. image:: images/digital-inputs-hardware/hall-effect-sensor-to-roborio.svg :alt: Connecting a Hall Effect sensor to a roboRIO DIO channel. -Connecting a sensor that uses multiple DIO ports -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Connecting a sensor that uses multiple DIO ports Some sensors (such as :doc:`quadrature encoders `) may need to connect to multiple DIO ports in order to function. In general, these sensors will only ever require a single power and a single ground pin - only the signal pin of the additional port(s) will be needed. @@ -51,8 +46,7 @@ Some sensors (such as :doc:`quadrature encoders `) may need t .. |ground| unicode:: 0x23DA -Footnotes ---------- +## Footnotes .. [1] More precisely, the signal reads "high" when it rises above 2.0V, and reads "low" when it falls back below 0.8V - behavior between these two thresholds is not guaranteed to be consistent. .. [2] The roboRIO also offers 3.3V logic via the "MXP" expansion port; however, the use of this is far less common than the 5V. diff --git a/source/docs/hardware/sensors/encoders-hardware.rst b/source/docs/hardware/sensors/encoders-hardware.rst index 1ac5c2b5d9..c58b7c7a4f 100644 --- a/source/docs/hardware/sensors/encoders-hardware.rst +++ b/source/docs/hardware/sensors/encoders-hardware.rst @@ -1,14 +1,12 @@ .. include:: -Encoders - Hardware -=================== +# Encoders - Hardware .. note:: This section covers encoder hardware. For a software guide to encoders, see :ref:`docs/software/hardware-apis/sensors/encoders-software:Encoders - Software`. Encoders are by far the most common method for measuring rotational motion in FRC\ |reg|, and for good reason - they are cheap, easy-to-use, and reliable. As they produce digital signals, they are less-prone to noise and interference than analog devices (such as :doc:`potentiometers `). -Types of Encoders ------------------ +## Types of Encoders There are three main ways encoders connect physically that are typically used in FRC: @@ -26,8 +24,7 @@ There are also three main ways the encoder data is communicated that are typical .. note:: Some encoders may support more then one communication method -Shafted Encoders -^^^^^^^^^^^^^^^^ +### Shafted Encoders .. image:: images/encoders-hardware/greyhill-63r-encoder.svg :alt: Diagram of the Greyhill 63R Optical Encoder. @@ -37,11 +34,10 @@ Shafted encoders have a sealed body with a shaft protruding out of it that must Examples of shafted encoders: -- `Grayhill 63r `__ -- `US Digital MA3 `__ +- [Grayhill 63r](https://www.mouser.com/datasheet/2/626/grhls00779_1-2289364.pdf) +- [US Digital MA3](https://www.usdigital.com/products/encoders/absolute/shaft/ma3/) -On-shaft Encoders -^^^^^^^^^^^^^^^^^ +### On-shaft Encoders .. image:: images/encoders-hardware/amt10x-encoders.svg :alt: Diagram of the AMT103 and the AMT102 shaft encoders. @@ -51,13 +47,12 @@ On-shaft encoders couple to a shaft by fitting *around* it, forming a friction c Examples of On-shaft encoders: -- `AMT103-V `__ available through FIRST Choice -- `CIMcoder `__ -- `REV Through Bore Encoder `__ -- `US Digital E4T `__ +- [AMT103-V](https://www.cuidevices.com/product/motion/rotary-encoders/incremental/modular/amt10-v-kit/amt103-v) available through FIRST Choice +- [CIMcoder](https://www.andymark.com/products/cimcoder-encoder-cim-motor-high-resolution) +- [REV Through Bore Encoder](https://www.revrobotics.com/rev-11-1271/) +- [US Digital E4T](https://www.andymark.com/products/e4t-oem-miniature-optical-encoder-kit) -Magnetic Encoders -^^^^^^^^^^^^^^^^^ +### Magnetic Encoders .. image:: images/encoders-hardware/ctre-magnetic-encoder.png :alt: Picture of the CTRE Mag Encoder. @@ -67,13 +62,12 @@ Magnetic encoders require no mechanical coupling to the shaft at all; rather, th Examples of magnetic encoders: -- `CTRE Mag Encoder `__ -- `Thrifty Absolute Magnetic Encoder `__ -- `Team 221 Lamprey2 `__ +- [CTRE Mag Encoder](https://store.ctr-electronics.com/srx-mag-encoder/) +- [Thrifty Absolute Magnetic Encoder](https://www.thethriftybot.com/products/thrifty-absolute-magnetic-encoder) +- [Team 221 Lamprey2](https://www.andymark.com/products/lamprey-absolute-encoder) -Quadrature Encoders -^^^^^^^^^^^^^^^^^^^ +### Quadrature Encoders The term "quadrature" refers to the method by which the motion is measured/encoded. A quadrature encoder produces two square-wave pulses that are 90-degrees out-of-phase from each other, as seen in the picture below: @@ -86,29 +80,26 @@ As each square wave pulse is a digital signal, quadrature encoders connect to th Examples of quadrature encoders: -- `AMT103-V `__ available through FIRST Choice -- `CIMcoder `__ +- [AMT103-V](https://www.cuidevices.com/product/motion/rotary-encoders/incremental/modular/amt10-v-kit/amt103-v) available through FIRST Choice +- [CIMcoder](https://www.andymark.com/products/cimcoder-encoder-cim-motor-high-resolution) - `CTRE Mag Encoder `_ -- `Grayhill 63r `__ -- `REV Through Bore Encoder `__ -- `US Digital E4T `__ +- [Grayhill 63r](https://www.mouser.com/datasheet/2/626/grhls00779_1-2289364.pdf) +- [REV Through Bore Encoder](https://www.revrobotics.com/rev-11-1271/) +- [US Digital E4T](https://www.andymark.com/products/e4t-oem-miniature-optical-encoder-kit) -Quadrature Encoder Wiring -~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Quadrature Encoder Wiring .. image:: images/digital-inputs-hardware/e4t-oem-miniature-optical-encoder-to-roborio.svg :alt: Wiring the E4T Optical Encoder to two DIO ports. :width: 400 -Quadrature Encoders, such as the `E4T OEM Miniature Optical Encoder `__, can be wired to two digital input ports as shown above. +Quadrature Encoders, such as the [E4T OEM Miniature Optical Encoder](https://www.andymark.com/products/e4t-oem-miniature-optical-encoder-kit), can be wired to two digital input ports as shown above. -Index -~~~~~ +#### Index Some quadrature encoders have a third index pin which pulses when the encoder completes a revolution. -Quaderature Encoder Resolution -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Quaderature Encoder Resolution .. warning:: The acronyms "CPR" and "PPR" are *both* used by varying sources to denote both edges per revolution *and* cycles per revolution, so the acronym alone is not enough to tell which is of the two is meant when by a given value. When in doubt, consult the technical manual of your specific encoder. @@ -122,8 +113,7 @@ Thus, a resolution stated in edges per revolution has a value four times that of In general, the resolution of your encoder in edges-per-revolution should be somewhat finer than your smallest acceptable error in positioning. Thus, if you want to know the mechanism plus-or-minus one degree, you should have an encoder with a resolution somewhat higher than 360 edges per revolution. -Duty Cycle Encoders -^^^^^^^^^^^^^^^^^^^ +### Duty Cycle Encoders .. image:: /docs/software/hardware-apis/sensors/images/encoders-software/encoding-direction.png :alt: The PWM signal pattern for minimum and maximum angles. @@ -132,14 +122,13 @@ Duty cycle encoders connect to a single digital input on the roboRIO. They outpu Examples of duty cycle encoders: -- `AndyMark Mag Encoder `__ -- `CTRE Mag Encoder `__ -- `REV Through Bore Encoder `__ -- `Team 221 Lamprey2 `__ -- `US Digital MA3 `__ +- [AndyMark Mag Encoder](https://www.andymark.com/products/am-mag-encoder) +- [CTRE Mag Encoder](https://store.ctr-electronics.com/srx-mag-encoder/) +- [REV Through Bore Encoder](https://www.revrobotics.com/rev-11-1271/) +- [Team 221 Lamprey2](https://www.andymark.com/products/lamprey-absolute-encoder) +- [US Digital MA3](https://www.usdigital.com/products/encoders/absolute/shaft/ma3/) -Analog Encoders -^^^^^^^^^^^^^^^ +### Analog Encoders .. image:: images/encoders-hardware/absolute-encoder-to-roborio.svg :alt: The connection of a US Digital MA3 Analog encoder to the roboRIO analog input. @@ -148,6 +137,6 @@ Analog encoders connect to a analog input on the roboRIO. They output a voltage Examples of analog encoders: -- `Team 221 Lamprey2 `__ -- `Thrifty Absolute Magnetic Encoder `__ -- `US Digital MA3 `__ +- [Team 221 Lamprey2](https://www.andymark.com/products/lamprey-absolute-encoder) +- [Thrifty Absolute Magnetic Encoder](https://www.thethriftybot.com/products/thrifty-absolute-magnetic-encoder) +- [US Digital MA3](https://www.usdigital.com/products/encoders/absolute/shaft/ma3/) diff --git a/source/docs/hardware/sensors/gyros-hardware.rst b/source/docs/hardware/sensors/gyros-hardware.rst index e01251151c..0edeac0d67 100644 --- a/source/docs/hardware/sensors/gyros-hardware.rst +++ b/source/docs/hardware/sensors/gyros-hardware.rst @@ -1,7 +1,6 @@ .. include:: -Gyroscopes - Hardware -===================== +# Gyroscopes - Hardware .. note:: This section covers gyro hardware. For a software guide to gyros, see :ref:`docs/software/hardware-apis/sensors/gyros-software:Gyroscopes - Software`. @@ -9,36 +8,33 @@ Gyroscopes (or "gyros", for short) are devices that measure rate-of-rotation. T Several popular FRC\ |reg| devices known as :ref:`IMUs ` (Inertial Measurement Units) combine 3-axis gyros, accelerometers and other position sensors into one device. Some popular examples are: - - `Analog Devices ADIS16448 and ADIS 16470 IMUs `__ - - `CTRE Pigeon IMU `__ - - `Kauai Labs NavX `__ + - [Analog Devices ADIS16448 and ADIS 16470 IMUs](https://www.analog.com/en/landing-pages/001/first.html) + - [CTRE Pigeon IMU](https://store.ctr-electronics.com/gadgeteer-pigeon-imu/) + - [Kauai Labs NavX](https://pdocs.kauailabs.com/navx-mxp/) -Types of Gyros --------------- +## Types of Gyros There are two types of Gyros commonly-used in FRC: single-axis gyros, three-axis gyros and IMUs, which often include a 3-axis gyro. -Single-axis Gyros -^^^^^^^^^^^^^^^^^ +### Single-axis Gyros .. image:: images/gyros-hardware/analog-devices-frc-gyro-to-roborio.svg :alt: The Analog Devices 1-axis Gyro plugged into the SPI port of the roboRIO. :width: 400 -As per their name, single-axis gyros measure rotation rate around a single axis. This axis is generally specified on the physical device, and mounting the device in the proper orientation so that the desired axis is measured is highly important. Some single-axis gyros can output an analog voltage corresponding to the measured rate of rotation, and so connect to the roboRIO's :doc:`analog input ` ports. Other single-axis gyros, such as the `ADXRS450 `__ pictured above, use the :ref:`SPI port ` on the roboRIO instead. +As per their name, single-axis gyros measure rotation rate around a single axis. This axis is generally specified on the physical device, and mounting the device in the proper orientation so that the desired axis is measured is highly important. Some single-axis gyros can output an analog voltage corresponding to the measured rate of rotation, and so connect to the roboRIO's :doc:`analog input ` ports. Other single-axis gyros, such as the [ADXRS450](https://wiki.analog.com/first) pictured above, use the :ref:`SPI port ` on the roboRIO instead. -The `Analog Devices ADXRS450 FRC Gyro Board `__ that has been in FIRST Choice in recent years is a commonly used single axis gyro. +The [Analog Devices ADXRS450 FRC Gyro Board](https://www.analog.com/en/landing-pages/001/first.html) that has been in FIRST Choice in recent years is a commonly used single axis gyro. -Three-axis Gyros -^^^^^^^^^^^^^^^^ +### Three-axis Gyros .. image:: images/gyros-hardware/adis16470-gyro-to-roborio.jpeg - :alt: This is the ADIS16470 IMU plugged in to the SPI port. + :alt: This is the ADIS16470 :term:`IMU` plugged in to the SPI port. :width: 400 -Three-axis gyros measure rotation rate around all three spacial axes (typically labeled x, y, and z). The motion around these axis is called pitch, yaw, and roll. +Three-axis gyros measure rotation rate around all three spatial axes (typically labeled x, y, and z). The motion around these axis is called pitch, yaw, and roll. -The `Analog Devices ADIS16470 IMU Board for FIRST Robotics `__ that has been in FIRST Choice in recent years is a commonly used three-axis gyro. +The [Analog Devices ADIS16470 IMU Board for FIRST Robotics](https://www.analog.com/en/landing-pages/001/first.html) that has been in FIRST Choice in recent years is a commonly used three-axis gyro. .. image:: images/gyros-hardware/drive-yaw-pitch-roll.svg :alt: The 3 axis: yaw, pitch, and roll and how they relate to robot movement. diff --git a/source/docs/hardware/sensors/images/encoders-hardware/ctre-magnetic-encoder.png b/source/docs/hardware/sensors/images/encoders-hardware/ctre-magnetic-encoder.png index 44d6d52a39..8608fe553e 100644 Binary files a/source/docs/hardware/sensors/images/encoders-hardware/ctre-magnetic-encoder.png and b/source/docs/hardware/sensors/images/encoders-hardware/ctre-magnetic-encoder.png differ diff --git a/source/docs/hardware/sensors/images/lidar/1-dimentional-lidar.png b/source/docs/hardware/sensors/images/lidar/1-dimentional-lidar.png index 2bf4f17e1c..268c72c38a 100644 Binary files a/source/docs/hardware/sensors/images/lidar/1-dimentional-lidar.png and b/source/docs/hardware/sensors/images/lidar/1-dimentional-lidar.png differ diff --git a/source/docs/hardware/sensors/images/lidar/2-dimentional-lidar.png b/source/docs/hardware/sensors/images/lidar/2-dimentional-lidar.png index edadd44089..d307cdbc83 100644 Binary files a/source/docs/hardware/sensors/images/lidar/2-dimentional-lidar.png and b/source/docs/hardware/sensors/images/lidar/2-dimentional-lidar.png differ diff --git a/source/docs/hardware/sensors/images/proximity-switches-hardware/inductive-proximity-switch.png b/source/docs/hardware/sensors/images/proximity-switches-hardware/inductive-proximity-switch.png index 2a4a5e89e0..69c343ecbd 100644 Binary files a/source/docs/hardware/sensors/images/proximity-switches-hardware/inductive-proximity-switch.png and b/source/docs/hardware/sensors/images/proximity-switches-hardware/inductive-proximity-switch.png differ diff --git a/source/docs/hardware/sensors/images/serial-buses/can-bus-talon-srx-chain.png b/source/docs/hardware/sensors/images/serial-buses/can-bus-talon-srx-chain.png index 3946b4d16b..e0baea2966 100644 Binary files a/source/docs/hardware/sensors/images/serial-buses/can-bus-talon-srx-chain.png and b/source/docs/hardware/sensors/images/serial-buses/can-bus-talon-srx-chain.png differ diff --git a/source/docs/hardware/sensors/images/serial-buses/i2c-pinout.png b/source/docs/hardware/sensors/images/serial-buses/i2c-pinout.png index 9393ddb693..d0a1448d93 100644 Binary files a/source/docs/hardware/sensors/images/serial-buses/i2c-pinout.png and b/source/docs/hardware/sensors/images/serial-buses/i2c-pinout.png differ diff --git a/source/docs/hardware/sensors/images/serial-buses/mxp-pinout.png b/source/docs/hardware/sensors/images/serial-buses/mxp-pinout.png index 33b044fbb9..7a256ffb91 100644 Binary files a/source/docs/hardware/sensors/images/serial-buses/mxp-pinout.png and b/source/docs/hardware/sensors/images/serial-buses/mxp-pinout.png differ diff --git a/source/docs/hardware/sensors/images/serial-buses/rs232-pinout.png b/source/docs/hardware/sensors/images/serial-buses/rs232-pinout.png index e56694c23a..c3650859c6 100644 Binary files a/source/docs/hardware/sensors/images/serial-buses/rs232-pinout.png and b/source/docs/hardware/sensors/images/serial-buses/rs232-pinout.png differ diff --git a/source/docs/hardware/sensors/images/serial-buses/spi-pinout.png b/source/docs/hardware/sensors/images/serial-buses/spi-pinout.png index bc838d2ccd..150af7036d 100644 Binary files a/source/docs/hardware/sensors/images/serial-buses/spi-pinout.png and b/source/docs/hardware/sensors/images/serial-buses/spi-pinout.png differ diff --git a/source/docs/hardware/sensors/images/triangulating-rangefinders/infrared-proximity-sensor.png b/source/docs/hardware/sensors/images/triangulating-rangefinders/infrared-proximity-sensor.png index 308ccb1251..b7ca5b133d 100644 Binary files a/source/docs/hardware/sensors/images/triangulating-rangefinders/infrared-proximity-sensor.png and b/source/docs/hardware/sensors/images/triangulating-rangefinders/infrared-proximity-sensor.png differ diff --git a/source/docs/hardware/sensors/images/ultrasonics-hardware/mb-lv-ez1-serial.png b/source/docs/hardware/sensors/images/ultrasonics-hardware/mb-lv-ez1-serial.png index af95641c3a..1dcedda9fa 100644 Binary files a/source/docs/hardware/sensors/images/ultrasonics-hardware/mb-lv-ez1-serial.png and b/source/docs/hardware/sensors/images/ultrasonics-hardware/mb-lv-ez1-serial.png differ diff --git a/source/docs/hardware/sensors/index.rst b/source/docs/hardware/sensors/index.rst index fef3f246ee..7366441e3e 100644 --- a/source/docs/hardware/sensors/index.rst +++ b/source/docs/hardware/sensors/index.rst @@ -1,5 +1,4 @@ -Sensors -======= +# Sensors .. toctree:: :maxdepth: 1 diff --git a/source/docs/hardware/sensors/lidar.rst b/source/docs/hardware/sensors/lidar.rst index d3de5e1e31..5c4549d756 100644 --- a/source/docs/hardware/sensors/lidar.rst +++ b/source/docs/hardware/sensors/lidar.rst @@ -1,29 +1,25 @@ .. include:: -LIDAR - Hardware -================ +# LIDAR - Hardware LIDAR (light detection and ranging) sensors are a variety of rangefinder seeing increasing use in FRC\ |reg|. LIDAR sensors work quite similarly to :doc:`ultrasonics `, but use light instead of sound. A laser is pulsed, and the sensor measures the time until the pulse bounces back. -Types of LIDAR --------------- +## Types of LIDAR There are two types of LIDAR sensors commonly used in current FRC: 1-dimensional LIDAR, and 2-dimensional LIDAR. -1-Dimensional LIDAR -^^^^^^^^^^^^^^^^^^^ +### 1-Dimensional LIDAR .. image:: images/lidar/1-dimentional-lidar.png :alt: Garmin LIDAR-Lite a 1D LIDAR device. -A 1-dimensional (1D) LIDAR sensor works much like an ultrasonic sensor - it measures the distance to the nearest object more or less along a line in front of it. 1D LIDAR sensors can often be more-reliable than ultrasonics, as they have narrower "beam profiles" and are less susceptible to interference. Pictured above is the `Garmin LIDAR-Lite Optical Distance Sensor `__. +A 1-dimensional (1D) LIDAR sensor works much like an ultrasonic sensor - it measures the distance to the nearest object more or less along a line in front of it. 1D LIDAR sensors can often be more-reliable than ultrasonics, as they have narrower "beam profiles" and are less susceptible to interference. Pictured above is the [Garmin LIDAR-Lite Optical Distance Sensor](https://www.andymark.com/products/lidar-lite-3). 1D LIDAR sensors generally output an analog voltage proportional to the measured distance, and thus connect to the roboRIO's :doc:`analog input ` ports or to one of the :doc:`roboRIO's serial buses `. -2-Dimensional LIDAR -^^^^^^^^^^^^^^^^^^^ +### 2-Dimensional LIDAR .. image:: images/lidar/2-dimentional-lidar.png :alt: RPLIDAR pictured is one option for 2D LIDAR @@ -32,8 +28,7 @@ A 2-dimensional (2D) LIDAR sensor measures distance in all directions in a plane Since, by nature, 2D LIDAR sensors need to send a large amount of data back to the roboRIO, they almost always connect to one of the roboRIO's :doc:`serial buses `. -Caveats -------- +## Caveats LIDAR sensors do suffer from a few common drawbacks: diff --git a/source/docs/hardware/sensors/proximity-switches.rst b/source/docs/hardware/sensors/proximity-switches.rst index 5abfbef9e3..9b724ef730 100644 --- a/source/docs/hardware/sensors/proximity-switches.rst +++ b/source/docs/hardware/sensors/proximity-switches.rst @@ -1,14 +1,12 @@ .. include:: -Proximity Switches - Hardware -============================= +# Proximity Switches - Hardware .. note:: This section covers proximity switch hardware. For a guide to using proximity switches in software, see :ref:`docs/software/hardware-apis/sensors/digital-inputs-software:Digital Inputs - Software`. One of the most common sensing tasks on a robot is detecting when an object (be it a mechanism, game piece, or field element) is within a certain distance of a known point on the robot. This type of sensing is accomplished by a "proximity switch." -Proximity switch operation --------------------------- +## Proximity switch operation Proximity switches are switches - they operate a circuit between an "open" state (in which there *is not* connectivity across the circuit) and a "closed" one (in which there *is*). Thus, proximity switches generate a digital signal, and accordingly, they are almost always connected to the roboRIO's :doc:`digital input ` ports. @@ -16,8 +14,7 @@ Proximity switches can be either "normally-open," in which activating the switch The digital inputs on the roboRIO have pull-up resistors that will make the input be high (1 value) when the switch is open, but when the switch closes the value goes to 0 since the input is now connected to ground. -Types of Proximity Switches ---------------------------- +## Types of Proximity Switches There are several types of proximity switches that are commonly used in FRC\ |reg|: @@ -27,8 +24,7 @@ There are several types of proximity switches that are commonly used in FRC\ |re - `Photoelectric Proximity Switches`_ - `Time-of-flight Proximity Switches`_ -Mechanical Proximity Switches ("limit switches") -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Mechanical Proximity Switches ("limit switches") .. image:: images/digital-inputs-hardware/limit-switch-to-roborio.svg :alt: A normally open limit switch connected to a channel of the roboRIO DIO. @@ -39,22 +35,20 @@ Limit switches vary in size, the geometry of the switch-arm, and in the amount o See this :ref:`article ` for writing the software for Limit Switches. -Magnetic Proximity Switches -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Magnetic Proximity Switches .. image:: images/digital-inputs-hardware/hall-effect-sensor-to-roborio.svg :alt: A magnetic proximity switch hooked up to a DIO channel of the roboRIO. Magnetic proximity switches are activated when a magnet comes within a certain range of the sensor. Accordingly, they are "no-contact" switches - they do not require contact with the object being sensed. -There are two major types of magnetic proximity switches - reed switches and hall-effect sensors. In a reed switch, the magnetic field causes a pair of flexible metal contacts (the "reeds") to touch each other, closing the circuit. A hall-effect sensor, on the other hand, detects the induced voltage transversely across a current-carrying conductor. Hall-effect sensors are generally the cheaper and more reliable of the two. Pictured above is the `Hall effect sensor from West Coast Products `__. +There are two major types of magnetic proximity switches - reed switches and hall-effect sensors. In a reed switch, the magnetic field causes a pair of flexible metal contacts (the "reeds") to touch each other, closing the circuit. A hall-effect sensor, on the other hand, detects the induced voltage transversely across a current-carrying conductor. Hall-effect sensors are generally the cheaper and more reliable of the two. Pictured above is the [Hall effect sensor from West Coast Products](https://wcproducts.com/products/wcp-sensors). Magnetic proximity switches may be either "unipolar," "bipolar," or "omnipolar." A unipolar switch activates and deactivates depending on the presence of a given pole of the magnet (either north or south, depending on the switch). A bipolar switch activates from the proximity of one pole, and deactivates from the proximity of the opposite pole. An omnipolar switch will activate in the presence of either pole, and deactivates when no magnet is present. While magnetic proximity switches are often more reliable than their mechanical counterparts, they require the user to mount a magnet on the object to be sensed - thus, they are mostly used for sensing mechanism location. -Inductive Proximity Switches -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Inductive Proximity Switches .. image:: images/proximity-switches-hardware/inductive-proximity-switch.png :alt: Example industrial inductive proximity switch. @@ -63,25 +57,23 @@ Inductive proximity switches are activated when a conductor of any sort comes wi Inductive proximity switches are used for many of the same purposes as magnetic proximity switches. Their more-general nature (activating in the presence of any conductor, rather than just a magnet) can be either a help or a hindrance, depending on the nature of the application. -Photoelectric Proximity Switches -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Photoelectric Proximity Switches .. image:: images/proximity-switches-hardware/ir-digital-obstacle-sensor-to-roborio.svg :alt: Connecting a photoelectric proximity switch to a DIO port. -Photoelectric proximity switches are another type of no-contact proximity switch in widespread use in FRC. Photoelectric proximity switches contain a light source (usually an IR laser) and a photoelectric sensor that activates the switch when the detected light (which bounces off of the sensor target) exceeds a given threshold. One such sensor is the `IR Obstacle Avoidance Module `__ pictured below. +Photoelectric proximity switches are another type of no-contact proximity switch in widespread use in FRC. Photoelectric proximity switches contain a light source (usually an IR laser) and a photoelectric sensor that activates the switch when the detected light (which bounces off of the sensor target) exceeds a given threshold. One such sensor is the [IR Obstacle Avoidance Module](https://www.electrodragon.com/product/infraredir-obstacle-avoidance-sensor-moduleadjust-distance/) pictured above. Since photoelectric proximity switches rely on measuring the amount of reflected light, they are often inconsistent in their triggering range between different materials - accordingly, most photoelectric sensors have an adjustable activation point (typically controlled by turning a screw somewhere on the sensor body). On the other hand, photoelectric sensors are also extremely versatile, as they can detect a greater variety of objects than the other types of no-contact switches. -Photoelectric sensors are also often used in a "beam break" configuration, in which the emitter is separate from the sensor. These typically activate when an object is interposed between the emitter and the sensor. Pictured above is a `beam break sensor with an IR LED transmitter and IR receiver `__. +Photoelectric sensors are also often used in a "beam break" configuration, in which the emitter is separate from the sensor. These typically activate when an object is interposed between the emitter and the sensor. Pictured below is a [beam break sensor with an IR LED transmitter and IR receiver](https://www.adafruit.com/product/2167). .. image:: images/proximity-switches-hardware/ir-beam-break-sensor-to-roborio.svg :alt: Connecting a beam break receiver and transmitter each to one DIO channel on the roboRIO. -Time-of-flight Proximity Switches -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Time-of-flight Proximity Switches .. image:: images/proximity-switches-hardware/time-of-flight-i2c-distance-sensor-to-roborio.svg :alt: VL53L0X time of flight sensor hooked up to the I2C port. -Time-of-flight Proximity Switches are newer to the market and are not commonly found in FRC. They use a concentrated light source, such as a small laser, and measure the time between the emission of light and when the receiver detects it. Using the speed of light, it can produce a very accurate distance measurement for a very small target area. Range on this type of sensor can range greatly, between 30mm to around 1000mm for the `VL53L0X sensor `__ pictured above. There are also longer range versions available. More information about time of flight sensors can be found in `this article `__ and more about the circuitry can be found in `this article `__. +Time-of-flight Proximity Switches are newer to the market and are not commonly found in FRC. They use a concentrated light source, such as a small laser, and measure the time between the emission of light and when the receiver detects it. Using the speed of light, it can produce a very accurate distance measurement for a very small target area. Range on this type of sensor can range greatly, between 30mm to around 1000mm for the [VL53L0X sensor](https://www.adafruit.com/product/3317) pictured above. There are also longer range versions available. More information about time of flight sensors can be found in [this article](https://learn.adafruit.com/adafruit-vl53l0x-micro-lidar-distance-sensor-breakout) and more about the circuitry can be found in [this article](https://www.allaboutcircuits.com/technical-articles/how-do-time-of-flight-sensors-work-pmdtechnologies-tof-3D-camera/). diff --git a/source/docs/hardware/sensors/sensor-overview-hardware.rst b/source/docs/hardware/sensors/sensor-overview-hardware.rst index 2e2bb77489..1484568f6b 100644 --- a/source/docs/hardware/sensors/sensor-overview-hardware.rst +++ b/source/docs/hardware/sensors/sensor-overview-hardware.rst @@ -1,7 +1,6 @@ .. include:: -Sensor Overview - Hardware -========================== +# Sensor Overview - Hardware .. note:: This section covers sensor hardware, not the use of sensors in code. For a software sensor guide, see :ref:`docs/software/hardware-apis/sensors/sensor-overview-software:Sensor Overview - Software`. @@ -9,13 +8,11 @@ In order to be effective, it is often vital for robots to be able to gather info Additionally, sensors can be extremely important for robot safety - many robot mechanisms are capable of breaking themselves if used incorrectly. Sensors provide a safeguard against this, allowing robots to, for example, disable a motor if a mechanism is against a hard-stop. -Types of Sensors ----------------- +## Types of Sensors Sensors used in FRC can be generally categorized in two different ways: by function, and by communication protocol. The former categorization is relevant for robot design; the latter for wiring and programming. -Sensors by Function -^^^^^^^^^^^^^^^^^^^ +### Sensors by Function Sensors can provide feedback on a variety of different aspects of the robot's state. Sensor functions common to FRC include: @@ -41,8 +38,7 @@ Sensors can provide feedback on a variety of different aspects of the robot's st - :doc:`Gyroscopes ` -Sensors by Communication Protocol -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Sensors by Communication Protocol In order for a sensor to be useful, it must be able to "talk" to the roboRIO. There are several main methods by which sensors can communicate their readings to the roboRIO: diff --git a/source/docs/hardware/sensors/serial-buses.rst b/source/docs/hardware/sensors/serial-buses.rst index 488ea3e914..f97bcef761 100644 --- a/source/docs/hardware/sensors/serial-buses.rst +++ b/source/docs/hardware/sensors/serial-buses.rst @@ -1,14 +1,12 @@ .. include:: -Serial Buses -============ +# Serial Buses In addition to the :doc:`digital ` and :doc:`analog ` inputs, the roboRIO also offers several methods of serial communication with peripheral devices. Both the digital and analog inputs are highly limited in the amount of data that can be sent over them. Serial buses allow users to make use of far more-robust and higher-bandwidth communications protocols with sensors that collect large amounts of data, such as inertial measurement units (IMUs) or 2D LIDAR sensors. -Types of supported serial buses -------------------------------- +## Types of supported serial buses The roboRIO supports many basic types of serial communications: @@ -18,10 +16,9 @@ The roboRIO supports many basic types of serial communications: - `USB Host`_ - `CAN Bus`_ -Additionally, the roboRIO supports communications with peripheral devices over the CAN bus. However, as the FRC\ |reg| CAN protocol is quite idiosyncratic, relatively few peripheral sensors support it (though it is heavily used for motor controllers). +Additionally, the roboRIO supports communications with peripheral devices over the :term:`CAN` bus. However, as the FRC\ |reg| CAN protocol is quite idiosyncratic, relatively few peripheral sensors support it (though it is heavily used for motor controllers). -I2C ---- +## I2C .. image:: images/roborio/roborio-i2c.svg :alt: The I2C port on the roboRIO. @@ -29,14 +26,13 @@ I2C .. image:: images/serial-buses/i2c-pinout.png :alt: I2C roboRIO port pin specifications. -To communicate to peripheral devices over I2C, each pin should be wired to its corresponding pin on the device. I2C allows users to wire a "chain" of slave devices to a single port, so long as those devices have separate IDs set. +To communicate to peripheral devices over :term:`I2C`, each pin should be wired to its corresponding pin on the device. I2C allows users to wire a "chain" of slave devices to a single port, so long as those devices have separate IDs set. -The I2C bus can also be used through the `MXP expansion port`_. The I2C bus on the MXP is independent. For example, a device on the main bus can have the same ID as a device on the MXP bus. +The I2C bus can also be used through the `MXP expansion port`_. The I2C bus on the :term:`MXP` is independent. For example, a device on the main bus can have the same ID as a device on the MXP bus. .. warning:: Be sure to familiarize yourself on the following known issue before using the onboard I2C port: :ref:`docs/yearly-overview/known-issues:Onboard I2C Causing System Lockups` -SPI ---- +## SPI .. image:: images/roborio/roborio-spi.svg :alt: Show the SPI port on the roboRIO. @@ -44,12 +40,11 @@ SPI .. image:: images/serial-buses/spi-pinout.png :alt: SPI roboRIO port pin specifications. -To communicate to peripheral devices over SPI, each pin should be wired to its corresponding pin on the device. The SPI port supports communications to up to four devices (corresponding to the Chip Select (CS) 0-3 pins on the diagram above). +To communicate to peripheral devices over :term:`SPI`, each pin should be wired to its corresponding pin on the device. The SPI port supports communications to up to four devices (corresponding to the Chip Select (CS) 0-3 pins on the diagram above). The SPI bus can also be used through the `MXP expansion port`_. The MXP port provides independent clock, and input/output lines and an additional CS. -RS-232 ------- +## RS-232 .. image:: images/roborio/roborio-rs-232.svg :alt: Location of the RS-232 port on the roboRIO. @@ -61,17 +56,15 @@ To communicate to peripheral devices over RS-232, each pin should be wired to it The RS-232 bus can also be used through the `MXP expansion port`_. -The roboRIO RS-232 serial port uses RS-232 signaling levels (+/- 15v). The MXP serial port uses CMOS signaling levels (+/- 3.3v). +The roboRIO RS-232 serial port uses RS-232 signaling levels (+/- 15v). The MXP serial port uses CMOS signaling levels with a 3.3v output, and a 3.3v to 5v tolerant input. .. note:: By default, the onboard RS-232 port is utilized by the roboRIO's serial console. In order to use it for an external device, the serial console must be disabled using the :ref:`Imaging Tool ` or :ref:`docs/software/roborio-info/roborio-web-dashboard:roboRIO Web Dashboard`. -USB Client ----------- +## USB Client One of the USB ports on the roboRIO is a USB-B, or USB client port. This can be connected to devices, such as a Driver Station computer, with a standard USB cable. -USB Host --------- +## USB Host .. image:: images/roborio/roborio-usb-host.svg :alt: Location of the two USB ports on the roboRIO at top center. @@ -79,8 +72,7 @@ USB Host Two of the USB ports on the roboRIO is a USB-A, or USB host port. These can be connected to devices, such as cameras or sensors, with a standard USB cable. -MXP Expansion Port ------------------- +## MXP Expansion Port .. image:: images/roborio/roborio-mxp.svg :alt: Location of the MXP port on the roboRIO directly above the NI logo. @@ -88,12 +80,11 @@ MXP Expansion Port .. image:: images/serial-buses/mxp-pinout.png :alt: MXP pinout. -Several of the serial buses are also available for use through the roboRIO's MXP Expansion Port. This port allows users to make use of many additional :doc:`digital ` and :doc:`analog ` inputs, as well as the various serial buses. +Several of the serial buses are also available for use through the roboRIO's :term:`MXP` Expansion Port. This port allows users to make use of many additional :doc:`digital ` and :doc:`analog ` inputs, as well as the various serial buses. Many peripheral devices attach directly to the MXP port for convenience, requiring no wiring on the part of the user. -CAN Bus -------- +## CAN Bus .. image:: images/roborio/roborio-can.svg :alt: Show the location of the CAN bus terminals in the top left corner of the roboRIO. @@ -105,9 +96,9 @@ Additionally, the roboRIO supports communications with peripheral devices over t Several sensors primarily use the CAN bus. Some examples include: -- `CAN Based Time-of-Flight Range/Distance Sensor from playingwithfusion.com `__ -- TalonSRX-based sensors, such as the `Gadgeteer Pigeon IMU `__ and the `SRX MAG Encoder `__ -- `CANifier `__ +- [CAN Based Time-of-Flight Range/Distance Sensor from playingwithfusion.com](https://www.playingwithfusion.com/productview.php?pdid=96&catid=1009) +- TalonSRX-based sensors, such as the [Gadgeteer Pigeon IMU ](https://store.ctr-electronics.com/gadgeteer-pigeon-imu/) and the [SRX MAG Encoder](https://store.ctr-electronics.com/srx-mag-encoder/) +- [CANifier](https://store.ctr-electronics.com/canifier/) - Power monitoring sensors built into the :ref:`CTRE Power Distribution Panel (PDP) ` and the :ref:`REV Power Distribution Hub (PDH) ` More information about using devices connected to the CAN bus can be found in the article about :ref:`using can devices `. diff --git a/source/docs/hardware/sensors/triangulating-rangefinders.rst b/source/docs/hardware/sensors/triangulating-rangefinders.rst index b3ab52041f..668f541d5a 100644 --- a/source/docs/hardware/sensors/triangulating-rangefinders.rst +++ b/source/docs/hardware/sensors/triangulating-rangefinders.rst @@ -1,25 +1,22 @@ .. include:: -Triangulating Rangefinders -========================== +# Triangulating Rangefinders .. image:: images/triangulating-rangefinders/infrared-proximity-sensor.png :alt: Sharp-brand triangulating rangefinder. -Triangulating rangefinders (often called "IR rangefinders," as they commonly function in the infrared wavelength band) are another common type of rangefinder used in FRC\ |reg|. The sensor shown above is a `common Sharp-brand sensor `__ +Triangulating rangefinders (often called "IR rangefinders," as they commonly function in the infrared wavelength band) are another common type of rangefinder used in FRC\ |reg|. The sensor shown above is a [common Sharp-brand sensor](https://www.sparkfun.com/products/242) Unlike :doc:`LIDAR `, triangulating rangefinders do not measure the time between the emission of a pulse and the receiving of a reflection. Rather, most IR rangefinders work by emitting a constant beam at a slight angle, and measuring the position of the reflected beam. The closer the point of contact of the reflected beam to the emitter, the closer the object to the sensor. -Using IR rangefinders ---------------------- +## Using IR rangefinders .. image:: images/triangulating-rangefinders/ir-proximity-sensor-to-roborio.svg :alt: Connecting the Sharp GP2Y0A21YK IR rangefinder to the analog in port of the roboRIO. IR Rangefinders generally output an analog voltage proportional to the distance to the target, and thus connect to the :doc:`analog input ` ports on the RIO. -Caveats -------- +## Caveats IR rangefinders suffer similar drawbacks to 1D LIDAR sensors - they are very sensitive to the reflectivity of the target in the wavelength of the emitted laser. diff --git a/source/docs/hardware/sensors/ultrasonics-hardware.rst b/source/docs/hardware/sensors/ultrasonics-hardware.rst index 51d401b42a..37f4b38a2b 100644 --- a/source/docs/hardware/sensors/ultrasonics-hardware.rst +++ b/source/docs/hardware/sensors/ultrasonics-hardware.rst @@ -1,43 +1,37 @@ .. include:: -Ultrasonics - Hardware -====================== +# Ultrasonics - Hardware .. note:: This section covers ultrasonic sensor hardware. For a software guide to ultrasonics, see :ref:`docs/software/hardware-apis/sensors/ultrasonics-software:Ultrasonics - Software`. Ultrasonic rangefinders are some of the most common rangefinders used in FRC\ |reg|. They are cheap, easy-to-use, and fairly reliable. Ultrasonic rangefinders work by emitting a pulse of high-frequency sound, and then measuring how long it takes the echo to reach the sensor after bouncing off the target. From the measured time and the speed of sound in air, it is possible to calculate the distance to the target. -Types of ultrasonics --------------------- +## Types of ultrasonics While all ultrasonic rangefinders operate on the "ping-response" principle outlined above, they may vary in the way they communicate with the roboRIO. -Analog ultrasonics -^^^^^^^^^^^^^^^^^^ +### Analog ultrasonics .. image:: images/analog-inputs-hardware/ultrasonic-sensor-to-roborio.svg :alt: Connecting a MB1013 to the analog in port of the roboRIO. Analog ultrasonics output a simple analog voltage corresponding to the distance to the target, and thus connect to an :doc:`analog input ` port. The user will need to calibrate the voltage-to-distance conversion in :ref:`software `. -Ping-response ultrasonics -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Ping-response ultrasonics .. image:: images/ultrasonics-hardware/vex-ultrasonic-rangefinder-to-roborio.svg :alt: Connecting the input and output channels of the VEX Ultrasonic Range Finder to two DIO ports. While, as mentioned, all ultrasonics are functionally ping-response devices, a "ping response" ultrasonic is one configured to connect to :ref:`both a digital input and a digital output `. The digital output is used to send the ping, while the input is used to read the response. -Serial ultrasonics -^^^^^^^^^^^^^^^^^^ +### Serial ultrasonics .. image:: images/ultrasonics-hardware/mb-lv-ez1-serial.png :alt: A Maxbotix RS-232 Ultrasonic sensor. Some more-complicated ultrasonic sensors may communicate with the RIO over one of the :doc:`serial buses `, such as RS-232. -Caveats -------- +## Caveats Ultrasonic sensors are generally quite easy to use, however there are a few caveats. As ultrasonics work by measuring the time between the pulse and its echo, they generally measure distance only to the *closest* target in their range. Thus, it is extremely important to pick the right sensor for the job. The documentation for ultrasonic sensors will generally include a picture of the "beam pattern" that shows the shape of the "window" in which the ultrasonic will detect a target - pay close attention to this when selecting your sensor. diff --git a/source/docs/legal/privacy-policy.rst b/source/docs/legal/privacy-policy.rst new file mode 100644 index 0000000000..92a266df2f --- /dev/null +++ b/source/docs/legal/privacy-policy.rst @@ -0,0 +1,68 @@ +# Privacy Policy + +WPILib is a project developed by volunteers who are largely based in the United States. We take privacy seriously and collect only the minimum amount of information needed to support our mission of providing user-friendly libraries and software tools to enable FIRST Robotics Competition teams to be successful in writing software for their robots. + +## Information We Collect + +WPILib-developed desktop applications do not collect personal information. They do create and store logs of application errors and user interactions on the user's system, but this data is not shared online unless the user takes action to upload or share it. + +The WPILib libraries do not collect personal information. When run on the roboRIO, the libraries collect limited metrics data (“usage reporting”) regarding what pieces of the libraries are used by the deployed software (user-written code linked to the WPILib libraries). Only when the robot code is running and connected to the competition field at a FIRST Robotics Competition event is this information shared with and stored by the Field Management System. This information is associated solely with the FRC team and not with any individual. An anonymized (team number removed) version of this metrics data is publicly shared by FIRST after each competition season, and is used to inform future development of library features. + +[FIRST Privacy Policy](https://www.firstinspires.org/about/privacy-policy). + +## Third-Party Data Collection + +WPILib uses and installs a number of third-party applications, each of which has their own privacy policy. We also use a variety of third-party services for distribution of WPILib software, each of which has their own privacy policy, detailed below. + +### GitHub + +The WPILib source code repositories are hosted by GitHub. The WPILib Visual Studio Code extension also connects to GitHub for online browsing of vendor dependency updates. + +[GitHub Privacy Policy](https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement) + +### ReadTheDocs (docs.wpilib.org) + +Our online documentation site is hosted by ReadTheDocs. + +[ReadTheDocs Privacy Policy](https://docs.readthedocs.io/en/stable/privacy-policy.html) + +### CloudFlare + +WPILib uses Cloudflare for distribution of the WPILib installer. + +[Cloudflare Privacy Policy](https://www.cloudflare.com/privacypolicy/) + +### JFrog Artifactory + +WPILib uses a JFrog-hosted Artifactory instance for distribution of WPILib software components. If a build is performed while connected to the Internet, Artifactory may be contacted to check for and download updates. + +[JFrog Privacy Policy](https://jfrog.com/privacy-notice/) + +### Gradle + +WPILib uses Gradle to build robot projects. If a build is performed while connected to the Internet, Gradle may check for updates to software components and will upload information about the build to its services. + +[Gradle Privacy Policy](https://gradle.com/legal/privacy/) + +### Visual Studio Code + +Visual Studio Code is a desktop application that is separately distributed by Microsoft. The WPILib installer prompts to download and install Visual Studio Code as part of the installation process. Visual Studio Code auto-updates are disabled by the WPILib installer, but crash reporting and usage reporting are still enabled by default. + +[Visual Studio Code Privacy Policy](https://code.visualstudio.com/docs) + +### AdvantageScope + +The AdvantageScope desktop application is distributed and installed with the WPILib installer. + +[AdvantageScope Privacy Policy](https://docs.advantagescope.org/legal/privacy-policy/) + +### Elastic + +The Elastic desktop application is distributed and installed with the WPILib installer. + +[Elastic Privacy Policy](https://github.com/Gold872/elastic-dashboard/blob/main/PRIVACY.md) + +## Contact + +Questions about this policy can be directed to wpilib@wpi.edu. + diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/capture-file-properties.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/capture-file-properties.png new file mode 100644 index 0000000000..f41aa6ad0c Binary files /dev/null and b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/capture-file-properties.png differ diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/data-properties.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/data-properties.png index 7a84bf3a32..ccbc00f6f1 100644 Binary files a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/data-properties.png and b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/data-properties.png differ diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/extra-counters.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/extra-counters.png index 6b43c5408f..e9a8bd6127 100644 Binary files a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/extra-counters.png and b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/extra-counters.png differ diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/graph-properties.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/graph-properties.png index 235e7e2a18..6c17971be8 100644 Binary files a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/graph-properties.png and b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/graph-properties.png differ diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/graph.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/graph.png index f146688b6c..a0261302bf 100644 Binary files a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/graph.png and b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/graph.png differ diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/network-counter.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/network-counter.png index aa451e583d..3f7c598a0f 100644 Binary files a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/network-counter.png and b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/network-counter.png differ diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/performance-monitor.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/performance-monitor.png index 92c668f4cc..c548ce7546 100644 Binary files a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/performance-monitor.png and b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/performance-monitor.png differ diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/start-capture.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/start-capture.png index 3d237cd469..7a99ede662 100644 Binary files a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/start-capture.png and b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/start-capture.png differ diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/start-menu.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/start-menu.png index 63c64d53db..e7228c1c32 100644 Binary files a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/start-menu.png and b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/start-menu.png differ diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/summary.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/summary.png deleted file mode 100644 index 3554577b0b..0000000000 Binary files a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/summary.png and /dev/null differ diff --git a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/view-usage.png b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/view-usage.png index ccd205b109..b830e83be0 100644 Binary files a/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/view-usage.png and b/source/docs/networking/networking-introduction/images/measuring-bandwidth-usage/view-usage.png differ diff --git a/source/docs/networking/networking-introduction/images/networking-basics/ip-address-parts.png b/source/docs/networking/networking-introduction/images/networking-basics/ip-address-parts.png index 16e749d9d8..1bf276e46c 100644 Binary files a/source/docs/networking/networking-introduction/images/networking-basics/ip-address-parts.png and b/source/docs/networking/networking-introduction/images/networking-basics/ip-address-parts.png differ diff --git a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/antenna.png b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/antenna.png index 0dc2b99b09..45bb41e2ee 100644 Binary files a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/antenna.png and b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/antenna.png differ diff --git a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/barrel-jack.png b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/barrel-jack.png index 283424a65e..3a8480ac8c 100644 Binary files a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/barrel-jack.png and b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/barrel-jack.png differ diff --git a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/board.png b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/board.png index 676f8f1c5f..37818dbf85 100644 Binary files a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/board.png and b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/board.png differ diff --git a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/case-screws.png b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/case-screws.png index faaa3d697d..35a9367cfb 100644 Binary files a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/case-screws.png and b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/case-screws.png differ diff --git a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/electrical-tape.png b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/electrical-tape.png index b7d581021f..8fb69bb68c 100644 Binary files a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/electrical-tape.png and b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/electrical-tape.png differ diff --git a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/grab-ethernet.png b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/grab-ethernet.png index 4355302aac..703d043466 100644 Binary files a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/grab-ethernet.png and b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/grab-ethernet.png differ diff --git a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/lift-cover.png b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/lift-cover.png index 27ad87b4eb..6b5ce18bbb 100644 Binary files a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/lift-cover.png and b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/lift-cover.png differ diff --git a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/rubber-feet.png b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/rubber-feet.png index 349675bbb4..bcf7165434 100644 Binary files a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/rubber-feet.png and b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/rubber-feet.png differ diff --git a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/shield.png b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/shield.png index ea9f49d4e9..f7f392adaa 100644 Binary files a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/shield.png and b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/shield.png differ diff --git a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/side-latch.png b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/side-latch.png index 068a036edd..8b116f1b08 100644 Binary files a/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/side-latch.png and b/source/docs/networking/networking-introduction/images/om5p-ac-radio-modification/side-latch.png differ diff --git a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/adapter-settings.png b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/adapter-settings.png index 63daea3e06..609b7d0631 100644 Binary files a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/adapter-settings.png and b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/adapter-settings.png differ diff --git a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/disable-network-adapter.png b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/disable-network-adapter.png index 55e66ab8e6..767a9e0919 100644 Binary files a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/disable-network-adapter.png and b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/disable-network-adapter.png differ diff --git a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/roborio-ip-address.png b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/roborio-ip-address.png index 973ce1705e..0becdf0a51 100644 Binary files a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/roborio-ip-address.png and b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/roborio-ip-address.png differ diff --git a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/settings-change-adapter.png b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/settings-change-adapter.png index b6185ed044..84e6bb313d 100644 Binary files a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/settings-change-adapter.png and b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/settings-change-adapter.png differ diff --git a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/settings-network-internet.png b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/settings-network-internet.png index 15cfddd491..b04f41b742 100644 Binary files a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/settings-network-internet.png and b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/settings-network-internet.png differ diff --git a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/start-menu-win10.png b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/start-menu-win10.png index 9f64463d24..4641fbd1b8 100644 Binary files a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/start-menu-win10.png and b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/start-menu-win10.png differ diff --git a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/win10-dhcp.png b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/win10-dhcp.png index 73c937de96..501748cdee 100644 Binary files a/source/docs/networking/networking-introduction/images/roborio-troubleshooting/win10-dhcp.png and b/source/docs/networking/networking-introduction/images/roborio-troubleshooting/win10-dhcp.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/add-to-firewall.png b/source/docs/networking/networking-introduction/images/windows-firewall/add-to-firewall.png index 9e5b92dbfc..951cf04757 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/add-to-firewall.png and b/source/docs/networking/networking-introduction/images/windows-firewall/add-to-firewall.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/allow-programs.png b/source/docs/networking/networking-introduction/images/windows-firewall/allow-programs.png index f385702716..e0addb1ba8 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/allow-programs.png and b/source/docs/networking/networking-introduction/images/windows-firewall/allow-programs.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/allow-through-firewall.png b/source/docs/networking/networking-introduction/images/windows-firewall/allow-through-firewall.png index 022a45e192..161ec13213 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/allow-through-firewall.png and b/source/docs/networking/networking-introduction/images/windows-firewall/allow-through-firewall.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/checkboxes.png b/source/docs/networking/networking-introduction/images/windows-firewall/checkboxes.png index e6e33ab0e2..13f9bf13c3 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/checkboxes.png and b/source/docs/networking/networking-introduction/images/windows-firewall/checkboxes.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/disable-firewall-toggle.png b/source/docs/networking/networking-introduction/images/windows-firewall/disable-firewall-toggle.png index 5621ed6fce..c034f1da96 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/disable-firewall-toggle.png and b/source/docs/networking/networking-introduction/images/windows-firewall/disable-firewall-toggle.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/open-windows-security.png b/source/docs/networking/networking-introduction/images/windows-firewall/open-windows-security.png index f5d5b9cec8..b18caed6c2 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/open-windows-security.png and b/source/docs/networking/networking-introduction/images/windows-firewall/open-windows-security.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/select-firewall.png b/source/docs/networking/networking-introduction/images/windows-firewall/select-firewall.png index ea7b270649..371cbee542 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/select-firewall.png and b/source/docs/networking/networking-introduction/images/windows-firewall/select-firewall.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/select-network.png b/source/docs/networking/networking-introduction/images/windows-firewall/select-network.png index af8d5933fd..8a38f53986 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/select-network.png and b/source/docs/networking/networking-introduction/images/windows-firewall/select-network.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/select-settings.png b/source/docs/networking/networking-introduction/images/windows-firewall/select-settings.png index 4f115023a9..9d1ef426a7 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/select-settings.png and b/source/docs/networking/networking-introduction/images/windows-firewall/select-settings.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/select-update-security.png b/source/docs/networking/networking-introduction/images/windows-firewall/select-update-security.png index 2244cb15cd..721d441091 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/select-update-security.png and b/source/docs/networking/networking-introduction/images/windows-firewall/select-update-security.png differ diff --git a/source/docs/networking/networking-introduction/images/windows-firewall/select-windows-security.png b/source/docs/networking/networking-introduction/images/windows-firewall/select-windows-security.png index 81bbc579f8..e4cbaded0a 100644 Binary files a/source/docs/networking/networking-introduction/images/windows-firewall/select-windows-security.png and b/source/docs/networking/networking-introduction/images/windows-firewall/select-windows-security.png differ diff --git a/source/docs/networking/networking-introduction/index.rst b/source/docs/networking/networking-introduction/index.rst index a71f74bc25..b077752309 100644 --- a/source/docs/networking/networking-introduction/index.rst +++ b/source/docs/networking/networking-introduction/index.rst @@ -1,5 +1,4 @@ -Networking Introduction -======================= +# Networking Introduction This section outlines basic robot configuration and usage relating to communication between the driver station and roboRIO. diff --git a/source/docs/networking/networking-introduction/ip-configurations.rst b/source/docs/networking/networking-introduction/ip-configurations.rst index b0c04e3c2b..ca5141141b 100644 --- a/source/docs/networking/networking-introduction/ip-configurations.rst +++ b/source/docs/networking/networking-introduction/ip-configurations.rst @@ -1,19 +1,21 @@ -IP Configurations -================= +# IP Configurations .. note:: This document describes the IP configuration used at events, both on the fields and in the pits, potential issues and workaround configurations. -TE.AM IP Notation ------------------ +## TE.AM IP Notation -The notation TE.AM is used as part of IPs in numerous places in this document. This notation refers to splitting your four digit team number into two digit pairs for the IP address octets. +The notation TE.AM is used as part of IPs in numerous places in this document. This notation refers to splitting your five digit team number into digits for the IP address octets. Where AM is the last two digits of the team number, and TE is the first three digits. Leading zeros are optional. This scheme supports team numbers up to 25599. Example: ``10.TE.AM.2`` +Team 1 - ``10.0.1.2`` + Team 12 - ``10.0.12.2`` Team 122 - ``10.1.22.2`` +Team 1002 - ``10.10.2.2`` + Team 1212 - ``10.12.12.2`` Team 1202 - ``10.12.2.2`` @@ -22,15 +24,17 @@ Team 1220 - ``10.12.20.2`` Team 3456 - ``10.34.56.2`` -On the Field ------------- +Team 10000 - ``10.100.0.2`` + +Team 12345 - ``10.123.45.2`` + +## On the Field This section describes networking when connected to the Field Network for match play -On the Field DHCP Configuration -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### On the Field DHCP Configuration -The Field Network runs a DHCP server with pools for each team that will hand out addresses in the range of ``10.TE.AM.20`` to ``10.TE.AM.199`` with a subnet mask of ``255.255.255.0``, and a default gateway of ``10.TE.AM.4``. +The Field Network runs a :term:`DHCP` server with pools for each team that will hand out addresses in the range of ``10.TE.AM.20`` to ``10.TE.AM.199`` with a subnet mask of ``255.255.255.0``, and a default gateway of ``10.TE.AM.4``. When configured for an event, the Team Radio runs a DHCP server with a pool for devices onboard the robot that will hand out addresses in the range of 10.TE.AM.200 to 10.TE.AM.219 with a subnet mask of 255.255.255.0, and a gateway of 10.TE.AM.1. - OpenMesh OM5P-AN or OM5P-AC radio - Static ``10.TE.AM.1`` programmed by @@ -41,8 +45,7 @@ When configured for an event, the Team Radio runs a DHCP server with a pool for - IP camera (if used) - DHCP ``10.TE.AM.Y`` assigned by Robot Radio - Other devices (if used) - DHCP ``10.TE.AM.Z`` assigned by Robot Radio -On the Field Static Configuration -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### On the Field Static Configuration It is also possible to configure static IPs on your devices to accommodate devices or software which do not support mDNS. When doing so you want to make sure to avoid addresses that will be in use when the robot is on the field network. These addresses are ``10.TE.AM.1`` for the OpenMesh radio, ``10.TE.AM.4`` for the field router, and anything greater than ``10.TE.AM.20`` which may be assigned to a device configured for DHCP or else reserved. The roboRIO network configuration can be set from the webdashboard. @@ -50,20 +53,18 @@ It is also possible to configure static IPs on your devices to accommodate devic - roboRIO - Static ``10.TE.AM.2`` would be a reasonable choice, subnet mask of ``255.255.255.0`` (default) - Driver Station - Static ``10.TE.AM.5`` would be a reasonable choice, - subnet mask **must** be ``255.0.0.0`` to enable the DS to reach both the robot and FMS Server, without additionally configuring the default gateway. + subnet mask **must** be ``255.0.0.0`` to enable the DS to reach both the robot and :term:`FMS` Server, without additionally configuring the default gateway. If a static address is assigned and the subnet mask is set to ``255.255.255.0``, then the default gateway must be configured to ``10.TE.AM.4``. - IP Camera (if used) - Static ``10.TE.AM.11`` would be a reasonable choice, subnet ``255.255.255.0`` should be fine - Other devices - Static ``10.TE.AM.6-.10`` or ``.12-.19`` (.11 if camera not present) subnet ``255.255.255.0`` -In the Pits ------------ +## In the Pits .. note:: **New for 2018:** There is now a DHCP server running on the wired side of the Robot Radio in the event configuration. -In the Pits DHCP Configuration -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### In the Pits DHCP Configuration - OpenMesh radio - Static ``10.TE.AM.1`` programmed by Kiosk. - roboRIO - ``10.TE.AM.2``, assigned by Robot Radio @@ -72,7 +73,6 @@ In the Pits DHCP Configuration - IP camera (if used) - DHCP, ``10.TE.AM.Y``, assigned by Robot Radio - Other devices (if used) - DHCP, ``10.TE.AM.Z``, assigned by Robot Radio -In the Pits Static Configuration -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### In the Pits Static Configuration It is also possible to configure static IPs on your devices to accommodate devices or software which do not support mDNS. When doing so you want to make sure to avoid addresses that will be in use when the robot is on the field network. These addresses are ``10.TE.AM.1`` for the OpenMesh radio and ``10.TE.AM.4`` for the field router. diff --git a/source/docs/networking/networking-introduction/measuring-bandwidth-usage.rst b/source/docs/networking/networking-introduction/measuring-bandwidth-usage.rst index 9e7930b716..1c9f9cb82e 100644 --- a/source/docs/networking/networking-introduction/measuring-bandwidth-usage.rst +++ b/source/docs/networking/networking-introduction/measuring-bandwidth-usage.rst @@ -1,35 +1,30 @@ .. include:: -Measuring Bandwidth Usage -========================= +# Measuring Bandwidth Usage -On the FRC\ |reg| Field each team is allocated limited network bandwidth (see R704 in the 2023 manual). The `FMS Whitepaper `__ provides more information on determining the bandwidth usage of the Axis camera, but some teams may wish to measure their overall bandwidth consumption. This document details how to make that measurement. +On the FRC\ |reg| Field each team is allocated limited network bandwidth (see R704 in the 2024 manual). Some teams may wish to measure their overall bandwidth consumption. This document details how to make that measurement. .. note:: Teams can simulate the bandwidth throttling at home using the FRC Bridge Configuration Utility with the bandwidth checkbox checked. -Measuring Bandwidth Using the Performance Monitor (Win 7/10) --------------------------------------------------------------- +## Measuring Bandwidth Using the Performance Monitor (Win 7/10) Windows contains a built-in tool called the Performance Monitor that can be used to monitor the bandwidth usage over a network interface. -Launching the Performance Monitor -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Launching the Performance Monitor .. image:: images/measuring-bandwidth-usage/start-menu.png :alt: Using the start menu to type "perfmon.msc". -Click ``Start`` and in the search box, type ``perfmon.msc`` and press Enter. +Open the Start menu and in the search box, type ``perfmon.msc`` and press Enter. -Open Real-Time Monitor -^^^^^^^^^^^^^^^^^^^^^^ +### Open Real-Time Monitor .. image:: images/measuring-bandwidth-usage/performance-monitor.png :alt: Click "Performance Monitor" under "Monitoring Tools" in the tree view. -In the left pane, click ``Performance Monitor`` to display the real-time monitor. +In the left pane, click :guilabel:`Performance Monitor` to display the real-time monitor. -Add Network Counter -^^^^^^^^^^^^^^^^^^^ +### Add Network Counter .. image:: images/measuring-bandwidth-usage/network-counter.png :alt: The "Add Counters" screen showinging the steps below. @@ -37,66 +32,58 @@ Add Network Counter #. Click the green plus near the top of the screen to add a counter #. In the top left pane, locate and click on ``Network Interface`` to select it #. In the bottom left pane, locate the desired network interface (or use All instances to monitor all interfaces) -#. Click ``Add>>`` to add the counter to the right pane. -#. Click ``OK`` to add the counters to the graph. +#. Click :guilabel:`Add >>` to add the counter to the right pane. +#. Click :guilabel:`OK` to add the counters to the graph. -Remove Extra Counters -^^^^^^^^^^^^^^^^^^^^^ +### Remove Extra Counters .. image:: images/measuring-bandwidth-usage/extra-counters.png :alt: Removing the counter at the bottom of the graph screen. -In the bottom pane, select each counter other than ``Bytes Total/sec`` and press the ``Delete`` key. The ``Bytes Total/sec`` entry should be the only entry remaining in the pane. +In the bottom pane, select each counter other than ``Bytes Total/sec`` and press the :kbd:`Delete` key. The ``Bytes Total/sec`` entry should be the only entry remaining in the pane. -Configure Data Properties -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Configure Data Properties .. image:: images/measuring-bandwidth-usage/data-properties.png :alt: Scale dropdown highlighted on the Performance Monitor Properties page. -Press :kbd:`Ctrl+Q` to bring up the Properties window. Click on the dropdown next to ``Scale`` and select ``1.0``. Then click on the ``Graph`` tab. +Press :kbd:`Ctrl+Q` to bring up the Properties window. Click on the dropdown next to ``Scale`` and select ``1.0``. Then click on the :guilabel:`Graph` tab. -Configure Graph Properties -^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Configure Graph Properties .. image:: images/measuring-bandwidth-usage/graph-properties.png :alt: Moving to the Graph tab the Maximum field under Vertical Scale is highlighted. -In the ``Maximum Box`` under ``Vertical Scale`` enter 917504 (this is 7 Megabits converted to Bytes). If desired, turn on the horizontal grid by checking the box. Then click ``OK`` to close the dialog. +In the ``Maximum Box`` under ``Vertical Scale`` enter 524288 (this is 4 Megabits converted to Bytes). If desired, turn on the horizontal grid by checking the box. Then click :guilabel:`OK` to close the dialog. -Viewing Bandwidth Usage -^^^^^^^^^^^^^^^^^^^^^^^ +### Viewing Bandwidth Usage .. image:: images/measuring-bandwidth-usage/graph.png :alt: Observing the bandwidth usage on the chart screen. -You may now connect to your robot as normal over the selected interface (if you haven't done so already). The graph will show the total bandwidth usage of the connection, with the bandwidth cap at the top of the graph. The Last, Average, Min and Max values are also displayed at the bottom of the graph. Note that these values are in Bytes/Second meaning the cap is 917,504. With just the Driver Station open you should see a flat line at ~100000 Bytes/Second. +You may now connect to your robot as normal over the selected interface (if you haven't done so already). The graph will show the total bandwidth usage of the connection, with the bandwidth cap at the top of the graph. The Last, Average, Min and Max values are also displayed at the bottom of the graph. Note that these values are in Bytes/Second meaning the cap is 524,288. With just the Driver Station open you should see a flat line at ~100000 Bytes/Second. -Measuring Bandwidth Usage using Wireshark ------------------------------------------ +## Measuring Bandwidth Usage using Wireshark -If you can not use performance monitor, you will need to install a 3rd party program to monitor bandwidth usage. One program that can be used for this purpose is Wireshark. Download and install the latest version of Wireshark for your version of Windows. After installation is complete, locate and open Wireshark. Connect your computer to your robot, open the Driver Station and any Dashboard or custom programs you may be using. +If you can not use performance monitor, you will need to install a 3rd party program to monitor bandwidth usage. One program that can be used for this purpose is Wireshark. Download and install the latest version of Wireshark for your version of Windows from [this page](https://www.wireshark.org/download.html). After installation is complete, locate and open Wireshark. Connect your computer to your robot, open the Driver Station and any Dashboard or custom programs you may be using. -Select the interface and Start capture -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Select the interface and Start capture .. image:: images/measuring-bandwidth-usage/start-capture.png :alt: Selecting the Start button and choosing the NIC in Wireshark. -In the Wireshark program on the left side, select the interface you are using to connect to the robot and click ``Start``. +In the Wireshark program on the left side, double click the interface you are using to connect to the robot. -Open Statistics Summary -^^^^^^^^^^^^^^^^^^^^^^^ +### Open Capture File Properties -.. image:: images/measuring-bandwidth-usage/summary.png - :alt: In the menu bar at the top choosing "Statistics" then "Summary" +.. image:: images/measuring-bandwidth-usage/capture-file-properties.png + :alt: In the menu bar at the top choosing "Statistics" then "Capture File Properties" -Let the capture run for at least 1 minute, then click ``Statistics`` then ``Summary``. +Let the capture run for at least 1 minute, then click :guilabel:`Statistics` then :guilabel:`Capture File Properties`. -View Bandwidth Usage -^^^^^^^^^^^^^^^^^^^^ +### View Bandwidth Usage .. image:: images/measuring-bandwidth-usage/view-usage.png - :alt: Looking at the "Avg. MBit/sec" field of the Wireshark summary. + :alt: Looking at the "Average bits/sec" field of the Wireshark summary. -Average bandwidth usage, in Megabits/Second is displayed near the bottom of the summary window. +Average bandwidth usage, in bits/second is displayed near the bottom of the window. diff --git a/source/docs/networking/networking-introduction/networking-basics.rst b/source/docs/networking/networking-introduction/networking-basics.rst index e2318c918f..d92e3098fc 100644 --- a/source/docs/networking/networking-introduction/networking-basics.rst +++ b/source/docs/networking/networking-introduction/networking-basics.rst @@ -1,10 +1,8 @@ .. include:: -Networking Basics -================= +# Networking Basics -What is an IP Address? ----------------------- +## What is an IP Address? An IP address is a unique string of numbers, separated by periods that identifies each device on a network. Each IP address is divided up into 4 sections (octets) ranging from 0-255. @@ -17,8 +15,7 @@ This brings up our **first key point** of IP Addressing: Each device on the netw Since there are only 4 billion addresses, and there are more than 4 billion computers connected to the internet, we need to be as efficient as possible with giving out IP addresses. This brings us to public vs. private addresses. -Public vs Private IP Addresses ------------------------------- +## Public vs Private IP Addresses To be efficient with using IP Addresses, the idea of "Reserved IP Ranges" was implemented. In short, this means that there are ranges of IP Addresses that will never be assigned to web servers, and will only be used for local networks, such as those in your house. @@ -41,45 +38,38 @@ then passing the returned data back to the private IP that requested it. This al :alt: Devices on the private network send their traffic through the NAT device to communicate to the outside network and vice versa. .. note:: - For the FRC\ |reg| networks, we will use the ``10.0.0.0`` range. This range allows us to use the ``10.TE.AM.xx`` format for IP addresses, whereas using the Class B or C networks would only allow a subset of teams to follow the format. An example of this formatting would be ``10.17.50.1`` for FRC Team 1750. + For the FRC\ |reg| networks, we will use the ``10.0.0.0`` range. This range allows us to use the ``10.TE.AM.xx`` format for IP addresses, whereas using the Class B or C networks would only allow a subset of teams to follow the format (:ref:`TE.AM IP Notation `). -How are these addresses assigned? ---------------------------------- +## How are these addresses assigned? We’ve covered the basics of what IP addresses are, and which IP addresses we will use for the FRC competition, so now we need to discuss how these addresses will get assigned to the devices on our network. We already stated above that we can’t have two devices on the same network with the same IP Address, so we need a way to be sure that every device receives an address without overlapping. This can be done Dynamically (automatic), or Statically (manual). -Dynamically -^^^^^^^^^^^ +### Dynamically Dynamically assigning IP addresses means that we are letting a device on the network manage the IP address assignments. This is done through the Dynamic Host Configuration Protocol (DHCP). DHCP has many components to it, but for the scope of this document, we will think of it as a service that automatically manages the network. Whenever you plug in a new device to the network, the DHCP service sees the new device, then provides it with an available IP address and the other network settings required for the device to communicate. This can mean that there are times we do not know the exact IP address of each device. -What is a DHCP server? -~~~~~~~~~~~~~~~~~~~~~~ +#### What is a DHCP server? -A DHCP server is a device that runs the DHCP service to monitor the network for new devices to configure. In larger businesses, this could be a dedicated computer running the DHCP service and that computer would be the DHCP server. For home networks, FRC networks, and other smaller networks, the DHCP service is usually running on the router; in this case, the router is the DHCP server. +A :term:`DHCP` server is a device that runs the DHCP service to monitor the network for new devices to configure. In larger businesses, this could be a dedicated computer running the DHCP service and that computer would be the DHCP server. For home networks, FRC networks, and other smaller networks, the DHCP service is usually running on the router; in this case, the router is the DHCP server. This means that if you ever run into a situation where you need to have a DHCP server assigning IP addresses to your network devices, it’s as simple as finding the closest home router, and plugging it in. -Statically -^^^^^^^^^^ +### Statically Statically assigning IP addresses means that we are manually telling each device on the network which IP address we want it to have. This configuration happens through a setting on each device. By disabling DHCP on the network and assigning the addresses manually, we get the benefit of knowing the exact IP address of each device on the network, but because we set each one manually and there is no service keeping track of the used IP addresses, we have to keep track of this ourselves. While statically setting IP addresses, we must be careful not to assign duplicate addresses, and must be sure we are setting the other network settings (such as subnet mask and default gateway) correctly on each device. -What is link-local? -------------------- +## What is link-local? If a device does not have an IP address, then it cannot communicate on a network. This can become an issue if we have a device that is set to dynamically acquire its address from a DHCP server, but there is no DHCP server on the network. An example of this would be when you have a laptop directly connected to a roboRIO and both are set to dynamically acquire an IP address. Neither device is a DHCP server, and since they are the only two devices on the network, they will not be assigned IP addresses automatically. Link-local addresses give us a standard set of addresses that we can "fall-back" to if a device set to acquire dynamically is not able to acquire an address. If this happens, the device will assign itself an IP address in the ``169.254.xx.yy`` address range; this is a link-local address. In our roboRIO and computer example above, both devices will realize they haven’t been assigned an IP address and assign themselves a link-local address. Once they are both assigned addresses in the ``169.254.xx.yy`` range, they will be in the same network and will be able to communicate, even though they were set to dynamic and a DHCP server did not assign addresses. -IP Addressing for FRC ---------------------- +## IP Addressing for FRC See the :doc:`IP Networking Article ` for more information. -Mixing Dynamic and Static Configurations -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Mixing Dynamic and Static Configurations While on the field, the team should not notice any issues with having devices set statically in the ``10.TE.AM.xx`` range, and having the field assign DHCP addresses as long as there are no IP address conflicts as referred to in the section above. @@ -90,18 +80,15 @@ In the pits, a team may encounter issues with mixing Static and DHCP devices for .. warning:: When connected via USB to the roboRIO, a :ref:`docs/networking/networking-utilities/portforwarding:Port Forwarding` configuration is required to access devices connected to the OpenMesh radio (on the green network shown above). -Available Network Ports -^^^^^^^^^^^^^^^^^^^^^^^ +### Available Network Ports -Please see R704 of the 2023 Game Manual for information regarding available network ports. +Please see R704 of the 2024 Game Manual for information regarding available network ports. -mDNS ----- +## mDNS mDNS, or multicast Domain Name System is a protocol that allows us to benefit from the features of DNS, without having a DNS server on the network. To make this clearer, let’s take a step back and talk about what DNS is. -What is DNS? -^^^^^^^^^^^^ +### What is DNS? DNS (Domain Name System) can become a complex topic, but for the scope of this paper, we are going to just look at the high-level overview of DNS. In the most basic explanation, DNS is what allows us to relate human-friendly names for network devices to IP Addresses, and keep track of those IP addresses if they change. @@ -113,8 +100,7 @@ Example 2: On your home network, you have a server named ``MYCOMPUTER`` that you This is the second benefit to DNS and the most relevant for FRC. With DNS, if we reference devices by their friendly name instead of IP Address, we don’t have to change anything in our program if the IP Address changes. DNS will keep track of the changes and return the new address if it ever changes. -DNS for FRC -^^^^^^^^^^^ +### DNS for FRC On the field and in the pits, there is no DNS server that allows us to perform the lookups like we do for the Google website, but we’d still like to have the benefits of not remembering every IP Address, and not having to guess at every device’s address if DHCP assigns a different address than we expect. This is where mDNS comes into the picture. @@ -123,13 +109,11 @@ mDNS provides us the same benefits as traditional DNS, but is just implemented i .. note:: If a device used for FRC does not support mDNS, then it will be assigned an IP Address in the 10.TE.AM.20 - 10.TE.AM.255 range, but we won’t know the exact address to connect and we won’t be able to use the friendly name like before. In this case, the device would need to have a static IP Address. -mDNS - Principles -^^^^^^^^^^^^^^^^^ +### mDNS - Principles Multicast Domain Name System (mDNS) is a system which allows for resolution of hostnames to IP addresses on small networks with no dedicated name server. To resolve a hostname a device sends out a multicast message to the network querying for the device. The device then responds with a multicast message containing its IP. Devices on the network can store this information in a cache so subsequent requests for this address can be resolved from the cache without repeating the network query. -mDNS - Providers -^^^^^^^^^^^^^^^^ +### mDNS - Providers To use mDNS, an mDNS implementation is required to be installed on your PC. Here are some common mDNS implementations for each major platform: @@ -146,8 +130,7 @@ Linux: - **nss-mDNS/Avahi/Zeroconf:** Installed and enabled by default on some Linux variants (such as Ubuntu or Mint). May need to be installed or enabled on others (such as Arch) -mDNS - Firewalls -^^^^^^^^^^^^^^^^ +### mDNS - Firewalls .. note:: Depending on your PC configuration, no changes may be required, this section is provided to assist with troubleshooting. @@ -162,25 +145,21 @@ To work properly mDNS must be allowed to pass through your firewall. Because the - ``169.254.0.0 - 169.254.255.255`` - ``224.0.0.251`` -mDNS - Browser support -^^^^^^^^^^^^^^^^^^^^^^ +### mDNS - Browser support Most web-browsers should be able to utilize the mDNS address to access the roboRIO web server as long as an mDNS provider is installed. These browsers include Microsoft Edge, Firefox, and Google Chrome. -USB ---- +## USB If using the USB interface, no network setup is required (you do need the :ref:`docs/zero-to-robot/step-2/frc-game-tools:Installing the FRC Game Tools` installed to provide the roboRIO USB Driver). The roboRIO driver will automatically configure the IP address of the host (your computer) and roboRIO and the software listed above should be able to locate and utilize your roboRIO. -Ethernet/Wireless ------------------ +## Ethernet/Wireless -The :ref:`docs/zero-to-robot/step-3/radio-programming:Programming your Radio` will enable the DHCP server on the OpenMesh radio in the home use case (AP mode), if you are putting the OpenMesh in bridge mode and using a router, you can enable DHCP addressing on the router. The bridge is set to the same team-based IP address as before (``10.TE.AM.1``) and will hand out DHCP address from ``10.TE.AM.20`` to ``10.TE.AM.199``. When connected to the field, FMS will also hand out addresses in the same IP range. +The :ref:`docs/zero-to-robot/step-3/radio-programming:Programming your Radio` will enable the DHCP server on the OpenMesh radio in the home use case (AP mode), if you are putting the OpenMesh in bridge mode and using a router, you can enable DHCP addressing on the router. The bridge is set to the same team-based IP address as before (``10.TE.AM.1``) and will hand out DHCP address from ``10.TE.AM.20`` to ``10.TE.AM.199``. When connected to the field, :term:`FMS` will also hand out addresses in the same IP range. -Summary -------- +## Summary -IP Addresses are what allow us to communicate with devices on a network. For FRC, these addresses are going to be in the 10.TE.AM.xx range if we are connected to a DHCP server or if they are assigned statically, or in the link-local ``169.254.xx.yy`` range if the devices are set to DHCP, but there is no server present. For more information on how IP Addresses work, see `this `__ article by Microsoft. +IP Addresses are what allow us to communicate with devices on a network. For FRC, these addresses are going to be in the 10.TE.AM.xx range if we are connected to a DHCP server or if they are assigned statically, or in the link-local ``169.254.xx.yy`` range if the devices are set to DHCP, but there is no server present. For more information on how IP Addresses work, see [this](https://support.microsoft.com/en-us/help/164015/understanding-tcp-ip-addressing-and-subnetting-basics) article by Microsoft. If all devices on the network support mDNS, then all devices can be set to DHCP and referred to using their friendly names (ex. ``roboRIO-TEAM-FRC.local``). If some devices do not support mDNS, they will need to be set to use static addresses. diff --git a/source/docs/networking/networking-introduction/om5p-ac-radio-modification.rst b/source/docs/networking/networking-introduction/om5p-ac-radio-modification.rst index 35ab9db0c9..31b5d90181 100644 --- a/source/docs/networking/networking-introduction/om5p-ac-radio-modification.rst +++ b/source/docs/networking/networking-introduction/om5p-ac-radio-modification.rst @@ -1,7 +1,6 @@ .. include:: -OM5P-AC Radio Modification -========================== +# OM5P-AC Radio Modification The intended use case for the OM5P-AC radio does not subject it to the same shocks and forces as it sees in the FRC\ |reg| environment. If the radio is subjected to significant pressure on the bottom of the case, it is possible to cause a radio reboot by shorting a metal shield at the bottom of the radio to some exposed metal leads on the bottom of the board. This article details a modification to the radio to prevent this scenario. @@ -10,13 +9,11 @@ The intended use case for the OM5P-AC radio does not subject it to the same shoc - Avoid using the "mounting tab" features on the bottom of the radio. - You may wish to mount the radio to allow for some shock absorption. A little can go a long way, mounting the radio using hook and loop fastener or to a robot surface with a small amount of flex (plastic or sheet metal sheet, etc.) can significantly reduce the forces experienced by the radio. -Opening the Radio ------------------ +## Opening the Radio .. note:: The OpenMesh OM5P-AC is not designed to be a user serviceable device. Users perform this modification at their own risk. Make sure to work slowly and carefully to avoid damaging internal components such as radio antenna cables. -Case Screws -^^^^^^^^^^^ +### Case Screws .. image:: images/om5p-ac-radio-modification/rubber-feet.png :alt: Location of the two front rubber feet. @@ -26,16 +23,14 @@ Case Screws Locate the two rubber feet on the front side of the radio then pry them off the radio using fingernails, small flat screwdriver, etc. Using a small Phillips screwdriver, remove the two screws under the feet. -Side Latches -^^^^^^^^^^^^ +### Side Latches .. image:: images/om5p-ac-radio-modification/lift-cover.png :alt: Slightly lifting the top cover of the radio. There is a small latch on the lid of the radio near the middle of each long edge (you can see these latches more clearly in the next picture). Using a fingernail or very thin tool, slide along the gap between the lid and case from front to back towards the middle of the radio, you should hear a small pop as you near the middle of radio. Repeat on the other side (note: it's not hard to accidentally re-latch the first side while doing this, make sure both sides are unlatched before proceeding). The radio lid should now be slightly open on the front side as shown in the image above. -Remove Lid -^^^^^^^^^^ +### Remove Lid .. warning:: The board may stick to the lid as you remove it due to the heatsink pads. Look through the vents of the radio as you remove the lid to see if the board is coming with it, if it is you may need to insert a small tool to hold the board down to separate it from the lid. We recommend a small screwdriver or similar tool that fits through the vents, applied through the front corner on the barrel jack side, right above the screw hole. You can scroll down to the picture with the lid removed to see what the board looks like in this area. @@ -49,8 +44,7 @@ To begin removing the lid, slide it forward (lifting slightly) until the screw h Next, begin rotating the lid slightly away from the barrel jack side, as shown while continuing to lift. This will unhook the lid from the small triangle visible in the top right corner. Continue to rotate slightly in this direction while pushing the top left corner towards the barrel jack (don't try to lift further in this step) to unhook a similar feature in the top left corner. Then lift the lid completely away from the body. -Remove Board -^^^^^^^^^^^^ +### Remove Board .. image:: images/om5p-ac-radio-modification/antenna.png :alt: Note the three fragile antenna wires on the board. @@ -72,16 +66,14 @@ Tilt the board up (towards the short grey antenna cable) to expose the metal shi .. note:: When you perform this step, you may notice that there is a small reset button on the underside of the board that is larger than the hole in the case. Note that pressing the reset button with the FRC firmware installed has no effect and that drilling the case of the radio is not a permitted modification. -Apply Tape ----------- +## Apply Tape .. image:: images/om5p-ac-radio-modification/electrical-tape.png :alt: Applying electrical tape at the front of the shield near the Ethernet ports. Apply a piece of electrical tape to the metal shield in the area just inside of the network port/barrel jack openings. This will prevent the exposed leads on the underside of the board from short circuiting on this plate. -Re-assemble Radio ------------------ +## Re-assemble Radio Re-assemble the radio by reversing the instructions to open it: diff --git a/source/docs/networking/networking-introduction/roborio-network-troubleshooting.rst b/source/docs/networking/networking-introduction/roborio-network-troubleshooting.rst index 821fa96384..e006d08813 100644 --- a/source/docs/networking/networking-introduction/roborio-network-troubleshooting.rst +++ b/source/docs/networking/networking-introduction/roborio-network-troubleshooting.rst @@ -1,33 +1,28 @@ .. include:: -roboRIO Network Troubleshooting -=============================== +# roboRIO Network Troubleshooting -The roboRIO and FRC\ |reg| tools use dynamic IP addresses (DHCP) for network connectivity. This article describes steps for troubleshooting networking connectivity between your PC and your roboRIO +The roboRIO and FRC\ |reg| tools use dynamic IP addresses (:term:`DHCP`) for network connectivity. This article describes steps for troubleshooting networking connectivity between your PC and your roboRIO -Ping the roboRIO using mDNS ---------------------------- +## Ping the roboRIO using mDNS -The first step to identifying roboRIO networking issues is to isolate if it is an application issue or a general network issue. To do this, click **Start -> type cmd -> press Enter** to open the command prompt. Type ``ping roboRIO-####-FRC.local`` where #### is your team number (with no leading zeroes) and press enter. If the ping succeeds, the issue is likely with the specific application, verify your team number configuration in the application, and check your firewall configuration. +The first step to identifying roboRIO networking issues is to isolate if it is an application issue or a general network issue. To do this, click **Start -> type cmd -> press Enter** to open the command prompt. Type ``ping roboRIO-#####-FRC.local`` where ##### is your team number (with no leading zeroes) and press enter. If the ping succeeds, the issue is likely with the specific application, verify your team number configuration in the application, and check your firewall configuration. -Ping the roboRIO IP Address ---------------------------- +## Ping the roboRIO IP Address If there is no response, try pinging ``10.TE.AM.2`` (:ref:`TE.AM IP Notation `) using the command prompt as described above. If this works, you have an issue resolving the mDNS address on your PC. The two most common causes are not having an mDNS resolver installed on the system and a DNS server on the network that is trying to resolve the .local address using regular DNS. - Verify that you have an mDNS resolver installed on your system. On Windows, this is typically fulfilled by the NI FRC Game Tools. For more information on mDNS resolvers, see the :ref:`Network Basics article `. - Disconnect your computer from any other networks and make sure you have the OM5P-AN configured as an access point, using the :ref:`FRC Radio Configuration Utility `. Removing any other routers from the system will help verify that there is not a DNS server causing the issue. -Ping Fails ----------- +## Ping Fails .. image:: images/roborio-troubleshooting/win10-dhcp.png :alt: Windows 10+ image of the adapter setting If pinging the IP address directly fails, you may have an issue with the network configuration of the PC. The PC should be configured to **Automatic**. To check this, click :guilabel:`Start` -> :guilabel:`Settings` -> :guilabel:`Network & Internet`. Depending on your network, select :guilabel:`Wifi` or :guilabel:`Ethernet`. Then click on your connected network. Scroll down to **IP settings** and click :guilabel:`Edit` and ensure the :guilabel:`Automatic (DHCP)` option is selected. -USB Connection Troubleshooting ------------------------------- +## USB Connection Troubleshooting If you are attempting to troubleshoot the USB connection, try pinging the roboRIO's IP address. As long as there is only one roboRIO connected to the PC, it should be configured as 172.22.11.2. If this ping fails, make sure you have the roboRIO connected and powered, and that you have installed the NI FRC Game Tools. The game tools installs the roboRIO drivers needed for the USB connection. @@ -36,16 +31,14 @@ If this ping succeeds, but the .local ping fails, it is likely that either the r - Verify that your roboRIO has been imaged for your team number: :doc:`roboRIO 1` :doc:`roboRIO 2`. This sets the hostname used by mDNS. - :ref:`Disable all other network adapters ` -Ethernet Connection -------------------- +## Ethernet Connection .. image:: images/roborio-troubleshooting/roborio-ip-address.png :alt: The IP address from the roboRIO webdashboard. -If you are troubleshooting an Ethernet connection, it may be helpful to first make sure that you can connect to the roboRIO using the USB connection. Using the USB connection, open the :ref:`roboRIO webdashboard ` and verify that the roboRIO has an IP address on the ethernet interface. If you are tethering to the roboRIO directly this should be a self-assigned ``169.*.*.*`` address, if you are connected to the OM5P-AN radio, it should be an address of the form ``10.TE.AM.XX`` where TEAM is your four digit FRC team number. If the only IP address here is the USB address, verify the physical roboRIO ethernet connection. +If you are troubleshooting an Ethernet connection, it may be helpful to first make sure that you can connect to the roboRIO using the USB connection. Using the USB connection, open the :ref:`roboRIO webdashboard ` and verify that the roboRIO has an IP address on the ethernet interface. If you are tethering to the roboRIO directly this should be a self-assigned ``169.*.*.*`` address, if you are connected to the OM5P-AN radio, it should be an address of the form ``10.TE.AM.XX`` where TEAM is your five digit FRC team number(:ref:`TE.AM IP Notation `). If the only IP address here is the USB address, verify the physical roboRIO ethernet connection. -Disabling Network Adapters --------------------------- +## Disabling Network Adapters This is not always the same as turning the adapters off with a physical button or putting the PC into airplane mode. The following steps provide more detail on how to disable adapters. @@ -74,7 +67,6 @@ On the left pane, click :guilabel:`Change Adapter Settings`. For each adapter other than the one connected to the radio, right click on the adapter and select :guilabel:`Disable` from the menu. -Proxies -------- +## Proxies - Proxies. Having a proxy enabled may cause issues with the roboRIO networking. diff --git a/source/docs/networking/networking-introduction/windows-firewall-configuration.rst b/source/docs/networking/networking-introduction/windows-firewall-configuration.rst index 3d911c794a..780b6d0d67 100644 --- a/source/docs/networking/networking-introduction/windows-firewall-configuration.rst +++ b/source/docs/networking/networking-introduction/windows-firewall-configuration.rst @@ -1,12 +1,10 @@ .. include:: -Windows Firewall Configuration -============================== +# Windows Firewall Configuration Many of the programming tools used in FRC\ |reg| need network access for various reasons. Depending on the exact configuration, the Windows Firewall may potentially interfere with this access for one or more of these programs. -Disabling Windows Firewall --------------------------- +## Disabling Windows Firewall .. important:: Disabling your firewall requires administrator privileges to the PC. Additionally note that disabling the firewall is not recommended for computers that connect to the internet. @@ -42,8 +40,7 @@ Then click on the **On** toggle to turn it off. .. image:: images/windows-firewall/disable-firewall-toggle.png :alt: The toggle button in the center for each option. -Whitelisting Apps ------------------ +## Whitelisting Apps Alternatively, you can add exceptions to the Firewall for any FRC programs you are having issues with. diff --git a/source/docs/networking/networking-utilities/index.rst b/source/docs/networking/networking-utilities/index.rst index 333f2eb9eb..3aa7da8e6b 100644 --- a/source/docs/networking/networking-utilities/index.rst +++ b/source/docs/networking/networking-utilities/index.rst @@ -1,5 +1,4 @@ -Networking Utilities -==================== +# Networking Utilities .. toctree:: :maxdepth: 1 diff --git a/source/docs/networking/networking-utilities/portforwarding.rst b/source/docs/networking/networking-utilities/portforwarding.rst index e647d94bf8..56db516a75 100644 --- a/source/docs/networking/networking-utilities/portforwarding.rst +++ b/source/docs/networking/networking-utilities/portforwarding.rst @@ -1,54 +1,52 @@ -Port Forwarding -=============== +# Port Forwarding This class provides an easy way to forward local ports to another host/port. This is useful to provide a way to access Ethernet-connected devices from a computer tethered to the roboRIO USB port. This class acts as a raw TCP port forwarder, this means you can forward connections such as SSH. -Forwarding a Remote Port ------------------------- +## Forwarding a Remote Port -Often teams may wish to connect directly to the roboRIO for controlling their robot. The PortForwarding class (`Java `__, `C++ `__) can be used to forward the Raspberry Pi connection for usage during these times. The PortForwarding class establishes a bridge between the remote and the client. To forward a port in Java, simply do ``PortForwarder.add(int port, String remoteName, int remotePort)``. +Often teams may wish to connect directly to the roboRIO for controlling their robot. The PortForwarding class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/net/PortForwarder.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classwpi_1_1_port_forwarder.html)) can be used to forward the Raspberry Pi connection for usage during these times. The PortForwarding class establishes a bridge between the remote and the client. To forward a port in Java, simply do ``PortForwarder.add(int port, String remoteName, int remotePort)``. .. tab-set-code:: - .. code-block:: java + ```java + @Override + public void robotInit() { + PortForwarder.add(8888, "wpilibpi.local", 80); + } + ``` - @Override - public void robotInit() { - PortForwarder.add(8888, "wpilibpi.local", 80); - } + ```c++ + void Robot::RobotInit { + wpi::PortForwarder::GetInstance().Add(8888, "wpilibpi.local", 80); + } + ``` - .. code-block:: c++ - - void Robot::RobotInit { - wpi::PortForwarder::GetInstance().Add(8888, "wpilibpi.local", 80); - } - - .. code-block:: python - - wpiutil.PortForwarder.getInstance().add(8888, "wpilibpi.local", 80) + ```python + wpinet.PortForwarder.getInstance().add(8888, "wpilibpi.local", 80) + ``` .. important:: You **can not** use a port less than 1024 as your local forwarded port. It is also important to note that you **can not** use full URLs (``http://wpilibpi.local``) and should only use IP Addresses or DNS names. -Removing a Forwarded Port -------------------------- +## Removing a Forwarded Port To stop forwarding on a specified port, simply call ``remove(int port)`` with port being the port number. If you call ``remove()`` on a port that is not being forwarded, nothing will happen. .. tab-set-code:: - .. code-block:: java - - @Override - public void robotInit() { - PortForwarder.remove(8888); - } - - .. code-block:: c++ + ```java + @Override + public void robotInit() { + PortForwarder.remove(8888); + } + ``` - void Robot::RobotInit { - wpi::PortForwarder::GetInstance().Remove(8888); - } + ```c++ + void Robot::RobotInit { + wpi::PortForwarder::GetInstance().Remove(8888); + } + ``` - .. code-block:: python + ```python + wpinet.PortForwarder.getInstance().remove(8888) + ``` - wpiutil.PortForwarder.getInstance().remove(8888) diff --git a/source/docs/romi-robot/getting-to-know-romi.rst b/source/docs/romi-robot/getting-to-know-romi.rst index 2870571ff1..4bb68d96be 100644 --- a/source/docs/romi-robot/getting-to-know-romi.rst +++ b/source/docs/romi-robot/getting-to-know-romi.rst @@ -1,8 +1,6 @@ -Getting to know your Romi -========================= +# Getting to know your Romi -Directional Conventions ------------------------ +## Directional Conventions The front of the Romi is where the Raspberry Pi USB ports, GPIO pins and suspended caster wheel are. @@ -14,8 +12,7 @@ In all Romi documentation, references to driving forward use the above definitio .. image:: images/getting-to-know-romi/romi-forward.png :alt: Romi Forward Driving Direction -Hardware, Sensors, and GPIO ---------------------------- +## Hardware, Sensors, and GPIO The Romi has the following built-in hardware/peripherals: @@ -28,8 +25,7 @@ The Romi has the following built-in hardware/peripherals: .. note:: The Buzzer is currently not supported by WPILib. -Motors, Wheels, and Encoders -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Motors, Wheels, and Encoders The motors used on the Romi have a 120:1 gear reduction, and a no-load output speed of 150 RPM at 4.5V. The free current is 0.13 amps and the stall current is 1.25 amps. Stall torque is 25 oz-in (0.1765 N-m) but the built-in safety clutch might start slipping at lower torques. @@ -37,7 +33,7 @@ The wheels have a diameter of 70mm (2.75"). They have a trackwidth of 141mm (5.5 The encoders are connected directly to the motor output shaft and have 12 Counts Per Revolution (CPR). With the provided gear ratio, this nets 1440 counts per wheel revolution. -The motor PWM channels are listed in the table below. +The motor :term:`PWM` channels are listed in the table below. +-------------+--------------------------+ | Channel | Romi Hardware Component | @@ -65,8 +61,7 @@ The encoder channels are listed in the table below. .. note:: By default, the encoders count up when the Romi moves forward. -Inertial Measurement Unit -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Inertial Measurement Unit The Romi includes an STMicroelectronics LSM6DS33 Inertial Measurement Unit (IMU) which contains a 3-axis gyro and a 3-axis accelerometer. @@ -74,8 +69,7 @@ The accelerometer has selectable sensitivity of 2G, 4G, 8G, and 16G. The gyro ha The Romi Web UI also provides a means to calibrate the gyro and measure its zero-offsets before use with robot code. -Onboard LEDs and Push Buttons -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Onboard LEDs and Push Buttons The Romi 32U4 control board has 3 push buttons and 3 LEDs onboard that are exposed as Digital IO (DIO) channels to robot code. @@ -94,10 +88,9 @@ The Romi 32U4 control board has 3 push buttons and 3 LEDs onboard that are expos | DIO 3 | Yellow LED (output only) | +-------------+--------------------------------------+ -Writes to DIO 0, 4, 5, 6 and 7 will result in no-ops. +Writes to DIO 0, 4, 5, 6 and 7 will result in a :term:`no-op`. -Configurable GPIO Pins -^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Configurable GPIO Pins The control board has 5 configurable GPIO pins (named EXT0 through EXT4) that allow a user to connect external sensors and actuators to the Romi. @@ -110,8 +103,7 @@ The GPIO channels are exposed via a 3-pin, servo style interface, with connectio The power connections for the GPIO pins are initially left unconnected but can be hooked into the Romi's on-board 5V supply by using a jumper to connect the 5V pin to the power bus (as seen in the image above). Additionally, if more power than the Romi can provide is needed, the user can provide their own 5V power supply and connect it directly to power bus and ground pins. -GPIO Default Configuration -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### GPIO Default Configuration The table below shows the default configuration of the GPIO pins (EXT0 through EXT4). :ref:`The Romi Web UI ` allows the user to customize the functions of the 5 configurable GPIO pins. The UI will also provide the appropriate WPILib channel/device mappings on screen once the IO configuration is complete. diff --git a/source/docs/romi-robot/hardware-support.rst b/source/docs/romi-robot/hardware-support.rst index ada9ffd115..93dfe97fe3 100644 --- a/source/docs/romi-robot/hardware-support.rst +++ b/source/docs/romi-robot/hardware-support.rst @@ -1,19 +1,16 @@ -Romi Hardware Support -===================== +# Romi Hardware Support The Romi robot, having a different hardware architecture than a roboRIO, is compatible with a subset of commonly used FRC control system components. -Compatible Hardware ------------------------- +## Compatible Hardware In general, the Romi is compatible with the following: - Simple Digital Input/Output devices (e.g. bumper switches, single LEDs) -- Standard RC-style PWM output devices (e.g. servos, PWM based motor controllers) +- Standard RC-style :term:`PWM` output devices (e.g. servos, PWM based motor controllers) - Analog Input sensors (e.g distance sensors that report distance as a voltage) -Incompatible Hardware ---------------------- +## Incompatible Hardware Due to hardware limitations, the Romi Robot is not compatible with the following: @@ -23,8 +20,7 @@ Due to hardware limitations, the Romi Robot is not compatible with the following - CAN based devices - Romi built-in buzzer -Compatible Classes ------------------- +## Compatible Classes All classes listed here are supported by the Romi Robot. If a class is not listed here, assume that it is not supported and *will not* work. @@ -36,7 +32,7 @@ All classes listed here are supported by the Romi Robot. If a class is not liste - ``Servo`` - ``BuiltInAccelerometer`` -The following classes are provided by the `Romi Vendordep `__. +The following classes are provided by the [Romi Vendordep](https://raw.githubusercontent.com/wpilibsuite/romi-vendordep/main/RomiVendordep.json). - ``RomiGyro`` - ``RomiMotor`` diff --git a/source/docs/romi-robot/hardware.rst b/source/docs/romi-robot/hardware.rst index 903e1cd376..0c1531dc91 100644 --- a/source/docs/romi-robot/hardware.rst +++ b/source/docs/romi-robot/hardware.rst @@ -1,16 +1,14 @@ -Romi Hardware & Assembly -======================== +# Romi Hardware & Assembly To get started with the Romi, you will need to have the necessary hardware. -1. `Romi Kit from Pololu `__ – Order qualifies for free shipping -2. `Raspberry Pi `__ – 3B+ or 4 -3. `8GB (or larger) Micro SD card `__ -4. `Micro SD card reader `__ - if you don't already have one -5. `6 AA batteries `__ – Rechargeable is best (don't forget the charger) +1. [Romi Kit from Pololu](https://www.pololu.com/product/4022) – Order qualifies for free shipping +2. [Raspberry Pi](https://www.amazon.com/gp/product/B07BFH96M3/) – 3B+ or 4 +3. [8GB (or larger) Micro SD card](https://www.amazon.com/dp/B073K14CVB/) +4. [Micro SD card reader](https://www.amazon.com/gp/product/B0779V61XB/) - if you don't already have one +5. [6 AA batteries](https://www.amazon.com/gp/product/B07TW9T8JW/) – Rechargeable is best (don't forget the charger) -Assembly --------- +## Assembly The Romi Robot Kit for FIRST comes pre-soldered and only has to be put together before it can be used. Once you have gathered all the materials you can begin assembly: diff --git a/source/docs/romi-robot/images/getting-to-know-romi/romi-external-io.png b/source/docs/romi-robot/images/getting-to-know-romi/romi-external-io.png index 7992f6ae44..c33cf68a04 100644 Binary files a/source/docs/romi-robot/images/getting-to-know-romi/romi-external-io.png and b/source/docs/romi-robot/images/getting-to-know-romi/romi-external-io.png differ diff --git a/source/docs/romi-robot/images/getting-to-know-romi/romi-forward.png b/source/docs/romi-robot/images/getting-to-know-romi/romi-forward.png index 6512e69429..a259ecd796 100644 Binary files a/source/docs/romi-robot/images/getting-to-know-romi/romi-forward.png and b/source/docs/romi-robot/images/getting-to-know-romi/romi-forward.png differ diff --git a/source/docs/romi-robot/images/getting-to-know-romi/romi-front-view.png b/source/docs/romi-robot/images/getting-to-know-romi/romi-front-view.png index c210db6e2d..2b15de7c16 100644 Binary files a/source/docs/romi-robot/images/getting-to-know-romi/romi-front-view.png and b/source/docs/romi-robot/images/getting-to-know-romi/romi-front-view.png differ diff --git a/source/docs/romi-robot/images/hardware/assembly-batteries.png b/source/docs/romi-robot/images/hardware/assembly-batteries.png index 47becea173..7ab5739fe3 100644 Binary files a/source/docs/romi-robot/images/hardware/assembly-batteries.png and b/source/docs/romi-robot/images/hardware/assembly-batteries.png differ diff --git a/source/docs/romi-robot/images/hardware/assembly-caster-rear.png b/source/docs/romi-robot/images/hardware/assembly-caster-rear.png index 905fa462c1..5a8b15403d 100644 Binary files a/source/docs/romi-robot/images/hardware/assembly-caster-rear.png and b/source/docs/romi-robot/images/hardware/assembly-caster-rear.png differ diff --git a/source/docs/romi-robot/images/hardware/assembly-motor-clips.png b/source/docs/romi-robot/images/hardware/assembly-motor-clips.png index 98330e5cdd..dcb3210f49 100644 Binary files a/source/docs/romi-robot/images/hardware/assembly-motor-clips.png and b/source/docs/romi-robot/images/hardware/assembly-motor-clips.png differ diff --git a/source/docs/romi-robot/images/hardware/assembly-motors.png b/source/docs/romi-robot/images/hardware/assembly-motors.png index 684fb68c39..a2df5e4483 100644 Binary files a/source/docs/romi-robot/images/hardware/assembly-motors.png and b/source/docs/romi-robot/images/hardware/assembly-motors.png differ diff --git a/source/docs/romi-robot/images/hardware/assembly-raspberry-pi.png b/source/docs/romi-robot/images/hardware/assembly-raspberry-pi.png index be999015b7..25b8763680 100644 Binary files a/source/docs/romi-robot/images/hardware/assembly-raspberry-pi.png and b/source/docs/romi-robot/images/hardware/assembly-raspberry-pi.png differ diff --git a/source/docs/romi-robot/images/hardware/assembly-wheels.png b/source/docs/romi-robot/images/hardware/assembly-wheels.png index 7c01cce806..2f0952c899 100644 Binary files a/source/docs/romi-robot/images/hardware/assembly-wheels.png and b/source/docs/romi-robot/images/hardware/assembly-wheels.png differ diff --git a/source/docs/romi-robot/images/imaging-romi/firmware-upload-after.png b/source/docs/romi-robot/images/imaging-romi/firmware-upload-after.png index dbc9e7f34b..c70631a0ca 100644 Binary files a/source/docs/romi-robot/images/imaging-romi/firmware-upload-after.png and b/source/docs/romi-robot/images/imaging-romi/firmware-upload-after.png differ diff --git a/source/docs/romi-robot/images/imaging-romi/firmware-upload-before.png b/source/docs/romi-robot/images/imaging-romi/firmware-upload-before.png index 961c2ad772..20a7a99ec7 100644 Binary files a/source/docs/romi-robot/images/imaging-romi/firmware-upload-before.png and b/source/docs/romi-robot/images/imaging-romi/firmware-upload-before.png differ diff --git a/source/docs/romi-robot/images/imaging-romi/network-settings.png b/source/docs/romi-robot/images/imaging-romi/network-settings.png index b697af3d85..e035114165 100644 Binary files a/source/docs/romi-robot/images/imaging-romi/network-settings.png and b/source/docs/romi-robot/images/imaging-romi/network-settings.png differ diff --git a/source/docs/romi-robot/images/imaging-romi/romi-download.png b/source/docs/romi-robot/images/imaging-romi/romi-download.png index c7a3aaadc6..52c0d761d6 100644 Binary files a/source/docs/romi-robot/images/imaging-romi/romi-download.png and b/source/docs/romi-robot/images/imaging-romi/romi-download.png differ diff --git a/source/docs/romi-robot/images/programming-romi-in-labview/adding-romi-gyro.png b/source/docs/romi-robot/images/programming-romi-in-labview/adding-romi-gyro.png index 15b26b5a20..1217046994 100644 Binary files a/source/docs/romi-robot/images/programming-romi-in-labview/adding-romi-gyro.png and b/source/docs/romi-robot/images/programming-romi-in-labview/adding-romi-gyro.png differ diff --git a/source/docs/romi-robot/images/programming-romi-in-labview/installing-websockets.png b/source/docs/romi-robot/images/programming-romi-in-labview/installing-websockets.png index 59b3819d5f..ca8f12d421 100644 Binary files a/source/docs/romi-robot/images/programming-romi-in-labview/installing-websockets.png and b/source/docs/romi-robot/images/programming-romi-in-labview/installing-websockets.png differ diff --git a/source/docs/romi-robot/images/programming-romi/romi-vscode-connected.png b/source/docs/romi-robot/images/programming-romi/romi-vscode-connected.png index 0467800653..585ace67f3 100644 Binary files a/source/docs/romi-robot/images/programming-romi/romi-vscode-connected.png and b/source/docs/romi-robot/images/programming-romi/romi-vscode-connected.png differ diff --git a/source/docs/romi-robot/images/programming-romi/romi-vscode-launch-sim.png b/source/docs/romi-robot/images/programming-romi/romi-vscode-launch-sim.png index dc497f395e..c3a241fd80 100644 Binary files a/source/docs/romi-robot/images/programming-romi/romi-vscode-launch-sim.png and b/source/docs/romi-robot/images/programming-romi/romi-vscode-launch-sim.png differ diff --git a/source/docs/romi-robot/images/programming-romi/romi-vscode-new-project.png b/source/docs/romi-robot/images/programming-romi/romi-vscode-new-project.png index 4631b95bf4..3783534654 100644 Binary files a/source/docs/romi-robot/images/programming-romi/romi-vscode-new-project.png and b/source/docs/romi-robot/images/programming-romi/romi-vscode-new-project.png differ diff --git a/source/docs/romi-robot/images/programming-romi/romi-vscode-reference-example.png b/source/docs/romi-robot/images/programming-romi/romi-vscode-reference-example.png index 1427879709..bb6576457a 100644 Binary files a/source/docs/romi-robot/images/programming-romi/romi-vscode-reference-example.png and b/source/docs/romi-robot/images/programming-romi/romi-vscode-reference-example.png differ diff --git a/source/docs/romi-robot/images/programming-romi/romi-vscode-select-type.png b/source/docs/romi-robot/images/programming-romi/romi-vscode-select-type.png index 2a569b87a2..c93c8e994b 100644 Binary files a/source/docs/romi-robot/images/programming-romi/romi-vscode-select-type.png and b/source/docs/romi-robot/images/programming-romi/romi-vscode-select-type.png differ diff --git a/source/docs/romi-robot/images/programming-romi/romi-vscode-templates.png b/source/docs/romi-robot/images/programming-romi/romi-vscode-templates.png index 3e1ea24c9a..7529ce4f15 100644 Binary files a/source/docs/romi-robot/images/programming-romi/romi-vscode-templates.png and b/source/docs/romi-robot/images/programming-romi/romi-vscode-templates.png differ diff --git a/source/docs/romi-robot/images/romi.png b/source/docs/romi-robot/images/romi.png index 269a8f6c98..8bcc3bac86 100644 Binary files a/source/docs/romi-robot/images/romi.png and b/source/docs/romi-robot/images/romi.png differ diff --git a/source/docs/romi-robot/images/web-ui/romi-bridge-mode.png b/source/docs/romi-robot/images/web-ui/romi-bridge-mode.png index 3e4f5ef3de..e16628cf49 100644 Binary files a/source/docs/romi-robot/images/web-ui/romi-bridge-mode.png and b/source/docs/romi-robot/images/web-ui/romi-bridge-mode.png differ diff --git a/source/docs/romi-robot/images/web-ui/romi-enable-writable.png b/source/docs/romi-robot/images/web-ui/romi-enable-writable.png index 23d855db73..92f581029b 100644 Binary files a/source/docs/romi-robot/images/web-ui/romi-enable-writable.png and b/source/docs/romi-robot/images/web-ui/romi-enable-writable.png differ diff --git a/source/docs/romi-robot/images/web-ui/romi-network-settings.png b/source/docs/romi-robot/images/web-ui/romi-network-settings.png index cf24c8f33d..0bbc309572 100644 Binary files a/source/docs/romi-robot/images/web-ui/romi-network-settings.png and b/source/docs/romi-robot/images/web-ui/romi-network-settings.png differ diff --git a/source/docs/romi-robot/images/web-ui/romi-ui-console.png b/source/docs/romi-robot/images/web-ui/romi-ui-console.png index 870fa4f2ae..3262bad68d 100644 Binary files a/source/docs/romi-robot/images/web-ui/romi-ui-console.png and b/source/docs/romi-robot/images/web-ui/romi-ui-console.png differ diff --git a/source/docs/romi-robot/images/web-ui/romi-ui-imu-calibration.png b/source/docs/romi-robot/images/web-ui/romi-ui-imu-calibration.png index 7c6d08aff6..25bf62bc03 100644 Binary files a/source/docs/romi-robot/images/web-ui/romi-ui-imu-calibration.png and b/source/docs/romi-robot/images/web-ui/romi-ui-imu-calibration.png differ diff --git a/source/docs/romi-robot/images/web-ui/romi-ui-io-config.png b/source/docs/romi-robot/images/web-ui/romi-ui-io-config.png index 0679a2e338..c124c5c72a 100644 Binary files a/source/docs/romi-robot/images/web-ui/romi-ui-io-config.png and b/source/docs/romi-robot/images/web-ui/romi-ui-io-config.png differ diff --git a/source/docs/romi-robot/images/web-ui/romi-ui-service-status.png b/source/docs/romi-robot/images/web-ui/romi-ui-service-status.png index 92f37a135a..83a6558f02 100644 Binary files a/source/docs/romi-robot/images/web-ui/romi-ui-service-status.png and b/source/docs/romi-robot/images/web-ui/romi-ui-service-status.png differ diff --git a/source/docs/romi-robot/images/web-ui/romi-ui-service-update.png b/source/docs/romi-robot/images/web-ui/romi-ui-service-update.png index ffcb8eb168..826bd829a6 100644 Binary files a/source/docs/romi-robot/images/web-ui/romi-ui-service-update.png and b/source/docs/romi-robot/images/web-ui/romi-ui-service-update.png differ diff --git a/source/docs/romi-robot/images/web-ui/romi-ui-status.png b/source/docs/romi-robot/images/web-ui/romi-ui-status.png index 20f4888e0b..3b2805e419 100644 Binary files a/source/docs/romi-robot/images/web-ui/romi-ui-status.png and b/source/docs/romi-robot/images/web-ui/romi-ui-status.png differ diff --git a/source/docs/romi-robot/images/web-ui/romi-ui-tab-arrow.png b/source/docs/romi-robot/images/web-ui/romi-ui-tab-arrow.png index 2655e8c2e5..34d67ba46d 100644 Binary files a/source/docs/romi-robot/images/web-ui/romi-ui-tab-arrow.png and b/source/docs/romi-robot/images/web-ui/romi-ui-tab-arrow.png differ diff --git a/source/docs/romi-robot/imaging-romi.rst b/source/docs/romi-robot/imaging-romi.rst index 09dc89ada2..7a1d78dca2 100644 --- a/source/docs/romi-robot/imaging-romi.rst +++ b/source/docs/romi-robot/imaging-romi.rst @@ -1,5 +1,4 @@ -Imaging your Romi -================= +# Imaging your Romi The Romi has 2 microprocessor boards: @@ -8,28 +7,24 @@ The Romi has 2 microprocessor boards: Both boards need to have firmware installed so that the robot operates properly. -Raspberry Pi ------------- +## Raspberry Pi -Download -^^^^^^^^ +### Download The Raspberry Pi firmware is based on WPILibPi (formerly FRCVision) and must be downloaded and written to the Raspberry Pi micro SD card. Click on ``Assets`` at the bottom of the description to see the available image files: -`Romi WPILibPi `__ +[Romi WPILibPi](https://github.com/wpilibsuite/WPILibPi/releases) Be sure to download the Romi version and not the standard release of WPILibPi. The Romi version is suffixed with ``-Romi``. See the below image for an example. .. image:: images/imaging-romi/romi-download.png :alt: GitHub Romi Release -Imaging -^^^^^^^ +### Imaging The procedure for installing the image is described here: :doc:`WPILibPi Installation`. -Wireless Network Setup -^^^^^^^^^^^^^^^^^^^^^^ +### Wireless Network Setup Perform the following steps to get your Raspberry Pi ready to use with the Romi: @@ -58,8 +53,7 @@ Be sure to set the Dashboard to ``Read-only`` when all the changes have been com .. image:: images/imaging-romi/network-settings.png :alt: Romi web dashboard network settings -32U4 Control Board ------------------- +## 32U4 Control Board The Raspberry Pi can now be used to write the firmware image to the 32U4 Control Board. diff --git a/source/docs/romi-robot/index.rst b/source/docs/romi-robot/index.rst index 9c5d598c2d..7d2d2198a6 100644 --- a/source/docs/romi-robot/index.rst +++ b/source/docs/romi-robot/index.rst @@ -1,9 +1,8 @@ -Getting Started with Romi -========================= +# Getting Started with Romi -The Romi is a small and inexpensive robot designed for learning about programming FRC robots. All the same tools used for programming full-sized FRC robots can be used to program the Romi. The Romi comes with two drive motors with integrated wheel encoders. It also includes an IMU sensor that can be used for measuring headings and accelerations. Using it is as simple as writing a robot program, and running it on your computer. It will command the Romi to follow the steps in the program. +The Romi is a small and inexpensive robot designed for learning about programming FRC robots. All the same tools used for programming full-sized FRC robots can be used to program the Romi. The Romi comes with two drive motors with integrated wheel encoders. It also includes an :term:`IMU` sensor that can be used for measuring headings and accelerations. Using it is as simple as writing a robot program, and running it on your computer. It will command the Romi to follow the steps in the program. -.. tip:: A course that teaches programming using the Romi Robot is available via Thinkscape. Information on this course is available `here `__ +.. tip:: A course that teaches programming using the Romi Robot is available via Thinkscape. Information on this course is available [here](https://www.firstinspires.org/robotics/frc/blog/2021-skill-building-update-intro-to-programming-module) .. image:: images/romi.png :alt: Romi Robot diff --git a/source/docs/romi-robot/programming-romi-in-labview.rst b/source/docs/romi-robot/programming-romi-in-labview.rst index 9e66f0cf42..949abb8711 100644 --- a/source/docs/romi-robot/programming-romi-in-labview.rst +++ b/source/docs/romi-robot/programming-romi-in-labview.rst @@ -1,21 +1,18 @@ .. include:: -Programming the Romi (LabVIEW) -============================== +# Programming the Romi (LabVIEW) Writing a LabVIEW program for the Romi is very similar to writing a program for a regular roboRIO based robot. In fact, all the same tools can be used with the Romi. -Creating a Romi Project ------------------------ +## Creating a Romi Project Creating a new program for a Romi is no different than creating a normal FRC \|reg| program, similar to the :doc:`Zero To Robot ` programming steps. Initially, you may wish to create a separate project for use on just the Romi as the Romi hardware may be connected to different ports than on your roboRIO robot. -The Romi Robot used PWM ports 0 and 1 for left and right side respectively. +The Romi Robot used :term:`PWM` ports 0 and 1 for left and right side respectively. .. todo:: add information on romi gyro and encoders -Installing the WebSockets VI -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Installing the WebSockets VI One aspect where a Romi project differs from a regular FRC \|reg| robot project is that the code is not deployed directly to the Romi. Instead, a Romi project runs on your development computer, and leverages the WPILib simulation framework to communicate with the Romi robot. WebSockets is the protocol that LabVIEW uses to converse with the Romi. @@ -24,24 +21,21 @@ Open the :guilabel:`VI Package Manager` application. Type ``websockets`` into th .. image:: images/programming-romi-in-labview/installing-websockets.png :alt: Installing WebSockets via the VI Package Manager -Changing the Project Target -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Changing the Project Target The primary step needed to run your LabVIEW program on the Romi is to change the target to the Desktop. To change the project target, locate the Robot Main VI in the Project Explorer and click and drag it from the ``Target`` section to the ``My Computer`` section. .. image:: images/programming-romi-in-labview/romi-labview-target.jpg :alt: An image showing moving Robot Main VI to the My Computer section -Setting the Target IP -^^^^^^^^^^^^^^^^^^^^^ +### Setting the Target IP By default, your LabVIEW program will attempt to connect to a Romi with the IP address of ``10.0.0.2``. If you wish to use a different IP, you can specify it as an input to the ``Driver Station Start Communication VI`` inside ``Robot Main``. Locate the pink input terminal for ``Simulation URL`` then right-click and select :guilabel:`Create Constant` to create a constant pre-filled with the default value. You can then modify the IP address portion of the text, taking care to leave the protocol section (at the beginning) and port and suffix (at the end) the same. .. image:: images/programming-romi-in-labview/alternate-romi-ip.jpg :alt: An image showing the constant for an alternate IP -Running a Romi Program -^^^^^^^^^^^^^^^^^^^^^^ +### Running a Romi Program To run a Romi program, first, ensure that your Romi is powered on. Once you connect to the ``WPILibPi-`` network broadcast by the Romi, press the white :guilabel:`Run` arrow to start running the Romi program on your computer. @@ -49,19 +43,18 @@ Your Romi code is now running! The program will automatically attempt to connect It is recommended to run the Driver Station software on the same computer as the LabVIEW code. Once your program successfully connects to the Driver Station, it will automatically notify the Driver Station that the code is running on the Desktop, allowing the Driver Station to connect without you changing any information inside the Driver Station. Next, you'll need to point the Driver Station to your Romi. This is done by setting the team number to ``127.0.0.1``. You can then use the controls in the Driver Station to set the robot mode and enable/disable as normal. -.. note: If your robot code is unable to connect to the Romi, the Driver Station will also show no connectivity. +.. note:: If your robot code is unable to connect to the Romi, the Driver Station will also show no connectivity. -Using the Gyro or Encoder -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Using the Gyro or Encoder The gyro that is available on the Romi is available using the RomiGyro functions. This is located under -.. code-block:: text - - - WPI Robotics Library - - Sensors - - Third Party Libraries - - RomiGyro +```text +- WPI Robotics Library + - Sensors + - Third Party Libraries + - RomiGyro +``` .. image:: images/programming-romi-in-labview/adding-romi-gyro.png :alt: Tree structure of locating the RomiGyro function diff --git a/source/docs/romi-robot/programming-romi.rst b/source/docs/romi-robot/programming-romi.rst index f6b0c32a40..ed6b78e71a 100644 --- a/source/docs/romi-robot/programming-romi.rst +++ b/source/docs/romi-robot/programming-romi.rst @@ -1,19 +1,16 @@ -Programming the Romi -==================== +# Programming the Romi Writing a program for the Romi is very similar to writing a program for a regular FRC robot. In fact, all the same tools (Visual Studio Code, Driver Station, SmartDashboard, etc) can be used with the Romi. -Creating a Romi Program ------------------------ +## Creating a Romi Program Creating a new program for a Romi is like creating a normal FRC program, similar to the :doc:`Zero To Robot ` programming steps. WPILib comes with two templates for Romi projects, including one based on TimedRobot, and a Command-Based project template. Additionally, an example project is provided which showcases some of the built-in functionality of the Romi. This article will walk through creating a project from this example. -.. note:: In order to program the Romi using C++, a compatible C++ desktop compiler must be installed. See :ref:`Robot Simulation - Additional C++ Dependency `. +.. note:: In order to program the Romi using C++, a compatible C++ desktop compiler must be installed. See :ref:`Robot Simulation - Additional C++ Dependency `. -Creating a New WPILib Romi Project -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Creating a New WPILib Romi Project Bring up the Visual Studio Code command palette with :kbd:`Ctrl+Shift+P`, and type "New project" into the prompt. Select the "Create a new project" command: @@ -29,8 +26,7 @@ Next, a list of examples will appear. Scroll through the list to find the "RomiR Fill out the rest of the fields in the "New Project Creator" and click "Generate Project" to create the new robot project. -Running a Romi Program -^^^^^^^^^^^^^^^^^^^^^^ +### Running a Romi Program Once the robot project is generated, it is essentially ready to run. The project has a pre-built ``Drivetrain`` class and associated default command that lets you drive the Romi around using a joystick. @@ -38,11 +34,11 @@ One aspect where a Romi project differs from a regular FRC robot project is that To run a Romi program, first, ensure that your Romi is powered on. Next, connect to the ``WPILibPi-`` WiFi network broadcast by the Romi. If you changed the Romi network settings (for example, to connect it to your own WiFi network) you may change the IP address that your program uses to connect to the Romi. To do this, open the ``build.gradle`` file and update the ``wpi.sim.envVar`` line to the appropriate IP address. -.. rli:: https://raw.githubusercontent.com/wpilibsuite/vscode-wpilib/v2024.1.1-beta-2/vscode-wpilib/resources/gradle/javaromi/build.gradle +.. rli:: https://raw.githubusercontent.com/wpilibsuite/vscode-wpilib/v2024.3.2/vscode-wpilib/resources/gradle/javaromi/build.gradle :language: groovy - :lines: 44-47 + :lines: 43-46 :linenos: - :lineno-start: 44 + :lineno-start: 43 :emphasize-lines: 2 Now to start your Romi robot code, open the WPILib Command Palette (type :kbd:`Ctrl+Shift+P`) and select "Simulate Robot Code", or press :kbd:`F5`. diff --git a/source/docs/romi-robot/web-ui.rst b/source/docs/romi-robot/web-ui.rst index 8f8c168543..bdd188a13b 100644 --- a/source/docs/romi-robot/web-ui.rst +++ b/source/docs/romi-robot/web-ui.rst @@ -1,5 +1,4 @@ -The Romi Web UI -=============== +# The Romi Web UI The Romi Web UI comes installed as part of the WPILibPi Raspberry Pi image. It is accessible by clicking on the Romi tab in the navigation bar of the main WPILibPi Web UI. @@ -8,8 +7,7 @@ The Romi Web UI comes installed as part of the WPILibPi Raspberry Pi image. It i The rest of this section will walk through the various parts of the Romi Web UI and describe the relevant functionality. -Background Service Status -------------------------- +## Background Service Status .. image:: images/web-ui/romi-ui-service-status.png :alt: Romi Background Service Status @@ -18,8 +16,7 @@ This section of the Romi Web UI provides information about the currently running .. note:: Users will not need to use the functionality in this section often, but it can be useful for troubleshooting. -Romi Status ------------ +## Romi Status .. image:: images/web-ui/romi-ui-status.png :alt: Romi Status @@ -28,20 +25,18 @@ This section provides information about the Romi, including the service version, .. note:: If the firmware is not compatible, see the section on :doc:`Imaging your Romi ` -Web Service Update ------------------- +## Web Service Update .. image:: images/web-ui/romi-ui-service-update.png :alt: Web Service Update .. note:: The Raspberry Pi must be in **Writable** mode for this section to work. -The Romi WPILibPi image ships with the latest (at publication time) version of the Romi web service. To support upgrading to newer versions of the Romi web service, this section allows users to upload a pre-built bundle that can be obtained via the Romi web service `GitHub releases page `__. +The Romi WPILibPi image ships with the latest (at publication time) version of the Romi web service. To support upgrading to newer versions of the Romi web service, this section allows users to upload a pre-built bundle that can be obtained via the Romi web service [GitHub releases page](https://github.com/wpilibsuite/wpilib-ws-robot-romi/releases). To perform an upgrade, download the appropriate .tgz file from the GitHub Releases page. Next, select the downloaded .tgz file and click :guilabel:`Save`. The updated web service bundle will be uploaded to the Raspberry Pi, and be installed. After a short moment, the Romi Status section should update itself with the latest version information. -External IO Configuration -------------------------- +## External IO Configuration .. image:: images/web-ui/romi-ui-io-config.png :alt: External IO Configuration @@ -50,12 +45,11 @@ This section allows users to configure the 5 external GPIO channels on the Romi. .. note:: The Raspberry Pi must be in **Writable** mode for this section to work. -To change the configuration of a GPIO channel, select an appropriate option from the dropdown lists. All channels (with the exception of EXT 0) support Digital IO, Analog In and PWM as channel types. Once the appropriate selections are made, click on :guilabel:`Save External IO Configuration`. The web service will then restart and pick up the new IO configuration. +To change the configuration of a GPIO channel, select an appropriate option from the dropdown lists. All channels (with the exception of EXT 0) support Digital IO, Analog In and :term:`PWM` as channel types. Once the appropriate selections are made, click on :guilabel:`Save External IO Configuration`. The web service will then restart and pick up the new IO configuration. The "Robot Port" row provides the appropriate WPILib mapping for each configured GPIO channel. For example, EXT 0 is configured as a Digital IO channel, and will be accessible in WPILib as a DigitalInput (or DigitalOutput) channel 8. -IMU Calibration ---------------- +## IMU Calibration .. image:: images/web-ui/romi-ui-imu-calibration.png :alt: IMU Calibration @@ -68,21 +62,18 @@ To begin calibration, place the Romi on a flat, stable surface. Then, click the These offset values are saved to disk and persist between reboots. -Firmware --------- +## Firmware .. note:: See the section on :doc:`Imaging your Romi ` -Console Output --------------- +## Console Output .. image:: images/web-ui/romi-ui-console.png :alt: Console Output When enabled, this section allows users to view the raw console output that the Romi web service provides. This is useful for troubleshooting issues with the Romi, or just to find out more about what goes on behind the scenes. -Bridge Mode ------------ +## Bridge Mode Bridge mode allows your Romi robot to connect to a WiFi network instead of acting as an Access Point (AP). This is especially useful in remote learning environments, as you can use the internet while using the Romi without extra hardware. @@ -111,14 +102,13 @@ Bridge mode allows your Romi robot to connect to a WiFi network instead of actin Once the settings are applied, please reboot the Romi. You should now be able to navigate to ``wpilibpi.local`` in your web browser while connected to your specified network. -Unable to Access Romi -^^^^^^^^^^^^^^^^^^^^^ +### Unable to Access Romi If the Romi has the correct bridge settings and you are unable to access it, we have a few workarounds. - Ethernet into the Romi - Reimage the Romi -Some restricted networks can interfere with the hostname of the Romi resolving, you can workaround this by using `Angry IP Scanner `__ to find the IP address. +Some restricted networks can interfere with the hostname of the Romi resolving, you can workaround this by using [Angry IP Scanner](https://angryip.org/) to find the IP address. .. warning:: Angry IP Scanner is flagged by some antivirus as spyware as it pings devices on your network! It is a safe application! diff --git a/source/docs/software/advanced-controls/controllers/bang-bang.rst b/source/docs/software/advanced-controls/controllers/bang-bang.rst index 34acb75e2d..2d85acc59d 100644 --- a/source/docs/software/advanced-controls/controllers/bang-bang.rst +++ b/source/docs/software/advanced-controls/controllers/bang-bang.rst @@ -1,5 +1,4 @@ -Bang-Bang Control with BangBangController -========================================= +# Bang-Bang Control with BangBangController The :term:`bang-bang control` algorithm is a control strategy that employs only two states: on (when the measurement is below the setpoint) and off (otherwise). This is roughly equivalent to a proportional loop with infinite gain. @@ -7,32 +6,31 @@ This may initially seem like a poor control strategy, as PID loops are known to However, when controlling the velocity of high-inertia mechanisms under varying loads (like a shooter flywheel), a bang-bang controller can yield faster recovery time and thus better/more consistent performance than a proportional controller. Unlike an ordinary P loop, a bang-bang controller is *asymmetric* - that is, the controller turns on when the process variable is below the setpoint, and does nothing otherwise. This allows the control effort in the forward direction to be made as large as possible without risking destructive oscillations as the control loop tries to correct a resulting overshoot. -Asymmetric bang-bang control is provided in WPILib by the BangBangController class (`Java `__, `C++ `__). +Asymmetric bang-bang control is provided in WPILib by the BangBangController class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/BangBangController.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_bang_bang_controller.html), :external:py:class:`Python `). -Constructing a BangBangController ---------------------------------- +## Constructing a BangBangController Since a bang-bang controller does not have any gains, it does not need any constructor arguments (one can optionally specify the controller tolerance used by ``atSetpoint``, but it is not required). .. tab-set-code:: - .. code-block:: java + ```java + // Creates a BangBangController + BangBangController controller = new BangBangController(); + ``` - // Creates a BangBangController - BangBangController controller = new BangBangController(); + ```c++ + // Creates a BangBangController + frc::BangBangController controller; + ``` - .. code-block:: c++ + ```python + from wpimath.controller import BangBangController + # Creates a BangBangController + controller = BangBangController() + ``` - // Creates a BangBangController - frc::BangBangController controller; - - .. code-block:: python - - # Creates a BangBangController - controller = wpimath.BangBangController() - -Using a BangBangController --------------------------- +## Using a BangBangController .. warning:: Bang-bang control is an extremely aggressive algorithm that relies on response asymmetry to remain stable. Be *absolutely certain* that your motor controllers have been set to "coast mode" before attempting to control them with a bang-bang controller, or else the braking action will fight the controller and cause potentially destructive oscillation. @@ -40,41 +38,41 @@ Using a bang-bang controller is easy: .. tab-set-code:: - .. code-block:: java - - // Controls a motor with the output of the BangBang controller - motor.set(controller.calculate(encoder.getRate(), setpoint)); + ```java + // Controls a motor with the output of the BangBang controller + motor.set(controller.calculate(encoder.getRate(), setpoint)); + ``` - .. code-block:: c++ + ```c++ + // Controls a motor with the output of the BangBang controller + motor.Set(controller.Calculate(encoder.GetRate(), setpoint)); + ``` - // Controls a motor with the output of the BangBang controller - motor.Set(controller.Calculate(encoder.GetRate(), setpoint)); + ```python + # Controls a motor with the output of the BangBang controller + motor.set(controller.calculate(encoder.getRate(), setpoint)) + ``` - .. code-block:: python - - # Controls a motor with the output of the BangBang controller - motor.set(controller.calculate(encoder.getRate(), setpoint)) - -Combining Bang Bang Control with Feedforward --------------------------------------------- +## Combining Bang Bang Control with Feedforward Like a PID controller, best results are obtained in conjunction with a :ref:`feedforward ` controller that provides the necessary voltage to sustain the system output at the desired speed, so that the bang-bang controller is only responsible for rejecting disturbances. Since the bang-bang controller can *only* correct in the forward direction, however, it may be preferable to use a slightly conservative feedforward estimate to ensure that the shooter does not over-speed. .. tab-set-code:: - .. code-block:: java - - // Controls a motor with the output of the BangBang controller and a feedforward - // Shrinks the feedforward slightly to avoid overspeeding the shooter - motor.setVoltage(controller.calculate(encoder.getRate(), setpoint) * 12.0 + 0.9 * feedforward.calculate(setpoint)); - - .. code-block:: c++ + ```java + // Controls a motor with the output of the BangBang controller and a feedforward + // Shrinks the feedforward slightly to avoid overspeeding the shooter + motor.setVoltage(controller.calculate(encoder.getRate(), setpoint) * 12.0 + 0.9 * feedforward.calculate(setpoint)); + ``` - // Controls a motor with the output of the BangBang controller and a feedforward - // Shrinks the feedforward slightly to avoid overspeeding the shooter - motor.SetVoltage(controller.Calculate(encoder.GetRate(), setpoint) * 12.0 + 0.9 * feedforward.Calculate(setpoint)); + ```c++ + // Controls a motor with the output of the BangBang controller and a feedforward + // Shrinks the feedforward slightly to avoid overspeeding the shooter + motor.SetVoltage(controller.Calculate(encoder.GetRate(), setpoint) * 12.0 + 0.9 * feedforward.Calculate(setpoint)); + ``` - .. code-block:: python + ```python + # Controls a motor with the output of the BangBang controller and a feedforward + motor.setVoltage(controller.calculate(encoder.getRate(), setpoint) * 12.0 + 0.9 * feedforward.calculate(setpoint)) + ``` - # Controls a motor with the output of the BangBang controller and a feedforward - motor.setVoltage(controller.calculate(encoder.getRate(), setpoint) * 12.0 + 0.9 * feedforward.calculate(setpoint)) diff --git a/source/docs/software/advanced-controls/controllers/combining-feedforward-feedback.rst b/source/docs/software/advanced-controls/controllers/combining-feedforward-feedback.rst index 490e6fdb11..108c5cd64a 100644 --- a/source/docs/software/advanced-controls/controllers/combining-feedforward-feedback.rst +++ b/source/docs/software/advanced-controls/controllers/combining-feedforward-feedback.rst @@ -1,7 +1,6 @@ .. include:: -Combining Feedforward and PID Control -===================================== +# Combining Feedforward and PID Control .. todo:: link to conceptual article when available @@ -9,71 +8,69 @@ Combining Feedforward and PID Control Feedforward and feedback controllers can each be used in isolation, but are most effective when combined together. Thankfully, combining these two control methods is *exceedingly* straightforward - one simply adds their outputs together. -Using Feedforward with a PIDController --------------------------------------- +## Using Feedforward with a PIDController Users may add any feedforward they like to the output of the controller before sending it to their motors: .. tab-set-code:: - .. code-block:: java + ```java + // Adds a feedforward to the loop output before sending it to the motor + motor.setVoltage(pid.calculate(encoder.getDistance(), setpoint) + feedforward); + ``` - // Adds a feedforward to the loop output before sending it to the motor - motor.setVoltage(pid.calculate(encoder.getDistance(), setpoint) + feedforward); + ```c++ + // Adds a feedforward to the loop output before sending it to the motor + motor.SetVoltage(pid.Calculate(encoder.GetDistance(), setpoint) + feedforward); + ``` - .. code-block:: c++ - - // Adds a feedforward to the loop output before sending it to the motor - motor.SetVoltage(pid.Calculate(encoder.GetDistance(), setpoint) + feedforward); - - .. code-block:: python - - // Adds a feedforward to the loop output before sending it to the motor - motor.setVoltage(pid.calculate(encoder.getDistance(), setpoint) + feedforward) + ```python + # Adds a feedforward to the loop output before sending it to the motor + motor.setVoltage(pid.calculate(encoder.getDistance(), setpoint) + feedforward) + ``` Moreover, feedforward is a separate feature entirely from feedback, and thus has no reason to be handled in the same controller object, as this violates separation of concerns. WPILib comes with several helper classes to compute accurate feedforward voltages for common FRC\ |reg| mechanisms - for more information, see :ref:`docs/software/advanced-controls/controllers/feedforward:Feedforward Control in WPILib`. -Using Feedforward Components with PID -------------------------------------- +## Using Feedforward Components with PID -.. note:: Since feedforward voltages are physically meaningful, it is best to use the ``setVoltage()`` (`Java `__, `C++ `__) method when applying them to motors to compensate for "voltage sag" from the battery. +.. note:: Since feedforward voltages are physically meaningful, it is best to use the ``setVoltage()`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/motorcontrol/MotorController.html#setVoltage(double)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_motor_controller.html#a613c23a3336e103876e433bcb8b5ad3e), :external:py:meth:`Python `) method when applying them to motors to compensate for "voltage sag" from the battery. What might a more complete example of combined feedforward/PID control look like? Consider the :ref:`drive example ` from the feedforward page. We can easily modify this to include feedback control (with a ``SimpleMotorFeedforward`` component): .. tab-set-code:: - .. code-block:: java - - public void tankDriveWithFeedforwardPID(double leftVelocitySetpoint, double rightVelocitySetpoint) { - leftMotor.setVoltage(feedforward.calculate(leftVelocitySetpoint) - + leftPID.calculate(leftEncoder.getRate(), leftVelocitySetpoint)); - rightMotor.setVoltage(feedForward.calculate(rightVelocitySetpoint) - + rightPID.calculate(rightEncoder.getRate(), rightVelocitySetpoint)); - } - - .. code-block:: c++ - - void TankDriveWithFeedforwardPID(units::meters_per_second_t leftVelocitySetpoint, - units::meters_per_second_t rightVelocitySetpoint) { - leftMotor.SetVoltage(feedforward.Calculate(leftVelocitySetpoint) - + leftPID.Calculate(leftEncoder.getRate(), leftVelocitySetpoint.value())); - rightMotor.SetVoltage(feedforward.Calculate(rightVelocitySetpoint) - + rightPID.Calculate(rightEncoder.getRate(), rightVelocitySetpoint.value())); - } - - .. code-block:: python - - def tank_drive_with_feedforward_PID( - left_velocity_setpoint: float, - right_velocity_setpoint: float, - ) -> None: - leftMotor.setVoltage( - feedforward.calculate(left_velocity_setpoint) - + leftPID.calculate(leftEncoder.getRate(), left_velocity_setpoint) - ) - rightMotor.setVoltage( - feedforward.calculate(right_velocity_setpoint) - + rightPID.calculate(rightEncoder.getRate(), right_velocity_setpoint) - ) + ```java + public void tankDriveWithFeedforwardPID(double leftVelocitySetpoint, double rightVelocitySetpoint) { + leftMotor.setVoltage(feedforward.calculate(leftVelocitySetpoint) + + leftPID.calculate(leftEncoder.getRate(), leftVelocitySetpoint)); + rightMotor.setVoltage(feedForward.calculate(rightVelocitySetpoint) + + rightPID.calculate(rightEncoder.getRate(), rightVelocitySetpoint)); + } + ``` + + ```c++ + void TankDriveWithFeedforwardPID(units::meters_per_second_t leftVelocitySetpoint, + units::meters_per_second_t rightVelocitySetpoint) { + leftMotor.SetVoltage(feedforward.Calculate(leftVelocitySetpoint) + + leftPID.Calculate(leftEncoder.getRate(), leftVelocitySetpoint.value())); + rightMotor.SetVoltage(feedforward.Calculate(rightVelocitySetpoint) + + rightPID.Calculate(rightEncoder.getRate(), rightVelocitySetpoint.value())); + } + ``` + + ```python + def tank_drive_with_feedforward_PID( + left_velocity_setpoint: float, + right_velocity_setpoint: float, + ) -> None: + leftMotor.setVoltage( + feedforward.calculate(left_velocity_setpoint) + + leftPID.calculate(leftEncoder.getRate(), left_velocity_setpoint) + ) + rightMotor.setVoltage( + feedforward.calculate(right_velocity_setpoint) + + rightPID.calculate(rightEncoder.getRate(), right_velocity_setpoint) + ) + ``` Other mechanism types can be handled similarly. diff --git a/source/docs/software/advanced-controls/controllers/feedforward.rst b/source/docs/software/advanced-controls/controllers/feedforward.rst index d583590108..8e66e14ab1 100644 --- a/source/docs/software/advanced-controls/controllers/feedforward.rst +++ b/source/docs/software/advanced-controls/controllers/feedforward.rst @@ -1,7 +1,6 @@ .. include:: -Feedforward Control in WPILib -============================= +# Feedforward Control in WPILib .. note:: This article focuses on in-code implementation of feedforward control in WPILib. For a conceptual explanation of the feedforward equations used by WPILib, see :ref:`docs/software/advanced-controls/introduction/introduction-to-feedforward:Introduction to DC Motor Feedforward` @@ -9,26 +8,26 @@ You may have used feedback control (such as PID) for reference tracking (making A feedforward controller injects information about the system's dynamics (like a mathematical model does) or the intended movement. Feedforward handles parts of the control actions we already know must be applied to make a system track a reference, then feedback compensates for what we do not or cannot know about the system's behavior at runtime. -The WPILib Feedforward Classes ------------------------------- +## The WPILib Feedforward Classes WPILib provides a number of classes to help users implement accurate feedforward control for their mechanisms. In many ways, an accurate feedforward is more important than feedback to effective control of a mechanism. Since most FRC\ |reg| mechanisms closely obey well-understood system equations, starting with an accurate feedforward is both easy and hugely beneficial to accurate and robust mechanism control. -The WPILib feedforward classes closely match the available mechanism characterization tools available in the :ref:`SysId toolsuite `. The system identification toolsuite can be used to quickly and effectively determine the correct gains for each type of feedforward. If you are unable to empirically characterize your mechanism (due to space and/or time constraints), reasonable estimates of ``kG``, ``kV``, and ``kA`` can be obtained by fairly simple computation, and are also available from `ReCalc `__. ``kS`` is nearly impossible to model, and must be measured empirically. +The WPILib feedforward classes closely match the available mechanism characterization tools available in the :ref:`SysId toolsuite `. The system identification toolsuite can be used to quickly and effectively determine the correct gains for each type of feedforward. If you are unable to empirically characterize your mechanism (due to space and/or time constraints), reasonable estimates of ``kG``, ``kV``, and ``kA`` can be obtained by fairly simple computation, and are also available from [ReCalc](https://www.reca.lc/). ``kS`` is nearly impossible to model, and must be measured empirically. WPILib currently provides the following three helper classes for feedforward control: -* `SimpleMotorFeedforward`_ (`Java `__, `C++ `__) -* `ArmFeedforward`_ (`Java `__, `C++ `__) -* `ElevatorFeedforward`_ (`Java `__, `C++ `__) +* `SimpleMotorFeedforward`_ (Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/SimpleMotorFeedforward.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_simple_motor_feedforward.html), :external:py:class:`Python `) +* `ArmFeedforward`_ (Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/ArmFeedforward.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_arm_feedforward.html), :external:py:class:`Python `) +* `ElevatorFeedforward`_ (Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/ElevatorFeedforward.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_elevator_feedforward.html), :external:py:class:`Python `) -SimpleMotorFeedforward ----------------------- +## SimpleMotorFeedforward .. note:: In C++, the ``SimpleMotorFeedforward`` class is templated on the unit type used for distance measurements, which may be angular or linear. The passed-in gains *must* have units consistent with the distance units, or a compile-time error will be thrown. ``kS`` should have units of ``volts``, ``kV`` should have units of ``volts * seconds / distance``, and ``kA`` should have units of ``volts * seconds^2 / distance``. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. .. note:: The Java feedforward components will calculate outputs in units determined by the units of the user-provided feedforward gains. Users *must* take care to keep units consistent, as WPILibJ does not have a type-safe unit system. +.. note:: The API documentation for Python feedforward components indicate which unit is being used as ``wpimath.units.NAME``. Users *must* take care to use correct units, as Python does not have a type-safe unit system. + The ``SimpleMotorFeedforward`` class calculates feedforwards for mechanisms that consist of permanent-magnet DC motors with no external loading other than friction and inertia, such as flywheels and robot drives. To create a ``SimpleMotorFeedforward``, simply construct it with the required gains: @@ -37,16 +36,23 @@ To create a ``SimpleMotorFeedforward``, simply construct it with the required ga .. tab-set-code:: - .. code-block:: java - - // Create a new SimpleMotorFeedforward with gains kS, kV, and kA - SimpleMotorFeedforward feedforward = new SimpleMotorFeedforward(kS, kV, kA); + ```java + // Create a new SimpleMotorFeedforward with gains kS, kV, and kA + SimpleMotorFeedforward feedforward = new SimpleMotorFeedforward(kS, kV, kA); + ``` - .. code-block:: c++ + ```c++ + // Create a new SimpleMotorFeedforward with gains kS, kV, and kA + // Distance is measured in meters + frc::SimpleMotorFeedforward feedforward(kS, kV, kA); + ``` - // Create a new SimpleMotorFeedforward with gains kS, kV, and kA - // Distance is measured in meters - frc::SimpleMotorFeedforward feedforward(kS, kV, kA); + ```python + from wpimath.controller import SimpleMotorFeedforwardMeters + # Create a new SimpleMotorFeedforward with gains kS, kV, and kA + # Distance is measured in meters + feedforward = SimpleMotorFeedforwardMeters(kS, kV, kA) + ``` To calculate the feedforward, simply call the ``calculate()`` method with the desired motor velocity and acceleration: @@ -54,25 +60,32 @@ To calculate the feedforward, simply call the ``calculate()`` method with the de .. tab-set-code:: - .. code-block:: java + ```java + // Calculates the feedforward for a velocity of 10 units/second and an acceleration of 20 units/second^2 + // Units are determined by the units of the gains passed in at construction. + feedforward.calculate(10, 20); + ``` - // Calculates the feedforward for a velocity of 10 units/second and an acceleration of 20 units/second^2 - // Units are determined by the units of the gains passed in at construction. - feedforward.calculate(10, 20); + ```c++ + // Calculates the feedforward for a velocity of 10 meters/second and an acceleration of 20 meters/second^2 + // Output is in volts + feedforward.Calculate(10_mps, 20_mps_sq); + ``` - .. code-block:: c++ + ```python + # Calculates the feedforward for a velocity of 10 meters/second and an acceleration of 20 meters/second^2 + # Output is in volts + feedforward.calculate(10, 20) + ``` - // Calculates the feedforward for a velocity of 10 meters/second and an acceleration of 20 meters/second^2 - // Output is in volts - feedforward.Calculate(10_mps, 20_mps_sq); - -ArmFeedforward --------------- +## ArmFeedforward .. note:: In C++, the ``ArmFeedforward`` class assumes distances are angular, not linear. The passed-in gains *must* have units consistent with the angular unit, or a compile-time error will be thrown. ``kS`` and ``kG`` should have units of ``volts``, ``kV`` should have units of ``volts * seconds / radians``, and ``kA`` should have units of ``volts * seconds^2 / radians``. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. .. note:: The Java feedforward components will calculate outputs in units determined by the units of the user-provided feedforward gains. Users *must* take care to keep units consistent, as WPILibJ does not have a type-safe unit system. +.. note:: The API documentation for Python feedforward components indicate which unit is being used as ``wpimath.units.NAME``. Users *must* take care to use correct units, as Python does not have a type-safe unit system. + The ``ArmFeedforward`` class calculates feedforwards for arms that are controlled directly by a permanent-magnet DC motor, with external loading of friction, inertia, and mass of the arm. This is an accurate model of most arms in FRC. To create an ``ArmFeedforward``, simply construct it with the required gains: @@ -81,15 +94,21 @@ To create an ``ArmFeedforward``, simply construct it with the required gains: .. tab-set-code:: - .. code-block:: java + ```java + // Create a new ArmFeedforward with gains kS, kG, kV, and kA + ArmFeedforward feedforward = new ArmFeedforward(kS, kG, kV, kA); + ``` - // Create a new ArmFeedforward with gains kS, kG, kV, and kA - ArmFeedforward feedforward = new ArmFeedforward(kS, kG, kV, kA); + ```c++ + // Create a new ArmFeedforward with gains kS, kG, kV, and kA + frc::ArmFeedforward feedforward(kS, kG, kV, kA); + ``` - .. code-block:: c++ - - // Create a new ArmFeedforward with gains kS, kG, kV, and kA - frc::ArmFeedforward feedforward(kS, kG, kV, kA); + ```python + from wpimath.controller import ArmFeedforward + # Create a new ArmFeedforward with gains kS, kG, kV, and kA + feedforward = ArmFeedforward(kS, kG, kV, kA) + ``` To calculate the feedforward, simply call the ``calculate()`` method with the desired arm position, velocity, and acceleration: @@ -97,27 +116,35 @@ To calculate the feedforward, simply call the ``calculate()`` method with the de .. tab-set-code:: - .. code-block:: java - - // Calculates the feedforward for a position of 1 units, a velocity of 2 units/second, and - // an acceleration of 3 units/second^2 - // Units are determined by the units of the gains passed in at construction. - feedforward.calculate(1, 2, 3); - - .. code-block:: c++ - - // Calculates the feedforward for a position of 1 radians, a velocity of 2 radians/second, and - // an acceleration of 3 radians/second^2 - // Output is in volts - feedforward.Calculate(1_rad, 2_rad_per_s, 3_rad/(1_s * 1_s)); - -ElevatorFeedforward -------------------- + ```java + // Calculates the feedforward for a position of 1 units, a velocity of 2 units/second, and + // an acceleration of 3 units/second^2 + // Units are determined by the units of the gains passed in at construction. + feedforward.calculate(1, 2, 3); + ``` + + ```c++ + // Calculates the feedforward for a position of 1 radians, a velocity of 2 radians/second, and + // an acceleration of 3 radians/second^2 + // Output is in volts + feedforward.Calculate(1_rad, 2_rad_per_s, 3_rad/(1_s * 1_s)); + ``` + + ```python + # Calculates the feedforward for a position of 1 radians, a velocity of 2 radians/second, and + # an acceleration of 3 radians/second^2 + # Output is in volts + feedforward.calculate(1, 2, 3) + ``` + +## ElevatorFeedforward .. note:: In C++, the passed-in gains *must* have units consistent with the distance units, or a compile-time error will be thrown. ``kS`` and ``kG`` should have units of ``volts``, ``kV`` should have units of ``volts * seconds / distance``, and ``kA`` should have units of ``volts * seconds^2 / distance``. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. .. note:: The Java feedforward components will calculate outputs in units determined by the units of the user-provided feedforward gains. Users *must* take care to keep units consistent, as WPILibJ does not have a type-safe unit system. +.. note:: The API documentation for Python feedforward components indicate which unit is being used as ``wpimath.units.NAME``. Users *must* take care to use correct units, as Python does not have a type-safe unit system. + The ``ElevatorFeedforward`` class calculates feedforwards for elevators that consist of permanent-magnet DC motors loaded by friction, inertia, and the mass of the elevator. This is an accurate model of most elevators in FRC. To create a ``ElevatorFeedforward``, simply construct it with the required gains: @@ -126,16 +153,23 @@ To create a ``ElevatorFeedforward``, simply construct it with the required gains .. tab-set-code:: - .. code-block:: java - - // Create a new ElevatorFeedforward with gains kS, kG, kV, and kA - ElevatorFeedforward feedforward = new ElevatorFeedforward(kS, kG, kV, kA); + ```java + // Create a new ElevatorFeedforward with gains kS, kG, kV, and kA + ElevatorFeedforward feedforward = new ElevatorFeedforward(kS, kG, kV, kA); + ``` - .. code-block:: c++ + ```c++ + // Create a new ElevatorFeedforward with gains kS, kV, and kA + // Distance is measured in meters + frc::ElevatorFeedforward feedforward(kS, kG, kV, kA); + ``` - // Create a new ElevatorFeedforward with gains kS, kV, and kA - // Distance is measured in meters - frc::ElevatorFeedforward feedforward(kS, kG, kV, kA); + ```python + from wpimath.controller import ElevatorFeedforward + # Create a new ElevatorFeedforward with gains kS, kV, and kA + # Distance is measured in meters + feedforward = ElevatorFeedforward(kS, kG, kV, kA) + ``` To calculate the feedforward, simply call the ``calculate()`` method with the desired motor velocity and acceleration: @@ -143,40 +177,53 @@ To calculate the feedforward, simply call the ``calculate()`` method with the de .. tab-set-code:: - .. code-block:: java + ```java + // Calculates the feedforward for a velocity of 20 units/second + // and an acceleration of 30 units/second^2 + // Units are determined by the units of the gains passed in at construction. + feedforward.calculate(20, 30); + ``` - // Calculates the feedforward for a velocity of 20 units/second - // and an acceleration of 30 units/second^2 - // Units are determined by the units of the gains passed in at construction. - feedforward.calculate(20, 30); + ```c++ + // Calculates the feedforward for a velocity of 20 meters/second + // and an acceleration of 30 meters/second^2 + // Output is in volts + feedforward.Calculate(20_mps, 30_mps_sq); + ``` - .. code-block:: c++ + ```python + # Calculates the feedforward for a velocity of 20 meters/second + # and an acceleration of 30 meters/second^2 + # Output is in volts + feedforward.calculate(20, 30) + ``` - // Calculates the feedforward for a velocity of 20 meters/second - // and an acceleration of 30 meters/second^2 - // Output is in volts - feedforward.Calculate(20_mps, 30_mps_sq); +## Using Feedforward to Control Mechanisms -Using Feedforward to Control Mechanisms ---------------------------------------- - -.. note:: Since feedforward voltages are physically meaningful, it is best to use the ``setVoltage()`` (`Java `__, `C++ `__) method when applying them to motors to compensate for "voltage sag" from the battery. +.. note:: Since feedforward voltages are physically meaningful, it is best to use the ``setVoltage()`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/motorcontrol/MotorController.html#setVoltage(double)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_motor_controller.html#a613c23a3336e103876e433bcb8b5ad3e), :external:py:meth:`Python `) method when applying them to motors to compensate for "voltage sag" from the battery. Feedforward control can be used entirely on its own, without a feedback controller. This is known as "open-loop" control, and for many mechanisms (especially robot drives) can be perfectly satisfactory. A ``SimpleMotorFeedforward`` might be employed to control a robot drive as follows: .. tab-set-code:: - .. code-block:: java - - public void tankDriveWithFeedforward(double leftVelocity, double rightVelocity) { - leftMotor.setVoltage(feedforward.calculate(leftVelocity)); - rightMotor.setVoltage(feedForward.calculate(rightVelocity)); - } - - .. code-block:: c++ + ```java + public void tankDriveWithFeedforward(double leftVelocity, double rightVelocity) { + leftMotor.setVoltage(feedforward.calculate(leftVelocity)); + rightMotor.setVoltage(feedForward.calculate(rightVelocity)); + } + ``` + + ```c++ + void TankDriveWithFeedforward(units::meters_per_second_t leftVelocity, + units::meters_per_second_t rightVelocity) { + leftMotor.SetVoltage(feedforward.Calculate(leftVelocity)); + rightMotor.SetVoltage(feedforward.Calculate(rightVelocity)); + } + ``` + + ```python + def tankDriveWithFeedforward(self, leftVelocity: float, rightVelocity: float): + self.leftMotor.setVoltage(feedForward.calculate(leftVelocity)) + self.rightMotor.setVoltage(feedForward.calculate(rightVelocity)) + ``` - void TankDriveWithFeedforward(units::meters_per_second_t leftVelocity, - units::meters_per_second_t rightVelocity) { - leftMotor.SetVoltage(feedforward.Calculate(leftVelocity)); - rightMotor.SetVoltage(feedforward.Calculate(rightVelocity)); - } diff --git a/source/docs/software/advanced-controls/controllers/index.rst b/source/docs/software/advanced-controls/controllers/index.rst index 9b512d2f08..63aa58985c 100644 --- a/source/docs/software/advanced-controls/controllers/index.rst +++ b/source/docs/software/advanced-controls/controllers/index.rst @@ -1,7 +1,6 @@ .. include:: -Controllers -=========== +# Controllers This section describes various WPILib feedback and feedforward controller classes that are useful for controlling the motion of robot mechanisms, as well as motion-profiling classes that can automatically generate setpoints for use with these controllers. diff --git a/source/docs/software/advanced-controls/controllers/pidcontroller.rst b/source/docs/software/advanced-controls/controllers/pidcontroller.rst index ecdae1ff17..adda28cba7 100644 --- a/source/docs/software/advanced-controls/controllers/pidcontroller.rst +++ b/source/docs/software/advanced-controls/controllers/pidcontroller.rst @@ -1,17 +1,14 @@ -PID Control in WPILib -===================== +# PID Control in WPILib .. note:: This article focuses on in-code implementation of PID control in WPILib. For a conceptual explanation of the working of a PIDController, see :ref:`docs/software/advanced-controls/introduction/introduction-to-pid:Introduction to PID` -.. note:: For a guide on implementing PID control through the :ref:`command-based framework `, see :ref:`docs/software/commandbased/pid-subsystems-commands:PID Control through PIDSubsystems and PIDCommands`. +.. note:: For a guide on implementing PID control through the :ref:`command-based framework `, see :ref:`docs/software/commandbased/pid-subsystems-commands:PID Control in Command-based`. -WPILib supports PID control of mechanisms through the ``PIDController`` class (`Java `__, `C++ `__). This class handles the feedback loop calculation for the user, as well as offering methods for returning the error, setting tolerances, and checking if the control loop has reached its setpoint within the specified tolerances. +WPILib supports PID control of mechanisms through the ``PIDController`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/PIDController.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_p_i_d_controller.html), :external:py:class:`Python `). This class handles the feedback loop calculation for the user, as well as offering methods for returning the error, setting tolerances, and checking if the control loop has reached its setpoint within the specified tolerances. -Using the PIDController Class ------------------------------ +## Using the PIDController Class -Constructing a PIDController -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Constructing a PIDController .. note:: While ``PIDController`` may be used asynchronously, it does *not* provide any thread safety features - ensuring threadsafe operation is left entirely to the user, and thus asynchronous usage is recommended only for advanced teams. @@ -19,20 +16,25 @@ In order to use WPILib's PID control functionality, users must first construct a .. tab-set-code:: - .. code-block:: java + ```java + // Creates a PIDController with gains kP, kI, and kD + PIDController pid = new PIDController(kP, kI, kD); + ``` - // Creates a PIDController with gains kP, kI, and kD - PIDController pid = new PIDController(kP, kI, kD); + ```c++ + // Creates a PIDController with gains kP, kI, and kD + frc::PIDController pid{kP, kI, kD}; + ``` - .. code-block:: c++ - - // Creates a PIDController with gains kP, kI, and kD - frc::PIDController pid{kP, kI, kD}; + ```python + from wpimath.controller import PIDController + # Creates a PIDController with gains kP, kI, and kD + pid = PIDController(kP, kI, kD) + ``` An optional fourth parameter can be provided to the constructor, specifying the period at which the controller will be run. The ``PIDController`` object is intended primarily for synchronous use from the main robot loop, and so this value is defaulted to 20ms. -Using the Feedback Loop Output -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Using the Feedback Loop Output .. note:: The ``PIDController`` assumes that the ``calculate()`` method is being called regularly at an interval consistent with the configured period. Failure to do this will result in unintended loop behavior. @@ -40,27 +42,31 @@ Using the constructed ``PIDController`` is simple: simply call the ``calculate() .. tab-set-code:: - .. code-block:: java - - // Calculates the output of the PID algorithm based on the sensor reading - // and sends it to a motor - motor.set(pid.calculate(encoder.getDistance(), setpoint)); + ```java + // Calculates the output of the PID algorithm based on the sensor reading + // and sends it to a motor + motor.set(pid.calculate(encoder.getDistance(), setpoint)); + ``` - .. code-block:: c++ + ```c++ + // Calculates the output of the PID algorithm based on the sensor reading + // and sends it to a motor + motor.Set(pid.Calculate(encoder.GetDistance(), setpoint)); + ``` - // Calculates the output of the PID algorithm based on the sensor reading - // and sends it to a motor - motor.Set(pid.Calculate(encoder.GetDistance(), setpoint)); + ```python + # Calculates the output of the PID algorithm based on the sensor reading + # and sends it to a motor + motor.set(pid.calculate(encoder.getDistance(), setpoint)) + ``` -Checking Errors -^^^^^^^^^^^^^^^ +### Checking Errors .. note:: ``getPositionError()`` and ``getVelocityError()`` are named assuming that the loop is controlling a position - for a loop that is controlling a velocity, these return the velocity error and the acceleration error, respectively. The current error of the measured process variable is returned by the ``getPositionError()`` function, while its derivative is returned by the ``getVelocityError()`` function: -Specifying and Checking Tolerances -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Specifying and Checking Tolerances .. note:: If only a position tolerance is specified, the velocity tolerance defaults to infinity. @@ -74,31 +80,35 @@ To do this, we first must specify the tolerances with the ``setTolerance()`` met .. tab-set-code:: - .. code-block:: java - - // Sets the error tolerance to 5, and the error derivative tolerance to 10 per second - pid.setTolerance(5, 10); - + ```java + // Sets the error tolerance to 5, and the error derivative tolerance to 10 per second + pid.setTolerance(5, 10); // Returns true if the error is less than 5 units, and the - // error derivative is less than 10 units - pid.atSetpoint(); - - .. code-block:: c++ - - // Sets the error tolerance to 5, and the error derivative tolerance to 10 per second - pid.SetTolerance(5, 10); + // error derivative is less than 10 units + pid.atSetpoint(); + ``` + ```c++ + // Sets the error tolerance to 5, and the error derivative tolerance to 10 per second + pid.SetTolerance(5, 10); // Returns true if the error is less than 5 units, and the - // error derivative is less than 10 units - pid.AtSetpoint(); + // error derivative is less than 10 units + pid.AtSetpoint(); + ``` -Resetting the Controller -^^^^^^^^^^^^^^^^^^^^^^^^ + ```python + # Sets the error tolerance to 5, and the error derivative tolerance to 10 per second + pid.setTolerance(5, 10) + # Returns true if the error is less than 5 units, and the + # error derivative is less than 10 units + pid.atSetpoint() + ``` + +### Resetting the Controller It is sometimes desirable to clear the internal state (most importantly, the integral accumulator) of a ``PIDController``, as it may be no longer valid (e.g. when the ``PIDController`` has been disabled and then re-enabled). This can be accomplished by calling the ``reset()`` method. -Setting a Max Integrator Value -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Setting a Max Integrator Value .. note:: Integrators introduce instability and hysteresis into feedback loop systems. It is strongly recommended that teams avoid using integral gain unless absolutely no other solution will do - very often, problems that can be solved with an integrator can be better solved through use of a more-accurate :ref:`feedforward `. @@ -110,20 +120,25 @@ The range limits may be increased or decreased using the ``setIntegratorRange()` .. tab-set-code:: - .. code-block:: java - - // The integral gain term will never add or subtract more than 0.5 from - // the total loop output - pid.setIntegratorRange(-0.5, 0.5); + ```java + // The integral gain term will never add or subtract more than 0.5 from + // the total loop output + pid.setIntegratorRange(-0.5, 0.5); + ``` - .. code-block:: c++ + ```c++ + // The integral gain term will never add or subtract more than 0.5 from + // the total loop output + pid.SetIntegratorRange(-0.5, 0.5); + ``` - // The integral gain term will never add or subtract more than 0.5 from - // the total loop output - pid.SetIntegratorRange(-0.5, 0.5); + ```python + # The integral gain term will never add or subtract more than 0.5 from + # the total loop output + pid.setIntegratorRange(-0.5, 0.5) + ``` -Disabling Integral Gain if the Error is Too High -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Disabling Integral Gain if the Error is Too High Another way integral "wind-up" can be alleviated is by limiting the error range where integral gain is active. This can be achieved by setting ``IZone``. If the error is more than ``IZone``, the total accumulated error is reset, disabling integral gain. When the error is equal to or less than IZone, integral gain is enabled. @@ -133,30 +148,33 @@ By default, ``IZone`` is disabled. .. tab-set-code:: - .. code-block:: java - - // Disable IZone - pid.setIZone(Double.POSITIVE_INFINITY); - + ```java + // Disable IZone + pid.setIZone(Double.POSITIVE_INFINITY); // Integral gain will not be applied if the absolute value of the error is - // more than 2 - pid.setIZone(2); - - .. code-block:: c++ - - // Disable IZone - pid.SetIZone(std::numeric_limits::infinity()); + // more than 2 + pid.setIZone(2); + ``` + ```c++ + // Disable IZone + pid.SetIZone(std::numeric_limits::infinity()); // Integral gain will not be applied if the absolute value of the error is - // more than 2 - pid.SetIZone(2); + // more than 2 + pid.SetIZone(2); + ``` -Setting Continuous Input -^^^^^^^^^^^^^^^^^^^^^^^^ + ```python + # Disable IZone + pid.setIZone(math.inf) + # Integral gain will not be applied if the absolute value of the error is + # more than 2 + pid.setIZone(2) + ``` -.. warning:: If your mechanism is not capable of fully continuous rotational motion (e.g. a turret without a slip ring, whose wires twist as it rotates), *do not* enable continuous input unless you have implemented an additional safety feature to prevent the mechanism from moving past its limit! +### Setting Continuous Input -.. warning:: The continuous input function does *not* automatically wrap your input values - be sure that your input values, when using this feature, are never outside of the specified range! +.. warning:: If your mechanism is not capable of fully continuous rotational motion (e.g. a turret without a slip ring, whose wires twist as it rotates), *do not* enable continuous input unless you have implemented an additional safety feature to prevent the mechanism from moving past its limit! Some process variables (such as the angle of a turret) are measured on a circular scale, rather than a linear one - that is, each "end" of the process variable range corresponds to the same point in reality (e.g. 360 degrees and 0 degrees). In such a configuration, there are two possible values for any given error, corresponding to which way around the circle the error is measured. It is usually best to use the smaller of these errors. @@ -164,27 +182,40 @@ To configure a ``PIDController`` to automatically do this, use the ``enableConti .. tab-set-code:: - .. code-block:: java - - // Enables continuous input on a range from -180 to 180 - pid.enableContinuousInput(-180, 180); + ```java + // Enables continuous input on a range from -180 to 180 + pid.enableContinuousInput(-180, 180); + ``` - .. code-block:: c++ + ```c++ + // Enables continuous input on a range from -180 to 180 + pid.EnableContinuousInput(-180, 180); + ``` - // Enables continuous input on a range from -180 to 180 - pid.EnableContinuousInput(-180, 180); + ```python + # Enables continuous input on a range from -180 to 180 + pid.enableContinuousInput(-180, 180) + ``` -Clamping Controller Output --------------------------- +## Clamping Controller Output .. tab-set-code:: - .. code-block:: java + ```java + // Clamps the controller output to between -0.5 and 0.5 + MathUtil.clamp(pid.calculate(encoder.getDistance(), setpoint), -0.5, 0.5); + ``` - // Clamps the controller output to between -0.5 and 0.5 - MathUtil.clamp(pid.calculate(encoder.getDistance(), setpoint), -0.5, 0.5); + ```c++ + // Clamps the controller output to between -0.5 and 0.5 + std::clamp(pid.Calculate(encoder.GetDistance(), setpoint), -0.5, 0.5); + ``` - .. code-block:: c++ + ```python + # Python doesn't have a builtin clamp function + def clamp(v, minval, maxval): + return max(min(v, maxval), minval) + # Clamps the controller output to between -0.5 and 0.5 + clamp(pid.calculate(encoder.getDistance(), setpoint), -0.5, 0.5) + ``` - // Clamps the controller output to between -0.5 and 0.5 - std::clamp(pid.Calculate(encoder.GetDistance(), setpoint), -0.5, 0.5); diff --git a/source/docs/software/advanced-controls/controllers/profiled-pidcontroller.rst b/source/docs/software/advanced-controls/controllers/profiled-pidcontroller.rst index 140d6a29f4..5c3c0fc09e 100644 --- a/source/docs/software/advanced-controls/controllers/profiled-pidcontroller.rst +++ b/source/docs/software/advanced-controls/controllers/profiled-pidcontroller.rst @@ -1,22 +1,19 @@ -Combining Motion Profiling and PID Control with ProfiledPIDController -===================================================================== +# Combining Motion Profiling and PID Control with ProfiledPIDController .. note:: For a guide on implementing the ``ProfiledPIDController`` class in the :ref:`command-based framework ` framework, see :ref:`docs/software/commandbased/profilepid-subsystems-commands:Combining Motion Profiling and PID in Command-Based`. In the previous article, we saw how to use the ``TrapezoidProfile`` class to create and use a trapezoidal motion profile. The example code from that article demonstrates manually composing the ``TrapezoidProfile`` class with the external PID control feature of a "smart" motor controller. -This combination of functionality (a motion profile for generating setpoints combined with a PID controller for following them) is extremely common. To facilitate this, WPILib comes with a ``ProfiledPIDController`` class (`Java `__, `C++ `__) that does most of the work of combining these two functionalities. The API of the ``ProfiledPIDController`` is very similar to that of the ``PIDController``, allowing users to add motion profiling to a PID-controlled mechanism with very few changes to their code. +This combination of functionality (a motion profile for generating setpoints combined with a PID controller for following them) is extremely common. To facilitate this, WPILib comes with a ``ProfiledPIDController`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/ProfiledPIDController.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_profiled_p_i_d_controller.html), :external:py:class:`Python `) that does most of the work of combining these two functionalities. The API of the ``ProfiledPIDController`` is very similar to that of the ``PIDController``, allowing users to add motion profiling to a PID-controlled mechanism with very few changes to their code. -Using the ProfiledPIDController class -------------------------------------- +## Using the ProfiledPIDController class .. note:: In C++, the ``ProfiledPIDController`` class is templated on the unit type used for distance measurements, which may be angular or linear. The passed-in values *must* have units consistent with the distance units, or a compile-time error will be thrown. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. .. note:: Much of the functionality of ``ProfiledPIDController`` is effectively identical to that of ``PIDController``. Accordingly, this article will only cover features that are substantially-changed to accommodate the motion profiling functionality. For information on standard ``PIDController`` features, see :ref:`docs/software/advanced-controls/controllers/pidcontroller:PID Control in WPILib`. -Constructing a ProfiledPIDController -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Constructing a ProfiledPIDController .. note:: C++ is often able to infer the type of the inner classes, and thus a simple initializer list (without the class name) can be sent as a parameter. The full class name is included in the example below for clarity. @@ -24,47 +21,62 @@ Creating a ``ProfiledPIDController`` is nearly identical to :ref:`creating a PID .. tab-set-code:: - .. code-block:: java - - // Creates a ProfiledPIDController - // Max velocity is 5 meters per second - // Max acceleration is 10 meters per second - ProfiledPIDController controller = new ProfiledPIDController( - kP, kI, kD, - new TrapezoidProfile.Constraints(5, 10)); - - .. code-block:: c++ - - // Creates a ProfiledPIDController - // Max velocity is 5 meters per second - // Max acceleration is 10 meters per second - frc::ProfiledPIDController controller( - kP, kI, kD, - frc::TrapezoidProfile::Constraints{5_mps, 10_mps_sq}); - -Goal vs Setpoint -^^^^^^^^^^^^^^^^ + ```java + // Creates a ProfiledPIDController + // Max velocity is 5 meters per second + // Max acceleration is 10 meters per second + ProfiledPIDController controller = new ProfiledPIDController( + kP, kI, kD, + new TrapezoidProfile.Constraints(5, 10)); + ``` + + ```c++ + // Creates a ProfiledPIDController + // Max velocity is 5 meters per second + // Max acceleration is 10 meters per second + frc::ProfiledPIDController controller( + kP, kI, kD, + frc::TrapezoidProfile::Constraints{5_mps, 10_mps_sq}); + ``` + + ```python + from wpimath.controller import ProfiledPIDController + from wpimath.trajectory import TrapezoidProfile + # Creates a ProfiledPIDController + # Max velocity is 5 meters per second + # Max acceleration is 10 meters per second + controller = ProfiledPIDController( + kP, kI, kD, + TrapezoidProfile.Constraints(5, 10)) + ``` + +### Goal vs Setpoint A major difference between a standard ``PIDController`` and a ``ProfiledPIDController`` is that the actual *setpoint* of the control loop is not directly specified by the user. Rather, the user specifies a *goal* position or state, and the setpoint for the controller is computed automatically from the generated motion profile between the current state and the goal. So, while the user-side call looks mostly identical: .. tab-set-code:: - .. code-block:: java - - // Calculates the output of the PID algorithm based on the sensor reading - // and sends it to a motor - motor.set(controller.calculate(encoder.getDistance(), goal)); + ```java + // Calculates the output of the PID algorithm based on the sensor reading + // and sends it to a motor + motor.set(controller.calculate(encoder.getDistance(), goal)); + ``` - .. code-block:: c++ + ```c++ + // Calculates the output of the PID algorithm based on the sensor reading + // and sends it to a motor + motor.Set(controller.Calculate(encoder.GetDistance(), goal)); + ``` - // Calculates the output of the PID algorithm based on the sensor reading - // and sends it to a motor - motor.Set(controller.Calculate(encoder.GetDistance(), goal)); + ```python + # Calculates the output of the PID algorithm based on the sensor reading + # and sends it to a motor + motor.set(controller.calculate(encoder.getDistance(), goal)) + ``` The specified ``goal`` value (which can be either a position value or a ``TrapezoidProfile.State``, if nonzero velocity is desired) is *not* necessarily the *current* setpoint of the loop - rather, it is the *eventual* setpoint once the generated profile terminates. -Getting/Using the Setpoint -~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Getting/Using the Setpoint Since the ``ProfiledPIDController`` goal differs from the setpoint, is if often desirable to poll the current setpoint of the controller (for instance, to get values to use with :ref:`feedforward `). This can be done with the ``getSetpoint()`` method. @@ -72,56 +84,79 @@ The returned setpoint might then be used as in the following example: .. tab-set-code:: - .. code-block:: java - - double lastSpeed = 0; - double lastTime = Timer.getFPGATimestamp(); - + ```java + double lastSpeed = 0; + double lastTime = Timer.getFPGATimestamp(); + // Controls a simple motor's position using a SimpleMotorFeedforward + // and a ProfiledPIDController + public void goToPosition(double goalPosition) { + double pidVal = controller.calculate(encoder.getDistance(), goalPosition); + double acceleration = (controller.getSetpoint().velocity - lastSpeed) / (Timer.getFPGATimestamp() - lastTime); + motor.setVoltage( + pidVal + + feedforward.calculate(controller.getSetpoint().velocity, acceleration)); + lastSpeed = controller.getSetpoint().velocity; + lastTime = Timer.getFPGATimestamp(); + } + ``` + + ```c++ + units::meters_per_second_t lastSpeed = 0_mps; + units::second_t lastTime = frc2::Timer::GetFPGATimestamp(); // Controls a simple motor's position using a SimpleMotorFeedforward - // and a ProfiledPIDController - public void goToPosition(double goalPosition) { - double pidVal = controller.calculate(encoder.getDistance(), goalPosition); - double acceleration = (controller.getSetpoint().velocity - lastSpeed) / (Timer.getFPGATimestamp() - lastTime); - motor.setVoltage( + // and a ProfiledPIDController + void GoToPosition(units::meter_t goalPosition) { + auto pidVal = controller.Calculate(units::meter_t{encoder.GetDistance()}, goalPosition); + auto acceleration = (controller.GetSetpoint().velocity - lastSpeed) / + (frc2::Timer::GetFPGATimestamp() - lastTime); + motor.SetVoltage( + pidVal + + feedforward.Calculate(controller.GetSetpoint().velocity, acceleration)); + lastSpeed = controller.GetSetpoint().velocity; + lastTime = frc2::Timer::GetFPGATimestamp(); + } + ``` + + ```python + from wpilib import Timer + from wpilib.controller import ProfiledPIDController + from wpilib.controller import SimpleMotorFeedforward + def __init__(self): + # Assuming encoder, motor, controller are already defined + self.lastSpeed = 0 + self.lastTime = Timer.getFPGATimestamp() + # Assuming feedforward is a SimpleMotorFeedforward object + self.feedforward = SimpleMotorFeedforward(ks=0.0, kv=0.0, ka=0.0) + def goToPosition(self, goalPosition: float): + pidVal = self.controller.calculate(self.encoder.getDistance(), goalPosition) + acceleration = (self.controller.getSetpoint().velocity - self.lastSpeed) / (Timer.getFPGATimestamp() - self.lastTime) + self.motor.setVoltage( pidVal - + feedforward.calculate(controller.getSetpoint().velocity, acceleration)); - lastSpeed = controller.getSetpoint().velocity; - lastTime = Timer.getFPGATimestamp(); - } - - .. code-block:: c++ + + self.feedforward.calculate(self.controller.getSetpoint().velocity, acceleration)) + self.lastSpeed = controller.getSetpoint().velocity + self.lastTime = Timer.getFPGATimestamp() + ``` - units::meters_per_second_t lastSpeed = 0_mps; - units::second_t lastTime = frc2::Timer::GetFPGATimestamp(); +## Complete Usage Example - // Controls a simple motor's position using a SimpleMotorFeedforward - // and a ProfiledPIDController - void GoToPosition(units::meter_t goalPosition) { - auto pidVal = controller.Calculate(units::meter_t{encoder.GetDistance()}, goalPosition); - auto acceleration = (controller.GetSetpoint().velocity - lastSpeed) / - (frc2::Timer::GetFPGATimestamp() - lastTime); - motor.SetVoltage( - pidVal + - feedforward.Calculate(controller.GetSetpoint().velocity, acceleration)); - lastSpeed = controller.GetSetpoint().velocity; - lastTime = frc2::Timer::GetFPGATimestamp(); - } - -Complete Usage Example ----------------------- - -A more complete example of ``ProfiledPIDController`` usage is provided in the ElevatorProfilePID example project (`Java `__, `C++ `__): +A more complete example of ``ProfiledPIDController`` usage is provided in the ElevatorProfilePID example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp), [Python](https://github.com/robotpy/examples/tree/main/ElevatorProfiledPID)): .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java :language: java :lines: 5- :linenos: :lineno-start: 5 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp/Robot.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp/Robot.cpp :language: c++ :lines: 5- :linenos: :lineno-start: 5 + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/ElevatorProfiledPID/robot.py + :language: python + :lines: 8- + :linenos: + :lineno-start: 8 diff --git a/source/docs/software/advanced-controls/controllers/trapezoidal-profiles.rst b/source/docs/software/advanced-controls/controllers/trapezoidal-profiles.rst index 073d5d7b69..4379764fad 100644 --- a/source/docs/software/advanced-controls/controllers/trapezoidal-profiles.rst +++ b/source/docs/software/advanced-controls/controllers/trapezoidal-profiles.rst @@ -1,67 +1,77 @@ -Trapezoidal Motion Profiles in WPILib -===================================== +# Trapezoidal Motion Profiles in WPILib .. todo:: link to conceptual motion profiling article .. note:: This article covers the in-code generation of trapezoidal motion profiles. Documentation describing the involved concepts in more detail is forthcoming. -.. note:: For a guide on implementing the ``TrapezoidProfile`` class in the :ref:`command-based framework ` framework, see :ref:`docs/software/commandbased/profile-subsystems-commands:Motion Profiling through TrapezoidProfileSubsystems and TrapezoidProfileCommands`. +.. note:: For a guide on implementing the ``TrapezoidProfile`` class in the :ref:`command-based framework ` framework, see :doc:`/docs/software/commandbased/profile-subsystems-commands`. .. note:: The ``TrapezoidProfile`` class, used on its own, is most useful when composed with a custom controller (such as a "smart" motor controller with a built-in PID functionality). To integrate it with a WPILib ``PIDController``, see :doc:`profiled-pidcontroller`. While feedforward and feedback control offer convenient ways to achieve a given setpoint, we are often still faced with the problem of generating setpoints for our mechanisms. While the naive approach of immediately commanding a mechanism to its desired state may work, it is often suboptimal. To improve the handling of our mechanisms, we often wish to command mechanisms to a *sequence* of setpoints that smoothly interpolate between its current state, and its desired goal state. -To help users do this, WPILib provides a ``TrapezoidProfile`` class (`Java `__, `C++ `__). +To help users do this, WPILib provides a ``TrapezoidProfile`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/trajectory/TrapezoidProfile.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_trapezoid_profile.html), :external:py:class:`Python `). -Creating a TrapezoidProfile ---------------------------- +## Creating a TrapezoidProfile .. note:: In C++, the ``TrapezoidProfile`` class is templated on the unit type used for distance measurements, which may be angular or linear. The passed-in values *must* have units consistent with the distance units, or a compile-time error will be thrown. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. -Constraints -^^^^^^^^^^^ +### Constraints .. note:: The various :ref:`feedforward helper classes ` provide methods for calculating the maximum simultaneously-achievable velocity and acceleration of a mechanism. These can be very useful for calculating appropriate motion constraints for your ``TrapezoidProfile``. -In order to create a trapezoidal motion profile, we must first impose some constraints on the desired motion. Namely, we must specify a maximum velocity and acceleration that the mechanism will be expected to achieve during the motion. To do this, we create an instance of the ``TrapezoidProfile.Constraints`` class (`Java `__, `C++ `__): +In order to create a trapezoidal motion profile, we must first impose some constraints on the desired motion. Namely, we must specify a maximum velocity and acceleration that the mechanism will be expected to achieve during the motion. To do this, we create an instance of the ``TrapezoidProfile.Constraints`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/trajectory/TrapezoidProfile.Constraints.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_trapezoid_profile_1_1_constraints.html), :external:py:class:`Python `): .. tab-set-code:: - .. code-block:: java + ```java + // Creates a new set of trapezoidal motion profile constraints + // Max velocity of 10 meters per second + // Max acceleration of 20 meters per second squared + new TrapezoidProfile.Constraints(10, 20); + ``` - // Creates a new set of trapezoidal motion profile constraints - // Max velocity of 10 meters per second - // Max acceleration of 20 meters per second squared - new TrapezoidProfile.Constraints(10, 20); + ```c++ + // Creates a new set of trapezoidal motion profile constraints + // Max velocity of 10 meters per second + // Max acceleration of 20 meters per second squared + frc::TrapezoidProfile::Constraints{10_mps, 20_mps_sq}; + ``` - .. code-block:: c++ + ```python + from wpimath.trajectory import TrapezoidProfile + # Creates a new set of trapezoidal motion profile constraints + # Max velocity of 10 meters per second + # Max acceleration of 20 meters per second squared + TrapezoidProfile.Constraints(10, 20) + ``` - // Creates a new set of trapezoidal motion profile constraints - // Max velocity of 10 meters per second - // Max acceleration of 20 meters per second squared - frc::TrapezoidProfile::Constraints{10_mps, 20_mps_sq}; +### Start and End States -Start and End States -^^^^^^^^^^^^^^^^^^^^ - -Next, we must specify the desired starting and ending states for our mechanisms using the ``TrapezoidProfile.State`` class (`Java `__, `C++ `__). Each state has a position and a velocity: +Next, we must specify the desired starting and ending states for our mechanisms using the ``TrapezoidProfile.State`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/trajectory/TrapezoidProfile.State.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_trapezoid_profile_1_1_state.html), :external:py:class:`Python `). Each state has a position and a velocity: .. tab-set-code:: - .. code-block:: java - - // Creates a new state with a position of 5 meters - // and a velocity of 0 meters per second - new TrapezoidProfile.State(5, 0); + ```java + // Creates a new state with a position of 5 meters + // and a velocity of 0 meters per second + new TrapezoidProfile.State(5, 0); + ``` - .. code-block:: c++ + ```c++ + // Creates a new state with a position of 5 meters + // and a velocity of 0 meters per second + frc::TrapezoidProfile::State{5_m, 0_mps}; + ``` - // Creates a new state with a position of 5 meters - // and a velocity of 0 meters per second - frc::TrapezoidProfile::State{5_m, 0_mps}; + ```python + from wpimath.trajectory import TrapezoidProfile + # Creates a new state with a position of 5 meters + # and a velocity of 0 meters per second + TrapezoidProfile.State(5, 0) + ``` -Putting It All Together -^^^^^^^^^^^^^^^^^^^^^^^ +### Putting It All Together .. note:: C++ is often able to infer the type of the inner classes, and thus a simple initializer list (without the class name) can be sent as a parameter. The full class names are included in the example below for clarity. @@ -69,81 +79,103 @@ Now that we know how to create a set of constraints and the desired start/end st .. tab-set-code:: - .. code-block:: java - - // Creates a new TrapezoidProfile - // Profile will have a max vel of 5 meters per second - // Profile will have a max acceleration of 10 meters per second squared - TrapezoidProfile profile = new TrapezoidProfile(new TrapezoidProfile.Constraints(5, 10)); - - .. code-block:: c++ - - // Creates a new TrapezoidProfile - // Profile will have a max vel of 5 meters per second - // Profile will have a max acceleration of 10 meters per second squared - frc::TrapezoidProfile profile{ - frc::TrapezoidProfile::Constraints{5_mps, 10_mps_sq}}; - -Using a ``TrapezoidProfile`` ----------------------------- - -Sampling the Profile -^^^^^^^^^^^^^^^^^^^^ + ```java + // Creates a new TrapezoidProfile + // Profile will have a max vel of 5 meters per second + // Profile will have a max acceleration of 10 meters per second squared + TrapezoidProfile profile = new TrapezoidProfile(new TrapezoidProfile.Constraints(5, 10)); + ``` + + ```c++ + // Creates a new TrapezoidProfile + // Profile will have a max vel of 5 meters per second + // Profile will have a max acceleration of 10 meters per second squared + frc::TrapezoidProfile profile{ + frc::TrapezoidProfile::Constraints{5_mps, 10_mps_sq}}; + ``` + + ```python + from wpimath.trajectory import TrapezoidProfile + # Creates a new TrapezoidProfile + # Profile will have a max vel of 5 meters per second + # Profile will have a max acceleration of 10 meters per second squared + profile = TrapezoidProfile(TrapezoidProfile.Constraints(5, 10)) + ``` + +## Using a ``TrapezoidProfile`` + +### Sampling the Profile Once we've created a ``TrapezoidProfile``, using it is very simple: to get the profile state at the given time after the profile has started, call the ``calculate()`` method with the goal state and initial state: .. tab-set-code:: - .. code-block:: java - - // Profile will end stationary at 5 meters - // Profile will start stationary at zero position - // Returns the motion profile state after 5 seconds of motion - profile.calculate(5, new TrapezoidProfile.State(5, 0), new TrapezoidProfile.State(0, 0)); - - .. code-block:: c++ - - // Profile will end stationary at 5 meters - // Profile will start stationary at zero position - // Returns the motion profile state after 5 seconds of motion - profile.Calculate(5_s, - frc::TrapezoidProfile::State{5_m, 0_mps}, - frc::TrapezoidProfile::State{0_m, 0_mps}); - -Using the State -^^^^^^^^^^^^^^^ + ```java + // Profile will start stationary at zero position + // Profile will end stationary at 5 meters + // Returns the motion profile state after 5 seconds of motion + profile.calculate(5, new TrapezoidProfile.State(0, 0), new TrapezoidProfile.State(5, 0)); + ``` + + ```c++ + // Profile will start stationary at zero position + // Profile will end stationary at 5 meters + // Returns the motion profile state after 5 seconds of motion + profile.Calculate(5_s, + frc::TrapezoidProfile::State{0_m, 0_mps}, + frc::TrapezoidProfile::State{5_m, 0_mps}); + ``` + + ```python + # Profile will start stationary at zero position + # Profile will end stationary at 5 meters + # Returns the motion profile state after 5 seconds of motion + profile.calculate(5, TrapezoidProfile.State(0, 0), TrapezoidProfile.State(5, 0)) + ``` + +### Using the State The ``calculate`` method returns a ``TrapezoidProfile.State`` class (the same one that was used to specify the initial/end states when calculating the profile state). To use this for actual control, simply pass the contained position and velocity values to whatever controller you wish (for example, a PIDController): .. tab-set-code:: - .. code-block:: java + ```java + var setpoint = profile.calculate(elapsedTime, initialState, goalState); + controller.calculate(encoder.getDistance(), setpoint.position); + ``` - var setpoint = profile.calculate(elapsedTime, goalState, initialState); - controller.calculate(encoder.getDistance(), setpoint.position); + ```c++ + auto setpoint = profile.Calculate(elapsedTime, initialState, goalState); + controller.Calculate(encoder.GetDistance(), setpoint.position.value()); + ``` - .. code-block:: c++ + ```python + setpoint = profile.calculate(elapsedTime, initialState, goalState) + controller.calculate(encoder.getDistance(), setpoint.position) + ``` - auto setpoint = profile.Calculate(elapsedTime, goalState, initialState); - controller.Calculate(encoder.GetDistance(), setpoint.position.value()); - -Complete Usage Example ----------------------- +## Complete Usage Example .. note:: In this example, the initial state is re-computed every timestep. This is a somewhat different usage technique than is detailed above, but works according to the same principles - the profile is sampled at a time corresponding to the loop period to get the setpoint for the next loop iteration. -A more complete example of ``TrapezoidProfile`` usage is provided in the ElevatorTrapezoidProfile example project (`Java `__, `C++ `__): +A more complete example of ``TrapezoidProfile`` usage is provided in the ElevatorTrapezoidProfile example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/cpp), [Python](https://github.com/robotpy/examples/tree/main/ElevatorTrapezoidProfile)): .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/Robot.java :language: java :lines: 5- :linenos: :lineno-start: 5 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/cpp/Robot.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/cpp/Robot.cpp :language: c++ :lines: 5- :linenos: :lineno-start: 5 + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/ElevatorTrapezoidProfile/robot.py + :language: python + :lines: 8- + :linenos: + :lineno-start: 8 diff --git a/source/docs/software/advanced-controls/controls-glossary.rst b/source/docs/software/advanced-controls/controls-glossary.rst index 204b060754..4f32953a36 100644 --- a/source/docs/software/advanced-controls/controls-glossary.rst +++ b/source/docs/software/advanced-controls/controls-glossary.rst @@ -1,13 +1,12 @@ -Controls Glossary -================= +# Controls Glossary .. glossary:: bang-bang control - A very simple, no-tuning-required closed-loop control technique. It simply "turns on" the :term:`control effort` when the :term:`process variable` is too small, and "turns off" the control effort when the process variable is too big. It works well in some cases, but not all. See `"Bang-bang" control `__ on Wikipedia for more info. + A very simple, no-tuning-required closed-loop control technique. It simply "turns on" the :term:`control effort` when the :term:`process variable` is too small, and "turns off" the control effort when the process variable is too big. It works well in some cases, but not all. See ["Bang-bang" control](https://en.wikipedia.org/wiki/Bang%E2%80%93bang_control) on Wikipedia for more info. Cartesian coordinate system - A set of points in space where each point is described by a set of numbers, indicating its *coordinates* within that space. These coordinates are an expression of the :term:`orthogonal` distance of each point from a set of fixed, orthogonal axes (IE, a "rectangular" system). 2-dimension and 3-dimension spaces are most common in FRC (and likely what was learned in algebra 1), but any number of dimensions is theoretically possible. See `Cartesian coordinate system `__ on Wikipedia for more info. + A set of points in space where each point is described by a set of numbers, indicating its *coordinates* within that space. These coordinates are an expression of the :term:`orthogonal` distance of each point from a set of fixed, orthogonal axes (IE, a "rectangular" system). 2-dimension and 3-dimension spaces are most common in FRC (and likely what was learned in algebra 1), but any number of dimensions is theoretically possible. See [Cartesian coordinate system](https://en.wikipedia.org/wiki/Cartesian_coordinate_system) on Wikipedia for more info. churning losses Complex friction-like forces arising from the fact that when gears and bearings rotate, they must displace liquid lubricant. This reduces the efficiency of rotating mechanisms. @@ -25,10 +24,10 @@ Controls Glossary Used in position or negative feedback with a :term:`plant` to bring about a desired :term:`system state ` by driving the difference between a :term:`reference` signal and the :term:`output` to zero. convolution - A mathematical operation that calculates a weighted moving average of one function, with the weights assigned by a second function. A common way to "filter" sensor input is to apply a *convolution* to it, using a carefully-chosen filtering function. See `convolution `__. on Wikipedia for more info. + A mathematical operation that calculates a weighted moving average of one function, with the weights assigned by a second function. A common way to "filter" sensor input is to apply a *convolution* to it, using a carefully-chosen filtering function. See [convolution](https://en.wikipedia.org/wiki/Convolution) on Wikipedia for more info. counter-electromotive force - A :term:`voltage` generated in a spinning motor. The voltage is a result of the fact that has a coil of wire rotating near a magnet. See `Counter-electromotive_force `__ on Wikipedia for more info. + A :term:`voltage` generated in a spinning motor. The voltage is a result of the fact that has a coil of wire rotating near a magnet. See [Counter-electromotive_force](https://en.wikipedia.org/wiki/Counter-electromotive_force) on Wikipedia for more info. current The flow of electrons through a conductor. Current is described with a unit of "Amps" (or simply "A"), and is measured at a single point in a circuit. One amp is equal to :math:`6241509074000000000` electrons moving past the measurement point in one second. @@ -37,22 +36,22 @@ Controls Glossary A branch of physics concerned with the motion of bodies under the action of forces. In modern control, systems evolve according to their dynamics. derivative - A mathematical operation which evaluates the "rate-of-change" of a function at a given point. See `derivative `__ on Wikipedia for more info. + A mathematical operation which evaluates the "rate-of-change" of a function at a given point. See [derivative](https://en.wikipedia.org/wiki/Derivative) on Wikipedia for more info. error :term:`Reference ` minus an :term:`output` or :term:`state`. exponential search - An iterative process of finding a specific value within a wide search range by applying a multiplicative factor to the search value. See `exponential search `__ on Wikipedia for more info. + An iterative process of finding a specific value within a wide search range by applying a multiplicative factor to the search value. See [exponential search](https://en.wikipedia.org/wiki/Exponential_search) on Wikipedia for more info. exponential smoothing - A very common way to implement a simple low-pass filter, using an exponential window function in a :term:`convolution` with an input signal. The convolution operation simplifies down to a very simple set of math operations on the current input and previous output. See `exponential smoothing `__ on Wikipedia for more info. + A very common way to implement a simple low-pass filter, using an exponential window function in a :term:`convolution` with an input signal. The convolution operation simplifies down to a very simple set of math operations on the current input and previous output. See [exponential smoothing](https://en.wikipedia.org/wiki/Exponential_smoothing) on Wikipedia for more info. gain A scalar value that relates the magnitude of an input signal to the magnitude of an output signal. For example, ``gain`` in ``output = gain * input``. A gain greater than one would amplify an input signal, while a gain less than one would dampen an input signal. A negative gain would negate the input signal. Gaussian distribution - A special mathematical function that describes distributions of averages. The graph of a Gaussian function is a "bell curve" shape. This function is described by its mean (the location of the "peak" of the bell curve) and variance (a measure of how "spread out" the bell curve is). See `Gaussian distribution `__ on Wikipedia for more info. + A special mathematical function that describes distributions of averages. The graph of a Gaussian function is a "bell curve" shape. This function is described by its mean (the location of the "peak" of the bell curve) and variance (a measure of how "spread out" the bell curve is). See [Gaussian distribution](https://en.wikipedia.org/wiki/Gaussian_function) on Wikipedia for more info. gradient The :term:`derivative`, but applied to a function with multiple inputs. As a result, the output is both the magnitude of the rate of change, and the vector direction along which it occurs. @@ -69,16 +68,16 @@ Controls Glossary Inputs are often represented by the variable :math:`\mathbf{u}`, a column vector with one entry per :term:`input` to the :term:`system`. least-squares regression - A curve-fitting technique which picks a curve to minimizes the *square* of the error between the fitted curve, and the actual measured data. See `ordinary least-squares regression `__ on Wikipedia for more info. + A curve-fitting technique which picks a curve to minimize the *square* of the error between the fitted curve and the actual measured data. See [ordinary least-squares regression](https://en.wikipedia.org/wiki/Linear_regression) on Wikipedia for more info. LQR - Linear-Quadratic Regulator - A feedback control scheme which seeks to operate a system in a "most optimal" or "lowest cost" manner, in the sense of minimizing the square of some "cost function" that represents a combination of system error and control effort. This requires an accurate mathematical model of the system being controlled, and function describing the "cost" of any given system state. See `LQR `__ on Wikipedia for more info. + Linear-Quadratic Regulator - A feedback control scheme which seeks to operate a system in a "most optimal" or "lowest cost" manner, in the sense of minimizing the square of some "cost function" that represents a combination of system error and control effort. This requires an accurate mathematical model of the system being controlled, and function describing the "cost" of any given system state. See [LQR](https://en.wikipedia.org/wiki/Linear%E2%80%93quadratic_regulator) on Wikipedia for more info. measurement Measurements are :term:`outputs ` that are measured from a :term:`plant`, or physical system, using sensors. model - A set of mathematical equations that reflects some aspect of a physical :term:`system's ` behavior. + A set of mathematical equations that reflects some aspect of a physical :term:`system `\'s behavior. observer In control theory, a system that provides an estimate of the internal :term:`state` of a given real :term:`system` from measurements of the :term:`input` and :term:`output` of the real :term:`system`. WPILib includes a Kalman Filter class for observing linear systems, and ExtendedKalmanFilter and UnscentedKalmanFilter classes for nonlinear systems. @@ -87,18 +86,18 @@ Controls Glossary Having the property of being independent, or lacking mutual influence. For example, two lines are orthogonal if moving any number of units along one line causes zero displacement along the other line. In a :term:`cartesian coordinate system`, orthogonal lines are often said to have 90-degree angles between each other. output - Measurements from sensors. There can be more measurements then states. These outputs are used in the "correct" step of Kalman Filters. + Measurements from sensors. There can be more measurements than states. These outputs are used in the "correct" step of Kalman Filters. - Ex. A flywheel might have 1 :term:`output` from a encoder that measures it's velocity. - Ex. A drivetrain might use solvePNP and V-SLAM to find it's x/y/heading position on the field. It's fine that there are 6 measurements (solvePNP x/y/heading and V-SLAM x/y/heading) and 3 states (robot x/y/heading). - Outputs of a :term:`system` are often represented using the variable :math:`\mathbf{y}`, a column vector with one entry per :term:`output` (or thing we can measure). For example, if our :term:`system` had states for velocity and acceleration but our sensor could only measure velocity, our, our :term:`output` vector would only include the :term:`system`\'s velocity. + Outputs of a :term:`system` are often represented using the variable :math:`\mathbf{y}`, a column vector with one entry per :term:`output` (or thing we can measure). For example, if our :term:`system` had states for velocity and acceleration but our sensor could only measure velocity, our :term:`output` vector would only include the :term:`system`\'s velocity. phase portrait - A graph of a function's value and its :term:`derivative` as they change in time, given some initial starting conditions. They are useful for analyzing system behavior (stable/unstable operating points, limit cycles, etc.) given a certain set of parameters or starting conditions. See `phase portrait `__ on Wikipedia for more info. + A graph of a function's value and its :term:`derivative` as they change in time, given some initial starting conditions. They are useful for analyzing system behavior (stable/unstable operating points, limit cycles, etc.) given a certain set of parameters or starting conditions. See [phase portrait](https://en.wikipedia.org/wiki/Phase_portrait) on Wikipedia for more info. PID - Proportional-Integral-Derivative - A feedback controller which calculates a :term:`control signal` from a weighted sum of the :term:`error`, the rate of change of the error, and an accumulated sum of previous errors. See `PID controller `__. on Wikipedia for more info. + Proportional-Integral-Derivative - A feedback controller which calculates a :term:`control signal` from a weighted sum of the :term:`error`, the rate of change of the error, and an accumulated sum of previous errors. See [PID controller](https://en.wikipedia.org/wiki/PID_controller) on Wikipedia for more info. plant The :term:`system` or collection of actuators being controlled. @@ -108,7 +107,7 @@ Controls Glossary r-squared - A statistical measurement of how well a model predicts a set of data, representing the fraction of the observed variation in the independent variable that is accurately predicted by the model. The value typically runs from 0.0 (a terrible fit, equivalent to just guessing the average value of your independent variable) to 1.0 (a perfect fit). See `Coefficient_of_determination `__ on Wikipedia for more info. + A statistical measurement of how well a model predicts a set of data, representing the fraction of the observed variation in the independent variable that is accurately predicted by the model. The value typically runs from 0.0 (a terrible fit, equivalent to just guessing the average value of your independent variable) to 1.0 (a perfect fit). See [Coefficient_of_determination](https://en.wikipedia.org/wiki/Coefficient_of_determination) on Wikipedia for more info. reference The desired state. This value is used as the reference point for a controller's error calculation. @@ -117,7 +116,7 @@ Controls Glossary The time a :term:`system` takes to initially reach the :term:`reference` after applying a :term:`step input`. RMSE - Root Mean Squared Error - Statistical measurement of how well a curve is fit to a set of data. It is calculated as the square root of the average (mean) of the squares of all the errors between the actual sample and the curve fit. It has units of the original input data. See `Root Mean Squared Error `__ on Wikipedia for more info. + Root Mean Squared Error - Statistical measurement of how well a curve is fit to a set of data. It is calculated as the square root of the average (mean) of the squares of all the errors between the actual sample and the curve fit. It has units of the original input data. See [Root Mean Squared Error](https://en.wikipedia.org/wiki/Root-mean-square_deviation) on Wikipedia for more info. setpoint The term used to describe the :term:`reference` of a PID controller. @@ -126,18 +125,18 @@ Controls Glossary The time a :term:`system` takes to settle at the :term:`reference` after a :term:`step input` is applied. signum function - A non-continuous function that expresses the "sign" of its input. It is equal to -1 for all negative input numbers, 0 for an input of 0, and 1 for all positive input numbers. See `signum function `__, on Wikipedia for more info. + A non-continuous function that expresses the "sign" of its input. It is equal to -1 for all negative input numbers, 0 for an input of 0, and 1 for all positive input numbers. See [signum function](https://en.wikipedia.org/wiki/Sign_function) on Wikipedia for more info. state - A characteristic of a :term:`system` (e.g., velocity) that can be used to determine the :term:`system's ` future behavior. In state-space notation, the state of a system is written as a column vector describing it's position in state-space. + A characteristic of a :term:`system` (e.g., velocity) that can be used to determine the :term:`system `\'s future behavior. In state-space notation, the state of a system is written as a column vector describing its position in state-space. - - Ex. A drivetrain system might have the states :math:`\begin{bmatrix}x \\ y \\ \theta \end{bmatrix}` to describe it's position on the field. + - Ex. A drivetrain system might have the states :math:`\begin{bmatrix}x \\ y \\ \theta \end{bmatrix}` to describe its position on the field. - Ex. An elevator system might have the states :math:`\begin{bmatrix} \text{position} \\ \text{velocity} \end{bmatrix}` to describe its current height and velocity. - A :term:`system's ` state is often represented by the variable :math:`\mathbf{x}`, a column vector with one entry per :term:`state`. + A :term:`system `\'s state is often represented by the variable :math:`\mathbf{x}`, a column vector with one entry per :term:`state`. statistically robust - The property of a data processing algorithm which makes it resilient to a noisy or outlier-prone data set. Designing statistically robust algorithms on robots is important because real-world sensor data can often be unpredictable, but unexpected robot behavior is never desirable. See `Robust Statistics `__ on Wikipedia for more info. + The property of a data processing algorithm which makes it resilient to a noisy or outlier-prone data set. Designing statistically robust algorithms on robots is important because real-world sensor data can often be unpredictable, but unexpected robot behavior is never desirable. See [Robust Statistics](https://en.wikipedia.org/wiki/Robust_statistics) on Wikipedia for more info. steady-state error :term:`Error ` after :term:`system` reaches equilibrium. @@ -149,10 +148,10 @@ Controls Glossary The response of a :term:`system` to a :term:`step input`. system - A term encompassing a :term:`plant` and it's interaction with a :term:`controller` and :term:`observer`, which is treated as a single entity. Mathematically speaking, a :term:`system` maps :term:`inputs ` to :term:`outputs ` through a linear combination of :term:`states `. + A term encompassing a :term:`plant` and its interaction with a :term:`controller` and :term:`observer`, which are treated as a single entity. Mathematically speaking, a :term:`system` maps :term:`inputs ` to :term:`outputs ` through a linear combination of :term:`states `. system identification - The process of capturing a :term:`systems ` :term:`dynamics` in a mathematical model using measured data. The SysId toolsuite uses system identification to find kS, kV and kA terms. + The process of capturing a :term:`system `\'s :term:`dynamics` in a mathematical model using measured data. The SysId toolsuite uses system identification to find kS, kV and kA terms. system response The behavior of a :term:`system` over time for a given :term:`input`. @@ -161,7 +160,7 @@ Controls Glossary The measurement of how much an electric field is "pushing" electrons through a circuit. It is sometimes called "Electromotive Force", or "EMF". It is measured in units of "Volts". It always is defined between *two* points in a circuit. If one electron travels between two points that have one volt of EMF between them, it will have been accelerated to the point of having :math:`\frac{1}{6241509074000000000}` joules of energy. viscous drag - The force generated from an object moving *relatively* slowly through non-turbulent fluid. In this region, the force is roughly proportional to the *velocity* of the object. It describes the most common type of "air resistance" an FRC robot would encounter, as well as losses in a gearbox from displacing grease. See `Drag (physics) `__ on Wikipedia for more info. + The force generated from an object moving *relatively* slowly through non-turbulent fluid. In this region, the force is roughly proportional to the *velocity* of the object. It describes the most common type of "air resistance" an FRC robot would encounter, as well as losses in a gearbox from displacing grease. See [Drag (physics)](https://en.wikipedia.org/wiki/Drag_%28physics%29#Low_Reynolds_numbers:_Stokes'_drag) on Wikipedia for more info. x-dot :math:`\dot{\mathbf{x}}`, or x-dot: the derivative of the :term:`state` vector :math:`\mathbf{x}`. If the :term:`system` had just a velocity :term:`state`, then :math:`\dot{\mathbf{x}}` would represent the :term:`system`\'s acceleration. diff --git a/source/docs/software/advanced-controls/filters/debouncer.rst b/source/docs/software/advanced-controls/filters/debouncer.rst index 97066a3a59..2640ab3a8e 100644 --- a/source/docs/software/advanced-controls/filters/debouncer.rst +++ b/source/docs/software/advanced-controls/filters/debouncer.rst @@ -1,12 +1,10 @@ -Debouncer -========= +# Debouncer A debouncer is a filter used to eliminate unwanted quick on/off cycles (termed "bounces," originally from the physical vibrations of a switch as it is thrown). These cycles are usually due to a sensor error like noise or reflections and not the actual event the sensor is trying to record. -Debouncing is implemented in WPILib by the ``Debouncer`` class (`Java `__, `C++ `__), which filters a boolean stream so that the output only changes if the input sustains a change for some nominal time period. +Debouncing is implemented in WPILib by the ``Debouncer`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/filter/Debouncer.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_debouncer.html), :external:py:class:`Python `), which filters a boolean stream so that the output only changes if the input sustains a change for some nominal time period. -Modes ------ +## Modes The WPILib ``Debouncer`` can be configured in three different modes: @@ -14,33 +12,42 @@ The WPILib ``Debouncer`` can be configured in three different modes: * Falling: Debounces falling edges (transitions from `true` to `false`) only. * Both: Debounces all transitions. -Usage ------ +## Usage .. tab-set-code:: - .. code-block:: java + ```java + // Initializes a DigitalInput on DIO 0 + DigitalInput input = new DigitalInput(0); + // Creates a Debouncer in "both" mode. + Debouncer m_debouncer = new Debouncer(0.1, Debouncer.DebounceType.kBoth); + // So if currently false the signal must go true for at least .1 seconds before being read as a True signal. + if (m_debouncer.calculate(input.get())) { + // Do something now that the DI is True. + } + ``` + + ```c++ + // Initializes a DigitalInput on DIO 0 + frc::DigitalInput input{0}; + // Creates a Debouncer in "both" mode. + frc::Debouncer m_debouncer{100_ms, frc::Debouncer::DebounceType::kBoth}; + // So if currently false the signal must go true for at least .1 seconds before being read as a True signal. + if (m_debouncer.calculate(input.Get())) { + // Do something now that the DI is True. + } + ``` + + ```python + from wpilib import DigitalInput + from wpimath.filter import Debouncer + # Initializes a DigitalInput on DIO 0 + self.input = DigitalInput(0) + # Creates a Debouncer in "both" mode with a debounce time of 0.1 seconds + self.debouncer = Debouncer(0.1, Debouncer.DebounceType.kBoth) + # If currently false, the signal must go true for at least 0.1 seconds before being read as a True signal. + if self.debouncer.calculate(self.input.get()): + # Do something now that the DI is True. + pass + ``` - // Initializes a DigitalInput on DIO 0 - DigitalInput input = new DigitalInput(0); - - // Creates a Debouncer in "both" mode. - Debouncer m_debouncer = new Debouncer(0.1, Debouncer.DebounceType.kBoth); - - // So if currently false the signal must go true for at least .1 seconds before being read as a True signal. - if (m_debouncer.calculate(input.get())) { - // Do something now that the DI is True. - } - - .. code-block:: c++ - - // Initializes a DigitalInput on DIO 0 - frc::DigitalInput input{0}; - - // Creates a Debouncer in "both" mode. - frc::Debouncer m_debouncer{100_ms, frc::Debouncer::DebounceType::kBoth}; - - // So if currently false the signal must go true for at least .1 seconds before being read as a True signal. - if (m_debouncer.calculate(input.Get())) { - // Do something now that the DI is True. - } diff --git a/source/docs/software/advanced-controls/filters/images/firfilter.png b/source/docs/software/advanced-controls/filters/images/firfilter.png index 37eafc11a9..16c253f28b 100644 Binary files a/source/docs/software/advanced-controls/filters/images/firfilter.png and b/source/docs/software/advanced-controls/filters/images/firfilter.png differ diff --git a/source/docs/software/advanced-controls/filters/images/highpassfilter.png b/source/docs/software/advanced-controls/filters/images/highpassfilter.png index 1bfda60e50..34efdfea71 100644 Binary files a/source/docs/software/advanced-controls/filters/images/highpassfilter.png and b/source/docs/software/advanced-controls/filters/images/highpassfilter.png differ diff --git a/source/docs/software/advanced-controls/filters/images/medianfilter.png b/source/docs/software/advanced-controls/filters/images/medianfilter.png index 6aad2aaa9d..0b82150a12 100644 Binary files a/source/docs/software/advanced-controls/filters/images/medianfilter.png and b/source/docs/software/advanced-controls/filters/images/medianfilter.png differ diff --git a/source/docs/software/advanced-controls/filters/images/singlepolefilter.png b/source/docs/software/advanced-controls/filters/images/singlepolefilter.png index 15aaa0d5bd..62d41d710a 100644 Binary files a/source/docs/software/advanced-controls/filters/images/singlepolefilter.png and b/source/docs/software/advanced-controls/filters/images/singlepolefilter.png differ diff --git a/source/docs/software/advanced-controls/filters/index.rst b/source/docs/software/advanced-controls/filters/index.rst index 6b88d16c8b..481cebfc1d 100644 --- a/source/docs/software/advanced-controls/filters/index.rst +++ b/source/docs/software/advanced-controls/filters/index.rst @@ -1,5 +1,4 @@ -Filters -======= +# Filters .. note:: The data used to generate the various demonstration plots in this section can be found :download:`here `. diff --git a/source/docs/software/advanced-controls/filters/introduction.rst b/source/docs/software/advanced-controls/filters/introduction.rst index a685128fcd..fbf9323d07 100644 --- a/source/docs/software/advanced-controls/filters/introduction.rst +++ b/source/docs/software/advanced-controls/filters/introduction.rst @@ -1,35 +1,28 @@ -Introduction to Filters -======================= +# Introduction to Filters Filters are some of the most common tools used in modern technology, and find numerous applications in robotics in both signal processing and controls. Understanding the notion of a filter is crucial to understanding the utility of the various types of filters provided by WPILib. -What Is a Filter? ------------------ +## What Is a Filter? .. note:: For the sake of this article, we will assume all data are single-dimensional time-series data. Obviously, the concepts involved are more general than this - but a full/rigorous discussion of signals and filtering is out of the scope of this documentation. So, what exactly *is* a filter, then? Simply put, a filter is a mapping from a stream of inputs to a stream of outputs. That is to say, the value output by a filter (in principle) can depend not only on the *current* value of the input, but on *the entire set of past and future values* (of course, in practice, the filters provided by WPILib are implementable in real-time on streaming data; accordingly, they can only depend on the *past* values of the input, and not on future values). This is an important concept, because generally we use filters to remove/mitigate unwanted *dynamics* from a signal. When we filter a signal, we're interested in modifying *how the signal changes over time*. -Effects of Using a Filter -------------------------- +## Effects of Using a Filter -Noise Reduction -^^^^^^^^^^^^^^^ +### Noise Reduction One of the most typical uses of a filter is for noise reduction. A filter that reduces noise is called a *low-pass* filter (because it allows low frequencies to "pass through," while blocking high-frequencies). Most of the filters currently included in WPILib are effectively low-pass filters. -Rate Limiting -^^^^^^^^^^^^^ +### Rate Limiting Filters are also commonly used to reduce the rate at which a signal can change. This is closely related to noise reduction, and filters that reduce noise also tend to limit the rate of change of their output. -Edge Detection -^^^^^^^^^^^^^^ +### Edge Detection The counterpart to the low-pass filter is the high-pass filter, which only permits high frequencies to pass through to the output. High-pass filters can be somewhat tricky to build intuition for, but a common usage for a high-pass filter is edge-detection - since high-pass filters will reflect sudden changes in the input while ignoring slower changes, they are useful for determining the location of sharp discontinuities in the signal. -Phase Lag -^^^^^^^^^ +### Phase Lag An unavoidable negative effect of a real-time low-pass filter is the introduction of "phase lag." Since, as mentioned earlier, a real-time filter can only depend on past values of the signal (we cannot time-travel to obtain the future values), the filtered value takes some time to "catch up" when the input starts changing. The greater the noise-reduction, the greater the introduced delay. This is, in many ways, *the* fundamental trade-off of real-time filtering, and should be the primary driving factor of your filter design. diff --git a/source/docs/software/advanced-controls/filters/linear-filter.rst b/source/docs/software/advanced-controls/filters/linear-filter.rst index c506d52fdd..89670dde5f 100644 --- a/source/docs/software/advanced-controls/filters/linear-filter.rst +++ b/source/docs/software/advanced-controls/filters/linear-filter.rst @@ -1,5 +1,4 @@ -Linear Filters -============== +# Linear Filters The first (and most commonly-employed) sort of filter that WPILib supports is a *linear filter* - or, more specifically, a linear time-invariant (LTI) filter. @@ -11,10 +10,9 @@ Infinite impulse responses have infinite "support" - that is, they are nonzero o Finite impulse responses have finite "support" - that is, they are nonzero on a bounded region. The "archetypical" FIR filter is a flat moving average - that is, simply setting the output equal to the average of the past n inputs. FIR filters tend to have more-desirable properties than IIR filters, but are more costly to compute. -Linear filters are supported in WPILib through the ``LinearFilter`` class (`Java `__, `C++ `__). +Linear filters are supported in WPILib through the ``LinearFilter`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/filter/LinearFilter.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_linear_filter.html), , :external:py:class:`Python `). -Creating a LinearFilter ------------------------ +## Creating a LinearFilter .. note:: The C++ ``LinearFilter`` class is templated on the data type used for the input. @@ -22,8 +20,7 @@ Creating a LinearFilter While it is possible to directly instantiate ``LinearFilter`` class to build a custom filter, it is far more convenient (and common) to use one of the supplied factory methods, instead: -singlePoleIIR -^^^^^^^^^^^^^ +### singlePoleIIR .. image:: images/singlepolefilter.png :alt: A graph with two peaks with the input closely following the target signal. @@ -32,26 +29,33 @@ The ``singlePoleIIR()`` factory method creates a single-pole infinite impulse re .. tab-set-code:: - .. code-block:: java - - // Creates a new Single-Pole IIR filter - // Time constant is 0.1 seconds - // Period is 0.02 seconds - this is the standard FRC main loop period - LinearFilter filter = LinearFilter.singlePoleIIR(0.1, 0.02); - - .. code-block:: c++ - - // Creates a new Single-Pole IIR filter - // Time constant is 0.1 seconds - // Period is 0.02 seconds - this is the standard FRC main loop period - frc::LinearFilter filter = frc::LinearFilter::SinglePoleIIR(0.1_s, 0.02_s); + ```java + // Creates a new Single-Pole IIR filter + // Time constant is 0.1 seconds + // Period is 0.02 seconds - this is the standard FRC main loop period + LinearFilter filter = LinearFilter.singlePoleIIR(0.1, 0.02); + ``` + + ```c++ + // Creates a new Single-Pole IIR filter + // Time constant is 0.1 seconds + // Period is 0.02 seconds - this is the standard FRC main loop period + frc::LinearFilter filter = frc::LinearFilter::SinglePoleIIR(0.1_s, 0.02_s); + ``` + + ```python + from wpimath.filter import LinearFilter + # Creates a new Single-Pole IIR filter + # Time constant is 0.1 seconds + # Period is 0.02 seconds - this is the standard FRC main loop period + filter = LinearFilter.singlePoleIIR(0.1, 0.02) + ``` The "time constant" parameter determines the "characteristic timescale" of the filter's impulse response; the filter will cancel out any signal dynamics that occur on timescales significantly shorter than this. Relatedly, it is also the approximate timescale of the introduced :ref:`phase lag `. The reciprocal of this timescale, multiplied by 2 pi, is the "cutoff frequency" of the filter. The "period" parameter is the period at which the filter's ``calculate()`` method will be called. For the vast majority of implementations, this will be the standard main robot loop period of 0.02 seconds. -movingAverage -^^^^^^^^^^^^^ +### movingAverage .. image:: images/firfilter.png :alt: A graph with two peaks with the input closely following the target signal. @@ -60,22 +64,28 @@ The ``movingAverage`` factory method creates a simple flat moving average filter .. tab-set-code:: - .. code-block:: java - - // Creates a new flat moving average filter - // Average will be taken over the last 5 samples - LinearFilter filter = LinearFilter.movingAverage(5); - - .. code-block:: c++ - - // Creates a new flat moving average filter - // Average will be taken over the last 5 samples - frc::LinearFilter filter = frc::LinearFilter::MovingAverage(5); + ```java + // Creates a new flat moving average filter + // Average will be taken over the last 5 samples + LinearFilter filter = LinearFilter.movingAverage(5); + ``` + + ```c++ + // Creates a new flat moving average filter + // Average will be taken over the last 5 samples + frc::LinearFilter filter = frc::LinearFilter::MovingAverage(5); + ``` + + ```python + from wpimath.filter import LinearFilter + # Creates a new flat moving average filter + # Average will be taken over the last 5 samples + filter = LinearFilter.movingAverage(5) + ``` The "taps" parameter is the number of samples that will be included in the flat moving average. This behaves similarly to the "time constant" above - the effective time constant is the number of taps times the period at which ``calculate()`` is called. -highPass -^^^^^^^^ +### highPass .. image:: images/highpassfilter.png :alt: A graph with two peaks except the highpass only shows the rate of change centered around 0. @@ -84,26 +94,33 @@ The ``highPass`` factory method creates a simple first-order infinite impulse re .. tab-set-code:: - .. code-block:: java - - // Creates a new high-pass IIR filter - // Time constant is 0.1 seconds - // Period is 0.02 seconds - this is the standard FRC main loop period - LinearFilter filter = LinearFilter.highPass(0.1, 0.02); - - .. code-block:: c++ - - // Creates a new high-pass IIR filter - // Time constant is 0.1 seconds - // Period is 0.02 seconds - this is the standard FRC main loop period - frc::LinearFilter filter = frc::LinearFilter::HighPass(0.1_s, 0.02_s); + ```java + // Creates a new high-pass IIR filter + // Time constant is 0.1 seconds + // Period is 0.02 seconds - this is the standard FRC main loop period + LinearFilter filter = LinearFilter.highPass(0.1, 0.02); + ``` + + ```c++ + // Creates a new high-pass IIR filter + // Time constant is 0.1 seconds + // Period is 0.02 seconds - this is the standard FRC main loop period + frc::LinearFilter filter = frc::LinearFilter::HighPass(0.1_s, 0.02_s); + ``` + + ```python + from wpimath.filter import LinearFilter + # Creates a new high-pass IIR filter + # Time constant is 0.1 seconds + # Period is 0.02 seconds - this is the standard FRC main loop period + filter = LinearFilter.highPass(0.1, 0.02) + ``` The "time constant" parameter determines the "characteristic timescale" of the filter's impulse response; the filter will cancel out any signal dynamics that occur on timescales significantly longer than this. Relatedly, it is also the approximate timescale of the introduced :ref:`phase lead `. The reciprocal of this timescale, multiplied by 2 pi, is the "cutoff frequency" of the filter. The "period" parameter is the period at which the filter's ``calculate()`` method will be called. For the vast majority of implementations, this will be the standard main robot loop period of 0.02 seconds. -Using a LinearFilter --------------------- +## Using a LinearFilter .. note:: In order for the created filter to obey the specified timescale parameter, its ``calculate()`` function *must* be called regularly at the specified period. If, for some reason, a significant lapse in ``calculate()`` calls must occur, the filter's ``reset()`` method should be called before further use. @@ -111,12 +128,18 @@ Once your filter has been created, using it is easy - simply call the ``calculat .. tab-set-code:: - .. code-block:: java + ```java + // Calculates the next value of the output + filter.calculate(input); + ``` - // Calculates the next value of the output - filter.calculate(input); + ```c++ + // Calculates the next value of the output + filter.Calculate(input); + ``` - .. code-block:: c++ + ```python + # Calculates the next value of the output + filter.calculate(input) + ``` - // Calculates the next value of the output - filter.Calculate(input); diff --git a/source/docs/software/advanced-controls/filters/median-filter.rst b/source/docs/software/advanced-controls/filters/median-filter.rst index 8392fbf656..69ea251aaf 100644 --- a/source/docs/software/advanced-controls/filters/median-filter.rst +++ b/source/docs/software/advanced-controls/filters/median-filter.rst @@ -1,5 +1,4 @@ -Median Filter -============= +# Median Filter .. image:: images/medianfilter.png :alt: A graph with two peaks with the input closely following the target signal. @@ -8,10 +7,9 @@ A `statistically robust` alternative to the :ref:`moving-average filter `__, `C++ `__). +The median filter is supported in WPILib through the ``MedianFilter`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/filter/MedianFilter.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_median_filter.html), , :external:py:class:`Python `). -Creating a MedianFilter ------------------------ +## Creating a MedianFilter .. note:: The C++ ``MedianFilter`` class is templated on the data type used for the input. @@ -21,29 +19,40 @@ Creating a ``MedianFilter`` is simple: .. tab-set-code:: - .. code-block:: java + ```java + // Creates a MedianFilter with a window size of 5 samples + MedianFilter filter = new MedianFilter(5); + ``` - // Creates a MedianFilter with a window size of 5 samples - MedianFilter filter = new MedianFilter(5); + ```c++ + // Creates a MedianFilter with a window size of 5 samples + frc::MedianFilter filter(5); + ``` - .. code-block:: c++ + ```python + from wpimath.filter import MedianFilter + # Creates a MedianFilter with a window size of 5 samples + filter = MedianFilter(5) + ``` - // Creates a MedianFilter with a window size of 5 samples - frc::MedianFilter filter(5); - -Using a MedianFilter --------------------- +## Using a MedianFilter Once your filter has been created, using it is easy - simply call the ``calculate()`` method with the most recent input to obtain the filtered output: .. tab-set-code:: - .. code-block:: java + ```java + // Calculates the next value of the output + filter.calculate(input); + ``` - // Calculates the next value of the output - filter.calculate(input); + ```c++ + // Calculates the next value of the output + filter.Calculate(input); + ``` - .. code-block:: c++ + ```python + # Calculates the next value of the output + filter.calculate(input) + ``` - // Calculates the next value of the output - filter.Calculate(input); diff --git a/source/docs/software/advanced-controls/filters/slew-rate-limiter.rst b/source/docs/software/advanced-controls/filters/slew-rate-limiter.rst index f6876f9918..2b091adaf2 100644 --- a/source/docs/software/advanced-controls/filters/slew-rate-limiter.rst +++ b/source/docs/software/advanced-controls/filters/slew-rate-limiter.rst @@ -1,16 +1,14 @@ .. include:: -Slew Rate Limiter -================= +# Slew Rate Limiter A common use for filters in FRC\ |reg| is to soften the behavior of control inputs (for example, the joystick inputs from your driver controls). Unfortunately, a simple low-pass filter is poorly-suited for this job; while a low-pass filter will soften the response of an input stream to sudden changes, it will also wash out fine control detail and introduce phase lag. A better solution is to limit the rate-of-change of the control input directly. This is performed with a *slew rate limiter* - a filter that caps the maximum rate-of-change of the signal. A slew rate limiter can be thought of as a sort of primitive motion profile. In fact, the slew rate limiter is the first-order equivalent of the :ref:`Trapezoidal Motion Profile ` supported by WPILib - it is precisely the limiting case of trapezoidal motion when the acceleration constraint is allowed to tend to infinity. Accordingly, the slew rate limiter is a good choice for applying a de-facto motion profile to a stream of velocity setpoints (or voltages, which are usually approximately proportional to velocity). For input streams that control positions, it is usually better to use a proper trapezoidal profile. -Slew rate limiting is supported in WPILib through the ``SlewRateLimiter`` class (`Java `__, `C++ `__). +Slew rate limiting is supported in WPILib through the ``SlewRateLimiter`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/filter/SlewRateLimiter.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_slew_rate_limiter.html), :external:py:class:`Python `). -Creating a SlewRateLimiter --------------------------- +## Creating a SlewRateLimiter .. note:: The C++ ``SlewRateLimiter`` class is templated on the unit type of the input. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. @@ -20,35 +18,44 @@ Creating a SlewRateLimiter is simple: .. tab-set-code:: - .. code-block:: java + ```java + // Creates a SlewRateLimiter that limits the rate of change of the signal to 0.5 units per second + SlewRateLimiter filter = new SlewRateLimiter(0.5); + ``` - // Creates a SlewRateLimiter that limits the rate of change of the signal to 0.5 units per second - SlewRateLimiter filter = new SlewRateLimiter(0.5); + ```c++ + // Creates a SlewRateLimiter that limits the rate of change of the signal to 0.5 volts per second + frc::SlewRateLimiter filter{0.5_V / 1_s}; + ``` - .. code-block:: c++ + ```python + from wpimath.filter import SlewRateLimiter + # Creates a SlewRateLimiter that limits the rate of change of the signal to 0.5 units per second + filter = SlewRateLimiter(0.5) + ``` - // Creates a SlewRateLimiter that limits the rate of change of the signal to 0.5 volts per second - frc::SlewRateLimiter filter{0.5_V / 1_s}; - -Using a SlewRateLimiter ------------------------ +## Using a SlewRateLimiter Once your filter has been created, using it is easy - simply call the ``calculate()`` method with the most recent input to obtain the filtered output: .. tab-set-code:: - .. code-block:: java - - // Calculates the next value of the output - filter.calculate(input); + ```java + // Calculates the next value of the output + filter.calculate(input); + ``` - .. code-block:: c++ + ```c++ + // Calculates the next value of the output + filter.Calculate(input); + ``` - // Calculates the next value of the output - filter.Calculate(input); + ```python + # Calculates the next value of the output + filter.calculate(input) + ``` -Using a SlewRateLimiter with DifferentialDrive -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Using a SlewRateLimiter with DifferentialDrive .. note:: The C++ example below templates the filter on ``units::scalar`` for use with doubles, since joystick values are typically dimensionless. @@ -56,18 +63,24 @@ A typical use of a SlewRateLimiter is to limit the acceleration of a robot's dri .. tab-set-code:: - .. code-block:: java - - // Ordinary call with no ramping applied - drivetrain.arcadeDrive(forward, turn); - - // Slew-rate limits the forward/backward input, limiting forward/backward acceleration - drivetrain.arcadeDrive(filter.calculate(forward), turn); - - .. code-block:: c++ - - // Ordinary call with no ramping applied - drivetrain.ArcadeDrive(forward, turn); + ```java + // Ordinary call with no ramping applied + drivetrain.arcadeDrive(forward, turn); + // Slew-rate limits the forward/backward input, limiting forward/backward acceleration + drivetrain.arcadeDrive(filter.calculate(forward), turn); + ``` + + ```c++ + // Ordinary call with no ramping applied + drivetrain.ArcadeDrive(forward, turn); + // Slew-rate limits the forward/backward input, limiting forward/backward acceleration + drivetrain.ArcadeDrive(filter.Calculate(forward), turn); + ``` + + ```python + # Ordinary call with no ramping applied + drivetrain.arcadeDrive(forward, turn) + # Slew-rate limits the forward/backward input, limiting forward/backward acceleration + drivetrain.arcadeDrive(filter.calculate(forward), turn) + ``` - // Slew-rate limits the forward/backward input, limiting forward/backward acceleration - drivetrain.ArcadeDrive(filter.Calculate(forward), turn); diff --git a/source/docs/software/advanced-controls/geometry/coordinate-systems.rst b/source/docs/software/advanced-controls/geometry/coordinate-systems.rst deleted file mode 100644 index f5a83d8221..0000000000 --- a/source/docs/software/advanced-controls/geometry/coordinate-systems.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. include:: - -Coordinate Systems -================== - -In FRC\ |reg|, there are two main coordinate systems that we use for representing objects' positions. - -Field Coordinate System ------------------------ - -The field coordinate system (or global coordinate system) is an absolute coordinate system where a point on the field is designated as the origin. Positive :math:`\theta` (theta) is in the counter-clockwise direction, and the positive x-axis points away from your alliance's driver station wall, and the positive y-axis is perpendicular and to the left of the positive x-axis. - -.. image:: diagrams/field-system.svg - :alt: In this system the coordinates are fixed based upon the field. - -.. note:: The axes are shown at the middle of the field for visibility. The origins of the coordinate system for each alliance are shown below. - -Below is an example of a field coordinate system overlaid on the 2020 FRC field. The red axes shown are for the red alliance, and the blue axes shown are for the blue alliance. - -.. image:: images/infinite-recharge.jpg - :alt: Image of the Infinite Recharge field. - -Robot Coordinate System ------------------------ - -The robot coordinate system (or local coordinate system) is a relative coordinate system where the robot is the origin. The direction the robot is facing is the positive x axis, and the positive y axis is perpendicular, to the left of the robot. Positive :math:`\theta` is counter-clockwise. - -.. note:: WPILib's ``Gyro`` class is clockwise-positive, so you have to invert the reading in order to get the rotation with either coordinate system. - -.. image:: diagrams/robot-system.svg - :alt: The robot coordinate system is based on the robot's position. diff --git a/source/docs/software/advanced-controls/geometry/diagrams/field-system.svg b/source/docs/software/advanced-controls/geometry/diagrams/field-system.svg deleted file mode 100644 index 76b19cf9e6..0000000000 --- a/source/docs/software/advanced-controls/geometry/diagrams/field-system.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
+θ
x
x
y
y
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/source/docs/software/advanced-controls/geometry/diagrams/robot-system.svg b/source/docs/software/advanced-controls/geometry/diagrams/robot-system.svg deleted file mode 100644 index be85b55b93..0000000000 --- a/source/docs/software/advanced-controls/geometry/diagrams/robot-system.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
+θ
x
x
y
y
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/source/docs/software/advanced-controls/geometry/images/infinite-recharge.jpg b/source/docs/software/advanced-controls/geometry/images/infinite-recharge.jpg deleted file mode 100644 index 7e6b4f2ce3..0000000000 Binary files a/source/docs/software/advanced-controls/geometry/images/infinite-recharge.jpg and /dev/null differ diff --git a/source/docs/software/advanced-controls/geometry/index.rst b/source/docs/software/advanced-controls/geometry/index.rst index 90a79d7fc8..dd076b6d8d 100644 --- a/source/docs/software/advanced-controls/geometry/index.rst +++ b/source/docs/software/advanced-controls/geometry/index.rst @@ -1,11 +1,9 @@ -Geometry Classes -================ +# Geometry Classes This section covers the geometry classes of WPILib. .. toctree:: :maxdepth: 1 - coordinate-systems pose transformations diff --git a/source/docs/software/advanced-controls/geometry/pose.rst b/source/docs/software/advanced-controls/geometry/pose.rst index ce036ec987..65a1a055dc 100644 --- a/source/docs/software/advanced-controls/geometry/pose.rst +++ b/source/docs/software/advanced-controls/geometry/pose.rst @@ -1,25 +1,21 @@ -Translation, Rotation, and Pose -=============================== +# Translation, Rotation, and Pose -Translation ------------ +## Translation -Translation in 2 dimensions is represented by WPILib's ``Translation2d`` class (`Java `__, `C++ `__). This class has an x and y component, representing the point :math:`(x, y)` or the vector :math:`\begin{bmatrix}x \\ y \end{bmatrix}` on a 2-dimensional coordinate system. +Translation in 2 dimensions is represented by WPILib's ``Translation2d`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/geometry/Translation2d.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_translation2d.html), :external:py:class:`Python `). This class has an x and y component, representing the point :math:`(x, y)` or the vector :math:`\begin{bmatrix}x \\ y \end{bmatrix}` on a 2-dimensional coordinate system. You can get the distance to another ``Translation2d`` object by using the ``getDistance(Translation2d other)``, which returns the distance to another Translation2d by using the Pythagorean theorem. -.. note:: ``Translation2d`` uses the C++ Units library. If you're planning on using other WPILib classes that use ``Translation2d`` in Java, such as the trajectory generator, make sure to use meters. +.. note:: ``Translation2d`` uses the C++ Units library. If you're planning on using other WPILib classes that use ``Translation2d`` in Java/Python, such as the trajectory generator, make sure to use meters. -Rotation --------- +## Rotation -Rotation in 2 dimensions is represented by WPILib's ``Rotation2d`` class (`Java `__, `C++ `__). This class has an angle component, which represents the robot's rotation relative to an axis on a 2-dimensional coordinate system. Positive rotations are counterclockwise. +Rotation in 2 dimensions is represented by WPILib's ``Rotation2d`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/geometry/Rotation2d.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_rotation2d.html), :external:py:class:`Python `). This class has an angle component, which represents the robot's rotation relative to an axis on a 2-dimensional coordinate system. Positive rotations are counterclockwise. -.. note:: ``Rotation2d`` uses the C++ Units library. The constructor in Java accepts either the angle in radians, or the sine and cosine of the angle, but the ``fromDegrees`` method will construct a ``Rotation2d`` object from degrees. +.. note:: ``Rotation2d`` uses the C++ Units library. The constructor in Java/Python accepts either the angle in radians, or the sine and cosine of the angle, but the ``fromDegrees`` method will construct a ``Rotation2d`` object from degrees. .. note:: ``Rotation2d`` does not wrap the value of the angle, so if a value of 400 degrees is passed into the constructor, then 400 degrees will be returned in subsequent value calls. -Pose ----- +## Pose -Pose is a combination of both translation and rotation and is represented by the ``Pose2d`` class (`Java `__, `C++ `__). It can be used to describe the pose of your robot in the field coordinate system, or the pose of objects, such as vision targets, relative to your robot in the robot coordinate system. ``Pose2d`` can also represent the vector :math:`\begin{bmatrix}x \\ y \\ \theta\end{bmatrix}`. +Pose is a combination of both translation and rotation and is represented by the ``Pose2d`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/geometry/Pose2d.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_pose2d.html), :external:py:class:`Python `). It can be used to describe the pose of your robot in the field coordinate system, or the pose of objects, such as vision targets, relative to your robot in the robot coordinate system. ``Pose2d`` can also represent the vector :math:`\begin{bmatrix}x \\ y \\ \theta\end{bmatrix}`. diff --git a/source/docs/software/advanced-controls/geometry/transformations.rst b/source/docs/software/advanced-controls/geometry/transformations.rst index 680b8e24a3..8f13862024 100644 --- a/source/docs/software/advanced-controls/geometry/transformations.rst +++ b/source/docs/software/advanced-controls/geometry/transformations.rst @@ -1,37 +1,33 @@ -Transformations -=============== +# Transformations -Translation2d -------------- +## Translation2d Operations on a ``Translation2d`` perform operations to the vector represented by the ``Translation2d``. -- Addition: Addition between two ``Translation2d`` a and b can be performed using ``plus`` in Java, or the ``+`` operator in C++. Addition adds the two vectors. -- Subtraction: Subtraction between two ``Translation2d`` can be performed using ``minus`` in Java, or the binary ``-`` operator in C++. Subtraction subtracts the two vectors. -- Multiplication: Multiplication of a ``Translation2d`` and a scalar can be performed using ``times`` in Java, or the ``*`` operator in C++. This multiplies the vector by the scalar. -- Division: Division of a ``Translation2d`` and a scalar can be performed using ``div`` in Java, or the ``/`` operator in C++. This divides the vector by the scalar. +- Addition: Addition between two ``Translation2d`` a and b can be performed using ``plus`` in Java, or the ``+`` operator in C++/Python. Addition adds the two vectors. +- Subtraction: Subtraction between two ``Translation2d`` can be performed using ``minus`` in Java, or the binary ``-`` operator in C++/Python. Subtraction subtracts the two vectors. +- Multiplication: Multiplication of a ``Translation2d`` and a scalar can be performed using ``times`` in Java, or the ``*`` operator in C++/Python. This multiplies the vector by the scalar. +- Division: Division of a ``Translation2d`` and a scalar can be performed using ``div`` in Java, or the ``/`` operator in C++/Python. This divides the vector by the scalar. - Rotation: Rotation of a ``Translation2d`` by a counter-clockwise rotation :math:`\theta` about the origin can be performed by using ``rotateBy``. This is equivalent to multiplying the vector by the matrix :math:`\begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{bmatrix}` -- Additionally, you can rotate a ``Translation2d`` by 180 degrees by using ``unaryMinus`` in Java, or the unary ``-`` operator in C++. +- Additionally, you can rotate a ``Translation2d`` by 180 degrees by using ``unaryMinus`` in Java, or the unary ``-`` operator in C++/Python. -Rotation2d ----------- +## Rotation2d Transformations for ``Rotation2d`` are just arithmetic operations on the angle measure represented by the ``Rotation2d``. -- ``plus`` (Java) or ``+`` (C++): Adds the rotation component of ``other`` to this ``Rotation2d``'s rotation component -- ``minus`` (Java) or binary ``-`` (C++): Subtracts the rotation component of ``other`` to this ``Rotation2d``'s rotation component -- ``unaryMinus`` (Java) or unary ``-`` (C++): Multiplies the rotation component by a scalar of -1. -- ``times`` (Java) or ``*`` (C++) : Multiplies the rotation component by a scalar. +- ``plus`` (Java) or ``+`` (C++/Python): Adds the rotation component of ``other`` to this ``Rotation2d``'s rotation component +- ``minus`` (Java) or binary ``-`` (C++/Python): Subtracts the rotation component of ``other`` to this ``Rotation2d``'s rotation component +- ``unaryMinus`` (Java) or unary ``-`` (C++/Python): Multiplies the rotation component by a scalar of -1. +- ``times`` (Java) or ``*`` (C++/Python) : Multiplies the rotation component by a scalar. -Transform2d and Twist2d ------------------------ +## Transform2d and Twist2d -WPILib provides 2 classes, ``Transform2d`` (`Java `__, `C++ `__), which represents a transformation to a pose, and ``Twist2d`` (`Java `__, `C++ `__) which represents a movement along an arc. ``Transform2d`` and ``Twist2d`` all have x, y and :math:`\theta` components. +WPILib provides 2 classes, ``Transform2d`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/geometry/Transform2d.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_transform2d.html), :external:py:class:[Python](wpimath.geometry.Transform2d>`), which represents a transformation to a pose, and ``Twist2d`` ([Java] (https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/geometry/Twist2d.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/structfrc_1_1_twist2d.html), :external:py:class:`Python `) which represents a movement along an arc. ``Transform2d`` and ``Twist2d`` all have x, y and :math:`\theta` components. ``Transform2d`` represents a **relative** transformation. It has an translation and a rotation component. Transforming a ``Pose2d`` by a ``Transform2d`` rotates the translation component of the transform by the rotation of the pose, and then adds the rotated translation component and the rotation component to the pose. In other words, ``Pose2d.plus(Transform2d)`` returns :math:`\begin{bmatrix} x_p \\ y_p \\ \theta_p \end{bmatrix}+\begin{bmatrix} cos\theta_p & -sin\theta_p & 0 \\ sin\theta_p & cos\theta_p & 0 \\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix}x_t \\ y_t \\ \theta_t \end{bmatrix}` -``Twist2d`` represents a change in distance along an arc. Usually, this class is used to represent the movement of a drivetrain, where the x component is the forward distance driven, the y component is the distance driven to the side (left positive), and the :math:`\theta` component is the change in heading. The underlying math behind finding the pose exponential (new pose after moving the pose forward along the curvature of the twist) can be found `here `_ in chapter 10. +``Twist2d`` represents a change in distance along an arc. Usually, this class is used to represent the movement of a drivetrain, where the x component is the forward distance driven, the y component is the distance driven to the side (left positive), and the :math:`\theta` component is the change in heading. The underlying math behind finding the pose exponential (new pose after moving the pose forward along the curvature of the twist) can be found [here](https://file.tavsys.net/control/controls-engineering-in-frc.pdf) in chapter 10. .. note:: For nonholonomic drivetrains, the y component of a ``Twist2d`` should always be 0. -Both classes can be used to estimate robot location. Twist2d is used in WPILib's odometry classes to update the robot's pose based on movement, while Transform2d can be used to estimate the robot's global position from vision data. +Both classes can be used to estimate robot location. Twist2d is used in WPILib's :term:`odometry` classes to update the robot's :term:`pose` based on movement, while Transform2d can be used to estimate the robot's global position from vision data. diff --git a/source/docs/software/advanced-controls/index.rst b/source/docs/software/advanced-controls/index.rst index 284c9c2d22..4db2c21cdf 100644 --- a/source/docs/software/advanced-controls/index.rst +++ b/source/docs/software/advanced-controls/index.rst @@ -1,5 +1,4 @@ -Advanced Controls -================= +# Advanced Controls This section covers advanced control features in WPILib, such as various feedback/feedforward control algorithms and trajectory following. @@ -10,6 +9,7 @@ This section covers advanced control features in WPILib, such as various feedbac introduction/index filters/index geometry/index + system-identification/index controllers/index trajectories/index state-space/index diff --git a/source/docs/software/advanced-controls/introduction/common-control-issues.rst b/source/docs/software/advanced-controls/introduction/common-control-issues.rst index 19d9c9bab5..be5f211971 100644 --- a/source/docs/software/advanced-controls/introduction/common-control-issues.rst +++ b/source/docs/software/advanced-controls/introduction/common-control-issues.rst @@ -1,10 +1,8 @@ -Common Control Loop Tuning Issues -================================= +# Common Control Loop Tuning Issues There are a number of common issues which can arise while tuning feedforward and feedback controllers. -Integral Term Windup --------------------- +## Integral Term Windup Beware that if :math:`K_i` is too large, integral windup can occur. Following a large change in :term:`setpoint`, the integral term can accumulate an error larger than the maximal :term:`control effort`. As a result, the system overshoots and continues to increase until this accumulated error is unwound. @@ -16,8 +14,7 @@ There are a few ways to mitigate this: .. important:: Most mechanisms in FRC do not require any integral control, and systems that seem to require integral control to respond well probably have an inaccurate feedforward model. -Voltage Sag ------------ +## Voltage Sag When we operate mechanisms on our robot, we draw current from its battery. This causes the available "bus voltage" that all the robot mechanisms operate off of to drop. This means that the performance of our mechanisms will vary depending on the loading and action of the robot - this is not ideal. @@ -25,8 +22,7 @@ To fix this, most voltage controllers offer a "voltage compensation" setting for Keep in mind that voltage compensation cannot increase the voltage applied to the motor beyond what is available on the bus - if your actuator is saturating (described below), you'll have to account for that separately. -Actuator Saturation -------------------- +## Actuator Saturation A controller calculates its output based on the error between the :term:`setpoint` and the current :term:`state`. :term:`Plant ` in the real world don't have unlimited control authority available for the controller to apply - that is to say, real mechanisms have some maximum achievable torque/acceleration and velocity. diff --git a/source/docs/software/advanced-controls/introduction/control-system-basics.rst b/source/docs/software/advanced-controls/introduction/control-system-basics.rst index af35e4d766..8b27247a96 100644 --- a/source/docs/software/advanced-controls/introduction/control-system-basics.rst +++ b/source/docs/software/advanced-controls/introduction/control-system-basics.rst @@ -1,10 +1,8 @@ -Control System Basics -===================== +# Control System Basics -.. note:: This article includes sections of `Controls Engineering in FRC `__ by Tyler Veness with permission. +.. note:: This article includes sections of [Controls Engineering in FRC](https://file.tavsys.net/control/controls-engineering-in-frc.pdf) by Tyler Veness with permission. -The Need for Control Systems ----------------------------- +## The Need for Control Systems Control systems are all around us and we interact with them daily. A small list of ones you may have seen includes heaters and air conditioners with thermostats, cruise control and the anti-lock braking system (ABS) on automobiles, and fan speed modulation on modern laptops. Control systems monitor or control the behavior of systems like these and may consist of humans controlling them directly (manual control), or of only machines (automatic control). @@ -20,8 +18,7 @@ How can we prove closed-loop controllers on an autonomous car, for example, will Controls engineering is, put simply, the engineering process applied to control theory. As such, it's more than just applied math. While control theory has some beautiful math behind it, controls engineering is an engineering discipline like any other that is filled with trade-offs. The solutions control theory gives should always be sanity checked and informed by our performance specifications. We don't need to be perfect; we just need to be good enough to meet our specifications. -Nomenclature ------------- +## Nomenclature Most resources for advanced engineering topics assume a level of knowledge well above that which is necessary. Part of the problem is the use of jargon. While it efficiently communicates ideas to those within the field, new people who aren't familiar with it are lost. @@ -34,8 +31,7 @@ Controllers which incorporate information fed back from the plant's output are c .. note:: The input and output of a system are defined from the plant's point of view. The negative feedback controller shown is driving the difference between the reference and output, also known as the error, to zero. -What is Gain? -------------- +## What is Gain? *Gain* is a proportional value that shows the relationship between the magnitude of an input signal to the magnitude of an output signal at steady-state. Many systems contain a method by which the gain can be altered, providing more or less "power" to the system. @@ -44,8 +40,7 @@ The figure below shows a system with a hypothetical input and output. Since the .. image:: images/control-system-basics-whatisgain.png :alt: A system diagram with hypothetical input and output -What is a Model? ----------------- +## What is a Model? A *model* of your mechanism is a mathematical description of its behavior. Specifically, this mathematical description must define the mechanism's inputs and outputs, and how the output values change over time as a function of its input values. @@ -53,8 +48,7 @@ The mathematical description is often just simple algebra equations. It can also :term:`Classical Mechanics` defines many of the equations used to build up models of system behavior. Many of the values inside those equations can be determined by doing experiments on the mechanism. -Block Diagrams --------------- +## Block Diagrams When designing or analyzing a control system, it is useful to model it graphically. Block diagrams are used for this purpose. They can be manipulated and simplified systematically. @@ -70,8 +64,7 @@ The below figure is a block diagram with more formal notation in a feedback conf :math:`\mp` means "minus or plus" where a minus represents negative feedback. -A Note on Dimensionality ------------------------- +## A Note on Dimensionality For the purposes of the introductory section, all systems and controllers (except feedforward controllers) are assumed to be "single-in, single-out" (SISO) - this means they only map single values to single values. For example, a DC motor is considered to take an :term:`input` of a single scalar value (voltage) and yield an :term:`output` of only a single scalar value in return (either position or velocity). This forces us to consider *position controllers* and *velocity controllers* as separate entities - this is sometimes source of confusion in situations when we want to control both (such as when following a motion profiles). Limiting ourselves to SISO systems also means that we are unable to analyze more-complex "multiple-in, multiple-out" (MIMO) systems like drivetrains that cannot be represented with a single state (there are at least two independent sets of wheels in a drive). diff --git a/source/docs/software/advanced-controls/introduction/images/control-system-basic-blockdiagram-2.png b/source/docs/software/advanced-controls/introduction/images/control-system-basic-blockdiagram-2.png index cb4c369412..a18f0246f1 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/control-system-basic-blockdiagram-2.png and b/source/docs/software/advanced-controls/introduction/images/control-system-basic-blockdiagram-2.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/control-system-basics-blockdiagrams-1.png b/source/docs/software/advanced-controls/introduction/images/control-system-basics-blockdiagrams-1.png index 45feab36ba..de1844a9ab 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/control-system-basics-blockdiagrams-1.png and b/source/docs/software/advanced-controls/introduction/images/control-system-basics-blockdiagrams-1.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/control-system-basics-ctrl-plus-plant.png b/source/docs/software/advanced-controls/introduction/images/control-system-basics-ctrl-plus-plant.png index 552bd211b1..3e685e2327 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/control-system-basics-ctrl-plus-plant.png and b/source/docs/software/advanced-controls/introduction/images/control-system-basics-ctrl-plus-plant.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/control-system-basics-feedbackplant.png b/source/docs/software/advanced-controls/introduction/images/control-system-basics-feedbackplant.png index 12dd97ad23..0642b01fa0 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/control-system-basics-feedbackplant.png and b/source/docs/software/advanced-controls/introduction/images/control-system-basics-feedbackplant.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/control-system-basics-whatisgain.png b/source/docs/software/advanced-controls/introduction/images/control-system-basics-whatisgain.png index b2a513b798..1265b7b011 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/control-system-basics-whatisgain.png and b/source/docs/software/advanced-controls/introduction/images/control-system-basics-whatisgain.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-d-controller.png b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-d-controller.png index 2112a5cf1d..b416ba2d92 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-d-controller.png and b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-d-controller.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-damped-controller.png b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-damped-controller.png index 2443129255..ea530f865f 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-damped-controller.png and b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-damped-controller.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-p-controller.png b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-p-controller.png index dbfd49aa21..0cf3662636 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-p-controller.png and b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-p-controller.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-block-diagram.png b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-block-diagram.png index 53c593c048..37a83a37bf 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-block-diagram.png and b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-block-diagram.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-overshoot.png b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-overshoot.png index 59e7baa346..470179dc08 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-overshoot.png and b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-overshoot.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-steadystate.png b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-steadystate.png index b7f2ddcf2e..e3ddda3f51 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-steadystate.png and b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pi-controller-steadystate.png differ diff --git a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pid-controller-block.png b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pid-controller-block.png index be45864488..4e0461cc14 100644 Binary files a/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pid-controller-block.png and b/source/docs/software/advanced-controls/introduction/images/introduction-to-pid-pid-controller-block.png differ diff --git a/source/docs/software/advanced-controls/introduction/index.rst b/source/docs/software/advanced-controls/introduction/index.rst index af7d809aed..d7555ed28c 100644 --- a/source/docs/software/advanced-controls/introduction/index.rst +++ b/source/docs/software/advanced-controls/introduction/index.rst @@ -1,5 +1,4 @@ -Advanced Controls Introduction -============================== +# Advanced Controls Introduction .. toctree:: :maxdepth: 1 diff --git a/source/docs/software/advanced-controls/introduction/introduction-to-feedforward.rst b/source/docs/software/advanced-controls/introduction/introduction-to-feedforward.rst index 81c4e35d85..9905280525 100644 --- a/source/docs/software/advanced-controls/introduction/introduction-to-feedforward.rst +++ b/source/docs/software/advanced-controls/introduction/introduction-to-feedforward.rst @@ -1,12 +1,10 @@ -Introduction to DC Motor Feedforward -==================================== +# Introduction to DC Motor Feedforward -.. note:: For a guide on implementing PID control in code with WPILib, see :ref:`docs/software/advanced-controls/controllers/feedforward:Feedforward Control in WPILib`. +.. note:: For a guide on implementing feedforward control in code with WPILib, see :ref:`docs/software/advanced-controls/controllers/feedforward:Feedforward Control in WPILib`. This page explains the conceptual and mathematical workings of WPILib's SimpleMotorFeedforward (and the other related classes). -The Permanent-Magnet DC Motor Feedforward Equation --------------------------------------------------- +## The Permanent-Magnet DC Motor Feedforward Equation Recall from earlier that the point of a feedforward controller is to use the known dynamics of a mechanism to make a best guess at the :term:`control effort` required to put the mechanism in the state you want. In order to do this, we need to have some idea of what kind of mechanism we are controlling - that will determine the relationship between :term:`control effort` and :term:`output`, and let us guess at what value of the former will give us the desired value of the latter. @@ -26,16 +24,14 @@ We can interpret the coefficients in the above equation as follows: :math:`K_a` describes the voltage needed to induce a given acceleration in the motor shaft. As with ``kV``, the relationship between voltage and acceleration (at constant velocity) is almost perfectly linear for FRC components. -For more information, see `this paper `__. +For more information, see [this paper](https://www.chiefdelphi.com/uploads/default/original/3X/f/7/f79d24101e6f1487e76099774e4ba60683e86cda.pdf). -Variants of the Feedforward Equation ------------------------------------- +## Variants of the Feedforward Equation Some of WPILib's other feedforward classes introduce additional terms into the above equation to account for known differences from the simple case described above - details for each tool can be found below: -Elevator Feedforward -~~~~~~~~~~~~~~~~~~~~ +#### Elevator Feedforward An elevator consists of a permanent-magnet DC motor attached to a mass under the force of gravity. Compared to the feedforward equation for an unloaded motor, it differs only in the inclusion of a constant :math:`K_g` term that accounts for the action of gravity: @@ -43,8 +39,7 @@ An elevator consists of a permanent-magnet DC motor attached to a mass under the where :math:`V` is the applied voltage, :math:`d` is the displacement (position) of the drive, :math:`\dot{d}` is its velocity, and :math:`\ddot{d}` is its acceleration. -Arm Feedforward -~~~~~~~~~~~~~~~ +#### Arm Feedforward An arm consists of a permanent-magnet DC motor attached to a mass on a stick held under the force of gravity. Like the elevator feedforward, it includes a :math:`K_g` term to account for the effect of gravity - unlike the elevator feedforward, however, this term is multiplied by the cosine of the arm angle (since the gravitational force does not act directly on the motor): @@ -52,10 +47,9 @@ An arm consists of a permanent-magnet DC motor attached to a mass on a stick hel where :math:`V` is the applied voltage, :math:`\theta` is the angular displacement (position) of the arm, :math:`\dot{\theta}` is its angular velocity, and :math:`\ddot{\theta}` is its angular acceleration. -Using the Feedforward ---------------------- +## Using the Feedforward -In order to use the feedforward, we need to plug in values for each unknown in the above voltage-balance equation *other than the voltage*. As mentioned :ref:`earlier `, the values of the gains :math:`K_g`, :math:`K_v`, :math:`K_a` can be obtained through theoretical modeling with `ReCalc `__. Explicit measurement with :doc:`SysId ` will yield the aforementioned gains in addition to :math:`K_s`. That leaves us needing values for velocity, acceleration, and (in the case of the arm feedforward) position. +In order to use the feedforward, we need to plug in values for each unknown in the above voltage-balance equation *other than the voltage*. As mentioned :ref:`earlier `, the values of the gains :math:`K_g`, :math:`K_v`, :math:`K_a` can be obtained through theoretical modeling with [ReCalc] (https://www.reca.lc/). Explicit measurement with :doc:`SysId ` will yield the aforementioned gains in addition to :math:`K_s`. That leaves us needing values for velocity, acceleration, and (in the case of the arm feedforward) position. Typically, these come from our setpoints - remember that with feedforward we are making a "guess" as to the output we need based on where we want the system to be. diff --git a/source/docs/software/advanced-controls/introduction/introduction-to-pid.rst b/source/docs/software/advanced-controls/introduction/introduction-to-pid.rst index 38a4bfff33..d3d590f0e5 100644 --- a/source/docs/software/advanced-controls/introduction/introduction-to-pid.rst +++ b/source/docs/software/advanced-controls/introduction/introduction-to-pid.rst @@ -1,14 +1,12 @@ .. include:: -Introduction to PID -=================== +# Introduction to PID .. note:: For a guide on implementing PID control with WPILib, see :ref:`docs/software/advanced-controls/controllers/pidcontroller:PID Control in WPILib`. This page explains the conceptual and mathematical workings of a PID controller. :ref:`A video explanation from WPI is also available `. -What is a PID Controller? -------------------------- +## What is a PID Controller? The PID controller is a common :ref:`feedback controller` consisting of proportional, integral, and derivative terms, hence the name. This article will build up the definition of a PID controller term by term while trying to provide some intuition for how each term behaves. @@ -23,7 +21,7 @@ The :term:`error` :math:`e(t)` is the difference between the :term:`reference` a For those already familiar with PID control, this interpretation may not be consistent with the classical explanation of the P, I, and D terms corresponding to response to "past", "present", and "future" errors. While that model has merit, we will instead be approaching PID control from the viewpoint of modern control theory, as proportional controllers applied to different physical quantities we care about. This will provide a more complete explanation of the derivative term's behavior for constant and moving :term:`setpoints `. -Roughly speaking: the proportional term drives the position error to zero, the derivative term drives the velocity error to zero, and the integral term drives the total accumulated error-over-time to zero. All three terms are added together to produce the :term:`control signal` We'll go into more detail on each of these below. +Roughly speaking: the proportional term drives the position error to zero, the derivative term drives the velocity error to zero, and the integral term drives the total accumulated error-over-time to zero. All three terms are added together to produce the :term:`control signal`. We'll go into more detail on each of these below. .. note:: Throughout the WPILib documentation, you'll see two ways of writing the tunable constants of the PID controller. @@ -35,8 +33,7 @@ Roughly speaking: the proportional term drives the position error to zero, the d Despite the differences in capitalization, the two formats refer to the same concept. -Proportional Term ------------------ +## Proportional Term The *Proportional* term attempts to drive the position error to zero by contributing to the control signal proportionally to the current position error. Intuitively, this tries to move the :term:`output` towards the :term:`reference`. @@ -58,8 +55,7 @@ Proportional gains act like a "software-defined springs" that pull the :term:`sy so the "force" with which the proportional controller pulls the :term:`system's ` :term:`output` toward the :term:`setpoint` is proportional to the :term:`error`, just like a spring. -Derivative Term ---------------- +## Derivative Term The *Derivative* term attempts to drive the derivative of the error to zero by contributing to the control signal proportionally to the derivative of the error. Intuitively, this tries to make the :term:`output` move at the same rate as the :term:`reference`. @@ -93,8 +89,7 @@ Notice how :math:`\frac{r_k - r_{k-1}}{dt}` is the velocity of the :term:`setpoi If the :term:`setpoint` is constant, the implicit velocity :term:`setpoint` is zero, so the :math:`K_d` term slows the :term:`system` down if it's moving. This acts like a "software-defined damper". These are commonly seen on door closers, and their damping force increases linearly with velocity. -Integral Term -------------- +## Integral Term .. important:: Integral gain is generally not recommended for FRC\ |reg| use. It is almost always better to use a feedforward controller to eliminate steady-state error. If you do employ integral gain, it is crucial to provide some protection against :ref:`integral windup `. @@ -125,8 +120,7 @@ A common way of eliminating :term:`steady-state error` is to integrate the :term :alt: Figure 2.6 and 2.6 graphs :align: center -Putting It All Together ------------------------ +## Putting It All Together .. note:: For information on using the WPILib provided PIDController, see the :ref:`relevant article `. @@ -143,8 +137,7 @@ The below figure shows a block diagram for a PID controller. :alt: Block diagram of a PID controller :align: center -Response Types --------------- +## Response Types A :term:`system` driven by a PID controller generally has three types of responses: underdamped, over-damped, and critically damped. These are shown in figure 2.8. diff --git a/source/docs/software/advanced-controls/introduction/picking-control-strategy.rst b/source/docs/software/advanced-controls/introduction/picking-control-strategy.rst index edb0244760..598a1e72e6 100644 --- a/source/docs/software/advanced-controls/introduction/picking-control-strategy.rst +++ b/source/docs/software/advanced-controls/introduction/picking-control-strategy.rst @@ -1,7 +1,6 @@ -Picking a Control Strategy -========================== +# Picking a Control Strategy -.. note:: This article includes sections of `Controls Engineering in FRC `__ by Tyler Veness with permission. +.. note:: This article includes sections of [Controls Engineering in FRC](https://file.tavsys.net/control/controls-engineering-in-frc.pdf) by Tyler Veness with permission. When designing a control algorithm for a robot mechanism, there are a number of different approaches to take. These range from very simple approaches, to advanced and complex ones. Each has *tradeoffs*. Some will work better than others in different situations, some require more mathematical analysis than others. @@ -17,8 +16,7 @@ There are two fundamental types of mechanism controller that we will cover here: These are not mutually exclusive, and in fact it is usually best to use both. The tutorial pages that follow will cover three types of mechanism (turret, flywheel, and vertical arm), and allow you to experiment with how each type of system responds to each type of control strategy, both individually and combined. -Feedforward Control: Making a Best Guess ----------------------------------------- +## Feedforward Control: Making a Best Guess "Feedforward control" means providing the mechanism with the control signal you think it needs to make the mechanism do what you want, without any knowledge of where the mechanism currently is. A feedforward controller feeds information we already know about the system *forward* into an estimate of the required :term:`control effort`. The feedforward controller does *not* adjust this in response to the measured behavior of the system to try to correct for errors from the guess. @@ -26,15 +24,13 @@ Feedforward control is also sometimes referred to as "open-loop control", becaus This is the type of control you are implicitly using whenever you use a joystick to "directly" control the speed of a motor through the applied voltage. It is the simplest and most straightforward type of control, and is probably the one you encountered first when programming a FRC motor, though it may not have been referred to by name. -When Do We Need Feedforward Control? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### When Do We Need Feedforward Control? In general, feedforward control is *required* whenever the system requires some constant control signal to remain at the desired setpoint (such as position control of a vertical arm where gravity will cause the arm to fall, or velocity control where internal motor dynamics and friction will cause the motor to slow down over time). Feedback controllers naturally fall to zero output when they achieve their setpoint, and so a feedforward controller is needed to provide the signal to *keep* the mechanism where we want it. Some control strategies instead account for this in the feedback controller with integral gain - however, this is slow and prone to oscillation. It is almost always better to use a feedforward controller to account for the output needed to maintain the setpoint. -Feedforward and Position Control -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Feedforward and Position Control The WPILib feedforward classes require velocity and acceleration setpoints to generate an estimated control voltage. This is because the equations-of-motion of a permanent-magnet DC motor relate the applied voltage to velocity and acceleration; it is a fact of physics that we cannot change. @@ -44,8 +40,7 @@ Many teams do not wish to incur the extra technical cost of using a motion profi Most FRC mechanisms are well-described by WPILib's feedforward classes, though pure feedforward control typically only yields acceptable results for velocity control of mechanisms with little external load. In other cases, errors from the system model will be unavoidable and a feedback controller will be necessary to correct for them. -Feedback Control: Correcting for Errors and Disturbances --------------------------------------------------------- +## Feedback Control: Correcting for Errors and Disturbances Even with unlimited study, it is impossible to know every force that will be exerted on a robot's mechanism in perfect detail. For example, in a flywheel shooter, the timing and exact forces associated with a ball being put through the mechanism are extremely difficult to measure accurately. For another example, consider the fact that gearboxes gradually throw off grease as they operate, increasing their internal friction over time. This is a *very* complex process to model well. @@ -53,8 +48,7 @@ In practice, this means that the "guess" made by our feedforward controller will The simplest feedback controller possible is a "proportional controller", which responds proportionally to the current error (i.e. difference between the desired state and measured state). More advanced controllers (such as the PID controller) add response to the rate-of-change of the error and to the total accumulated error. All of these operate on the principle that the system response is roughly linear, in order to "nudge" the system towards the setpoint based on local measurements of the error. -When Do We Need Feedback Control? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### When Do We Need Feedback Control? In general, there are two scenarios in which we *need* feedback control: @@ -63,8 +57,7 @@ In general, there are two scenarios in which we *need* feedback control: In each of these situations, the *best* solution is to combine a feedforward controller and a feedback controller by adding their outputs together. However, in the case of a simple position controller with no external loading, a pure feedback controller can work acceptably. -Feedback-Only Control -~~~~~~~~~~~~~~~~~~~~~ +#### Feedback-Only Control Feedforward controllers are extremely helpful and quite simple, but they require *explicit* knowledge of the system behavior in order to generate a guess at the required control signal. In many controls textbooks, you may see a set of techniques which rely on feedback control only. These are very common in industry, and works well in many cases, especially when the underlying system behavior is not easy to explicitly model, or when you want to quickly reach a "good enough" solution without spending the time to thoroughly investigate your system behavior. @@ -76,42 +69,37 @@ Feedback-only control typically only works well in situations where: When these criteria are met (such as in the turret tuning tutorial), feedback-only control can yield acceptable results. In other situations, it is necessary to use a feedforward model to reduce the amount of work done by the feedback controller. In FRC, our systems are almost all modeled by well-understood equations with working code support, so it is almost always a good idea to include a feedforward controller. -Modeling: How do you expect your system to behave? --------------------------------------------------- +## Modeling: How do you expect your system to behave? It's easiest to control a system if we have some prior knowledge of how the system responds to inputs. Even the "pure feedback" strategy described above implicitly assumes things about the system response (e.g. that it is approximately linear), and consequently won't work in cases where the system does not respond in the expected way. To control our system *optimally*, we need some way to reliably predict how it will respond to inputs. -This can be done by combining several concepts you may be familiar with from physics: drawing free body diagrams of the forces that act on the mechanism, taking measurements of mass and moment of inertia from your CAD models, applying standard equations of how DC motors or pneumatic cylinders convert energy into mechanical force and motion, etc. +This can be done by combining several concepts you may be familiar with from physics: drawing free body diagrams of the forces that act on the mechanism, taking measurements of mass and moment of inertia from your :term:`CAD` models, applying standard equations of how DC motors or pneumatic cylinders convert energy into mechanical force and motion, etc. The act of creating a consistent mathematical description of your system is called *modeling* your system's behavior. The resulting set of equations are called a *model* of how you expect the system to behave. Not every system requires an explicit model to be controlled (we will see in the turret tutorial that a pure, manually-tuned feedback controller is satisfactory *in some cases*), but an explicit model is *always* helpful. Note that models do not have to be perfectly accurate to be useful. As we will see in later tuning exercises, even using a simple model of a mechanism can make the tuning effort much simpler. -Obtaining Models for Your Mechanisms -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Obtaining Models for Your Mechanisms If modeling your mechanism seems daunting, don't worry! Most mechanisms in FRC are modeled by well-studied equations and code for interacting with those models is included in WPILib. Usually, all that is needed is to determine a set of physical parameters (sometimes called "tuning constants" or "gains") that depend on the specific details of your mechanism/robot. These can be estimated theoretically from other known parameters of your system (such as mass, length, and choice of motor/gearbox), or measured from your mechanism's actual behavior through a system identification routine. When in doubt, ask a mentor or :ref:`support resource `! -Theoretical Modeling -^^^^^^^^^^^^^^^^^^^^ +### Theoretical Modeling -`ReCalc is an online calculator `__ which estimates physical parameters for a number of common FRC mechanisms. Importantly, it can generate estimate the ``kV``, ``kA``, and ``kG`` gains for the WPILib feedforward classes. +[ReCalc is an online calculator](https://www.reca.lc/) which estimates physical parameters for a number of common FRC mechanisms. Importantly, it can generate estimate the ``kV``, ``kA``, and ``kG`` gains for the WPILib feedforward classes. -The :doc:`WPILib system identification tool ` supports a "theoretical mode" that can be used to determine PID gains for feedback control from the ``kV`` and ``kA`` gains from ReCalc, enabling (in theory) full tuning of a control loop without running any test routines. +The :doc:`WPILib system identification tool ` supports a "theoretical mode" that can be used to determine PID gains for feedback control from the ``kV`` and ``kA`` gains from ReCalc, enabling (in theory) full tuning of a control loop without running any test routines. Remember, however, that theory is not reality and purely theoretical gains are not guaranteed to work well. There is *never* a substitute for testing. -System Identification -^^^^^^^^^^^^^^^^^^^^^ +### System Identification A good way to improve the accuracy of a simple physics model is to perform experiments on the real mechanism, record data, and use the data to *derive* the constants associated with different parts of the model. This is very useful for physical quantities which are difficult or impossible to predict, but easy to measure (ex: friction in a gearbox). -:doc:`WPILib's system identification tool ` supports some common FRC mechanisms, including drivetrain. It deploys its own code to the robot to exercise the mechanism, record data, and derive gains for both feedforward and feedback control schemes. +:doc:`WPILib's system identification tool ` supports some common FRC mechanisms, including drivetrain. It deploys its own code to the robot to exercise the mechanism, record data, and derive gains for both feedforward and feedback control schemes. -Manual Tuning: What to Do with No Explicit Model -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Manual Tuning: What to Do with No Explicit Model Sometimes, you have to tune a system without at an explicit model. Maybe the system is uniquely complicated, or maybe you're under time constraints and need something that works quickly, even if it doesn't work optimally. Model-based control requires a correct mathematical model of the system, and for better or for worse, we do not always have one. diff --git a/source/docs/software/advanced-controls/introduction/pid-video.rst b/source/docs/software/advanced-controls/introduction/pid-video.rst index 9a3286d28c..edc0226be3 100644 --- a/source/docs/software/advanced-controls/introduction/pid-video.rst +++ b/source/docs/software/advanced-controls/introduction/pid-video.rst @@ -1,5 +1,4 @@ -PID Introduction Video by WPI -============================= +# PID Introduction Video by WPI Have you ever had trouble designing a robot system to move quickly and then stop at exactly a desired position? Challenges like this can arise when driving fixed distances or speeds, operating an arm or elevator, or any other motor controlled system that requires specific motion. In this video, WPI Professor Dmitry Berenson talks about robot controls and how PID controls work. .. raw:: html diff --git a/source/docs/software/advanced-controls/introduction/tuning-flywheel.rst b/source/docs/software/advanced-controls/introduction/tuning-flywheel.rst index 552da33780..bcf8f980b0 100644 --- a/source/docs/software/advanced-controls/introduction/tuning-flywheel.rst +++ b/source/docs/software/advanced-controls/introduction/tuning-flywheel.rst @@ -1,10 +1,8 @@ -Tuning a Flywheel Velocity Controller -===================================== +# Tuning a Flywheel Velocity Controller In this section, we will tune a simple velocity controller for a flywheel. The tuning principles explained here will also work for almost any velocity control scenario. -Flywheel Model Description --------------------------- +## Flywheel Model Description Our "Flywheel" consists of: @@ -24,8 +22,7 @@ Where: .. note:: A more detailed description of the mathematics of the system :ref:`can be found here`. -Picking the Control Strategy for a Flywheel Velocity Controller ---------------------------------------------------------------- +## Picking the Control Strategy for a Flywheel Velocity Controller In general: the more voltage that is applied to the motor, the faster the flywheel will spin. Once voltage is removed, friction and :term:`back-EMF` oppose the motion and bring the flywheel to a stop. @@ -37,8 +34,7 @@ To consistently launch a gamepiece, a good first step is to make sure it is spin The tutorials below will demonstrate the behavior of the system under bang-bang, pure feedforward, pure feedback (PID), and combined feedforward-feedback control strategies. Follow the instructions to learn how to manually tune these controllers, and expand the "tuning solution" to view an optimal model-based set of tuning parameters. -Bang-Bang Control -~~~~~~~~~~~~~~~~~ +#### Bang-Bang Control Interact with the simulation below to see how the flywheel system responds when controlled by a bang-bang controller. @@ -64,8 +60,7 @@ There are no tuneable controller parameters for a bang-bang controller - you can Try adjusting the setpoint up and down. You should see that for almost all values, the output converges to be somewhat near the setpoint. -Common Issues with Bang-Bang Controllers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Common Issues with Bang-Bang Controllers Note that the system behavior is not perfect, because of delays in the control loop. These can result from the nature of the sensors, measurement filters, loop iteration timers, or even delays in the control hardware itself. Collectively, these cause a cycle of "overshoot" and "undershoot", as the output repeatedly goes above and below the setpoint. This oscillation is unavoidable with a bang-bang controller. @@ -77,8 +72,7 @@ Finally, this technique only works for mechanisms that accelerate relatively slo Bang-bang control sacrifices a lot for simplicity and high performance (in the sense of fast convergence to the setpoint). To achieve "smoother" control, we need to consider a different control strategy. -Pure Feedforward Control -~~~~~~~~~~~~~~~~~~~~~~~~ +#### Pure Feedforward Control Interact with the simulation below to see how the flywheel system responds when controlled only by a feedforward controller. @@ -106,8 +100,7 @@ To tune the feedforward controller, increase the velocity feedforward gain :math We can see that a pure feedforward control strategy works reasonably well for flywheel velocity control. As we mentioned earlier, this is why it's possible to control most motors "directly" with joysticks, without any explicit "control loop" at all. However, we can still do better - the pure feedforward strategy cannot reject disturbances, and so takes a while to recover after the ball is introduced. Additionally, the motor may not perfectly obey the feedforward equation (even after accounting for vibration/noise). To account for these, we need a feedback controller. -Pure Feedback Control -~~~~~~~~~~~~~~~~~~~~~ +#### Pure Feedback Control Interact with the simulation below to see how the flywheel system responds when controlled by only a feedback (PID) controller. @@ -139,13 +132,11 @@ Perform the following: In this particular example, for a setpoint of 300, values of :math:`K_p = 0.1`, :math:`K_i = 0.0`, and :math:`K_d = 0.0` will produce somewhat reasonable results. Since this control strategy is not very good, it will not work well for all setpoints. You can attempt to improve this behavior by incorporating some :math:`K_i`, but it is very difficult to achieve good behavior across a wide range of setpoints. -Issues with Feedback Control Alone -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Issues with Feedback Control Alone Because a non-zero amount of :term:`control effort` is required to keep the flywheel spinning, even when the :term:`output` and :term:`setpoint` are equal, this feedback-only strategy is flawed. In order to optimally control a flywheel, a combined feedforward-feedback strategy is needed. -Combined Feedforward and Feedback Control -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Combined Feedforward and Feedback Control Interact with the simulation below to see how the flywheel system responds under simultaneous feedforward and feedback (PID) control. @@ -173,16 +164,13 @@ Tuning the combined flywheel controller is simple - we first tune the feedforwar Note that the combined feedforward-feedback controller works well across all setpoints, and recovers very quickly after the external disturbance of the ball contacting the flywheel. -Tuning Conclusions ------------------- +## Tuning Conclusions -Applicability of Velocity Control -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Applicability of Velocity Control A gamepiece-launching flywheel is one of the most visible applications of velocity control. It is also applicable to drivetrain control - following a pre-defined path in autonomous involves controlling the velocity of the wheels with precision, under a variety of different loads. -Choice of Control Strategies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Choice of Control Strategies Because we are controlling velocity, we can achieve fairly good performance with a :ref:`pure feedforward controller `. This is because a permanent-magnet DC motor's steady-state velocity is roughly proportional to the voltage applied, and is the reason that you can drive your robot around with joysticks without appearing to use any control loop at all - in that case, you are implicitly using a proportional feedforward model. @@ -196,13 +184,11 @@ Tuning with only feedback can produce reasonable results in cases where no :term Adding an integral gain to the :term:`controller` is often a sub-optimal way to eliminate :term:`steady-state error` - you can see how sloppy and "laggy" it is in the simulation above! As we will see soon, a better approach is to combine the PID controller with a feedforward controller. -Velocity and Position Control -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Velocity and Position Control Velocity control also differs from position control in the effect of inertia - in a position controller, inertia tends to cause the mechanism to swing past the setpoint even if the control voltage drops to zero near the setpoint. This makes aggressive control strategies infeasible, as they end up wasting lots of energy fighting self-induced oscillations. In a velocity controller, however, the effect is different - the rotor shaft stops accelerating as soon as you stop applying a control voltage (in fact, it will slow down due to friction and back-EMF), so such overshoots are rare (in fact, overshoot typically occurs in velocity controllers only as a result of loop delay). This enables the use of an extremely simple, extremely aggressive control strategy called :ref:`bang-bang control `. -Feedforward Simplifications -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Feedforward Simplifications For the sake of simplicity, the simulations above omit the :math:`K_s` term from the WPILib SimpleMotorFeedforward equation. On actual mechanisms, however, this can be important - especially if there's a lot of friction in the mechanism gearing. A flywheel with a lot of static friction will not have a linear control voltage-velocity relationship unless the feedforward controller includes a :math:`K_s` term to cancel it out. @@ -211,7 +197,6 @@ To measure :math:`K_s` manually, slowly increase the voltage to the mechanism un Additionally, there is no need for a :math:`K_a` term in the feedforward for velocity control unless the setpoint is changing - for a flywheel, this is not a concern, and so the gain is omitted here. -Footnotes ---------- +## Footnotes .. [1] For this simulation, we model a ball being injected to the flywheel as a velocity-dependant (frictional) torque fighting the spinning of the wheel for one quarter of a wheel rotation, right around the 5 second mark. This is a very simplistic way to model the ball, but is sufficient to illustrate the controller's behavior under a sudden load. It would not be sufficient to predict the ball's trajectory, or the actual "pulldown" in :term:`output` for the system. diff --git a/source/docs/software/advanced-controls/introduction/tuning-turret.rst b/source/docs/software/advanced-controls/introduction/tuning-turret.rst index 63dc63d994..f836d83e18 100644 --- a/source/docs/software/advanced-controls/introduction/tuning-turret.rst +++ b/source/docs/software/advanced-controls/introduction/tuning-turret.rst @@ -1,10 +1,8 @@ -Tuning a Turret Position Controller -=================================== +# Tuning a Turret Position Controller In this section, we will tune a simple position controller for a turret. The tuning principles explained here will also work for almost any position-control scenarios under no external loading. -Turret Model Description ------------------------- +## Turret Model Description A turret rotates some mechanism side-to-side to position it for scoring gamepieces. @@ -24,8 +22,7 @@ Where: * The controller's :term:`setpoint` :math:`r(t)` is the desired position of the turret * The controller's :term:`control effort`, :math:`u(t)` is the voltage applied to the motor driving the turret -Picking the Control Strategy for a Turret Position Controller -------------------------------------------------------------- +## Picking the Control Strategy for a Turret Position Controller In general: the more voltage that is applied to the motor, the faster the motor (and turret) will spin. Once voltage is removed, friction and back-EMF slowly decrease the spinning until the turret stops. We want to make the turret rotate to a given position. @@ -33,8 +30,7 @@ The tutorials below will demonstrate the behavior of the system under pure feedf This simulation does not include any motion profile generation, so acceleration setpoints are not very well-defined. Accordingly, the `kA` term of the feedforward equation is not used by the controller. This means there will be some amount of delay/lag inherent to the feedforward-only response. -Pure Feedforward Control -~~~~~~~~~~~~~~~~~~~~~~~~ +#### Pure Feedforward Control Interact with the simulation below to examine how the turret system responds when controlled only by a feedforward controller. @@ -69,8 +65,7 @@ Note that the turret may "lag" the commanded motion - this is normal, and is fin The exact gain used by the plant is :math:`K_v = 0.2`. Note that due to timing inaccuracy in browser simulations, the :math:`K_v` that works best in the simulation may be somewhat smaller than this. -Issues with Feed-Forward Control Alone -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Issues with Feed-Forward Control Alone As mentioned above, our simulated mechanism perfectly obeys the WPILib :ref:`docs/software/advanced-controls/controllers/feedforward:SimpleMotorFeedforward` equation (as long as the "system noise" option is disabled). We might then expect, like in the case of the :ref:`flywheel velocity controller `, that we should be able to achieve perfect convergence-to-setpoint with a feedforward loop alone. @@ -80,8 +75,7 @@ The resulting behavior from the feedforward controller is to output a single "vo You may notice that *smooth* motion below the turret's maximum achievable speed can be followed accurately in the simulation with feedforward alone. This is misleading, however, because no real mechanism perfectly obeys its feedforward equation. With the "system noise" option enabled, we can see that even smooth, slow motion eventually results in compounding position errors when only feedforward control is used. To accurately converge to the setpoint, we need to use a feedback (PID) controller. -Pure Feedback Control -~~~~~~~~~~~~~~~~~~~~~ +#### Pure Feedback Control Interact with the simulation below to examine how the turret system responds when controlled only by a feedback (PID) controller. @@ -112,13 +106,11 @@ Perform the following: Gains of :math:`K_p = 0.3` and :math:`K_d = 0.05` yield rapid and stable convergence to the setpoint. Other, similar gains will work nearly as well. -Issues with Feedback Control Alone -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Issues with Feedback Control Alone Note that even with system noise enabled, the feedback controller is able to drive the turret to the setpoint in a stable manner over time. However, it may not be possible to smoothly track a moving setpoint without lag using feedback alone, as the feedback controller can only respond to errors once they have built up. To get the best of both worlds, we need to combine our feedback controller with a feedforward controller. -Combined Feedforward and Feedback Control -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Combined Feedforward and Feedback Control Interact with the simulation below to examine how the turret system responds under simultaneous feedforward and feedback control. @@ -146,11 +138,9 @@ Tuning the combined turret controller is simple - we first tune the feedforward Once tuned properly, the combined controller should accurately track a smoothly moving setpoint, and also accurately converge to the setpoint over time after a "jump" command. -Tuning Conclusions ------------------- +## Tuning Conclusions -Choice of Control Strategies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Choice of Control Strategies Like in the case of the :ref:`vertical arm `, and unlike the case of the :ref:`flywheel `, we are trying to control the *position* rather than the *velocity* of our mechanism. @@ -162,13 +152,11 @@ Controlling a mechanism with only feedback can produce reasonable results in cas We saw in the feedforward-only example above that an accurate feedforward can track slow, smooth velocity setpoints quite well. Combining a feedforward controller with the feedback controller gives the smooth velocity-following of a feedforward controller with the stable long-term error elimination of a feedback controller. -Reasons for Non-Ideal Performance -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Reasons for Non-Ideal Performance This simulation does not include any motion profile generation, so acceleration setpoints are not very well-defined. Accordingly, the `kA` term of the feedforward equation is not used by the controller. This means there will be some amount of delay/lag inherent to the feedforward-only response. -A Note on Feedforward and Static Friction ------------------------------------------ +## A Note on Feedforward and Static Friction For the sake of simplicity, the simulations above omit the :math:`K_s` term from the WPILib SimpleMotorFeedforward equation. On actual mechanisms, however, this can be important - especially if there's a lot of friction in the mechanism gearing. A turret with a lot of static friction will be very hard to control accurately with feedback alone - it will get "stuck" near (but not at) the setpoint when the loop output falls below :math:`K_s`. diff --git a/source/docs/software/advanced-controls/introduction/tuning-vertical-arm.rst b/source/docs/software/advanced-controls/introduction/tuning-vertical-arm.rst index 1d56c39568..82f5b52ae0 100644 --- a/source/docs/software/advanced-controls/introduction/tuning-vertical-arm.rst +++ b/source/docs/software/advanced-controls/introduction/tuning-vertical-arm.rst @@ -1,10 +1,8 @@ -Tuning a Vertical Arm Position Controller -========================================= +# Tuning a Vertical Arm Position Controller In this section, we will tune a simple position controller for a vertical arm. The same tuning principles explained below will work also for almost all position-control scenarios under the load of gravity. -Arm Model Description ---------------------- +## Arm Model Description Vertical arms are commonly used to lift gamepieces from the ground up to a scoring position. Other similar examples include shooter hoods and elevators. @@ -24,15 +22,13 @@ Where: * The controller's :term:`setpoint` :math:`r(t)` is the desired angle of the arm * The controller's :term:`control effort`, :math:`u(t)` is the voltage applied to the motor driving the arm -Picking the Control Strategy for a Vertical Arm ------------------------------------------------ +## Picking the Control Strategy for a Vertical Arm Applying voltage to the motor causes a force on the mechanism that drives the arm up or down. If there is no voltage, gravity still acts on the arm to pull it downward. Generally, it is desirable to fight this effect, and keep the arm at a specific angle. The tutorials below will demonstrate the behavior of the system under pure feedforward, pure feedback (PID), and combined feedforward-feedback control strategies. Follow the instructions to learn how to manually tune these controllers, and expand the "tuning solution" to view an optimal model-based set of tuning parameters. Even though WPILib tooling can provide you with optimal gains, it is worth going through the manual tuning process to see how the different control strategies interact with the mechanism. -Pure Feedforward Control -~~~~~~~~~~~~~~~~~~~~~~~~ +#### Pure Feedforward Control Interact with the simulation below to examine how the turret system responds when controlled only by a feedforward controller. @@ -67,8 +63,7 @@ To tune the feedforward controller, perform the following: The exact gains used by the simulation are :math:`K_g = 1.75` and :math:`K_v = 1.95`. -Issues with Feed-Forward Control Alone -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Issues with Feed-Forward Control Alone As mentioned above, our simulated mechanism almost-perfectly obeys the WPILib :ref:`docs/software/advanced-controls/controllers/feedforward:ArmFeedforward` equation (as long as the "system noise" option is disabled). We might then expect, like in the case of the :ref:`flywheel velocity controller `, that we should be able to achieve perfect convergence-to-setpoint with a feedforward loop alone. @@ -78,8 +73,7 @@ The resulting behavior from the feedforward controller is to output a single "vo You will notice that, once properly tuned, the mechanism can track slow/smooth movement with a surprising amount of accuracy - however, there are some obvious problems with this approach. Our feedforward equation corrects for the force of gravity *at the setpoint* - this results in poor behavior if our arm is far from the setpoint. With the "system noise" option enabled, we can also see that even smooth, slow motion eventually results in compounding position errors when only feedforward control is used. To accurately converge to and remain at the setpoint, we need to use a feedback (PID) controller. -Pure Feedback Control -~~~~~~~~~~~~~~~~~~~~~ +#### Pure Feedback Control Interact with the simulation below to examine how the vertical arm system responds when controlled only by a feedback (PID) controller. @@ -112,8 +106,7 @@ Perform the following: There is no good tuning solution for this control strategy. Values of :math:`K_p = 5` and :math:`K_d = 1` yield a reasonable approach to a stable equilibrium, but that equilibrium is not actually at the setpoint! -Issues with Feedback Control Alone -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Issues with Feedback Control Alone A set of gains that works well for one setpoint will act poorly for a different setpoint. @@ -121,8 +114,7 @@ Adding some integral gain can push us to the setpoint over time, but it's unstab Because a non-zero amount of :term:`control effort` is required to keep the arm at a constant height, even when the :term:`output` and :term:`setpoint` are equal, this feedback-only strategy is flawed. In order to optimally control a vertical arm, a combined feedforward-feedback strategy is needed. -Combined Feedforward and Feedback Control -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Combined Feedforward and Feedback Control Interact with the simulation below to examine how the vertical arm system responds under simultaneous feedforward and feedback control. @@ -150,11 +142,9 @@ Tuning the combined arm controller is simple - we first tune the feedforward con Once tuned properly, the combined controller accurately tracks a smoothly moving setpoint, and also accurately converge to the setpoint over time after a "jump" command. -Tuning Conclusions ------------------- +## Tuning Conclusions -Choice of Control Strategies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Choice of Control Strategies Like in the case of the :ref:`turret `, and unlike the case of the :ref:`flywheel `, we are trying to control the *position* rather than the *velocity* of our mechanism. @@ -167,16 +157,14 @@ The core reason the feedback-only control strategy fails for the vertical arm is We saw in the feedforward-only example above that an accurate feedforward can track slow, smooth velocity setpoints quite well. Combining a feedforward controller with the feedback controller gives the smooth velocity-following of a feedforward controller with the stable long-term error elimination of a feedback controller. -Reasons for Non-Ideal Performance -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Reasons for Non-Ideal Performance This simulation does not include any motion profile generation, so acceleration setpoints are not very well-defined. Accordingly, the `kA` term of the feedforward equation is not used by the controller. This means there will be some amount of delay/lag inherent to the feedforward-only response. The control law is good, but not perfect. There is usually some overshoot even for smoothly-moving setpoints - this is combination of the lack of :math:`K_a` in the feedforward (see the note above for why it is omitted here), and some discretization error in the simulation. Attempting to move the setpoint too quickly can also cause the setpoint and mechanism to diverge, which (as mentioned earlier) will result in poor behavior due to the :math:'K_g' term correcting for the wrong force, as it is calculated from the setpoint, not the measurement. Using the measurement to correct for gravity is called "feedback linearization" (as opposed to "feedforward linearization" when the setpoint is used), and can be a better control strategy if your measurements are sufficiently fast and accurate. -A Note on Feedforward and Static Friction ------------------------------------------ +## A Note on Feedforward and Static Friction For the sake of simplicity, the simulations above omit the :math:`K_s` term from the WPILib SimpleMotorFeedforward equation. On actual mechanisms, however, this can be important - especially if there's a lot of friction in the mechanism gearing. -In the case of a vertical arm or elevator, :math:`K_s` can be somewhat tedious to estimate separately from :math:`K_g`. If your arm or elevator has enough friction for :math:`K_s` to be important, it is recommended that you use the :doc:`WPILib system identification tool ` to determine your system gains. +In the case of a vertical arm or elevator, :math:`K_s` can be somewhat tedious to estimate separately from :math:`K_g`. If your arm or elevator has enough friction for :math:`K_s` to be important, it is recommended that you use the :doc:`WPILib system identification tool ` to determine your system gains. diff --git a/source/docs/software/advanced-controls/introduction/tutorial-intro.rst b/source/docs/software/advanced-controls/introduction/tutorial-intro.rst index 1712cc5889..1989d6a0a2 100644 --- a/source/docs/software/advanced-controls/introduction/tutorial-intro.rst +++ b/source/docs/software/advanced-controls/introduction/tutorial-intro.rst @@ -1,5 +1,4 @@ -Introduction To Controls Tuning Tutorials -========================================= +# Introduction To Controls Tuning Tutorials The WPILib docs include three interactive tuning simulations. Their goal is to allow students to learn how tuning parameters impact system behavior, without having to deal with software bugs or other real-world behavior. @@ -9,22 +8,19 @@ Ultimately, students should use the examples to build intuition and make their t This page details a few tips while working with the tutorials. -Parameter Exponential Search ----------------------------- +## Parameter Exponential Search While interacting with the simulations, you will get instructions to "increase" or "decrease" different parameters. When "increasing" a value, multiply it by two until the expected effect is observed. After the first time the value becomes too large (i.e. the behavior is unstable or the mechanism overshoots), reduce the value to halfway between the first too-large value encountered and the previous value tested before that. Continue iterating this "split-half" procedure to zero in on the optimal value (if the response undershoots, pick the halfway point between the new value and the last value immediately above it - if it overshoots, pick the halfway point between the new value and the last value immediately below it). This is called an :term:`exponential search`, and is a very efficient way to find positive values of unknown scale. -System Noise ------------- +## System Noise The "system noise" option introduces random, gaussian error into the plant to provide a more realistic situation of system behavior. Leave the setting turned off at first to learn the system's ideal behavior. Later, turn it on to see how your tuning works in the presence of real-world effects. -Be Systematic -------------- +## Be Systematic As seen in :ref:`the introduction to PID `, a PID controller has *three* tuned constants.Feedforward components will add even more. This means searching for the "correct" constants manually can be quite difficult - it is therefore necessary to approach the tuning procedure systematically. diff --git a/source/docs/software/advanced-controls/state-space/images/elevator-phase-portrait.png b/source/docs/software/advanced-controls/state-space/images/elevator-phase-portrait.png index 97a5647baa..e56bdb12f3 100644 Binary files a/source/docs/software/advanced-controls/state-space/images/elevator-phase-portrait.png and b/source/docs/software/advanced-controls/state-space/images/elevator-phase-portrait.png differ diff --git a/source/docs/software/advanced-controls/state-space/images/filter_comparison.png b/source/docs/software/advanced-controls/state-space/images/filter_comparison.png index b05e5f547a..595f59b947 100644 Binary files a/source/docs/software/advanced-controls/state-space/images/filter_comparison.png and b/source/docs/software/advanced-controls/state-space/images/filter_comparison.png differ diff --git a/source/docs/software/advanced-controls/state-space/images/normal-distribution.png b/source/docs/software/advanced-controls/state-space/images/normal-distribution.png index 6f1dfff822..31249ca6ce 100644 Binary files a/source/docs/software/advanced-controls/state-space/images/normal-distribution.png and b/source/docs/software/advanced-controls/state-space/images/normal-distribution.png differ diff --git a/source/docs/software/advanced-controls/state-space/images/pendulum-balance.png b/source/docs/software/advanced-controls/state-space/images/pendulum-balance.png index 92e2d585df..041119262c 100644 Binary files a/source/docs/software/advanced-controls/state-space/images/pendulum-balance.png and b/source/docs/software/advanced-controls/state-space/images/pendulum-balance.png differ diff --git a/source/docs/software/advanced-controls/state-space/images/pendulum-closed-loop.png b/source/docs/software/advanced-controls/state-space/images/pendulum-closed-loop.png index c71f0b2164..c5261bb289 100644 Binary files a/source/docs/software/advanced-controls/state-space/images/pendulum-closed-loop.png and b/source/docs/software/advanced-controls/state-space/images/pendulum-closed-loop.png differ diff --git a/source/docs/software/advanced-controls/state-space/images/pendulum-phase-plot.png b/source/docs/software/advanced-controls/state-space/images/pendulum-phase-plot.png index 02e1076e43..28fb446ac1 100644 Binary files a/source/docs/software/advanced-controls/state-space/images/pendulum-phase-plot.png and b/source/docs/software/advanced-controls/state-space/images/pendulum-phase-plot.png differ diff --git a/source/docs/software/advanced-controls/state-space/images/state-space-graph.png b/source/docs/software/advanced-controls/state-space/images/state-space-graph.png index bb9526bb4f..014a4e7f8b 100644 Binary files a/source/docs/software/advanced-controls/state-space/images/state-space-graph.png and b/source/docs/software/advanced-controls/state-space/images/state-space-graph.png differ diff --git a/source/docs/software/advanced-controls/state-space/index.rst b/source/docs/software/advanced-controls/state-space/index.rst index f8a9223476..a3961244e8 100644 --- a/source/docs/software/advanced-controls/state-space/index.rst +++ b/source/docs/software/advanced-controls/state-space/index.rst @@ -1,5 +1,4 @@ -State-Space and Model Based Control with WPILib -=============================================== +# State-Space and Model Based Control with WPILib This section provides an introduction to and describes WPILib support for state-space control. diff --git a/source/docs/software/advanced-controls/state-space/state-space-debugging.rst b/source/docs/software/advanced-controls/state-space/state-space-debugging.rst index ffd0f2a26d..4d3929b82f 100644 --- a/source/docs/software/advanced-controls/state-space/state-space-debugging.rst +++ b/source/docs/software/advanced-controls/state-space/state-space-debugging.rst @@ -1,16 +1,13 @@ -Debugging State-Space Models and Controllers -============================================ +# Debugging State-Space Models and Controllers -Checking Signs --------------- +## Checking Signs One of the most common causes of bugs with state-space controllers is signs being flipped. For example, models included in WPILib expect positive voltage to result in a positive acceleration, and vice versa. If applying a positive voltage does not make the mechanism accelerate forwards, or if moving "forwards" makes encoder (or other sensor readings) decrease, they should be inverted so that positive voltage input results in a positive encoder reading. For example, if I apply an :term:`input` of :math:`[12, 12]^T` (full forwards for the left and right motors) to my differential drivetrain, my wheels should propel my robot "forwards" (along the +X axis locally), and for my encoders to read a positive velocity. .. important:: The WPILib ``DifferentialDrive``, by default, does not invert any motors. You may need to call the ``setInverted(true)`` method on the motor controller object to invert so that positive input creates forward motion. -The Importance of Graphs ------------------------- +## The Importance of Graphs Reliable data of the :term:`system's ` :term:`state`\s, :term:`input`\s and :term:`output`\s over time is important when debugging state-space controllers and observers. One common approach is to send this data over NetworkTables and use tools such as :ref:`Shuffleboard `, which allow us to both graph the data in real-time as well as save it to a CSV file for plotting later with tools such as Google Sheets, Excel or Python. @@ -20,19 +17,24 @@ Reliable data of the :term:`system's ` :term:`state`\s, :term:`input`\s .. tab-set-code:: - .. code-block:: java - - @Override - public void robotPeriodic() { - NetworkTableInstance.getDefault().flush(); - } - - .. code-block:: c++ - - void RobotPeriodic() { - NetworkTableInstance::GetDefault().Flush(); - } - -Compensating for Input Lag --------------------------- + ```java + @Override + public void robotPeriodic() { + NetworkTableInstance.getDefault().flush(); + } + ``` + + ```c++ + void RobotPeriodic() { + NetworkTableInstance::GetDefault().Flush(); + } + ``` + + ```python + from ntcore import NetworkTableInstance + def robotPeriodic(self): + NetworkTableInstance.getDefault().flush() + ``` + +## Compensating for Input Lag Often times, some sensor input data (i.e. velocity readings) may be delayed due to onboard filtering that smart motor controllers tend to perform. By default, LQR's K gain assumes no input delay, so introducing significant delay on the order of tens of milliseconds can cause instability. To combat this, the LQR's K gain can be reduced, trading off performance for stability. A code example for how to compensate for this latency in a mathematically rigorous manner is available :ref:`here `. diff --git a/source/docs/software/advanced-controls/state-space/state-space-flywheel-walkthrough.rst b/source/docs/software/advanced-controls/state-space/state-space-flywheel-walkthrough.rst index 8a71ab0a61..2088ce0c70 100644 --- a/source/docs/software/advanced-controls/state-space/state-space-flywheel-walkthrough.rst +++ b/source/docs/software/advanced-controls/state-space/state-space-flywheel-walkthrough.rst @@ -1,27 +1,24 @@ .. include:: -State-Space Controller Walkthrough -================================== +# State-Space Controller Walkthrough .. note:: Before following this tutorial, readers are recommended to have read an :ref:`docs/software/advanced-controls/state-space/state-space-intro:Introduction to state-space control`. The goal of this tutorial is to provide "end-to-end" instructions on implementing a state-space controller for a flywheel. By following this tutorial, readers will learn how to: -1. Create an accurate state-space model of a flywheel using :term:`system identification` or CAD software. +1. Create an accurate state-space model of a flywheel using :term:`system identification` or :term:`CAD` software. 2. Implement a Kalman Filter to filter encoder velocity measurements without lag. 3. Implement a :ref:`LQR ` feedback controller which, when combined with model-based feedforward, will generate voltage :term:`inputs ` to drive the flywheel to a :term:`reference`. This tutorial is intended to be approachable for teams without a great deal of programming expertise. While the WPILib library offers significant flexibility in the manner in which its state-space control features are implemented, closely following the implementation outlined in this tutorial should provide teams with a basic structure which can be reused for a variety of state-space systems. -The full example is available in the state-space flywheel (`Java `__/`C++ `__) and state-space flywheel system identification (`Java `__/`C++ `__) example projects. +The full example is available in the state-space flywheel ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java)/[C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp)/[Python](https://github.com/robotpy/examples/blob/main/StateSpaceFlywheel/robot.py)) and state-space flywheel system identification ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java)/[C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp)/[Python](https://github.com/robotpy/examples/blob/main/StateSpaceFlywheelSysId/robot.py)) example projects. -Why Use State-Space Control? ----------------------------- +## Why Use State-Space Control? Because state-space control focuses on creating an accurate model of our system, we can accurately predict how our :term:`model` will respond to control :term:`inputs `. This allows us to simulate our mechanisms without access to a physical robot, as well as easily choose :term:`gains ` that we know will work well. Having a model also allows us to create lagless filters, such as Kalman Filters, to optimally filter sensor readings. -Modeling Our Flywheel ---------------------- +## Modeling Our Flywheel :ref:`Recall ` that continuous state-space systems are modeled using the following system of equations: @@ -43,8 +40,7 @@ A continuous-time state-space system writes :term:`x-dot`, or the instantaneous Next, we will model our flywheel as a continuous-time state-space system. WPILib's ``LinearSystem`` will convert this to discrete-time internally. Review :ref:`state-space notation ` for more on continuous-time and discrete-time systems. -Modeling with System Identification -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Modeling with System Identification To rewrite this in state-space notation using :term:`system identification`, we recall from the flywheel :ref:`state-space notation example `, where we rewrote the following equation in terms of :math:`\mathbf{a}`. @@ -66,38 +62,51 @@ The second part of state-space notation relates the system's current :term:`stat The ``LinearSystem`` class contains methods for easily creating state-space systems identified using :term:`system identification`. This example shows a flywheel model with a kV of 0.023 and a kA of 0.001: .. tab-set:: - .. tab-item:: JAVA + .. tab-item:: Java :sync: java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java :language: java - :lines: 33-47 + :lines: 32-46 :linenos: :lineno-start: 33 .. tab-item:: C++ - :sync: cpp + :sync: c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp + :language: c++ :lines: 17 :linenos: :lineno-start: 17 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp + :language: c++ :lines: 30-46 :linenos: :lineno-start: 30 + .. tab-item:: Python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheelSysId/robot.py + :language: python + :lines: 23-27 + :linenos: + :lineno-start: 23 + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheelSysId/robot.py + :language: python + :lines: 37-48 + :linenos: + :lineno-start: 37 -Modeling Using Flywheel Moment of Inertia and Gearing -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A flywheel can also be modeled without access to a physical robot, using information about the motors, gearing and flywheel's :term:`moment of inertia`. A full derivation of this model is presented in Section 8.2.1 of `Controls Engineering in FRC `__. +### Modeling Using Flywheel Moment of Inertia and Gearing -The ``LinearSystem`` class contains methods to easily create a model of a flywheel from the flywheel's motors, gearing and :term:`moment of inertia`. The moment of inertia can be calculated using CAD software or using physics. The examples used here are detailed in the flywheel example project (`Java `__/`C++ `__). +A flywheel can also be modeled without access to a physical robot, using information about the motors, gearing and flywheel's :term:`moment of inertia`. A full derivation of this model is presented in Section 12.3 of [Controls Engineering in FRC](https://file.tavsys.net/control/controls-engineering-in-frc.pdf). + +The ``LinearSystem`` class contains methods to easily create a model of a flywheel from the flywheel's motors, gearing and :term:`moment of inertia`. The moment of inertia can be calculated using :term:`CAD` software or using physics. The examples used here are detailed in the flywheel example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/v2023.2.1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel)/[C++](https://github.com/wpilibsuite/allwpilib/blob/v2023.2.1/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp)/[Python](https://github.com/robotpy/examples/blob/main/StateSpaceFlywheel/robot.py)). .. note:: For WPILib's state-space classes, gearing is written as output over input -- that is, if the flywheel spins slower than the motors, this number should be greater than one. @@ -105,32 +114,46 @@ The ``LinearSystem`` class contains methods to easily create a model of a flywhe .. tab-set:: - .. tab-item:: JAVA + .. tab-item:: Java :sync: java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java :language: java - :lines: 34-47 + :lines: 33-46 :linenos: - :lineno-start: 34 + :lineno-start: 33 .. tab-item:: C++ - :sync: cpp + :sync: c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp + :language: c++ :lines: 17-17 :linenos: :lineno-start: 17 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp + :language: c++ :lines: 17,31-46 :linenos: :lineno-start: 31 -Kalman Filters: Observing Flywheel State ----------------------------------------- + .. tab-item:: Python + :sync: python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py + :language: python + :lines: 21-25 + :linenos: + :lineno-start: 21 + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py + :language: python + :lines: 37-46 + :linenos: + :lineno-start: 37 + +## Kalman Filters: Observing Flywheel State Kalman filters are used to filter our velocity measurements using our state-space model to generate a state estimate :math:`\mathbf{\hat{x}}`. As our flywheel model is linear, we can use a Kalman filter to estimate the flywheel's velocity. WPILib's Kalman filter takes a ``LinearSystem`` (which we found above), along with standard deviations of model and sensor measurements. We can adjust how "smooth" our state estimate is by adjusting these weights. Larger state standard deviations will cause the filter to "distrust" our state estimate and favor new measurements more highly, while larger measurement standard deviations will do the opposite. @@ -145,36 +168,44 @@ Because the feedback controller computes error using the :term:`x-hat` estimated .. tab-set:: - .. tab-item:: JAVA + .. tab-item:: Java :sync: java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java :language: java - :lines: 49-59 + :lines: 48-58 :linenos: - :lineno-start: 59 + :lineno-start: 48 .. tab-item:: C++ - :sync: cpp + :sync: c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp + :language: c++ :lines: 13-13 :linenos: :lineno-start: 13 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp + :language: c++ :lines: 48-53 :linenos: :lineno-start: 48 + .. tab-item:: Python + :sync: python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py + :language: python + :lines: 48-54 + :linenos: + :lineno-start: 48 + Because Kalman filters use our state-space model in the :ref:`docs/software/advanced-controls/state-space/state-space-observers:Predict step`, it is important that our model is as accurate as possible. One way to verify this is to record a flywheel's input voltage and velocity over time, and replay this data by calling only ``predict`` on the Kalman filter. Then, the kV and kA gains (or moment of inertia and other constants) can be adjusted until the model closely matches the recorded data. .. todo:: do we need to elaborate on this^ more? -Linear-Quadratic Regulators and Plant Inversion Feedforward ------------------------------------------------------------ +## Linear-Quadratic Regulators and Plant Inversion Feedforward :ref:`docs/software/advanced-controls/state-space/state-space-intro:The Linear-Quadratic Regulator` finds a feedback controller to drive our flywheel :term:`system` to its :term:`reference`. Because our flywheel has just one state, the control law picked by our LQR will be in the form :math:`\mathbf{u = K (r - x)}` where :math:`\mathbf{K}` is a 1x1 matrix; in other words, the control law picked by LQR is simply a proportional controller, or a PID controller with only a P gain. This gain is chosen by our LQR based on the state excursion and control efforts we pass it. More on tuning LQR controllers can be found in the :ref:`LQR application example `. @@ -182,104 +213,136 @@ Much like ``SimpleMotorFeedforward`` can be used to generate feedforward voltage .. tab-set:: - .. tab-item:: JAVA + .. tab-item:: Java :sync: java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java :language: java - :lines: 60-71 + :lines: 59-70 :linenos: - :lineno-start: 60 + :lineno-start: 59 .. tab-item:: C++ - :sync: cpp + :sync: c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp + :language: c++ :lines: 11 :linenos: :lineno-start: 11 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp + :language: c++ :lines: 55-75 :linenos: :lineno-start: 54 -Bringing it All Together: LinearSystemLoop ------------------------------------------- + .. tab-item:: Python + :sync: python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py + :language: python + :lines: 56-66 + :linenos: + :lineno-start: 56 + +## Bringing it All Together: LinearSystemLoop LinearSystemLoop combines our system, controller, and observer that we created earlier. The constructor shown will also instantiate a ``PlantInversionFeedforward``. .. tab-set:: - .. tab-item:: JAVA + .. tab-item:: Java :sync: java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java :language: java - :lines: 73-75 + :lines: 72-74 :linenos: - :lineno-start: 73 + :lineno-start: 72 .. tab-item:: C++ - :sync: cpp + :sync: c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp + :language: c++ :lines: 15-15 :linenos: :lineno-start: 15 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp + :language: c++ :lines: 71-74 :linenos: :lineno-start: 71 + .. tab-item:: Python + :sync: python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py + :language: python + :lines: 68-71 + :linenos: + :lineno-start: 68 + Once we have our ``LinearSystemLoop``, the only thing left to do is actually run it. To do that, we'll periodically update our Kalman filter with our new encoder velocity measurements and apply new voltage commands to it. To do that, we first set the :term:`reference`, then ``correct`` with the current flywheel speed, ``predict`` the Kalman filter into the next timestep, and apply the inputs generated using ``getU``. .. tab-set:: - .. tab-item:: JAVA + .. tab-item:: Java :sync: java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java :language: java - :lines: 96-121 + :lines: 95-120 :linenos: - :lineno-start: 96 + :lineno-start: 95 .. tab-item:: C++ - :sync: cpp + :sync: c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp + :language: c++ :lines: 5-17 :linenos: :lineno-start: 5 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp + :language: c++ :lines: 92-114 :linenos: :lineno-start: 92 -Angle Wrap with LQR -------------------- + .. tab-item:: Python + :sync: python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py + :language: python + :lines: 87-109 + :linenos: + :lineno-start: 87 + +## Angle Wrap with LQR Mechanisms with a continuous angle can have that angle wrapped by calling the code below instead of ``lqr.Calculate(x, r)``. .. tab-set-code:: - .. code-block:: java + ```java + var error = lqr.getR().minus(x); + error.set(0, 0, MathUtil.angleModulus(error.get(0, 0))); + var u = lqr.getK().times(error); + ``` - var error = lqr.getR().minus(x); - error.set(0, 0, MathUtil.angleModulus(error.get(0, 0))); - var u = lqr.getK().times(error); + ```c++ + Eigen::Vector error = lqr.R() - x; + error(0) = frc::AngleModulus(units::radian_t{error(0)}).value(); + Eigen::Vector u = lqr.K() * error; + ``` - .. code-block:: cpp + ```python + error = lqr.R() - x + error[0] = wpimath.angleModulus(error[0]) + u = lqr.K() * error + ``` - Eigen::Vector error = lqr.R() - x; - error(0) = frc::AngleModulus(units::radian_t{error(0)}).value(); - Eigen::Vector u = lqr.K() * error; diff --git a/source/docs/software/advanced-controls/state-space/state-space-intro.rst b/source/docs/software/advanced-controls/state-space/state-space-intro.rst index a943e30dc0..57c29ac63a 100644 --- a/source/docs/software/advanced-controls/state-space/state-space-intro.rst +++ b/source/docs/software/advanced-controls/state-space/state-space-intro.rst @@ -1,31 +1,26 @@ -Introduction to State-Space Control -=================================== +# Introduction to State-Space Control -.. note:: This article is from `Controls Engineering in FRC `__ by Tyler Veness with permission. +.. note:: This article is from [Controls Engineering in FRC](https://file.tavsys.net/control/controls-engineering-in-frc.pdf) by Tyler Veness with permission. -From PID to Model-Based Control -------------------------------- +## From PID to Model-Based Control When tuning PID controllers, we focus on fiddling with controller parameters relating to the current, past, and future :term:`error` (P, I and D terms) rather than the underlying system states. While this approach works in a lot of situations, it is an incomplete view of the world. Model-based control focuses on developing an accurate model of the :term:`system` (mechanism) we are trying to control. These models help inform :term:`gains ` picked for feedback controllers based on the physical responses of the system, rather than an arbitrary proportional :term:`gain` derived through testing. This allows us not only to predict ahead of time how a system will react, but also test our controllers without a physical robot and save time debugging simple bugs. -.. note:: State-space control makes extensive use of linear algebra. More on linear algebra in modern control theory, including an introduction to linear algebra and resources, can be found in Chapter 4 of `Controls Engineering in FRC `__. +.. note:: State-space control makes extensive use of linear algebra. More on linear algebra in modern control theory, including an introduction to linear algebra and resources, can be found in Chapter 5 of [Controls Engineering in FRC](https://file.tavsys.net/control/controls-engineering-in-frc.pdf). If you've used WPILib's feedforward classes for ``SimpleMotorFeedforward`` or its sister classes, or used SysId to pick PID :term:`gains ` for you, you're already familiar with model-based control! The ``kv`` and ``ka`` :term:`gains ` can be used to describe how a motor (or arm, or drivetrain) will react to voltage. We can put these constants into standard state-space notation using WPILib's ``LinearSystem``, something we will do in a later article. -Vocabulary ----------- +## Vocabulary For the background vocabulary that will be used throughout this article, see the :ref:`Glossary `. -Introduction to Linear Algebra ------------------------------- +## Introduction to Linear Algebra -For a short and intuitive introduction to the core concepts of Linear Algebra, we recommend chapters 1 through 4 of `3Blue1Brown's Essence of linear algebra series `__ (Vectors, what even are they?, Linear combinations, span, and basis vectors, Linear transformations and matrices, and Matrix multiplication as composition). +For a short and intuitive introduction to the core concepts of Linear Algebra, we recommend chapters 1 through 4 of [3Blue1Brown's Essence of linear algebra series](https://www.youtube.com/watch?v=fNk_zzaMoSs&list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab) (Vectors, what even are they?, Linear combinations, span, and basis vectors, Linear transformations and matrices, and Matrix multiplication as composition). -What is State-Space? --------------------- +## What is State-Space? Recall that 2D space has two axes: x and y. We represent locations within this space as a pair of numbers packaged in a vector, and each coordinate is a measure of how far to move along the corresponding axis. State-space is a :term:`Cartesian coordinate system` with an axis for each state variable, and we represent locations within it the same way we do for 2D space: with a list of numbers in a vector. Each element in the vector corresponds to a state of the system. This example shows two example state vectors in the state-space of an elevator model with the states :math:`[\text{position}, \text{velocity}]`: @@ -36,8 +31,7 @@ In this image, the vectors representing states in state-space are arrows. From n In addition to the :term:`state`, :term:`inputs ` and :term:`outputs ` are represented as vectors. Since the mapping from the current states and inputs to the change in state is a system of equations, it’s natural to write it in matrix form. This matrix equation can be written in state-space notation. -What is State-Space Notation? ------------------------------ +## What is State-Space Notation? State-space notation is a set of matrix equations which describe how a system will evolve over time. These equations relate the change in state :math:`\dot{\mathbf{x}}`, and the :term:`output` :math:`\mathbf{y}`, to linear combinations of the current state vector :math:`\mathbf{x}` and :term:`input` vector :math:`\mathbf{u}`. @@ -76,8 +70,7 @@ A continuous-time state-space system can be converted into a discrete-time syste .. important:: WPILib's LinearSystem takes continuous-time system matrices, and converts them internally to the discrete-time form where necessary. -State-space Notation Example: Flywheel from Kv and Ka -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### State-space Notation Example: Flywheel from Kv and Ka :ref:`Recall ` that we can model the motion of a flywheel connected to a brushed DC motor with the equation :math:`V = K_v \cdot v + K_a \cdot a`, where V is voltage output, v is the flywheel's angular velocity and a is its angular acceleration. This equation can be rewritten as :math:`a = \frac{V - K_v \cdot v}{K_a}`, or :math:`a = \frac{-K_v}{K_a} \cdot v + \frac{1}{K_a} \cdot V`. Notice anything familiar? This equation relates the angular acceleration of the flywheel to its angular velocity and the voltage applied. @@ -93,8 +86,7 @@ The output and state are the same, so the output equation is the following: That's it! That's the state-space model of a system for which we have the :math:`K_v` and :math:`K_a` constants. This same math is used in system identification to model flywheels and drivetrain velocity systems. -Visualizing State-Space Responses: Phase Portrait -------------------------------------------------- +## Visualizing State-Space Responses: Phase Portrait A :term:`phase portrait` can help give a visual intuition for the response of a system in state-space. The vectors on the graph have their roots at some point :math:`\mathbf{x}` in state-space, and point in the direction of :math:`\mathbf{\dot{x}}`, the direction that the system will evolve over time. This example shows a model of a pendulum with the states of angle and angular velocity. @@ -105,10 +97,9 @@ To trace a potential trajectory that a system could take through state-space, ch Note that near the edges of the phase portrait, the X axis wraps around as a rotation of :math:`\pi` radians counter clockwise and a rotation of :math:`\pi` radians clockwise will end at the same point. -For more on differential equations and phase portraits, see `3Blue1Brown's Differential Equations video `__ -- they do a great job of animating the pendulum phase space at around 15:30. +For more on differential equations and phase portraits, see [3Blue1Brown's Differential Equations video](https://www.youtube.com/watch?v=p_di4Zn4wz4) -- they do a great job of animating the pendulum phase space at around 15:30. -Visualizing Feedforward -^^^^^^^^^^^^^^^^^^^^^^^ +### Visualizing Feedforward This phase portrait shows the "open loop" responses of the system -- that is, how it will react if we were to let the state evolve naturally. If we want to, say, balance the pendulum horizontal (at :math:`(\frac{\pi}{2}, 0)` in state space), we would need to somehow apply a control :term:`input` to counteract the open loop tendency of the pendulum to swing downward. This is what feedforward is trying to do -- make it so that our phase portrait will have an equilibrium at the :term:`reference` position (or setpoint) in state-space. @@ -117,8 +108,7 @@ Looking at our phase portrait from before, we can see that at :math:`(\frac{\pi} .. image:: images/pendulum-balance.png :alt: Pendulum phase plot with equilibrium at (pi/2, 0). -Feedback Control -~~~~~~~~~~~~~~~~ +#### Feedback Control In the case of a DC motor, with just a mathematical model and knowledge of all current states of the system (i.e., angular velocity), we can predict all future states given the future voltage inputs. But if the system is disturbed in any way that isn’t modeled by our equations, like a load or unexpected friction, the angular velocity of the motor will deviate from the model over time. To combat this, we can give the motor corrective commands using a feedback controller. @@ -138,13 +128,11 @@ To add some feedback, we arbitrarily pick a :math:`\mathbf{K}` of [2, 2], where But how can we choose an optimal :term:`gain` matrix K for our system? While we can manually choose :term:`gains ` and simulate the system response or tune it on-robot like a PID controller, modern control theory has a better answer: the Linear-Quadratic Regulator (LQR). -The Linear-Quadratic Regulator -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### The Linear-Quadratic Regulator Because model-based control means that we can predict the future states of a system given an initial condition and future control inputs, we can pick a mathematically optimal :term:`gain` matrix :math:`\mathbf{K}`. To do this, we first have to define what a "good" or "bad" :math:`\mathbf{K}` would look like. We do this by summing the square of error and control input over time, which gives us a number representing how "bad" our control law will be. If we minimize this sum, we will have arrived at the optimal control law. -LQR: Definition -~~~~~~~~~~~~~~~ +#### LQR: Definition Linear-Quadratic Regulators work by finding a :term:`control law` that minimizes the following cost function, which weights the sum of :term:`error` and :term:`control effort` over time, subject to the linear :term:`system` dynamics :math:`\mathbf{x_{k+1} = Ax_k + Bu_k}`. @@ -155,8 +143,7 @@ The :term:`control law` that minimizes :math:`\mathbf{J}` can be written as :mat .. note:: LQR design's :math:`\mathbf{Q}` and :math:`\mathbf{R}` matrices don't need discretization, but the :math:`\mathbf{K}` calculated for continuous-time and discrete time :term:`systems ` will be different. -LQR: tuning -~~~~~~~~~~~ +#### LQR: tuning Like PID controllers can be tuned by adjusting their gains, we also want to change how our control law balances our error and input. For example, a spaceship might want to minimize the fuel it expends to reach a given reference, while a high-speed robotic arm might need to react quickly to disturbances. @@ -173,34 +160,48 @@ For example, we might use the following Q and R for an elevator system with posi .. tab-set-code:: - .. code-block:: Java - - // Example system -- must be changed to match your robot. - LinearSystem elevatorSystem = LinearSystemId.identifyPositionSystem(5, 0.5); - LinearQuadraticRegulator controller = new LinearQuadraticRegulator(elevatorSystem, - // q's elements - VecBuilder.fill(0.02, 0.4), - // r's elements - VecBuilder.fill(12.0), - // our dt - 0.020); - - - .. code-block:: C++ - - // Example system -- must be changed to match your robot. - LinearSystem<2, 1, 1> elevatorSystem = frc::LinearSystemId::IdentifyVelocitySystem(5, 0.5); - LinearQuadraticRegulator<2, 1> controller{ - elevatorSystem, - // q's elements - {0.02, 0.4}, - // r's elements - {12.0}, - // our dt - 0.020_s}; - -LQR: example application -^^^^^^^^^^^^^^^^^^^^^^^^ + ```Java + // Example system -- must be changed to match your robot. + LinearSystem elevatorSystem = LinearSystemId.identifyPositionSystem(5, 0.5); + LinearQuadraticRegulator controller = new LinearQuadraticRegulator(elevatorSystem, + // q's elements + VecBuilder.fill(0.02, 0.4), + // r's elements + VecBuilder.fill(12.0), + // our dt + 0.020); + ``` + + ```C++ + // Example system -- must be changed to match your robot. + LinearSystem<2, 1, 1> elevatorSystem = frc::LinearSystemId::IdentifyVelocitySystem(5, 0.5); + LinearQuadraticRegulator<2, 1> controller{ + elevatorSystem, + // q's elements + {0.02, 0.4}, + // r's elements + {12.0}, + // our dt + 0.020_s}; + ``` + + ```python + from wpimath.controller import LinearQuadraticRegulator_2_1 + from wpimath.system.plant import LinearSystemId + # Example system -- must be changed to match your robot. + elevatorSystem = LinearSystemId.identifyPositionSystemMeters(5, 0.5) + controller = LinearQuadraticRegulator_2_1( + elevatorSystem, + # q's elements + (0.02, 0.4), + # r's elements + (12.0,), + # our dt + 0.020, + ) + ``` + +### LQR: example application Let's apply a Linear-Quadratic Regulator to a real-world example. Say we have a flywheel velocity system determined through system identification to have :math:`K_v = 1 \frac{\text{volts}}{\text{radian per second}}` and :math:`K_a = 1.5 \frac{\text{volts}}{\text{radian per second squared}}`. Using the flywheel example above, we have the following linear :term:`system`: @@ -216,8 +217,7 @@ The following graph shows the flywheel's angular velocity and applied voltage ov .. image:: images/flywheel-lqr-ex.jpg :alt: Flywheel velocity and voltage over time. -LQR and Measurement Latency Compensation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### LQR and Measurement Latency Compensation Oftentimes, our sensors have a delay associated with their measurements. For example the SPARK MAX motor controller over CAN can have up to 30ms of delay associated with velocity measurements. @@ -240,22 +240,28 @@ Multiplying :math:`\mathbf{K}` by :math:`\mathbf{A} - \mathbf{BK}` essentially a The code below shows how to adjust the LQR controller's K gain for sensor input delays: .. tab-set-code:: - .. code-block:: java - - // Adjust our LQR's controller for 25 ms of sensor input delay. We - // provide the linear system, discretization timestep, and the sensor - // input delay as arguments. - controller.latencyCompensate(elevatorSystem, 0.02, 0.025); - - .. code-block:: c++ - - // Adjust our LQR's controller for 25 ms of sensor input delay. We - // provide the linear system, discretization timestep, and the sensor - // input delay as arguments. - controller.LatencyCompensate(elevatorSystem, 20_ms, 25_ms); - -Linearization -------------- + ```java + // Adjust our LQR's controller for 25 ms of sensor input delay. We + // provide the linear system, discretization timestep, and the sensor + // input delay as arguments. + controller.latencyCompensate(elevatorSystem, 0.02, 0.025); + ``` + + ```c++ + // Adjust our LQR's controller for 25 ms of sensor input delay. We + // provide the linear system, discretization timestep, and the sensor + // input delay as arguments. + controller.LatencyCompensate(elevatorSystem, 20_ms, 25_ms); + ``` + + ```python + # Adjust our LQR's controller for 25 ms of sensor input delay. We + # provide the linear system, discretization timestep, and the sensor + # input delay as arguments. + controller.latencyCompensate(elevatorSystem, 0.020, 0.025) + ``` + +## Linearization Linearization is a tool used to approximate nonlinear functions and state-space systems using linear ones. In two-dimensional space, linear functions are straight lines while nonlinear functions curve. A common example of a nonlinear function and its corresponding linear approximation is :math:`y=\sin{x}`. This function can be approximated by :math:`y=x` near zero. This approximation is accurate while near :math:`x=0`, but looses accuracy as we stray further from the linearization point. For example, the approximation :math:`\sin{x} \approx x` is accurate to within 0.02 within 0.5 radians of :math:`y = 0`, but quickly loses accuracy past that. In the following picture, we see :math:`y =\sin{x}`, :math:`y=x` and the difference between the approximation and the true value of :math:`\sin{x}` at :math:`x`. diff --git a/source/docs/software/advanced-controls/state-space/state-space-observers.rst b/source/docs/software/advanced-controls/state-space/state-space-observers.rst index 027356bca8..170211cb70 100644 --- a/source/docs/software/advanced-controls/state-space/state-space-observers.rst +++ b/source/docs/software/advanced-controls/state-space/state-space-observers.rst @@ -1,5 +1,4 @@ -State Observers and Kalman Filters -================================== +# State Observers and Kalman Filters State observers combine information about a system's behavior and external measurements to estimate the true :term:`state` of the system. A common observer used for linear systems is the Kalman Filter. Kalman filters are advantageous over other :ref:`filters ` as they fuse measurements from one or more sensors with a state-space model of the system to optimally estimate a system's state. @@ -8,8 +7,7 @@ This image shows flywheel velocity measurements over time, run through a variety .. image:: images/filter_comparison.png :alt: Filter comparison between: Kalman, Median, and IIR. -Gaussian Functions ------------------- +## Gaussian Functions Kalman filters utilize a :term:`Gaussian distribution` to model the noise in a process [1]_. In the case of a Kalman filter, the estimated :term:`state` of the system is the mean, while the variance is a measure of how certain (or uncertain) the filter is about the true :term:`state`. @@ -28,10 +26,9 @@ Covariance matrices are written in the following form: \text{cov}(x_n, x_1) & \text{cov}(x_n, x_2) & \ldots & \text{cov}(x_n, x_n) \\ \end{bmatrix} -Kalman Filters --------------- +## Kalman Filters -.. important:: It is important to develop an intuition for what a Kalman filter is actually doing. The book `Kalman and Bayesian Filters in Python by Roger Labbe `__ provides a great visual and interactive introduction to Bayesian filters. The Kalman filters in WPILib use linear algebra to gentrify the math, but the ideas are similar to the single-dimensional case. We suggest reading through Chapter 4 to gain an intuition for what these filters are doing. +.. important:: It is important to develop an intuition for what a Kalman filter is actually doing. The book [Kalman and Bayesian Filters in Python by Roger Labbe](https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python) provides a great visual and interactive introduction to Bayesian filters. The Kalman filters in WPILib use linear algebra to gentrify the math, but the ideas are similar to the single-dimensional case. We suggest reading through Chapter 4 to gain an intuition for what these filters are doing. To summarize, Kalman filters (and all Bayesian filters) have two parts: prediction and correction. Prediction projects our state estimate forward in time according to our system's dynamics, and correct steers the estimated state towards the measured state. While filters often perform both in the same timestep, it's not strictly necessary -- For example, WPILib's pose estimators call predict frequently, and correct only when new measurement data is available (for example, from a low-framerate vision system). @@ -63,8 +60,7 @@ The following shows the equations of a discrete-time Kalman filter: The state estimate :math:`\mathbf{x}`, together with :math:`\mathbf{P}`, describe the mean and covariance of the Gaussian function that describes our filter's estimate of the system's true state. -Process and Measurement Noise Covariance Matrices -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Process and Measurement Noise Covariance Matrices The process and measurement noise covariance matrices :math:`\mathbf{Q}` and :math:`\mathbf{R}` describe the variance of each of our states and measurements. Remember that for a Gaussian function, variance is the square of the function's standard deviation. In a WPILib, Q and R are diagonal matrices whose diagonals contain their respective variances. For example, a Kalman filter with states :math:`\begin{bmatrix}\text{position} \\ \text{velocity} \end{bmatrix}` and measurements :math:`\begin{bmatrix}\text{position} \end{bmatrix}` with state standard deviations :math:`\begin{bmatrix}0.1 \\ 1.0\end{bmatrix}` and measurement standard deviation :math:`\begin{bmatrix}0.01\end{bmatrix}` would have the following :math:`\mathbf{Q}` and :math:`\mathbf{R}` matrices: @@ -72,54 +68,57 @@ The process and measurement noise covariance matrices :math:`\mathbf{Q}` and :ma Q = \begin{bmatrix}0.01 & 0 \\ 0 & 1.0\end{bmatrix}, R = \begin{bmatrix}0.0001\end{bmatrix} -Error Covariance Matrix -^^^^^^^^^^^^^^^^^^^^^^^ +### Error Covariance Matrix The error covariance matrix :math:`\mathbf{P}` describes the covariance of the state estimate :math:`\mathbf{\hat{x}}`. Informally, :math:`\mathbf{P}` describes our certainty about the estimated :term:`state`. If :math:`\mathbf{P}` is large, our uncertainty about the true state is large. Conversely, a :math:`\mathbf{P}` with smaller elements would imply less uncertainty about our true state. As we project the model forward, :math:`\mathbf{P}` increases as our certainty about the system's true state decreases. -Predict step ------------- +## Predict step In prediction, our state estimate is updated according to the linear system dynamics :math:`\mathbf{\dot{x} = Ax + Bu}`. Furthermore, our error covariance :math:`\mathbf{P}` increases by the process noise covariance matrix :math:`\mathbf{Q}`. Larger values of :math:`\mathbf{Q}` will make our error covariance :math:`\mathbf{P}` grow more quickly. This :math:`\mathbf{P}` is used in the correction step to weight the model and measurements. -Correct step ------------- +## Correct step In the correct step, our state estimate is updated to include new measurement information. This new information is weighted against the state estimate :math:`\mathbf{\hat{x}}` by the Kalman gain :math:`\mathbf{K}`. Large values of :math:`\mathbf{K}` more highly weight incoming measurements, while smaller values of :math:`\mathbf{K}` more highly weight our state prediction. Because :math:`\mathbf{K}` is related to :math:`\mathbf{P}`, larger values of :math:`\mathbf{P}` will increase :math:`\mathbf{K}` and more heavily weight measurements. If, for example, a filter is predicted for a long duration, the large :math:`\mathbf{P}` would heavily weight the new information. Finally, the error covariance :math:`\mathbf{P}` decreases to increase our confidence in the state estimate. -Tuning Kalman Filters ---------------------- +## Tuning Kalman Filters WPILib's Kalman Filter classes' constructors take a linear system, a vector of process noise standard deviations and measurement noise standard deviations. These are converted to :math:`\mathbf{Q}` and :math:`\mathbf{R}` matrices by filling the diagonals with the square of the standard deviations, or variances, of each state or measurement. By decreasing a state's standard deviation (and therefore its corresponding entry in :math:`\mathbf{Q}`), the filter will distrust incoming measurements more. Similarly, increasing a state's standard deviation will trust incoming measurements more. The same holds for the measurement standard deviations -- decreasing an entry will make the filter more highly trust the incoming measurement for the corresponding state, while increasing it will decrease trust in the measurement. .. tab-set:: .. tab-item:: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java :language: java - :lines: 49-58 + :lines: 48-57 :linenos: - :lineno-start: 49 + :lineno-start: 48 .. tab-item:: C++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp + :language: c++ :lines: 5-18 :linenos: :lineno-start: 5 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp + :language: c++ :lines: 48-53 :linenos: :lineno-start: 48 -Footnotes ---------- + .. tab-item:: Python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py + :language: python + :lines: 48-54 + :linenos: + :lineno-start: 48 + +## Footnotes .. [1] In a real robot, noise comes from all sorts of sources. Stray electromagnetic radiation adds extra voltages to sensor readings, vibrations and temperature variations throw off inertial measurement units, gear lash causes encoders to have inaccuracies when directions change... all sorts of things. It's important to realize that, by themselves, each of these sources of "noise" aren't guaranteed to follow any pattern. Some of them might be the "white noise" random vibrations you've probably heard on the radio. Others might be "pops" or single-loop errors. Others might be nominally zero, but strongly correlated with events on the robot. However, the :term:`Central Limit Theorem` shows mathematically that regardless of how the individual sources of noise are distributed, as we add more and more of them up their combined effect eventually is distributed like a Gaussian. Since we do not know the exact individual sources of noise, the best choice of a model we can make is indeed that Gaussian function. diff --git a/source/docs/software/advanced-controls/state-space/state-space-pose-estimators.rst b/source/docs/software/advanced-controls/state-space/state-space-pose-estimators.rst index c047cffb59..4eb65cdd33 100644 --- a/source/docs/software/advanced-controls/state-space/state-space-pose-estimators.rst +++ b/source/docs/software/advanced-controls/state-space/state-space-pose-estimators.rst @@ -1,5 +1,4 @@ -Pose Estimators -=============== +# Pose Estimators WPILib includes pose estimators for differential, swerve and mecanum drivetrains. These estimators are designed to be drop-in replacements for the existing :ref:`odometry ` classes that also support fusing latency-compensated robot pose estimates with encoder and gyro measurements. These estimators can account for encoder drift and noisy vision data. These estimators can behave identically to their corresponding odometry classes if only ``update`` is called on these estimators. @@ -10,13 +9,13 @@ Here's how to initialize a ``DifferentialDrivePoseEstimator``: .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-4/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java :language: java :lines: 86-94 :linenos: :lineno-start: 86 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-4/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h :language: c++ :lines: 158-165 :linenos: @@ -26,13 +25,13 @@ Add odometry measurements every loop by calling ``Update()``. .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-4/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java :language: java :lines: 227-228 :linenos: :lineno-start: 227 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-4/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp :language: c++ :lines: 84-86 :linenos: @@ -42,20 +41,19 @@ Add vision pose measurements occasionally by calling ``AddVisionMeasurement()``. .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-4/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java :language: java - :lines: 236-241 + :lines: 236-245 :linenos: :lineno-start: 236 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-4/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/cpp/Drivetrain.cpp :language: c++ :lines: 93-106 :linenos: :lineno-start: 93 -Tuning Pose Estimators ----------------------- +## Tuning Pose Estimators All pose estimators offer user-customizable standard deviations for model and measurements (defaults are used if you don't provide them). Standard deviation is a measure of how spread out the noise is for a random signal. Giving a state a smaller standard deviation means it will be trusted more during data fusion. diff --git a/source/docs/software/pathplanning/system-identification/additional-utils.rst b/source/docs/software/advanced-controls/system-identification/additional-utils.rst similarity index 55% rename from source/docs/software/pathplanning/system-identification/additional-utils.rst rename to source/docs/software/advanced-controls/system-identification/additional-utils.rst index 8a31d8cb26..a2780c8e35 100644 --- a/source/docs/software/pathplanning/system-identification/additional-utils.rst +++ b/source/docs/software/advanced-controls/system-identification/additional-utils.rst @@ -1,29 +1,12 @@ -Additional Utilities and Tools -============================== +# Additional Utilities and Tools This page mainly covers useful information about additional functionality that this tool provides. - -JSON Converters ---------------- - -There are a two JSON Utility tools that can be used in the :guilabel:`JSON Converters` tab: FRC-Char Converter and JSON to CSV Converter. - -.. image:: images/json-converters.png - :alt: Picture of the json converters - -The FRC-Char Converter reads in an FRC-Char JSON and converts it into a SysId JSON that the tool can read. - -The JSON to CSV Converter takes a SysId JSON and outputs a CSV file. If the JSON had Drivetrain Mechanism data, the columns are: ``Timestamp (s)``, ``Test``, ``Left Volts (V)`` , ``Right Volts (V)``, ``Left Position ({0})``, ``Right Position ({units})``, ``Left Velocity ({units}/s)``, ``Right Velocity ({units}/s)``, ``Gyro Position (deg)``, ``Gyro Rate (deg/s)``. -If the JSON had General Mechanism data, the CSV has the following columns: ``Timestamp (s)``, ``Test``, ``Volts (V)``, ``Position({units})``, ``Velocity ({units}/s)``. - -ImGui Tips ----------- +## ImGui Tips The following are essentially handy features that come with the ImGui framework that SysId uses: -Showing and Hiding Plot Data -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Showing and Hiding Plot Data To add or remove certain data from the plots, click on the color of the data that you would like to hide or remove. For example, if we want to hide sim data, we can click the green color box. @@ -34,8 +17,7 @@ For example, if we want to hide sim data, we can click the green color box. .. image:: images/post-sim-hide.png :alt: Picture of a plot after sim is hidden -Auto Sizing Plots -^^^^^^^^^^^^^^^^^ +### Auto Sizing Plots If you zoom in to plots and want to revert back to the normally sized plots, just double click on the plot and it will automatically resize it. Here is a plot that is zoomed in: @@ -48,8 +30,7 @@ After double clicking, it is automatically resized: .. image:: images/resized-plot.png :alt: Picture of the previously zoomed in plot that is resized -Setting Slider Values -^^^^^^^^^^^^^^^^^^^^^ +### Setting Slider Values To set the value of a slider as a number rather than sliding the widget, you can CTRL + Click the slider and it will allow you to input a number. Here is a regular slider: diff --git a/source/docs/software/pathplanning/system-identification/analyzing-feedback.rst b/source/docs/software/advanced-controls/system-identification/analyzing-gains.rst similarity index 86% rename from source/docs/software/pathplanning/system-identification/analyzing-feedback.rst rename to source/docs/software/advanced-controls/system-identification/analyzing-gains.rst index 7ed5a84138..0da6f2284c 100644 --- a/source/docs/software/pathplanning/system-identification/analyzing-feedback.rst +++ b/source/docs/software/advanced-controls/system-identification/analyzing-gains.rst @@ -1,7 +1,24 @@ .. include:: -Feedback Analysis -================= +# Analyzing Data + +## Feedforward Analysis + +.. note:: For information on what the calculated feedback gains mean, see :ref:`docs/software/advanced-controls/introduction/introduction-to-feedforward:The Permanent-Magnet DC Motor Feedforward Equation`. For information on using the calculated feedback gains in code, see :ref:`feedforward control `. + +Click the dropdown arrow on the :guilabel:`Feedforward` Section. + +.. note:: If you would like to change units, you will have to press the :guilabel:`Override Units` button and fill out the information on the popup. + +.. image:: images/feedback-analysis.png + :alt: Analyzing data for feedforward + +The computed mechanism system parameters will then be displayed. + +.. image:: images/feedforward-values.png + :alt: Analysis coefficient results + +## Feedback Analysis .. important:: These gains are, in effect, "educated guesses" - they are not guaranteed to be perfect, and should be viewed as a "starting point" for further tuning. @@ -12,8 +29,7 @@ To view the feedback constants, click on the dropdown arrow on the :guilabel:`Fe This view can be used to calculate optimal feedback gains for a PD or P controller for your mechanism (via :term:`LQR`). -Enter Controller Parameters ---------------------------- +## Enter Controller Parameters .. note:: The "Spark Max" preset assumes that the user has configured the controller to operate in the units of analysis with the SPARK MAX API's position/velocity scaling factor feature. @@ -36,8 +52,7 @@ To specify the correct settings for your PID controller, use the following optio .. note:: If you select a smart motor controller as the preset (e.g. TalonSRX, SPARK MAX, etc.) the :guilabel:`Convert Gains` checkbox will be automatically checked. This means the tool will convert your gains so that they can be used through the smart motor controller's PID methods. Therefore, if you would like to use WPILib's PID Loops, you must uncheck that box. -Measurement Delays -^^^^^^^^^^^^^^^^^^ +### Measurement Delays .. note:: If you are using default smart motor controller settings or WPILib PID Control without additional filtering, SysId handles this for you. @@ -51,8 +66,7 @@ The following only applies if the user decides to implement their own custom fil Where ``T`` is the period at which measurements are sampled (RIO default is 20 ms) and ``n`` is the size of the moving window used. -Specify Optimality Criteria ---------------------------- +## Specify Optimality Criteria Finally, the user must specify what will be considered an "optimal" controller. This takes the form of desired tolerances for the system error and control effort - note that it is *not* guaranteed that the system will obey these tolerances at all times. @@ -63,8 +77,7 @@ As a rule, smaller values for the :guilabel:`Max Acceptable Error` and larger va The :guilabel:`Max Acceptable Control Effort` should never exceed 12V, as that corresponds to full battery voltage, and ideally should be somewhat lower than this. -Select Loop Type ----------------- +## Select Loop Type It is typical to control mechanisms with both position and velocity PIDs, depending on application. Either can be selected using the drop-down :guilabel:`Loop Type` menu. diff --git a/source/docs/software/advanced-controls/system-identification/creating-routine.rst b/source/docs/software/advanced-controls/system-identification/creating-routine.rst new file mode 100644 index 0000000000..ea904c860e --- /dev/null +++ b/source/docs/software/advanced-controls/system-identification/creating-routine.rst @@ -0,0 +1,68 @@ +# Creating an Identification Routine + +## Types of Tests + +A standard motor identification routine consists of two types of tests: + +- **Quasistatic:** In this test, the mechanism is gradually sped-up such that the voltage corresponding to acceleration is negligible (hence, "as if static"). +- **Dynamic:** In this test, a constant 'step voltage' is given to the mechanism, so that the behavior while accelerating can be determined. + +Each test type is run both forwards and backwards, for four tests in total. The tests can be run in any order, but running a "backwards" test directly after a "forwards" test is generally advisable (as it will more or less reset the mechanism to its original position). ``SysIdRoutine`` provides command factories that may be used to run the tests, for example as part of an autonomous routine. Previous versions of SysId used a project generator to create and deploy robot code to run these tests, but it proved to be very fragile and difficult to maintain. The user code-based workflow enables teams to use mechanism code they already know works, including soft and hard limits. + +## User Code Setup + +.. note:: Some familiarity with your language's units library is recommended and knowing how to use Consumers is required. This page assumes you are using the Commands framework. + +To assist in creating SysId-compatible identification routines, WPILib provides the ``SysIdRoutine`` class. Users should create a ``SysIdRoutine`` object, which take both a ``Config`` object describing the test settings and a ``Mechanism`` object describing how the routine will control the relevant motors and log the measurements needed to perform the fit. + +### Routine Config + +The ``Config`` object takes in a a voltage ramp rate for use in Quasistatic tests, a steady state step voltage for use in Dynamic tests, a time to use as the maximum test duration for safety reasons, and a callback method that accepts the current test state (such as "dynamic-forward") for use by a 3rd party logging solution. The constructor may be left blank to default the ramp rate to 1 volt per second and the step voltage to 7 volts. + +.. note:: Not all 3rd party loggers will interact with SysIdRoutine directly. CTRE users who do not wish to use SysIdRoutine directly for logging should use the [SignalLogger](https://pro.docs.ctr-electronics.com/en/latest/docs/api-reference/api-usage/signal-logging.html) API and use Tuner X to convert to wpilog. REV users may use Team 6328's [Unofficial REV-Compatible Logger (URCL)](https://docs.advantagescope.org/more-features/urcl). In both cases the log callback should be set to ``null``. Once the log file is in hand, it may be used with SysId just like any other. + +The timeout and state callback are optional and defaulted to 10 seconds and null (which will log the data to a normal WPILog file) respectively. + +### Declaring the Mechanism + +The ``Mechanism`` object takes a voltage consumer, a log consumer, the subsystem being characterized, and the name of the mechanism (to record in the log). The drive callback takes in the routine-generated voltage command and passes it to the relevant motors. The log callback reads the motor voltage, position, and velocity for each relevant motor and adds it to the running log. The subsystem is required so that it may be added to the requirements of the routine commands. The name is optional and will be defaulted to the string returned by getName(). + +The callbacks can either be created in-place via Lambda expressions or can be their own standalone functions and be passed in via method references. Best practice is to create the routine and callbacks inside the subsystem, to prevent leakage. + +.. tab-set-code:: + + ```java + // Creates a SysIdRoutine + SysIdRoutine routine = new SysIdRoutine( + new SysIdRoutine.Config(), + new SysIdRoutine.Mechanism(this::voltageDrive, this::logMotors, this) + ); + ``` + +### Mechanism Callbacks + +The ``Mechanism`` callbacks are essentially just plumbing between the routine and your motors and sensors. + +The ``drive`` callback exists so that you can pass the requested voltage directly to your motor controller(s). + +The ``log`` callback reads sensors so that the routine can log the voltage, position, and velocity at each timestep. + +See the SysIdRoutine ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/sysid), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/SysId)) example project for example callbacks. + +### Test Factories + +To be able to run the tests, SysIdRoutine exposes test "factories", or functions that each return a command that will execute a given test. + +.. tab-set-code:: + + ```java + public Command sysIdQuasistatic(SysIdRoutine.Direction direction) { + return routine.quasistatic(direction); + } + + public Command sysIdDynamic(SysIdRoutine.Direction direction) { + return routine.dynamic(direction); + } + ``` + +Either bind the factory methods to either controller buttons or create an autonomous routine with them. It is recommended to bind them to buttons that the user must hold down for the duration of the test so that the user can stop the routine quickly if it exceeds safe limits. diff --git a/source/docs/software/advanced-controls/system-identification/images/accel-vs-vel-plot.png b/source/docs/software/advanced-controls/system-identification/images/accel-vs-vel-plot.png new file mode 100644 index 0000000000..10c76adb41 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/accel-vs-vel-plot.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/controller-settings.png b/source/docs/software/advanced-controls/system-identification/images/controller-settings.png new file mode 100644 index 0000000000..5bdb24e2c1 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/controller-settings.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/dynamic-test-too-long.png b/source/docs/software/advanced-controls/system-identification/images/dynamic-test-too-long.png new file mode 100644 index 0000000000..854b7e98d7 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/dynamic-test-too-long.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/entries-selected.png b/source/docs/software/advanced-controls/system-identification/images/entries-selected.png new file mode 100644 index 0000000000..a9f088dc2a Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/entries-selected.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/feedback-analysis.png b/source/docs/software/advanced-controls/system-identification/images/feedback-analysis.png new file mode 100644 index 0000000000..c5becfd671 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/feedback-analysis.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/feedforward-analysis.png b/source/docs/software/advanced-controls/system-identification/images/feedforward-analysis.png new file mode 100644 index 0000000000..e6d7579429 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/feedforward-analysis.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/feedforward-values.png b/source/docs/software/advanced-controls/system-identification/images/feedforward-values.png new file mode 100644 index 0000000000..d1105abcb7 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/feedforward-values.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/hide-sim-data.png b/source/docs/software/advanced-controls/system-identification/images/hide-sim-data.png new file mode 100644 index 0000000000..ed49358246 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/hide-sim-data.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/high-threshold.png b/source/docs/software/advanced-controls/system-identification/images/high-threshold.png new file mode 100644 index 0000000000..f0928514fc Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/high-threshold.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/input-slider.png b/source/docs/software/advanced-controls/system-identification/images/input-slider.png new file mode 100644 index 0000000000..e7b9c41f65 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/input-slider.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/log-loaded.png b/source/docs/software/advanced-controls/system-identification/images/log-loaded.png new file mode 100644 index 0000000000..d246c429ef Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/log-loaded.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/looptype.png b/source/docs/software/advanced-controls/system-identification/images/looptype.png new file mode 100644 index 0000000000..d801d482cb Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/looptype.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/low-threshold.png b/source/docs/software/advanced-controls/system-identification/images/low-threshold.png new file mode 100644 index 0000000000..0c2074c665 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/low-threshold.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/motionthreshold-selector.png b/source/docs/software/advanced-controls/system-identification/images/motionthreshold-selector.png new file mode 100644 index 0000000000..9ee192da2d Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/motionthreshold-selector.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/optimality-criteria.png b/source/docs/software/advanced-controls/system-identification/images/optimality-criteria.png new file mode 100644 index 0000000000..318e5e44ea Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/optimality-criteria.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/plot-view.png b/source/docs/software/advanced-controls/system-identification/images/plot-view.png new file mode 100644 index 0000000000..5f8be4b6dc Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/plot-view.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/post-sim-hide.png b/source/docs/software/advanced-controls/system-identification/images/post-sim-hide.png new file mode 100644 index 0000000000..f3f37f473a Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/post-sim-hide.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/regular-slider.png b/source/docs/software/advanced-controls/system-identification/images/regular-slider.png new file mode 100644 index 0000000000..ae753579f1 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/regular-slider.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/resized-plot.png b/source/docs/software/advanced-controls/system-identification/images/resized-plot.png new file mode 100644 index 0000000000..78f198b672 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/resized-plot.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/scaling-units.png b/source/docs/software/advanced-controls/system-identification/images/scaling-units.png new file mode 100644 index 0000000000..b82ce9f3aa Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/scaling-units.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/selecting-data-file.png b/source/docs/software/advanced-controls/system-identification/images/selecting-data-file.png new file mode 100644 index 0000000000..c17d056d50 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/selecting-data-file.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/state-selected.png b/source/docs/software/advanced-controls/system-identification/images/state-selected.png new file mode 100644 index 0000000000..5df21b47f2 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/state-selected.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/test-duration-slider.png b/source/docs/software/advanced-controls/system-identification/images/test-duration-slider.png new file mode 100644 index 0000000000..0c274af56c Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/test-duration-slider.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/time-domain-plots.png b/source/docs/software/advanced-controls/system-identification/images/time-domain-plots.png new file mode 100644 index 0000000000..3097318e80 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/time-domain-plots.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/velo-noise.png b/source/docs/software/advanced-controls/system-identification/images/velo-noise.png new file mode 100644 index 0000000000..574624c34f Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/velo-noise.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/viewing-metrics.png b/source/docs/software/advanced-controls/system-identification/images/viewing-metrics.png new file mode 100644 index 0000000000..8170d45b66 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/viewing-metrics.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/window-size-selector.png b/source/docs/software/advanced-controls/system-identification/images/window-size-selector.png new file mode 100644 index 0000000000..d7e065da55 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/window-size-selector.png differ diff --git a/source/docs/software/advanced-controls/system-identification/images/zoomed-in-plot.png b/source/docs/software/advanced-controls/system-identification/images/zoomed-in-plot.png new file mode 100644 index 0000000000..9c981ee865 Binary files /dev/null and b/source/docs/software/advanced-controls/system-identification/images/zoomed-in-plot.png differ diff --git a/source/docs/software/advanced-controls/system-identification/index.rst b/source/docs/software/advanced-controls/system-identification/index.rst new file mode 100644 index 0000000000..d72434b53b --- /dev/null +++ b/source/docs/software/advanced-controls/system-identification/index.rst @@ -0,0 +1,12 @@ +# System Identification + +.. toctree:: + :maxdepth: 1 + + introduction + creating-routine + running-routine + loading-data + viewing-diagnostics + analyzing-gains + additional-utils diff --git a/source/docs/software/pathplanning/system-identification/introduction.rst b/source/docs/software/advanced-controls/system-identification/introduction.rst similarity index 59% rename from source/docs/software/pathplanning/system-identification/introduction.rst rename to source/docs/software/advanced-controls/system-identification/introduction.rst index 20fb2455bc..6802687211 100644 --- a/source/docs/software/pathplanning/system-identification/introduction.rst +++ b/source/docs/software/advanced-controls/system-identification/introduction.rst @@ -1,17 +1,14 @@ .. include:: -Introduction to System Identification -===================================== +# Introduction to System Identification -What is "System Identification?" --------------------------------- +## What is "System Identification?" In Control Theory, :term:`system identification` is the process of determining a mathematical model for the behavior of a system through statistical analysis of its inputs and outputs. This model is a rule describing how input voltage affects the way our measurements (typically encoder data) evolve in time. A "system identification" routine takes such a model and a dataset and attempts to fit parameters which would make your model most closely-match the dataset. Generally, the model is not perfect - the real-world data are polluted by both measurement noise (e.g. timing errors, encoder resolution limitations) and system noise (unmodeled forces acting on the system, like vibrations). However, even an imperfect model is usually "good enough" to give us accurate :ref:`feedforward control ` of the mechanism, and even to estimate optimal gains for :ref:`feedback control `. -Assumed Behavioral Model ------------------------- +## Assumed Behavioral Model If you haven't yet, read the full explanation of the feedforward equations used by the WPILib toolsuite in :ref:`docs/software/advanced-controls/introduction/introduction-to-feedforward:The Permanent-Magnet DC Motor Feedforward Equation`. @@ -23,71 +20,52 @@ Once these coefficients have been determined, we can then take a given desired v Some of the tools in this toolsuite introduce additional terms into the above equation to account for known differences from the simple case described above - details for each tool can be found below: -The WPILib System Identification Tool (SysId) ---------------------------------------------- +## The WPILib System Identification Tool (SysId) -The WPILib system identification tool consists of an application that runs on the user's PC and matching robot code that runs on the user's robot. The PC application will send control signals to the robot over NetworkTables, while the robot sends data back to the application. The application then processes the data and determines model parameters for the user's robot mechanism, as well as producing diagnostic plots. Data can be saved (in JSON format) for future use, if desired. +The WPILib system identification tool consists of the SysId application that runs on the user's PC and a routine that lives in the code running on the user's robot. The routine will generate control signals which user-defined callbacks will send to the motors being characterized, while the robot records data into a log file. After the routine completes, the user will retrieve this file from the roboRIO and load it into SysId. SysId then processes the data and determines model parameters for the user's robot mechanism, as well as producing diagnostic plots. -Included Tools -^^^^^^^^^^^^^^ +### Included Tools .. note:: With a bit of ingenuity, these tools can be used to accurately characterize a surprisingly large variety of robot mechanisms. Even if your mechanism does not seem to obviously match any of the tools, an understanding of the system equations often reveals that one of the included routines will do. The System Identification toolsuite currently supports: - Simple Motor Setups -- Drivetrains - Elevators - Arms -Several of these options use identical robot-side code, and differ only in the analysis routine used to interpret the data. +Several of these options use nearly identical robot-side code, and differ only in the analysis used by SysId to interpret the data. -Simple Motor Identification -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Simple Motor Identification The simple motor identification tool determines the best-fit parameters for the equation: -.. math:: V = kS \cdot sgn(\dot{d}) + kV \cdot \dot{d} + kA \cdot \ddot{d} +.. math:: V = K_s \cdot sgn(\dot{d}) + K_v \cdot \dot{d} + K_a \cdot \ddot{d} where :math:`V` is the applied voltage, :math:`d` is the displacement (position) of the drive, :math:`\dot{d}` is its velocity, and :math:`\ddot{d}` is its acceleration. This is the model for a permanent-magnet dc motor with no loading other than friction and inertia, as mentioned above, and is an accurate model for flywheels, turrets, and horizontal linear sliders. -Drivetrain Identification -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The drivetrain identification tool determines the best-fit parameters for the equation: - -.. math:: V = kS \cdot sgn(\dot{d}) + kV \cdot \dot{d} + kA \cdot \ddot{d} - -where :math:`V` is the applied voltage, :math:`d` is the displacement (position) of the drive, :math:`\dot{d}` is its velocity, and :math:`\ddot{d}` is its acceleration. This is the same modeling equation as is used in the simple motor identification - however, the drivetrain identification tool is specifically set up to run on differential drives, and will characterize each side of the drive independently if desired. - -The drivetrain identification tool can also determine the effective trackwidth of your robot using a gyro. More information on how to run the identification is available in the :ref:`track width identification ` article. - -Elevator Identification -~~~~~~~~~~~~~~~~~~~~~~~ +#### Elevator Identification The elevator identification tool determines the best-fit parameters for the equation: -.. math:: V = kG + kS \cdot sgn(\dot{d}) + kV \cdot \dot{d} + kA \cdot \ddot{d} +.. math:: V = K_g + K_s \cdot sgn(\dot{d}) + K_v \cdot \dot{d} + K_a \cdot \ddot{d} -where :math:`V` is the applied voltage, :math:`d` is the displacement (position) of the elevator, :math:`\dot{d}` is its velocity, and :math:`\ddot{d}` is its acceleration. The constant term (:math:`kG`) is added to correctly account for the effect of gravity. +where :math:`V` is the applied voltage, :math:`d` is the displacement (position) of the elevator, :math:`\dot{d}` is its velocity, and :math:`\ddot{d}` is its acceleration. The constant term (:math:`K_g`) is added to correctly account for the effect of gravity. -Arm Identification -~~~~~~~~~~~~~~~~~~ +#### Arm Identification The arm identification tool determines the best-fit parameters for the equation: -.. math:: V = kG \cdot cos(\theta) + kS \cdot sgn(\dot{\theta}) + kV \cdot \dot{\theta} + kA \cdot \ddot{\theta} +.. math:: V = K_g \cdot cos(\theta) + K_s \cdot sgn(\dot{\theta}) + K_v \cdot \dot{\theta} + K_a \cdot \ddot{\theta} -where :math:`V` is the applied voltage, :math:`\theta` is the angular displacement (position) of the arm, :math:`\dot{\theta}` is its angular velocity, and :math:`\ddot{\theta}` is its angular acceleration. The cosine term (:math:`kG`) is added to correctly account for the effect of gravity. +where :math:`V` is the applied voltage, :math:`\theta` is the angular displacement (position) of the arm, :math:`\dot{\theta}` is its angular velocity, and :math:`\ddot{\theta}` is its angular acceleration. The cosine term (:math:`K_g`) is added to correctly account for the effect of gravity. -Installing the System Identification Tool ------------------------------------------ +## Installing SysId -The system identification tool (also referred to as ``sysid``) is included with the WPILib Installer. +SysId is included with the WPILib Installer. .. note:: The old Python characterization tool from previous years is no longer supported. -Launching the System Identification Tool ----------------------------------------- +## Launching the SysId Tool The system identification tool can be opened from the ``Start Tool`` option in VS Code or by using the shortcut inside the WPILib Tools desktop folder (Windows). diff --git a/source/docs/software/advanced-controls/system-identification/loading-data.rst b/source/docs/software/advanced-controls/system-identification/loading-data.rst new file mode 100644 index 0000000000..4db45d7e1f --- /dev/null +++ b/source/docs/software/advanced-controls/system-identification/loading-data.rst @@ -0,0 +1,25 @@ +# Loading Data + +After downloading the WPILog containing the tests from the roboRIO, go to the ``Log Loader`` pane in SysId and click ``Open data log file...``. + +After the file loads, look for a ``string`` type entry with a name containing "state". Drag this entry into the Data Selector pane's Test State slot. + +.. note:: SysIdRoutine will name the entry "sysid-test-state-mechanism", where "mechanism" is the name passed to the ``Mechanism`` constructor or the subsystem name. + +.. image:: images/log-loaded.png + :alt: Log Loader and Data Selector panes showing the test state entry and where to move it + +Now the ``Data Selector`` pane will present ``Position``, ``Velocity``, and ``Voltage`` slots. In the ``Log Loader`` pane, find entries starting with each of those terms and containing the motor name you set in the ``log`` callback, and drag those into the ``Data Selector`` slots. + +.. image:: images/state-selected.png + :alt: Data Selector pane showing the Position, Velocity, and Voltage slots + +.. image:: images/entries-selected.png + :alt: The Data Selector pane after the Position, Velocity, and Voltage entries have been selected + +Ideally, the correct units for the position and velocity entries would have been set in the code before running the tests. If this was not the case, use the Units dropdown in the ``Data Selector`` pane to correct it. Additionally, if you did not account for a gear ratio or some other factor that scales the recorded values up or down uniformly, you can compensate for that by setting position and velocity scaling factors in the provided boxes. + +.. image:: images/scaling-units.png + :alt: Unit adjustment and scaling controls + +Ensure the correct analysis type has been selected, then click the ``Load`` button and move on to checking the fit diagnostics in the ``Diagnostics`` pane. diff --git a/source/docs/software/advanced-controls/system-identification/running-routine.rst b/source/docs/software/advanced-controls/system-identification/running-routine.rst new file mode 100644 index 0000000000..bf924ed4d0 --- /dev/null +++ b/source/docs/software/advanced-controls/system-identification/running-routine.rst @@ -0,0 +1,22 @@ +# Running the Identification Routine + +Once the code has been deployed, we can now run the system identification routine, and record the resulting data for analysis. + +.. note:: Ensure you have sufficient space around the robot before running any identification routine! The drive identification requires at least 10' of space, ideally closer to 20'. The robot drive can not be accurately characterized while on blocks. + +.. warning:: Only log files with a single routine in them are usable for analysis. Multiple motors can be run in one routine, but they must be run at the same time. If you run a routine on one motor and then run a routine on another motor without extracting the log or power-cycling the roboRIO in between, analysis will fail. + +## Running Tests +Perform the tests using the bindings you created in the previous section. + +.. warning:: Watch out for your mechanism and stop the test early if it exceeds safe limits! The routine only creates voltage commands for you to connect to your motors, it is up to you to set up hard or soft limits to prevent injury or damage. + +The entire routine should look something like this: + +.. note:: A drivetrain routine is shown below, but the same motions will occur on any mechanism. + +.. raw:: html + +
+ +After all four tests have been completed, use the ``DataLogTool`` to retrieve the log file from the roboRIO. diff --git a/source/docs/software/pathplanning/system-identification/viewing-diagnostics.rst b/source/docs/software/advanced-controls/system-identification/viewing-diagnostics.rst similarity index 85% rename from source/docs/software/pathplanning/system-identification/viewing-diagnostics.rst rename to source/docs/software/advanced-controls/system-identification/viewing-diagnostics.rst index a11376319a..5f3b9ec8ec 100644 --- a/source/docs/software/pathplanning/system-identification/viewing-diagnostics.rst +++ b/source/docs/software/advanced-controls/system-identification/viewing-diagnostics.rst @@ -1,30 +1,26 @@ -Viewing Diagnostics -=================== +# Viewing Diagnostics -Goodness-of-Fit Metrics ------------------------ +## Goodness-of-Fit Metrics There are three numerical accuracy metrics that are computed with this tool: acceleration :term:`r-squared`, simulated velocity r-squared, and the simulated velocity :term:`RMSE`. .. image:: images/viewing-metrics.png :alt: Analysis accuracy metrics -The acceleration r-squared is the fraction of the variance in measured acceleration (used as the independent variable in the SysId regression) explained by the linear model. This can be quite variable, because acceleration is very susceptible to system noise. Assuming the other fit metrics are acceptable, values near ``1`` indicate an "ideal" mechanism with few disturbances, while values near ``0`` indicate a noisy mechanism with substantial physical vibrations/losses. +The acceleration r-squared is the fraction of the variance in measured acceleration (used as the independent variable in the SysId regression) explained by the linear model. This can be quite variable, because acceleration is very susceptible to system noise. Mechanisms tend to vibrate quite a bit, so this value rarely goes above ``0.5``, even on very good datasets. If the acceleration r-squared goes below around ``0.2``, the kA gain will be of dubious quality and the mechanism vibration should be reduced if possible. Even if your r-squared is outside this range it may still be valid, but it is recommended to improve the data if practical. The simulated velocity r-squared is the fraction of the variance in measured velocity explained by a noiseless simulation of the motor movement stepped forward with the constants determined from the regression. A value north of ``.9`` indicates a good fit. The simulated velocity RMSE is the standard deviation of the velocity error from the simulated model. This is a good estimation of the amount of process noise present during the test routine, and can be used as a low-end estimate for the model noise term in :ref:`state-space control `. -Diagnostic Plots ----------------- +## Diagnostic Plots SysId also produces several diagnostic plots to help users evaluate the quality of their model fit. .. image:: images/plot-view.png :alt: Diagnostic plot location -Time-Domain Plots -^^^^^^^^^^^^^^^^^ +### Time-Domain Plots .. note:: To improve plot quality, the diagnostic plots are separated by direction. Be sure to view both the forward *and* backward plots when troubleshooting! @@ -37,10 +33,9 @@ The velocity time domain plots contain three sets of data: Raw Data, Filtered Da A successful quasistatic graph will be very nearly linear, while a successful dynamic graph will be an approximately exponential approach of the steady-speed. -Deviation from this behavior is a sign of an :ref:`error `, either in your robot setup, analysis settings, or your test procedure. +Deviation from this behavior is a sign of an :ref:`error `, either in your robot setup, analysis settings, or your test procedure. -Acceleration-Velocity Plot -^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Acceleration-Velocity Plot The acceleration-versus-velocity plot displays the mechanism velocity versus the portion of acceleration corresponding to factors other than friction (ideally, this would leave only back-EMF) and applied voltage across *all* of the tests. @@ -54,21 +49,18 @@ This plot should be quite linear, with patches of relatively noiseless quasistat However, if your robot or mechanism has low mass compared to the motor power, this may "eat" what little meaningful acceleration data you have. In these cases ``kA`` will tend towards zero and can be ignored for feedforward purposes. However, if ``kA`` cannot be accurately measured, the calculated feedback gains are likely to be inaccurate, and manual tuning may be required. -Common Failure Modes --------------------- +## Common Failure Modes When something has gone wrong with the identification, diagnostic plots and console output provide crucial clues as to *what* has gone wrong. This section describes some common failures encountered while running the system identification tool, the identifying features of their diagnostic plots, and the steps that can be taken to fix them. -Improperly Set Motion Threshold -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Improperly Set Motion Threshold One of the most-common errors is an inappropriate value for the motion threshold. .. image:: images/motionthreshold-selector.png :alt: Motion threshold selector -Velocity Threshold Too Low -~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Velocity Threshold Too Low .. image:: images/low-threshold.png :alt: Quasistatic time-domain plot with velocity threshold too low @@ -77,8 +69,7 @@ The presence of a "leading tail" (emphasized by added red circle) in the quasist To solve this, increase the velocity threshold and re-analyze the data. -Motion Threshold Too High -~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Motion Threshold Too High .. image:: images/high-threshold.png :alt: Quasistatic time-domain plot with velocity threshold too high @@ -87,14 +78,13 @@ While not nearly as problematic as a too-low threshold, a velocity threshold tha To solve this, decrease the velocity threshold and re-analyze the data. -Noisy Velocity Signals -^^^^^^^^^^^^^^^^^^^^^^ +### Noisy Velocity Signals .. note:: There are two types of noise that affect mechanical systems - signal noise and system noise. Signal noise corresponds to measurement error, while system noise corresponds to actual physical motion that is unaccounted-for by your model (e.g. vibration). If SysId suggests that your system is noisy, you must figure out which of the two types of noise is at play - signal noise is often easier to eliminate than system noise. .. image:: images/velo-noise.png -Many FRC setups suffer from poorly-installed encoders - errors in shaft concentricity (for optical encoders) and magnet location (For magnetic encoders) can both contribute to noisy velocity signals, as can inappropriate filtering settings. Encoder noise will be immediately visible in your diagnostic plots, as can be seen above. Encoder noise is especially common on the `toughbox mini `__ gearboxes provided in the kit of parts. +Many FRC setups suffer from poorly-installed encoders - errors in shaft concentricity (for optical encoders) and magnet location (For magnetic encoders) can both contribute to noisy velocity signals, as can inappropriate filtering settings. Encoder noise will be immediately visible in your diagnostic plots, as can be seen above. Encoder noise is especially common on the [toughbox mini](https://www.andymark.com/products/toughbox-mini-options) gearboxes provided in the kit of parts. System parameters can sometimes be accurately determined even from data polluted by encoder noise by increasing the window size setting. However, this sort of encoder noise is problematic for robot code much the same way it is problematic for the system identification tool. As the root cause of the noise is not known, it is recommended to try a different encoder setup if this is observed, either by moving the encoders to a different shaft, replacing them with a different type of encoder, or increasing the sample per average in project generation (adds an additional layer of filtering). diff --git a/source/docs/software/advanced-controls/trajectories/constraints.rst b/source/docs/software/advanced-controls/trajectories/constraints.rst index a638f7a8eb..89a1837ee6 100644 --- a/source/docs/software/advanced-controls/trajectories/constraints.rst +++ b/source/docs/software/advanced-controls/trajectories/constraints.rst @@ -1,11 +1,9 @@ -Trajectory Constraints -====================== +# Trajectory Constraints In the :ref:`previous article `, you might have noticed that no custom constraints were added when generating the trajectories. Custom constraints allow users to impose more restrictions on the velocity and acceleration at points along the trajectory based on location and curvature. For example, a custom constraint can keep the velocity of the trajectory under a certain threshold in a certain region or slow down the robot near turns for stability purposes. -WPILib-Provided Constraints ---------------------------- +## WPILib-Provided Constraints WPILib includes a set of predefined constraints that users can utilize when generating trajectories. The list of WPILib-provided constraints is as follows: - ``CentripetalAccelerationConstraint``: Limits the centripetal acceleration of the robot as it traverses along the trajectory. This can help slow down the robot around tight turns. @@ -19,40 +17,58 @@ WPILib includes a set of predefined constraints that users can utilize when gene .. note:: The ``DifferentialDriveVoltageConstraint`` only ensures that theoretical voltage commands do not go over the specified maximum using a :ref:`feedforward model `. If the robot were to deviate from the reference while tracking, the commanded voltage may be higher than the specified maximum. -Creating a Custom Constraint ----------------------------- +## Creating a Custom Constraint Users can create their own constraint by implementing the ``TrajectoryConstraint`` interface. .. tab-set-code:: - .. code-block:: java - - @Override - public double getMaxVelocityMetersPerSecond(Pose2d poseMeters, double curvatureRadPerMeter, - double velocityMetersPerSecond) { - // code here - } - + ```java + @Override + public double getMaxVelocityMetersPerSecond(Pose2d poseMeters, double curvatureRadPerMeter, + double velocityMetersPerSecond) { + // code here + } @Override - public MinMax getMinMaxAccelerationMetersPerSecondSq(Pose2d poseMeters, - double curvatureRadPerMeter, - double velocityMetersPerSecond) { - // code here - } - - .. code-block:: c++ + public MinMax getMinMaxAccelerationMetersPerSecondSq(Pose2d poseMeters, + double curvatureRadPerMeter, + double velocityMetersPerSecond) { + // code here + } + ``` - units::meters_per_second_t MaxVelocity( - const Pose2d& pose, units::curvature_t curvature, - units::meters_per_second_t velocity) override { - // code here - } + ```c++ + units::meters_per_second_t MaxVelocity( + const Pose2d& pose, units::curvature_t curvature, + units::meters_per_second_t velocity) override { + // code here + } + MinMax MinMaxAcceleration(const Pose2d& pose, units::curvature_t curvature, + units::meters_per_second_t speed) override { + // code here + } + ``` - MinMax MinMaxAcceleration(const Pose2d& pose, units::curvature_t curvature, - units::meters_per_second_t speed) override { - // code here - } + ```python + from wpimath import units + from wpimath.geometry import Pose2d + from wpimath.trajectory.constraint import TrajectoryConstraint + class MyConstraint(TrajectoryConstraint): + def maxVelocity( + self, + pose: Pose2d, + curvature: units.radians_per_meter, + velocity: units.meters_per_second, + ) -> units.meters_per_second: + ... + def minMaxAcceleration( + self, + pose: Pose2d, + curvature: units.radians_per_meter, + speed: units.meters_per_second, + ) -> TrajectoryConstraint.MinMax: + ... + ``` The ``MaxVelocity`` method should return the maximum allowed velocity for the given pose, curvature, and original velocity of the trajectory without any constraints. The ``MinMaxAcceleration`` method should return the minimum and maximum allowed acceleration for the given pose, curvature, and constrained velocity. -See the source code (`Java `_, `C++ `_) for the WPILib-provided constraints for more examples on how to write your own custom trajectory constraints. +See the source code ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint), [C++] (https://github.com/wpilibsuite/allwpilib/tree/main/wpimath/src/main/native/cpp/trajectory/constraint)) for the WPILib-provided constraints for more examples on how to write your own custom trajectory constraints. diff --git a/source/docs/software/advanced-controls/trajectories/examples/trajectory-generation-1/py/ExampleTrajectory.py b/source/docs/software/advanced-controls/trajectories/examples/trajectory-generation-1/py/ExampleTrajectory.py new file mode 100644 index 0000000000..7021136390 --- /dev/null +++ b/source/docs/software/advanced-controls/trajectories/examples/trajectory-generation-1/py/ExampleTrajectory.py @@ -0,0 +1,20 @@ +from wpimath.geometry import Pose2d, Rotation2d, Translation2d +from wpimath.trajectory import TrajectoryGenerator, TrajectoryConfig + + +def generateTrajectory(): + # 2018 cross scale auto waypoints. + sideStart = Pose2d.fromFeet(1.54, 23.23, Rotation2d.fromDegrees(-180)) + crossScale = Pose2d.fromFeet(23.7, 6.8, Rotation2d.fromDegrees(-160)) + + interiorWaypoints = [ + Translation2d.fromFeet(14.54, 23.23), + Translation2d.fromFeet(21.04, 18.23), + ] + + config = TrajectoryConfig.fromFps(12, 12) + config.setReversed(True) + + trajectory = TrajectoryGenerator.generateTrajectory( + sideStart, interiorWaypoints, crossScale, config + ) diff --git a/source/docs/software/advanced-controls/trajectories/holonomic.rst b/source/docs/software/advanced-controls/trajectories/holonomic.rst index 4ac38b8a0c..74ee8778bf 100644 --- a/source/docs/software/advanced-controls/trajectories/holonomic.rst +++ b/source/docs/software/advanced-controls/trajectories/holonomic.rst @@ -1,9 +1,7 @@ -Holonomic Drive Controller -========================== +# Holonomic Drive Controller The holonomic drive controller is a trajectory tracker for robots with holonomic drivetrains (e.g. swerve, mecanum, etc.). This can be used to accurately track trajectories with correction for minor disturbances. -Constructing a Holonomic Drive Controller ------------------------------------------ +## Constructing a Holonomic Drive Controller The holonomic drive controller should be instantiated with 2 PID controllers and 1 profiled PID controller. .. note:: For more information on PID control, see :ref:`docs/software/advanced-controls/controllers/pidcontroller:PID Control in WPILib`. @@ -13,74 +11,103 @@ The 2 PID controllers are controllers that should correct for error in the field The final parameter is a ``ProfiledPIDController`` for the rotation of the robot. Because the rotation dynamics of a holonomic drivetrain are decoupled from movement in the x and y directions, users can set custom heading references while following a trajectory. These heading references are profiled according to the parameters set in the ``ProfiledPIDController``. .. tab-set-code:: - .. code-block:: java - - var controller = new HolonomicDriveController( - new PIDController(1, 0, 0), new PIDController(1, 0, 0), - new ProfiledPIDController(1, 0, 0, - new TrapezoidProfile.Constraints(6.28, 3.14))); - // Here, our rotation profile constraints were a max velocity - // of 1 rotation per second and a max acceleration of 180 degrees - // per second squared. - - .. code-block:: c++ - - frc::HolonomicDriveController controller{ - frc::PIDController{1, 0, 0}, frc::PIDController{1, 0, 0}, - frc::ProfiledPIDController{ - 1, 0, 0, frc::TrapezoidProfile::Constraints{ - 6.28_rad_per_s, 3.14_rad_per_s / 1_s}}}; - // Here, our rotation profile constraints were a max velocity - // of 1 rotation per second and a max acceleration of 180 degrees - // per second squared. - -Getting Adjusted Velocities ---------------------------- + ```java + var controller = new HolonomicDriveController( + new PIDController(1, 0, 0), new PIDController(1, 0, 0), + new ProfiledPIDController(1, 0, 0, + new TrapezoidProfile.Constraints(6.28, 3.14))); + // Here, our rotation profile constraints were a max velocity + // of 1 rotation per second and a max acceleration of 180 degrees + // per second squared. + ``` + + ```c++ + frc::HolonomicDriveController controller{ + frc::PIDController{1, 0, 0}, frc::PIDController{1, 0, 0}, + frc::ProfiledPIDController{ + 1, 0, 0, frc::TrapezoidProfile::Constraints{ + 6.28_rad_per_s, 3.14_rad_per_s / 1_s}}}; + // Here, our rotation profile constraints were a max velocity + // of 1 rotation per second and a max acceleration of 180 degrees + // per second squared. + ``` + + ```python + from wpimath.controller import ( + HolonomicDriveController, + PIDController, + ProfiledPIDControllerRadians, + ) + from wpimath.trajectory import TrapezoidProfileRadians + controller = HolonomicDriveController( + PIDController(1, 0, 0), + PIDController(1, 0, 0), + ProfiledPIDControllerRadians( + 1, 0, 0, TrapezoidProfileRadians.Constraints(6.28, 3.14) + ), + ) + # Here, our rotation profile constraints were a max velocity + # of 1 rotation per second and a max acceleration of 180 degrees + # per second squared. + ``` + +## Getting Adjusted Velocities The holonomic drive controller returns "adjusted velocities" such that when the robot tracks these velocities, it accurately reaches the goal point. The controller should be updated periodically with the new goal. The goal is comprised of a desired pose, linear velocity, and heading. .. note:: The "goal pose" represents the position that the robot should be at a particular timestamp when tracking the trajectory. It does NOT represent the trajectory's endpoint. -The controller can be updated using the ``Calculate`` (C++) / ``calculate`` (Java) method. There are two overloads for this method. Both of these overloads accept the current robot position as the first parameter and the desired heading as the last parameter. For the middle parameters, one overload accepts the desired pose and the linear velocity reference while the other accepts a ``Trajectory.State`` object, which contains information about the goal pose. The latter method is preferred for tracking trajectories. +The controller can be updated using the ``Calculate`` (C++) / ``calculate`` (Java/Python) method. There are two overloads for this method. Both of these overloads accept the current robot position as the first parameter and the desired heading as the last parameter. For the middle parameters, one overload accepts the desired pose and the linear velocity reference while the other accepts a ``Trajectory.State`` object, which contains information about the goal pose. The latter method is preferred for tracking trajectories. .. tab-set-code:: - .. code-block:: java - - // Sample the trajectory at 3.4 seconds from the beginning. - Trajectory.State goal = trajectory.sample(3.4); - - // Get the adjusted speeds. Here, we want the robot to be facing - // 70 degrees (in the field-relative coordinate system). - ChassisSpeeds adjustedSpeeds = controller.calculate( - currentRobotPose, goal, Rotation2d.fromDegrees(70.0)); - - .. code-block:: c++ - - // Sample the trajectoty at 3.4 seconds from the beginning. - const auto goal = trajectory.Sample(3.4_s); - - // Get the adjusted speeds. Here, we want the robot to be facing - // 70 degrees (in the field-relative coordinate system). - const auto adjustedSpeeds = controller.Calculate( - currentRobotPose, goal, 70_deg); - -Using the Adjusted Velocities ------------------------------ + ```java + // Sample the trajectory at 3.4 seconds from the beginning. + Trajectory.State goal = trajectory.sample(3.4); + // Get the adjusted speeds. Here, we want the robot to be facing + // 70 degrees (in the field-relative coordinate system). + ChassisSpeeds adjustedSpeeds = controller.calculate( + currentRobotPose, goal, Rotation2d.fromDegrees(70.0)); + ``` + + ```c++ + // Sample the trajectoty at 3.4 seconds from the beginning. + const auto goal = trajectory.Sample(3.4_s); + // Get the adjusted speeds. Here, we want the robot to be facing + // 70 degrees (in the field-relative coordinate system). + const auto adjustedSpeeds = controller.Calculate( + currentRobotPose, goal, 70_deg); + ``` + + ```python + from wpimath.geometry import Rotation2d + # Sample the trajectory at 3.4 seconds from the beginning. + goal = trajectory.sample(3.4) + # Get the adjusted speeds. Here, we want the robot to be facing + # 70 degrees (in the field-relative coordinate system). + adjustedSpeeds = controller.calculate( + currentRobotPose, goal, Rotation2d.fromDegrees(70.0) + ) + ``` + +## Using the Adjusted Velocities The adjusted velocities are of type ``ChassisSpeeds``, which contains a ``vx`` (linear velocity in the forward direction), a ``vy`` (linear velocity in the sideways direction), and an ``omega`` (angular velocity around the center of the robot frame). The returned adjusted speeds can be converted into usable speeds using the kinematics classes for your drivetrain type. In the example code below, we will assume a swerve drive robot; however, the kinematics code is exactly the same for a mecanum drive robot except using ``MecanumDriveKinematics``. .. tab-set-code:: - .. code-block:: java - - SwerveModuleState[] moduleStates = kinematics.toSwerveModuleStates(adjustedSpeeds); - - SwerveModuleState frontLeft = moduleStates[0]; - SwerveModuleState frontRight = moduleStates[1]; - SwerveModuleState backLeft = moduleStates[2]; - SwerveModuleState backRight = moduleStates[3]; - - .. code-block:: c++ - - auto [fl, fr, bl, br] = kinematics.ToSwerveModuleStates(adjustedSpeeds); + ```java + SwerveModuleState[] moduleStates = kinematics.toSwerveModuleStates(adjustedSpeeds); + SwerveModuleState frontLeft = moduleStates[0]; + SwerveModuleState frontRight = moduleStates[1]; + SwerveModuleState backLeft = moduleStates[2]; + SwerveModuleState backRight = moduleStates[3]; + ``` + + ```c++ + auto [fl, fr, bl, br] = kinematics.ToSwerveModuleStates(adjustedSpeeds); + ``` + + ```python + fl, fr, bl, br = kinematics.toSwerveModuleStates(adjustedSpeeds) + ``` Because these swerve module states are still speeds and angles, you will need to use PID controllers to set these speeds and angles. diff --git a/source/docs/software/advanced-controls/trajectories/images/relative-to.png b/source/docs/software/advanced-controls/trajectories/images/relative-to.png index 0423505b30..505e512f78 100644 Binary files a/source/docs/software/advanced-controls/trajectories/images/relative-to.png and b/source/docs/software/advanced-controls/trajectories/images/relative-to.png differ diff --git a/source/docs/software/advanced-controls/trajectories/images/sysid-trackwidth.png b/source/docs/software/advanced-controls/trajectories/images/sysid-trackwidth.png index 8330400aab..368938f146 100644 Binary files a/source/docs/software/advanced-controls/trajectories/images/sysid-trackwidth.png and b/source/docs/software/advanced-controls/trajectories/images/sysid-trackwidth.png differ diff --git a/source/docs/software/advanced-controls/trajectories/images/transform-by.png b/source/docs/software/advanced-controls/trajectories/images/transform-by.png index 28f9e5bb8d..8d0c8785e7 100644 Binary files a/source/docs/software/advanced-controls/trajectories/images/transform-by.png and b/source/docs/software/advanced-controls/trajectories/images/transform-by.png differ diff --git a/source/docs/software/advanced-controls/trajectories/index.rst b/source/docs/software/advanced-controls/trajectories/index.rst index 2cce839f8f..187a8527a0 100644 --- a/source/docs/software/advanced-controls/trajectories/index.rst +++ b/source/docs/software/advanced-controls/trajectories/index.rst @@ -1,7 +1,6 @@ .. include:: -Trajectory Generation and Following with WPILib -=============================================== +# Trajectory Generation and Following with WPILib This section describes WPILib support for generating parameterized spline trajectories and following those trajectories with typical FRC\ |reg| robot drives. diff --git a/source/docs/software/advanced-controls/trajectories/manipulating-trajectories.rst b/source/docs/software/advanced-controls/trajectories/manipulating-trajectories.rst index a673e19f6d..95012e37cf 100644 --- a/source/docs/software/advanced-controls/trajectories/manipulating-trajectories.rst +++ b/source/docs/software/advanced-controls/trajectories/manipulating-trajectories.rst @@ -1,41 +1,49 @@ -Manipulating Trajectories -========================= +# Manipulating Trajectories Once a trajectory has been generated, you can retrieve information from it using certain methods. These methods will be useful when writing code to follow these trajectories. -Getting the total duration of the trajectory --------------------------------------------- -Because all trajectories have timestamps at each point, the amount of time it should take for a robot to traverse the entire trajectory is pre-determined. The ``TotalTime()`` (C++) / ``getTotalTimeSeconds()`` (Java) method can be used to determine the time it takes to traverse the trajectory. +## Getting the total duration of the trajectory +Because all trajectories have timestamps at each point, the amount of time it should take for a robot to traverse the entire trajectory is pre-determined. The ``TotalTime()`` (C++) / ``getTotalTimeSeconds()`` (Java) / ``totalTime`` (Python) method can be used to determine the time it takes to traverse the trajectory. .. tab-set-code:: - .. code-block:: java + ```java + // Get the total time of the trajectory in seconds + double duration = trajectory.getTotalTimeSeconds(); + ``` - // Get the total time of the trajectory in seconds - double duration = trajectory.getTotalTimeSeconds(); + ```c++ + // Get the total time of the trajectory + units::second_t duration = trajectory.TotalTime(); + ``` - .. code-block:: c++ + ```python + # Get the total time of the trajectory + duration = trajectory.totalTime() + ``` - // Get the total time of the trajectory - units::second_t duration = trajectory.TotalTime(); - -Sampling the trajectory ------------------------ -The trajectory can be sampled at various timesteps to get the pose, velocity, and acceleration at that point. The ``Sample(units::second_t time)`` (C++) / ``sample(double timeSeconds)`` (Java) method can be used to sample the trajectory at any timestep. The parameter refers to the amount of time passed since 0 seconds (the starting point of the trajectory). This method returns a ``Trajectory::Sample`` with information about that sample point. +## Sampling the trajectory +The trajectory can be sampled at various timesteps to get the pose, velocity, and acceleration at that point. The ``Sample(units::second_t time)`` (C++) / ``sample(double timeSeconds)`` (Java/Python) method can be used to sample the trajectory at any timestep. The parameter refers to the amount of time passed since 0 seconds (the starting point of the trajectory). This method returns a ``Trajectory::Sample`` with information about that sample point. .. tab-set-code:: - .. code-block:: java - - // Sample the trajectory at 1.2 seconds. This represents where the robot - // should be after 1.2 seconds of traversal. - Trajectory.Sample point = trajectory.sample(1.2); + ```java + // Sample the trajectory at 1.2 seconds. This represents where the robot + // should be after 1.2 seconds of traversal. + Trajectory.Sample point = trajectory.sample(1.2); + ``` - .. code-block:: c++ + ```c++ + // Sample the trajectory at 1.2 seconds. This represents where the robot + // should be after 1.2 seconds of traversal. + Trajectory::State point = trajectory.Sample(1.2_s); + ``` - // Sample the trajectory at 1.2 seconds. This represents where the robot - // should be after 1.2 seconds of traversal. - Trajectory::State point = trajectory.Sample(1.2_s); + ```python + # Sample the trajectory at 1.2 seconds. This represents where the robot + # should be after 1.2 seconds of traversal. + point = trajectory.sample(1.2) + ``` The ``Trajectory::Sample`` struct has several pieces of information about the sample point: @@ -47,6 +55,5 @@ The ``Trajectory::Sample`` struct has several pieces of information about the sa Note: The angular velocity at the sample point can be calculated by multiplying the velocity by the curvature. -Getting all states of the trajectory (advanced) ------------------------------------------------ -A more advanced user can get a list of all states of the trajectory by calling the ``States()`` (C++) / ``getStates()`` (Java) method. Each state represents a point on the trajectory. :ref:`When the trajectory is created ` using the ``TrajectoryGenerator::GenerateTrajectory(...)`` method, a list of trajectory points / states are created. When the user samples the trajectory at a particular timestep, a new sample point is interpolated between two existing points / states in the list. +## Getting all states of the trajectory (advanced) +A more advanced user can get a list of all states of the trajectory by calling the ``States()`` (C++) / ``getStates()`` (Java) / ``states`` (Python) method. Each state represents a point on the trajectory. :ref:`When the trajectory is created ` using the ``TrajectoryGenerator::GenerateTrajectory(...)`` method, a list of trajectory points / states are created. When the user samples the trajectory at a particular timestep, a new sample point is interpolated between two existing points / states in the list. diff --git a/source/docs/software/advanced-controls/trajectories/ramsete.rst b/source/docs/software/advanced-controls/trajectories/ramsete.rst index a715e49a09..9f54540b6e 100644 --- a/source/docs/software/advanced-controls/trajectories/ramsete.rst +++ b/source/docs/software/advanced-controls/trajectories/ramsete.rst @@ -1,81 +1,95 @@ -Ramsete Controller -================== +# Ramsete Controller The Ramsete Controller is a trajectory tracker that is built in to WPILib. This tracker can be used to accurately track trajectories with correction for minor disturbances. -Constructing the Ramsete Controller Object ------------------------------------------- +## Constructing the Ramsete Controller Object The Ramsete controller should be initialized with two gains, namely ``b`` and ``zeta``. Larger values of ``b`` make convergence more aggressive like a proportional term whereas larger values of ``zeta`` provide more damping in the response. These controller gains only dictate how the controller will output adjusted velocities. It does NOT affect the actual velocity tracking of the robot. This means that these controller gains are generally robot-agnostic. .. note:: Gains of ``2.0`` and ``0.7`` for ``b`` and ``zeta`` have been tested repeatedly to produce desirable results when all units were in meters. As such, a zero-argument constructor for ``RamseteController`` exists with gains defaulted to these values. .. tab-set-code:: - .. code-block:: java - - // Using the default constructor of RamseteController. Here - // the gains are initialized to 2.0 and 0.7. - RamseteController controller1 = new RamseteController(); - - // Using the secondary constructor of RamseteController where - // the user can choose any other gains. - RamseteController controller2 = new RamseteController(2.1, 0.8); - - .. code-block:: c++ - - // Using the default constructor of RamseteController. Here - // the gains are initialized to 2.0 and 0.7. - frc::RamseteController controller1; - - // Using the secondary constructor of RamseteController where - // the user can choose any other gains. - frc::RamseteController controller2{2.1, 0.8}; - -Getting Adjusted Velocities ---------------------------- + ```java + // Using the default constructor of RamseteController. Here + // the gains are initialized to 2.0 and 0.7. + RamseteController controller1 = new RamseteController(); + // Using the secondary constructor of RamseteController where + // the user can choose any other gains. + RamseteController controller2 = new RamseteController(2.1, 0.8); + ``` + + ```c++ + // Using the default constructor of RamseteController. Here + // the gains are initialized to 2.0 and 0.7. + frc::RamseteController controller1; + // Using the secondary constructor of RamseteController where + // the user can choose any other gains. + frc::RamseteController controller2{2.1, 0.8}; + ``` + + ```python + from wpimath.controller import RamseteController + # Using the default constructor of RamseteController. Here + # the gains are initialized to 2.0 and 0.7. + controller1 = RamseteController() + # Using the secondary constructor of RamseteController where + # the user can choose any other gains. + controller2 = RamseteController(2.1, 0.8) + ``` + +## Getting Adjusted Velocities The Ramsete controller returns "adjusted velocities" so that the when the robot tracks these velocities, it accurately reaches the goal point. The controller should be updated periodically with the new goal. The goal comprises of a desired pose, desired linear velocity, and desired angular velocity. Furthermore, the current position of the robot should also be updated periodically. The controller uses these four arguments to return the adjusted linear and angular velocity. Users should command their robot to these linear and angular velocities to achieve optimal trajectory tracking. .. note:: The "goal pose" represents the position that the robot should be at a particular timestep when tracking the trajectory. It does NOT represent the final endpoint of the trajectory. -The controller can be updated using the ``Calculate`` (C++) / ``calculate`` (Java) method. There are two overloads for this method. Both of these overloads accept the current robot position as the first parameter. For the other parameters, one of these overloads takes in the goal as three separate parameters (pose, linear velocity, and angular velocity) whereas the other overload accepts a ``Trajectory.State`` object, which contains information about the goal pose. For its ease, users should use the latter method when tracking trajectories. +The controller can be updated using the ``Calculate`` (C++) / ``calculate`` (Java/Python) method. There are two overloads for this method. Both of these overloads accept the current robot position as the first parameter. For the other parameters, one of these overloads takes in the goal as three separate parameters (pose, linear velocity, and angular velocity) whereas the other overload accepts a ``Trajectory.State`` object, which contains information about the goal pose. For its ease, users should use the latter method when tracking trajectories. .. tab-set-code:: - .. code-block:: java - - Trajectory.State goal = trajectory.sample(3.4); // sample the trajectory at 3.4 seconds from the beginning - ChassisSpeeds adjustedSpeeds = controller.calculate(currentRobotPose, goal); + ```java + Trajectory.State goal = trajectory.sample(3.4); // sample the trajectory at 3.4 seconds from the beginning + ChassisSpeeds adjustedSpeeds = controller.calculate(currentRobotPose, goal); + ``` + ```c++ + const Trajectory::State goal = trajectory.Sample(3.4_s); // sample the trajectory at 3.4 seconds from the beginning + ChassisSpeeds adjustedSpeeds = controller.Calculate(currentRobotPose, goal); + ``` - .. code-block:: c++ - - const Trajectory::State goal = trajectory.Sample(3.4_s); // sample the trajectory at 3.4 seconds from the beginning - ChassisSpeeds adjustedSpeeds = controller.Calculate(currentRobotPose, goal); + ```python + goal = trajectory.sample(3.4) # sample the trajectory at 3.4 seconds from the beginning + adjustedSpeeds = controller.calculate(currentRobotPose, goal) + ``` These calculations should be performed at every loop iteration, with an updated robot position and goal. -Using the Adjusted Velocities ------------------------------ +## Using the Adjusted Velocities The adjusted velocities are of type ``ChassisSpeeds``, which contains a ``vx`` (linear velocity in the forward direction), a ``vy`` (linear velocity in the sideways direction), and an ``omega`` (angular velocity around the center of the robot frame). Because the Ramsete controller is a controller for non-holonomic robots (robots which cannot move sideways), the adjusted speeds object has a ``vy`` of zero. The returned adjusted speeds can be converted to usable speeds using the kinematics classes for your drivetrain type. For example, the adjusted velocities can be converted to left and right velocities for a differential drive using a ``DifferentialDriveKinematics`` object. .. tab-set-code:: - .. code-block:: java - - ChassisSpeeds adjustedSpeeds = controller.calculate(currentRobotPose, goal); - DifferentialDriveWheelSpeeds wheelSpeeds = kinematics.toWheelSpeeds(adjustedSpeeds); - double left = wheelSpeeds.leftMetersPerSecond; - double right = wheelSpeeds.rightMetersPerSecond; - - .. code-block:: cpp - - ChassisSpeeds adjustedSpeeds = controller.Calculate(currentRobotPose, goal); - DifferentialDriveWheelSpeeds wheelSpeeds = kinematics.ToWheelSpeeds(adjustedSpeeds); - auto [left, right] = kinematics.ToWheelSpeeds(adjustedSpeeds); - -Because these new left and right velocities are still speeds and not voltages, two PID Controllers, one for each side may be used to track these velocities. Either the WPILib PIDController (`C++ `_, `Java `_) can be used, or the Velocity PID feature on smart motor controllers such as the TalonSRX and the SPARK MAX can be used. - -Ramsete in the Command-Based Framework --------------------------------------- + ```java + ChassisSpeeds adjustedSpeeds = controller.calculate(currentRobotPose, goal); + DifferentialDriveWheelSpeeds wheelSpeeds = kinematics.toWheelSpeeds(adjustedSpeeds); + double left = wheelSpeeds.leftMetersPerSecond; + double right = wheelSpeeds.rightMetersPerSecond; + ``` + + ```c++ + ChassisSpeeds adjustedSpeeds = controller.Calculate(currentRobotPose, goal); + DifferentialDriveWheelSpeeds wheelSpeeds = kinematics.ToWheelSpeeds(adjustedSpeeds); + auto [left, right] = kinematics.ToWheelSpeeds(adjustedSpeeds); + ``` + + ```python + adjustedSpeeds = controller.calculate(currentRobotPose, goal) + wheelSpeeds = kinematics.toWheelSpeeds(adjustedSpeeds) + left = wheelSpeeds.left + right = wheelSpeeds.right + ``` + +Because these new left and right velocities are still speeds and not voltages, two PID Controllers, one for each side may be used to track these velocities. Either the WPILib PIDController ([C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_p_i_d_controller.html), [Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/PIDController.html), :external:py:class:`Python `) can be used, or the Velocity PID feature on smart motor controllers such as the TalonSRX and the SPARK MAX can be used. + +## Ramsete in the Command-Based Framework For the sake of ease for users, a ``RamseteCommand`` class is built in to WPILib. For a full tutorial on implementing a path-following autonomous using RamseteCommand, see :ref:`docs/software/pathplanning/trajectory-tutorial/index:Trajectory Tutorial`. diff --git a/source/docs/software/advanced-controls/trajectories/trajectory-generation.rst b/source/docs/software/advanced-controls/trajectories/trajectory-generation.rst index ba2199d687..e408bda5a1 100644 --- a/source/docs/software/advanced-controls/trajectories/trajectory-generation.rst +++ b/source/docs/software/advanced-controls/trajectories/trajectory-generation.rst @@ -1,5 +1,4 @@ -Trajectory Generation -===================== +# Trajectory Generation WPILib contains classes that help generating trajectories. A trajectory is a smooth curve, with velocities and accelerations at each point along the curve, connecting two endpoints on the field. Generation and following of trajectories is incredibly useful for performing autonomous tasks. Instead of a simple autonomous routine -- which involves moving forward, stopping, turning 90 degrees to the right, then moving forward -- using trajectories allows for motion along a smooth curve. This has the advantage of speeding up autonomous routines, creating more time for other tasks; and when implemented well, makes autonomous navigation more accurate and precise. This article goes over how to generate a trajectory. The next few articles in this series will go over how to actually follow the generated trajectory. There are a few things that your robot must have before you dive into the world of trajectories: @@ -9,8 +8,7 @@ This article goes over how to generate a trajectory. The next few articles in th If you are looking for a simpler way to perform autonomous navigation, see :ref:`the section on driving to a distance `. -Splines -------- +## Splines A spline refers to a set of curves that interpolate between points. Think of it as connecting dots, except with curves. WPILib supports two types of splines: hermite clamped cubic and hermite quintic. * Hermite clamped cubic: This is the recommended option for most users. Generation of trajectories using these splines involves specifying the (x, y) coordinates of all points, and the headings at the start and end waypoints. The headings at the interior waypoints are automatically determined to ensure continuous curvature (rate of change of the heading) throughout. @@ -19,21 +17,19 @@ A spline refers to a set of curves that interpolate between points. Think of it Splines are used as a tool to generate trajectories; however, the spline itself does not have any information about velocities and accelerations. Therefore, it is not recommended that you use the spline classes directly. In order to generate a smooth path with velocities and accelerations, a *trajectory* must be generated. -Creating the trajectory config ------------------------------- +## Creating the trajectory config A configuration must be created in order to generate a trajectory. The config contains information about special constraints, the max velocity, the max acceleration in addition to the start velocity and end velocity. The config also contains information about whether the trajectory should be reversed (robot travels backward along the waypoints). The ``TrajectoryConfig`` class should be used to construct a config. The constructor for this class takes two arguments, the max velocity and max acceleration. The other fields (``startVelocity``, ``endVelocity``, ``reversed``, ``constraints``) are defaulted to reasonable values (``0``, ``0``, ``false``, ``{}``) when the object is created. If you wish to modify the values of any of these fields, you can call the following methods: -* ``setStartVelocity(double startVelocityMetersPerSecond)`` (Java) / ``SetStartVelocity(units::meters_per_second_t startVelocity)`` (C++) -* ``setEndVelocity(double endVelocityMetersPerSecond)`` (Java) / ``SetEndVelocity(units::meters_per_second_t endVelocity)`` (C++) -* ``setReversed(boolean reversed)`` (Java) / ``SetReversed(bool reversed)`` (C++) -* ``addConstraint(TrajectoryConstraint constraint)`` (Java) / ``AddConstraint(TrajectoryConstraint constraint)`` (C++) +* ``setStartVelocity(double startVelocityMetersPerSecond)`` (Java/Python) / ``SetStartVelocity(units::meters_per_second_t startVelocity)`` (C++) +* ``setEndVelocity(double endVelocityMetersPerSecond)`` (Java/Python) / ``SetEndVelocity(units::meters_per_second_t endVelocity)`` (C++) +* ``setReversed(boolean reversed)`` (Java/Python) / ``SetReversed(bool reversed)`` (C++) +* ``addConstraint(TrajectoryConstraint constraint)`` (Java/Python) / ``AddConstraint(TrajectoryConstraint constraint)`` (C++) .. note:: The ``reversed`` property simply represents whether the robot is traveling backward. If you specify four waypoints, a, b, c, and d, the robot will still travel in the same order through the waypoints when the ``reversed`` flag is set to ``true``. This also means that you must account for the direction of the robot when providing the waypoints. For example, if your robot is facing your alliance station wall and travels backwards to some field element, the starting waypoint should have a rotation of 180 degrees. -Generating the trajectory -------------------------- +## Generating the trajectory The method used to generate a trajectory is ``generateTrajectory(...)``. There are four overloads for this method. Two that use clamped cubic splines and the two others that use quintic splines. For each type of spline, there are two ways to construct a trajectory. The easiest methods are the overloads that accept ``Pose2d`` objects. @@ -56,50 +52,70 @@ Here is an example of generating a trajectory using clamped cubic splines for th .. tab-item:: C++ .. literalinclude:: examples/trajectory-generation-1/cpp/ExampleTrajectory.cpp - :language: cpp + :language: c++ :lines: 8-22 -.. note:: The Java code utilizes the `Units `_ utility, for easy unit conversions. + .. tab-item:: Python + + .. literalinclude:: examples/trajectory-generation-1/py/ExampleTrajectory.py + :language: python + :lines: 5-20 + +.. note:: The Java code utilizes the [Units](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/util/Units.html) utility, for easy unit conversions. .. note:: Generating a typical trajectory takes about 10 ms to 25 ms. This isn't long, but it's still highly recommended to generate all trajectories on startup (``robotInit``). -Concatenating Trajectories --------------------------- +## Concatenating Trajectories -Trajectories in Java can be combined into a single trajectory using the ``concatenate(trajectory)`` function. C++ users can simply add (``+``) the two trajectories together. +Trajectories in Java can be combined into a single trajectory using the ``concatenate(trajectory)`` function. C++/Python users can simply add (``+``) the two trajectories together. .. warning:: It is up to the user to ensure that the end of the initial and start of the appended trajectory match. It is also the user's responsibility to ensure that the start and end velocities of their trajectories match. .. tab-set-code:: - .. code-block:: java - - var trajectoryOne = - TrajectoryGenerator.generateTrajectory( - new Pose2d(0, 0, Rotation2d.fromDegrees(0)), - List.of(new Translation2d(1, 1), new Translation2d(2, -1)), - new Pose2d(3, 0, Rotation2d.fromDegrees(0)), - new TrajectoryConfig(Units.feetToMeters(3.0), Units.feetToMeters(3.0))); - - var trajectoryTwo = - TrajectoryGenerator.generateTrajectory( - new Pose2d(3, 0, Rotation2d.fromDegrees(0)), - List.of(new Translation2d(4, 4), new Translation2d(6, 3)), - new Pose2d(6, 0, Rotation2d.fromDegrees(0)), - new TrajectoryConfig(Units.feetToMeters(3.0), Units.feetToMeters(3.0))); - - var concatTraj = trajectoryOne.concatenate(trajectoryTwo); - - .. code-block:: cpp - - auto trajectoryOne = frc::TrajectoryGenerator::GenerateTrajectory( - frc::Pose2d(0_m, 0_m, 0_rad), - {frc::Translation2d(1_m, 1_m), frc::Translation2d(2_m, -1_m)}, - frc::Pose2d(3_m, 0_m, 0_rad), frc::TrajectoryConfig(3_fps, 3_fps_sq)); - - auto trajectoryTwo = frc::TrajectoryGenerator::GenerateTrajectory( - frc::Pose2d(3_m, 0_m, 0_rad), - {frc::Translation2d(4_m, 4_m), frc::Translation2d(5_m, 3_m)}, - frc::Pose2d(6_m, 0_m, 0_rad), frc::TrajectoryConfig(3_fps, 3_fps_sq)); + ```java + var trajectoryOne = + TrajectoryGenerator.generateTrajectory( + new Pose2d(0, 0, Rotation2d.fromDegrees(0)), + List.of(new Translation2d(1, 1), new Translation2d(2, -1)), + new Pose2d(3, 0, Rotation2d.fromDegrees(0)), + new TrajectoryConfig(Units.feetToMeters(3.0), Units.feetToMeters(3.0))); + var trajectoryTwo = + TrajectoryGenerator.generateTrajectory( + new Pose2d(3, 0, Rotation2d.fromDegrees(0)), + List.of(new Translation2d(4, 4), new Translation2d(6, 3)), + new Pose2d(6, 0, Rotation2d.fromDegrees(0)), + new TrajectoryConfig(Units.feetToMeters(3.0), Units.feetToMeters(3.0))); + var concatTraj = trajectoryOne.concatenate(trajectoryTwo); + ``` + + ```c++ + auto trajectoryOne = frc::TrajectoryGenerator::GenerateTrajectory( + frc::Pose2d(0_m, 0_m, 0_rad), + {frc::Translation2d(1_m, 1_m), frc::Translation2d(2_m, -1_m)}, + frc::Pose2d(3_m, 0_m, 0_rad), frc::TrajectoryConfig(3_fps, 3_fps_sq)); + auto trajectoryTwo = frc::TrajectoryGenerator::GenerateTrajectory( + frc::Pose2d(3_m, 0_m, 0_rad), + {frc::Translation2d(4_m, 4_m), frc::Translation2d(5_m, 3_m)}, + frc::Pose2d(6_m, 0_m, 0_rad), frc::TrajectoryConfig(3_fps, 3_fps_sq)); + auto concatTraj = m_trajectoryOne + m_trajectoryTwo; + ``` + + ```python + from wpimath.geometry import Pose2d, Rotation2d, Translation2d + from wpimath.trajectory import TrajectoryGenerator, TrajectoryConfig + trajectoryOne = TrajectoryGenerator.generateTrajectory( + Pose2d(0, 0, Rotation2d.fromDegrees(0)), + [Translation2d(1, 1), Translation2d(2, -1)], + Pose2d(3, 0, Rotation2d.fromDegrees(0)), + TrajectoryConfig.fromFps(3.0, 3.0), + ) + trajectoryTwo = TrajectoryGenerator.generateTrajectory( + Pose2d(3, 0, Rotation2d.fromDegrees(0)), + [Translation2d(4, 4), Translation2d(6, 3)], + Pose2d(6, 0, Rotation2d.fromDegrees(0)), + TrajectoryConfig.fromFps(3.0, 3.0), + ) + concatTraj = trajectoryOne + trajectoryTwo + ``` - auto concatTraj = m_trajectoryOne + m_trajectoryTwo; diff --git a/source/docs/software/advanced-controls/trajectories/transforming-trajectories.rst b/source/docs/software/advanced-controls/trajectories/transforming-trajectories.rst index 38b8d46bed..867db91533 100644 --- a/source/docs/software/advanced-controls/trajectories/transforming-trajectories.rst +++ b/source/docs/software/advanced-controls/trajectories/transforming-trajectories.rst @@ -1,12 +1,10 @@ -Transforming Trajectories -========================= +# Transforming Trajectories Trajectories can be transformed from one coordinate system to another and moved within a coordinate system using the ``relativeTo`` and the ``transformBy`` methods. These methods are useful for moving trajectories within space, or redefining an already existing trajectory in another frame of reference. .. note:: Neither of these methods changes the shape of the original trajectory. -The ``relativeTo`` Method -------------------------- +## The ``relativeTo`` Method The ``relativeTo`` method is used to redefine an already existing trajectory in another frame of reference. This method takes one argument: a pose, (via a ``Pose2d`` object) that is defined with respect to the current coordinate system, that represents the origin of the new coordinate system. @@ -14,23 +12,28 @@ For example, a trajectory defined in coordinate system A can be redefined in coo .. tab-set-code:: - .. code-block:: java + ```java + Pose2d bOrigin = new Pose2d(3, 3, Rotation2d.fromDegrees(30)); + Trajectory bTrajectory = aTrajectory.relativeTo(bOrigin); + ``` - Pose2d bOrigin = new Pose2d(3, 3, Rotation2d.fromDegrees(30)); - Trajectory bTrajectory = aTrajectory.relativeTo(bOrigin); + ```c++ + frc::Pose2d bOrigin{3_m, 3_m, frc::Rotation2d(30_deg)}; + frc::Trajectory bTrajectory = aTrajectory.RelativeTo(bOrigin); + ``` - .. code-block:: c++ - - frc::Pose2d bOrigin{3_m, 3_m, frc::Rotation2d(30_deg)}; - frc::Trajectory bTrajectory = aTrajectory.RelativeTo(bOrigin); + ```python + from wpimath.geometry import Pose2d, Rotation2d + bOrigin = Pose2d(3, 3, Rotation2d.fromDegrees(30)) + bTrajectory = aTrajectory.relativeTo(bOrigin) + ``` .. image:: images/relative-to.png :alt: Coordinate system representation of the trajectory. In the diagram above, the original trajectory (``aTrajectory`` in the code above) has been defined in coordinate system A, represented by the black axes. The red axes, located at (3, 3) and 30° with respect to the original coordinate system, represent coordinate system B. Calling ``relativeTo`` on ``aTrajectory`` will redefine all poses in the trajectory to be relative to coordinate system B (red axes). -The ``transformBy`` Method --------------------------- +## The ``transformBy`` Method The ``transformBy`` method can be used to move (i.e. translate and rotate) a trajectory within a coordinate system. This method takes one argument: a transform (via a ``Transform2d`` object) that maps the current initial position of the trajectory to a desired initial position of the same trajectory. @@ -38,15 +41,21 @@ For example, one may want to transform a trajectory that begins at (2, 2, 30 deg .. tab-set-code:: - .. code-block:: java - - Transform2d transform = new Pose2d(4, 4, Rotation2d.fromDegrees(50)).minus(trajectory.getInitialPose()); - Trajectory newTrajectory = trajectory.transformBy(transform); - - .. code-block:: c++ - - frc::Transform2d transform = Pose2d(4_m, 4_m, Rotation2d(50_deg)) - trajectory.InitialPose(); - frc::Trajectory newTrajectory = trajectory.TransformBy(transform); + ```java + Transform2d transform = new Pose2d(4, 4, Rotation2d.fromDegrees(50)).minus(trajectory.getInitialPose()); + Trajectory newTrajectory = trajectory.transformBy(transform); + ``` + + ```c++ + frc::Transform2d transform = Pose2d(4_m, 4_m, Rotation2d(50_deg)) - trajectory.InitialPose(); + frc::Trajectory newTrajectory = trajectory.TransformBy(transform); + ``` + + ```python + from wpimath.geometry import Pose2d, Rotation2d + transform = Pose2d(4, 4, Rotation2d.fromDegrees(50)) - trajectory.initialPose() + newTrajectory = trajectory.transformBy(transform) + ``` .. image:: images/transform-by.png :alt: Coordinate system plot of a transformed trajectory. diff --git a/source/docs/software/advanced-controls/trajectories/troubleshooting.rst b/source/docs/software/advanced-controls/trajectories/troubleshooting.rst index 41da7d9b4e..fdd4ba3e30 100644 --- a/source/docs/software/advanced-controls/trajectories/troubleshooting.rst +++ b/source/docs/software/advanced-controls/trajectories/troubleshooting.rst @@ -1,8 +1,6 @@ -Troubleshooting -=============== +# Troubleshooting -Troubleshooting Complete Failures ---------------------------------- +## Troubleshooting Complete Failures There are a number of things that can cause your robot to do completely the wrong thing. The below checklist covers some common mistakes. * My robot doesn't move. @@ -39,8 +37,7 @@ There are a number of things that can cause your robot to do completely the wron - Go to the next section. -Troubleshooting Poor Performance --------------------------------- +## Troubleshooting Poor Performance .. note:: This section is mostly concerned with troubleshooting poor trajectory tracking performance like a meter of error, not catastrophic failures like compilation errors, robots turning around and going in the wrong direction, or ``MalformedSplineException``\s. @@ -52,8 +49,7 @@ Because it can be so hard to locate the layer of the trajectory generator and fo .. note:: The below examples put diagnostic values onto :term:`NetworkTables`. The easiest way to graph these values is to :ref:`use Shuffleboard's graphing capabilities `. -Verify Odometry -^^^^^^^^^^^^^^^ +### Verify Odometry If your odometry is bad, then your Ramsete controller may misbehave, because it modifies your robot's target velocities based on where your odometry thinks the robot is. .. note:: :doc:`Sending your robot pose and trajectory to field2d ` can help verify that your robot is driving correctly relative to the robot trajectory. @@ -62,60 +58,51 @@ If your odometry is bad, then your Ramsete controller may misbehave, because it .. tab-set-code:: - .. code-block:: java - - NetworkTableEntry m_xEntry = NetworkTableInstance.getDefault().getTable("troubleshooting").getEntry("X"); - NetworkTableEntry m_yEntry = NetworkTableInstance.getDefault().getTable("troubleshooting").getEntry("Y"); - - @Override - public void periodic() { - // Update the odometry in the periodic block - m_odometry.update(Rotation2d.fromDegrees(getHeading()), m_leftEncoder.getDistance(), - m_rightEncoder.getDistance()); - - var translation = m_odometry.getPoseMeters().getTranslation(); - m_xEntry.setNumber(translation.getX()); - m_yEntry.setNumber(translation.getY()); - } - - .. code-block:: c++ - - NetworkTableEntry m_xEntry = nt::NetworkTableInstance::GetDefault().GetTable("troubleshooting")->GetEntry("X"); - NetworkTableEntry m_yEntry = nt::NetworkTableInstance::GetDefault().GetTable("troubleshooting")->GetEntry("Y"); - - void DriveSubsystem::Periodic() { - // Implementation of subsystem periodic method goes here. - m_odometry.Update(frc::Rotation2d(units::degree_t(GetHeading())), - units::meter_t(m_leftEncoder.GetDistance()), - units::meter_t(m_rightEncoder.GetDistance())); - - auto translation = m_odometry.GetPose().Translation(); - m_xEntry.SetDouble(translation.X().value()); - m_yEntry.SetDouble(translation.Y().value()); - } + ```java + NetworkTableEntry m_xEntry = NetworkTableInstance.getDefault().getTable("troubleshooting").getEntry("X"); + NetworkTableEntry m_yEntry = NetworkTableInstance.getDefault().getTable("troubleshooting").getEntry("Y"); + @Override + public void periodic() { + // Update the odometry in the periodic block + m_odometry.update(Rotation2d.fromDegrees(getHeading()), m_leftEncoder.getDistance(), + m_rightEncoder.getDistance()); + var translation = m_odometry.getPoseMeters().getTranslation(); + m_xEntry.setNumber(translation.getX()); + m_yEntry.setNumber(translation.getY()); + } + ``` + + ```c++ + NetworkTableEntry m_xEntry = nt::NetworkTableInstance::GetDefault().GetTable("troubleshooting")->GetEntry("X"); + NetworkTableEntry m_yEntry = nt::NetworkTableInstance::GetDefault().GetTable("troubleshooting")->GetEntry("Y"); + void DriveSubsystem::Periodic() { + // Implementation of subsystem periodic method goes here. + m_odometry.Update(frc::Rotation2d(units::degree_t(GetHeading())), + units::meter_t(m_leftEncoder.GetDistance()), + units::meter_t(m_rightEncoder.GetDistance())); + auto translation = m_odometry.GetPose().Translation(); + m_xEntry.SetDouble(translation.X().value()); + m_yEntry.SetDouble(translation.Y().value()); + } + ``` 2. Lay out a tape measure parallel to your robot and push your robot out about one meter along the tape measure. Lay out a tape measure along the Y axis and start over, pushing your robot one meter along the X axis and one meter along the Y axis in a rough arc. -3. Compare X and Y reported by the robot to actual X and Y. If X is off by more than 5 centimeters in the first test then you should check that you measured your wheel diameter correctly, and that your wheels are not worn down. If the second test is off by more than 5 centimeters in either X or Y then your track width (distance from the center of the left wheel to the center of the right wheel) may be incorrect; if you're sure that you measured the track width correctly with a tape measure then your robot's wheels may be slipping in a way that is not accounted for by track width--if this is the case then you should :ref:`run the track width identification ` using the "Drivetrain (Angular)" test in SysID and use that track width instead of the one from your tape measure. +3. Compare X and Y reported by the robot to actual X and Y. If X is off by more than 5 centimeters in the first test then you should check that you measured your wheel diameter correctly, and that your wheels are not worn down. If the second test is off by more than 5 centimeters in either X or Y then your track width (distance from the center of the left wheel to the center of the right wheel) may be incorrect; if you're sure that you measured the track width correctly with a tape measure then your robot's wheels may be slipping in a way that is not accounted for by track width, so try increasing the track width number or measuring it programmatically. -.. image:: images/sysid-trackwidth.png - :alt: Highlights the trackwidth section of identification. - :width: 350px - -Verify Feedforward -^^^^^^^^^^^^^^^^^^ +### Verify Feedforward If your feedforwards are bad then the P controllers for each side of the robot will not track as well, and your ``DifferentialDriveVoltageConstraint`` will not limit your robot's acceleration accurately. We mostly want to turn off the wheel P controllers so that we can isolate and test the feedforwards. 1. First, we must set disable the P controller for each wheel. Set the ``P`` gain to 0 for every controller. In the ``RamseteCommand`` example, you would set ``kPDriveVel`` to 0: .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/RobotContainer.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/RobotContainer.java :language: java :lines: 123-124 :linenos: :lineno-start: 123 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/RobotContainer.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/RamseteCommand/cpp/RobotContainer.cpp :language: c++ :lines: 79-80 :linenos: @@ -125,117 +112,106 @@ If your feedforwards are bad then the P controllers for each side of the robot w .. tab-set-code:: - .. code-block:: java - - RamseteController m_disabledRamsete = new RamseteController(); - m_disabledRamsete.setEnabled(false); - - // Be sure to pass your new disabledRamsete variable - RamseteCommand ramseteCommand = new RamseteCommand( - exampleTrajectory, - m_robotDrive::getPose, - m_disabledRamsete, - ... - ); - - .. code-block:: c++ - - frc::RamseteController m_disabledRamsete; - m_disabledRamsete.SetEnabled(false); - - // Be sure to pass your new disabledRamsete variable - frc2::RamseteCommand ramseteCommand( - exampleTrajectory, - [this]() { return m_drive.GetPose(); }, - m_disabledRamsete, - ... - ); + ```java + RamseteController m_disabledRamsete = new RamseteController(); + m_disabledRamsete.setEnabled(false); + // Be sure to pass your new disabledRamsete variable + RamseteCommand ramseteCommand = new RamseteCommand( + exampleTrajectory, + m_robotDrive::getPose, + m_disabledRamsete, + ... + ); + ``` + + ```c++ + frc::RamseteController m_disabledRamsete; + m_disabledRamsete.SetEnabled(false); + // Be sure to pass your new disabledRamsete variable + frc2::RamseteCommand ramseteCommand( + exampleTrajectory, + [this]() { return m_drive.GetPose(); }, + m_disabledRamsete, + ... + ); + ``` 3. Finally, we need to log desired wheel velocity and actual wheel velocity (you should put actual and desired velocities on the same graph if you're using Shuffleboard, or if your graphing software has that capability): .. tab-set-code:: - .. code-block:: java - - var table = NetworkTableInstance.getDefault().getTable("troubleshooting"); - var leftReference = table.getEntry("left_reference"); - var leftMeasurement = table.getEntry("left_measurement"); - var rightReference = table.getEntry("right_reference"); - var rightMeasurement = table.getEntry("right_measurement"); - - var leftController = new PIDController(kPDriveVel, 0, 0); - var rightController = new PIDController(kPDriveVel, 0, 0); - RamseteCommand ramseteCommand = new RamseteCommand( - exampleTrajectory, - m_robotDrive::getPose, - disabledRamsete, // Pass in disabledRamsete here - new SimpleMotorFeedforward(ksVolts, kvVoltSecondsPerMeter, kaVoltSecondsSquaredPerMeter), - kDriveKinematics, - m_robotDrive::getWheelSpeeds, - leftController, - rightController, - // RamseteCommand passes volts to the callback - (leftVolts, rightVolts) -> { - m_robotDrive.tankDriveVolts(leftVolts, rightVolts); - - leftMeasurement.setNumber(m_robotDrive.getWheelSpeeds().leftMetersPerSecond); - leftReference.setNumber(leftController.getSetpoint()); - - rightMeasurement.setNumber(m_robotDrive.getWheelSpeeds().rightMetersPerSecond); - rightReference.setNumber(rightController.getSetpoint()); - }, - m_robotDrive - ); - - .. code-block:: c++ - - auto table = - nt::NetworkTableInstance::GetDefault().GetTable("troubleshooting"); - auto leftRef = table->GetEntry("left_reference"); - auto leftMeas = table->GetEntry("left_measurement"); - auto rightRef = table->GetEntry("right_reference"); - auto rightMeas = table->GetEntry("right_measurement"); - - frc::PIDController leftController(DriveConstants::kPDriveVel, 0, 0); - frc::PIDController rightController(DriveConstants::kPDriveVel, 0, 0); - frc2::RamseteCommand ramseteCommand( - exampleTrajectory, [this]() { return m_drive.GetPose(); }, - frc::RamseteController(AutoConstants::kRamseteB, - AutoConstants::kRamseteZeta), - frc::SimpleMotorFeedforward( - DriveConstants::ks, DriveConstants::kv, DriveConstants::ka), - DriveConstants::kDriveKinematics, - [this] { return m_drive.GetWheelSpeeds(); }, leftController, - rightController, - [=](auto left, auto right) { - auto leftReference = leftRef; - auto leftMeasurement = leftMeas; - auto rightReference = rightRef; - auto rightMeasurement = rightMeas; - - m_drive.TankDriveVolts(left, right); - - leftMeasurement.SetDouble(m_drive.GetWheelSpeeds().left.value()); - leftReference.SetDouble(leftController.GetSetpoint()); - - rightMeasurement.SetDouble(m_drive.GetWheelSpeeds().right.value()); - rightReference.SetDouble(rightController.GetSetpoint()); - }, - {&m_drive}); + ```java + var table = NetworkTableInstance.getDefault().getTable("troubleshooting"); + var leftReference = table.getEntry("left_reference"); + var leftMeasurement = table.getEntry("left_measurement"); + var rightReference = table.getEntry("right_reference"); + var rightMeasurement = table.getEntry("right_measurement"); + var leftController = new PIDController(kPDriveVel, 0, 0); + var rightController = new PIDController(kPDriveVel, 0, 0); + RamseteCommand ramseteCommand = new RamseteCommand( + exampleTrajectory, + m_robotDrive::getPose, + disabledRamsete, // Pass in disabledRamsete here + new SimpleMotorFeedforward(ksVolts, kvVoltSecondsPerMeter, kaVoltSecondsSquaredPerMeter), + kDriveKinematics, + m_robotDrive::getWheelSpeeds, + leftController, + rightController, + // RamseteCommand passes volts to the callback + (leftVolts, rightVolts) -> { + m_robotDrive.tankDriveVolts(leftVolts, rightVolts); + leftMeasurement.setNumber(m_robotDrive.getWheelSpeeds().leftMetersPerSecond); + leftReference.setNumber(leftController.getSetpoint()); + rightMeasurement.setNumber(m_robotDrive.getWheelSpeeds().rightMetersPerSecond); + rightReference.setNumber(rightController.getSetpoint()); + }, + m_robotDrive + ); + ``` + + ```c++ + auto table = + nt::NetworkTableInstance::GetDefault().GetTable("troubleshooting"); + auto leftRef = table->GetEntry("left_reference"); + auto leftMeas = table->GetEntry("left_measurement"); + auto rightRef = table->GetEntry("right_reference"); + auto rightMeas = table->GetEntry("right_measurement"); + frc::PIDController leftController(DriveConstants::kPDriveVel, 0, 0); + frc::PIDController rightController(DriveConstants::kPDriveVel, 0, 0); + frc2::RamseteCommand ramseteCommand( + exampleTrajectory, [this]() { return m_drive.GetPose(); }, + frc::RamseteController(AutoConstants::kRamseteB, + AutoConstants::kRamseteZeta), + frc::SimpleMotorFeedforward( + DriveConstants::ks, DriveConstants::kv, DriveConstants::ka), + DriveConstants::kDriveKinematics, + [this] { return m_drive.GetWheelSpeeds(); }, leftController, + rightController, + [=](auto left, auto right) { + auto leftReference = leftRef; + auto leftMeasurement = leftMeas; + auto rightReference = rightRef; + auto rightMeasurement = rightMeas; + m_drive.TankDriveVolts(left, right); + leftMeasurement.SetDouble(m_drive.GetWheelSpeeds().left.value()); + leftReference.SetDouble(leftController.GetSetpoint()); + rightMeasurement.SetDouble(m_drive.GetWheelSpeeds().right.value()); + rightReference.SetDouble(rightController.GetSetpoint()); + }, + {&m_drive}); + ``` 4. Run the robot on a variety of trajectories (curved and straight line), and check to see if the actual velocity tracks the desired velocity by looking at graphs from NetworkTables. 5. If the desired and actual are off by *a lot* then you should check if the wheel diameter and ``encoderEPR`` you used for system identification were correct. If you've verified that your units and conversions are correct, then you should try recharacterizing on the same floor that you're testing on to see if you can get better data. -Verify P Gain -^^^^^^^^^^^^^ +### Verify P Gain If you completed the previous step and the problem went away then your problem can probably be found in one of the next steps. In this step we're going to verify that your wheel P controllers are well-tuned. If you're using Java then we want to turn off Ramsete so that we can just view our PF controllers on their own. -1. You must re-use all the code from the previous step that logs actual vs. desired velocity (and the code that disables Ramsete, if you're using Java), except that **the P gain must be set back to its previous nonzero value.** +1. You must reuse all the code from the previous step that logs actual vs. desired velocity (and the code that disables Ramsete, if you're using Java), except that **the P gain must be set back to its previous nonzero value.** 2. Run the robot again on a variety of trajectories, and check that your actual vs. desired graphs look good. 3. If the graphs do not look good (i.e. the actual velocity is very different from the desired) then you should try tuning your P gain and rerunning your test trajectories. -Check Constraints -^^^^^^^^^^^^^^^^^ +### Check Constraints .. note:: Make sure that your P gain is nonzero for this step and that you still have the logging code added in the previous steps. If you're using Java then you should remove the code to disable Ramsete. If your accuracy issue persisted through all of the previous steps then you might have an issue with your constraints. Below are a list of symptoms that the different available constraints will exhibit when poorly tuned. @@ -255,6 +231,5 @@ Test one constraint at a time! Remove the other constraints, tune your one remai - If your robot ends up at the wrong heading then this could be the culprit. If your robot doesn't seem to turn enough then you should increase the max centripetal acceleration, but if it seems to go around tight turns to quickly then you should decrease the maximum centripetal acceleration. -Check Trajectory Waypoints -^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Check Trajectory Waypoints It is possible that your trajectory itself is not very driveable. Try moving waypoints (and headings at the waypoints, if applicable) to reduce sharp turns. diff --git a/source/docs/software/advanced-controls/video-walkthrough.rst b/source/docs/software/advanced-controls/video-walkthrough.rst index 781edbc1f0..41ed0187d5 100644 --- a/source/docs/software/advanced-controls/video-walkthrough.rst +++ b/source/docs/software/advanced-controls/video-walkthrough.rst @@ -1,7 +1,6 @@ .. include:: -A Video Walkthrough of Model Based Validation of Autonomous in FRC -================================================================== +# A Video Walkthrough of Model Based Validation of Autonomous in FRC At the "RSN Spring Conference, Presented by WPI" in 2020, Tyler Veness from the WPILib team gave a presentation on Model Based Validation of Autonomous in FRC\ |reg|. .. raw:: html @@ -9,4 +8,4 @@ At the "RSN Spring Conference, Presented by WPI" in 2020, Tyler Veness from the
-The link to the presentation is available `here `_. +The link to the presentation is available [here](https://github.com/calcmogul/auton-driving-presentation). diff --git a/source/docs/software/advanced-gradlerio/code-formatting.rst b/source/docs/software/advanced-gradlerio/code-formatting.rst index 9fa240c47f..56e2ec27f9 100644 --- a/source/docs/software/advanced-gradlerio/code-formatting.rst +++ b/source/docs/software/advanced-gradlerio/code-formatting.rst @@ -1,102 +1,97 @@ -Using a Code Formatter -====================== +# Using a Code Formatter Code formatters exist to ensure that the style of code written is consistent throughout the entire codebase. This is used in many major projects; from Android to OpenCV. Teams may wish to add a formatter throughout their robot code to ensure that the codebase maintains readability and consistency throughout. -For this article, we will highlight using `Spotless `__ for Java teams and `wpiformat `__ for C++ teams. +For this article, we will highlight using [Spotless](https://github.com/diffplug/spotless) for Java teams and [wpiformat](https://github.com/wpilibsuite/styleguide/blob/main/wpiformat/README.rst) for C++ teams. -Spotless --------- +## Spotless -Configuration -^^^^^^^^^^^^^ +### Configuration Necessary ``build.gradle`` changes are required to get Spotless functional. In the ``plugins {}`` block of your ``build.gradle``, add the Spotless plugin so it appears similar to the below. -.. code-block:: groovy - - plugins { - id "java" - id "edu.wpi.first.GradleRIO" version "2022.1.1" - id 'com.diffplug.spotless' version '6.12.0' - } +```groovy +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2024.1.1" + id 'com.diffplug.spotless' version '6.20.0' +} +``` Then ensure you add a required ``spotless {}`` block to correctly configure spotless. This can just get placed at the end of your ``build.gradle``. -.. code-block:: groovy - - spotless { - java { - target fileTree('.') { - include '**/*.java' - exclude '**/build/**', '**/build-*/**' - } - toggleOffOn() - googleJavaFormat() - removeUnusedImports() - trimTrailingWhitespace() - endWithNewline() +```groovy +spotless { + java { + target fileTree('.') { + include '**/*.java' + exclude '**/build/**', '**/build-*/**' } - groovyGradle { - target fileTree('.') { - include '**/*.gradle' - exclude '**/build/**', '**/build-*/**' - } - greclipse() - indentWithSpaces(4) - trimTrailingWhitespace() - endWithNewline() + toggleOffOn() + googleJavaFormat() + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + } + groovyGradle { + target fileTree('.') { + include '**/*.gradle' + exclude '**/build/**', '**/build-*/**' } - format 'xml', { - target fileTree('.') { - include '**/*.xml' - exclude '**/build/**', '**/build-*/**' - } - eclipseWtp('xml') - trimTrailingWhitespace() - indentWithSpaces(2) - endWithNewline() + greclipse() + indentWithSpaces(4) + trimTrailingWhitespace() + endWithNewline() + } + format 'xml', { + target fileTree('.') { + include '**/*.xml' + exclude '**/build/**', '**/build-*/**' } - format 'misc', { - target fileTree('.') { - include '**/*.md', '**/.gitignore' - exclude '**/build/**', '**/build-*/**' - } - trimTrailingWhitespace() - indentWithSpaces(2) - endWithNewline() + eclipseWtp('xml') + trimTrailingWhitespace() + indentWithSpaces(2) + endWithNewline() + } + format 'misc', { + target fileTree('.') { + include '**/*.md', '**/.gitignore' + exclude '**/build/**', '**/build-*/**' } + trimTrailingWhitespace() + indentWithSpaces(2) + endWithNewline() } +} +``` -Running Spotless -^^^^^^^^^^^^^^^^ +### Running Spotless Spotless can be ran using ``./gradlew spotlessApply`` which will apply all formatting options. You can also specify a specific task by just adding the name of formatter. An example is ``./gradlew spotlessmiscApply``. In addition to formatting code, Spotless can also ensure the code is correctly formatted; this can be used by running ``./gradlew spotlessCheck``. Thus, Spotless can be used as a :doc:`CI check `, as shown in the following GitHub Actions workflow: -.. code-block:: yaml - - on: [push] - # A workflow run is made up of one or more jobs that can run sequentially or in parallel - jobs: - spotless: - # The type of runner that the job will run on - runs-on: ubuntu-latest - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: 17 - - run: ./gradlew spotlessCheck - -Explanation of Options -^^^^^^^^^^^^^^^^^^^^^^ +```yaml +on: [push] +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + spotless: + # The type of runner that the job will run on + runs-on: ubuntu-latest + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 17 + - run: ./gradlew spotlessCheck +``` + +### Explanation of Options Each ``format`` section highlights formatting of custom files in the project. The ``java`` and ``groovyGradle`` are natively supported by spotless, so they are defined differently. @@ -109,44 +104,41 @@ Breaking this down, we can split this into multiple parts. They are all similar, except for some small differences that will be explained. The below example will highlight the ``java {}`` block. -.. code-block:: groovy - - java { - target fileTree('.') { - include '**/*.java' - exclude '**/build/**', '**/build-*/**' - } - toggleOffOn() - googleJavaFormat() - removeUnusedImports() - trimTrailingWhitespace() - endWithNewline() - } - -Let's explain what each of the options mean. - -.. code-block:: groovy - +```groovy +java { target fileTree('.') { include '**/*.java' exclude '**/build/**', '**/build-*/**' } + toggleOffOn() + googleJavaFormat() + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() +} +``` -The above example tells spotless where our Java classes are and to exclude the ``build`` directory. The rest of the options are fairly self-explanatory. - -- ``toggleOffOn()`` adds the ability to have spotless ignore specific portions of a project. The usage looks like the following - -.. code-block:: java +Let's explain what each of the options mean. - // format:off +```groovy +target fileTree('.') { + include '**/*.java' + exclude '**/build/**', '**/build-*/**' +} +``` - public void myWeirdFunction() { +The above example tells spotless where our Java classes are and to exclude the ``build`` directory. The rest of the options are fairly self-explanatory. - } +- ``toggleOffOn()`` adds the ability to have spotless ignore specific portions of a project. The usage looks like the following - // format:on +```java +// format:off +public void myWeirdFunction() { +} +// format:on +``` -- ``googleJavaFormat()`` tells spotless to format according to the `Google Style Guide `__ +- ``googleJavaFormat()`` tells spotless to format according to the [Google Style Guide](https://google.github.io/styleguide/javaguide.html) - ``removeUnusedImports()`` will remove any unused imports from any of your Java classes - ``trimTrailingWhitespace()`` will remove any extra whitespace at the end of your lines - ``endWithNewline()`` will add a newline character to the end of your classes @@ -155,32 +147,28 @@ In the ``groovyGradle`` block, there is a ``greclipse`` option. This is the form Additionally, there is a ``eclipseWtp`` option in the ``xml`` block. This stands for "Gradle Web Tools Platform" and is the formatter to format ``xml`` files. Teams not using any XML files may wish to not include this configuration. -.. note:: A full list of configurations is available on the `Spotless README `__ +.. note:: A full list of configurations is available on the [Spotless README](https://github.com/diffplug/spotless) -Issues with Line Endings -^^^^^^^^^^^^^^^^^^^^^^^^ +### Issues with Line Endings Spotless will attempt to apply line endings per-OS, which means Git diffs will be constantly changing if two users are on different OSes (Unix vs Windows). It's recommended that teams who contribute to the same repository from multiple OSes utilize a ``.gitattributes`` file. The following should suffice for handling line endings. -.. code-block:: text - - *.gradle text eol=lf - *.java text eol=lf - *.md text eol=lf - *.xml text eol=lf +```text +*.gradle text eol=lf +*.java text eol=lf +*.md text eol=lf +*.xml text eol=lf +``` -wpiformat ---------- +## wpiformat -Requirements -^^^^^^^^^^^^ +### Requirements -- `Python 3.6 or higher `__ +- [Python 3.6 or higher](https://www.python.org/) -You can install `wpiformat `__ by typing ``pip3 install wpiformat`` into a terminal or command prompt. +You can install [wpiformat](https://github.com/wpilibsuite/styleguide/blob/main/wpiformat/README.rst) by typing ``pip3 install wpiformat`` into a terminal or command prompt. -Usage -^^^^^ +### Usage wpiformat can be ran by typing ``wpiformat`` in a console. This will format with ``clang-format``. Three configuration files are required (``.clang-format``, ``.styleguide``, ``.styleguide-license``). These must exist in the project root. @@ -189,27 +177,25 @@ wpiformat can be ran by typing ``wpiformat`` in a console. This will format with An example styleguide is shown below: -.. code-block:: text - - cppHeaderFileInclude { - \.h$ - \.hpp$ - \.inc$ - \.inl$ - } - - cppSrcFileInclude { - \.cpp$ - } - - modifiableFileExclude { - gradle/ - } +```text +cppHeaderFileInclude { + \.h$ + \.hpp$ + \.inc$ + \.inl$ +} +cppSrcFileInclude { + \.cpp$ +} +modifiableFileExclude { + gradle/ +} +``` .. note:: Teams can adapt ``.styleguide`` and ``.styleguide-license`` however they wish. It's important that these are not deleted, as they are required to run wpiformat! You can turn this into a :doc:`CI check ` by running ``git --no-pager diff --exit-code HEAD``, as shown in the example GitHub Actions workflow below: -.. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/.github/workflows/lint-format.yml +.. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/.github/workflows/lint-format.yml :language: yaml - :lines: 1-5, 12-34 + :lines: 1-5, 13-34 diff --git a/source/docs/software/advanced-gradlerio/compiler-args.rst b/source/docs/software/advanced-gradlerio/compiler-args.rst new file mode 100644 index 0000000000..989e3a6138 --- /dev/null +++ b/source/docs/software/advanced-gradlerio/compiler-args.rst @@ -0,0 +1,32 @@ +# Using Compiler Arguments + +Compiler arguments allow us to change the behavior of our compiler. This includes making warnings into errors, ignoring certain warnings and choosing optimization level. When compiling code a variety of flags are already included by default which can be found [here](https://github.com/wpilibsuite/native-utils/blob/v2024.7.2/src/main/java/edu/wpi/first/nativeutils/WPINativeUtilsExtension.java#L38). Normally it could be proposed that the solution is to pass them in as flags when compiling our code but this doesn't work in GradleRIO. Instead modify the build.gradle. + +.. warning:: Modifying arguments is dangerous and can cause unexpected behavior. + +## C++ + +### Platforms + +Different compilers and different platforms use a variety of different flags. Therefore to avoid breaking different platforms with compiler flags configure all flags per platform. The platforms that are supported are listed [here](https://github.com/wpilibsuite/native-utils/blob/v2024.7.2/src/main/java/edu/wpi/first/nativeutils/WPINativeUtilsExtension.java#L96) + +### Configuring for a Platform + +```groovy +nativeUtils.platformConfigs.named('windowsx86-64').configure { + it.cppCompiler.args.add("/utf-8") +} +``` + +native-utils is used to configure the platform, in this case, `windowsx86-64`. This can be replaced for any platform listed in the previous section. Then arguments, such as `/utf-8` is appended to the C++ compiler. + +## Java + +Arguments can also be configured for Java. This can be accomplished by editing `build.gradle` and appending arguments to the `FRCJavaArtifact`. An example of this is shown below. + +```groovy +frcJava(getArtifactClass('FRCJavaArtifact')) { + jvmArgs.add("-XX:+DisableExplicitGC") +} +``` + diff --git a/source/docs/software/advanced-gradlerio/deploy-git-data.rst b/source/docs/software/advanced-gradlerio/deploy-git-data.rst index d1a7aad0aa..e121ea0853 100644 --- a/source/docs/software/advanced-gradlerio/deploy-git-data.rst +++ b/source/docs/software/advanced-gradlerio/deploy-git-data.rst @@ -1,113 +1,65 @@ -Including Git Data in Deploy -============================ +# Including Git Data in Deploy + +This article will explain how to include metadata from Git in robot code using the [gversion](https://github.com/lessthanoptimal/gversion-plugin) Gradle plugin. This generates a file which can be used for accessing Git metadata in robot code. This can be used to track what revision of code is on the robot, such as by printing or logging it. + +.. note:: For Python teams, Git metadata is always copied to your robot during the deploy process. You can use :external:py:func:`wpilib.deployinfo.getDeployData` to retrieve the stored information. + +## Installing gversion + +To install gversion add the following line to the plugins block of ``build.gradle``. This tells Gradle to use gversion in the project. + +```groovy +plugins { + // ... + id "com.peterabeles.gversion" version "1.10" +} +``` + +In order to generate the file when building the project, add the following block to the bottom of ``build.gradle``. + +```groovy +project.compileJava.dependsOn(createVersionFile) +gversion { + srcDir = "src/main/java/" + classPackage = "frc.robot" + className = "BuildConstants" + dateFormat = "yyyy-MM-dd HH:mm:ss z" + timeZone = "America/New_York" // Use preferred time zone + indent = " " +} +``` + +The ``srcDir``, ``classPackage``, and ``className`` parameters tell the plugin where to put the manifest file, and what to name it. The ``timeZone`` field can be set to your team's time zone based on [this list of timezone IDs](https://docs.oracle.com/middleware/12211/wcs/tag-ref/MISC/TimeZones.html). The ``dateFormat`` parameter is based on [this Java class](https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html). + +To test this, run a build of your project through the WPILib menu in the top right. Once the code has finished building, there should be a file called ``BuildConstants.java`` in your ``src/main/java`` folder. The following is an example of what this file should look like: + +```Java +package frc.robot; +/** +* Automatically generated file containing build version information. +*/ +public final class BuildConstants { + public static final String MAVEN_GROUP = ""; + public static final String MAVEN_NAME = "GitVersionTest"; + public static final String VERSION = "unspecified"; + public static final int GIT_REVISION = 1; + public static final String GIT_SHA = "fad108a4b1c1dcdfc8859c6295ea64e06d43f557"; + public static final String GIT_DATE = "2023-10-26 17:38:59 EDT"; + public static final String GIT_BRANCH = "main"; + public static final String BUILD_DATE = "2023-10-27 12:29:57 EDT"; + public static final long BUILD_UNIX_TIME = 1698424197122L; + public static final int DIRTY = 0; + private BuildConstants(){} +} +``` + +``DIRTY`` indicates whether there are uncommitted changes in the project. A value of 0 indicates a clean repository, a value of 1 indicates that there are uncommitted changes, and a value -1 indicates an error. + +### Ignoring Generated Files with Git + +These files are regenerated every time code is built or deployed, so it isn't necessary to track them with Git. The aptly named [.gitignore](https://git-scm.com/docs/gitignore) file tells Git not to track any listed files and should exist by default in any project generated by the WPILib VS Code extension. Below is the line you should add to ``.gitignore`` to ignore the generated file: + +``` +src/main/java/frc/robot/BuildConstants.java +``` -This article will go over how to include information from Git, such as branch name or commit hash, into the robot code. This is necessary for using such information in robot code, such as printing out commit hash and branch name when the robot starts. - -.. note:: Git must be in the path for this to work. This should be enabled by default when `installing Git `__. - -Deploying Branch Name ---------------------- - -This example uses `git rev-parse `__ to extract data the name of the current branch. The Git command used for this is: - -.. code-block:: console - - $ git rev-parse --abbrev-ref HEAD - -The ``--abbrev-ref`` flag tells Git to use a short version of the name for the current commit that ``rev-parse`` is acting on. When HEAD is the most recent commit on a branch, this will return the name of that branch. - -Next, create a new task in the ``build.gradle`` file that will run the above Git command and write it to a file in the ``src/main/deploy`` directory. For example, the following is an example task named ``writeBranchName`` that will write the branch name to a file named ``branch.txt``. - -.. code-block:: groovy - - tasks.register("writeBranchName") { - // Define an output stream to write to instead of terminal - def stdout = new ByteArrayOutputStream() - - // Execute the git command - exec { - commandLine "git", "rev-parse", "--abbrev-ref", "HEAD" - // Write to the output stream instead of terminal - standardOutput = stdout - } - - // Parse the output into a string - def branch = stdout.toString().trim() - - // Create a new file - new File( - // Join project directory and deploy directory - projectDir.toString() + "/src/main/deploy", - // File name to write to - "branch.txt" - ).text = branch // Set the contents of the file to the variable branch - } - -This registers a `Gradle task `__ that uses the above Git command, saves the output to a variable, and then writes it to a file. Since it was written to the ``src/main/deploy`` directory, it will be included in the jar file deployed to the robot and accessible in code. - -The next step is to make the deploy task depend on the task you created, so that it will automatically run before the code is deployed. This example uses the task name ``writeBranchName`` from the previous example, but it should be replaced with the name of the task in your ``build.gradle``. - -.. code-block:: groovy - - deploy.targets.roborio.artifacts.frcStaticFileDeploy.dependsOn(writeBranchName) - -Deploying Commit Hash ---------------------- - -Similar to the previous example, ``git rev-parse`` will be used to parse the current commit hash. The Git command used for this is: - -.. code-block:: console - - $ git rev-parse --short HEAD - -Similar to the previous Git command, ``rev-parse`` is used to find information about the commit at HEAD. However, instead of using ``--abbrev-ref`` to find the branch name associated with that commit, ``--short`` is used to find the 7-character commit hash. - -.. note:: If you wish to use the full commit hash instead of the 7-character version, you can leave out the ``--short`` flag. - -Next is to create a task in ``build.gradle`` that runs this command and writes the output to a file. This is largely the same as the first example, except the task is named ``writeCommitHash``, the new Git command is used, and it is written to ``commit.txt`` instead of ``branch.txt``. - -.. code-block:: groovy - - tasks.register("writeCommitHash") { - def stdout = new ByteArrayOutputStream() - - exec { - commandLine "git", "rev-parse", "--short", "HEAD" - standardOutput = stdout - } - - def commitHash = stdout.toString().trim() - - new File( - projectDir.toString() + "/src/main/deploy", - "commit.txt" - ).text = commitHash - } - - deploy.targets.roborio.artifacts.frcStaticFileDeploy.dependsOn(writeCommitHash) - -Ignoring Generated Files with Git -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Since these files include data that is already tracked by Git and are regenerated every time code is deployed, it is recommended to not track these changes with Git by using the `gitignore `__ file. This file should exist by default in any project generated by the WPILib VS Code extension. Below is an example that continues to use the ``branch.txt`` and ``commit.txt`` file names: - -.. code-block:: - - src/main/deploy/branch.txt - src/main/deploy/commit.txt - ... - -Using Deployed Files --------------------- - -In order to access files that were written to the deploy directory in code, you have to use the ``getDeployDirectory()`` method of the `Filesystem `__ class in Java, or the ``GetDeployDirectory()`` function of the `frc::filesystem `__ namespace in C++. Below is an example of opening both files from the previous examples: - -.. note:: Opening and reading the files is slow and should not be performed during any periodic methods. Since the file will only change on deploy, it only needs to be read once. - -.. code-block:: java - - File deployDir = Filesystem.getDeployDirectory(); - File branchFile = new File(deployDir, "branch.txt"); - File commitFile = new File(deployDir, "commit.txt"); - -For more information on how to interact with the file objects, see the documentation of the `File `__ class. diff --git a/source/docs/software/advanced-gradlerio/external-libraries.rst b/source/docs/software/advanced-gradlerio/external-libraries.rst index 82aa947996..c545bf8c7f 100644 --- a/source/docs/software/advanced-gradlerio/external-libraries.rst +++ b/source/docs/software/advanced-gradlerio/external-libraries.rst @@ -1,12 +1,10 @@ -Using External Libraries with Robot Code -======================================== +# Using External Libraries with Robot Code .. warning:: Using external libraries may have unintended behavior with your robot code! It is not recommended unless you are aware of what you are doing! Often a team might want to add external Java or C++ libraries for usage with their robot code. This article highlights adding Java libraries to your Gradle dependencies, or the options that C++ teams have. -Java ----- +## Java .. note:: Any external dependencies that rely on native libraries (JNI) are likely not going to work. @@ -14,44 +12,41 @@ Java is quite simple to add external dependencies. You simply add the required ` Robot projects by default do not have a ``repositories {}`` block in the ``build.gradle`` file. You will have to add this yourself. Above the ``dependencies {}`` block, please add the following: -.. code-block:: groovy - - repositories { - mavenCentral() - ... - } +```groovy +repositories { + mavenCentral() + ... +} +``` ``mavenCentral()`` can be replaced with whatever repository the library you want to import is using. Now you have to add the dependency on the library itself. This is done by adding the necessary line to your ``dependencies {}`` block. The below example showcases adding Apache Commons to your Gradle project. -.. code-block:: groovy - - dependencies { - implementation 'org.apache.commons:commons-lang3:3.6' - ... - } +```groovy +dependencies { + implementation 'org.apache.commons:commons-lang3:3.6' + ... +} +``` Now you run a build and ensure the dependencies are downloaded. Intellisense may not work properly until a build is ran! -C++ ---- +## C++ Adding C++ dependencies to your robot project is non-trivial due to needing to compile for the roboRIO. You have a couple of options. 1. Copy the source code of the wanted library into your robot project. -2. Use the `vendordep template `__ as an example and create a vendordep. +2. Use the [vendordep template](https://github.com/wpilibsuite/vendor-template) as an example and create a vendordep. -Copying Source Code -^^^^^^^^^^^^^^^^^^^ +### Copying Source Code Simply copy the necessary source and/or headers into your robot project. You can then configure any necessary platform args like below: -.. code-block:: groovy - - nativeUtils.platformConfigs.named("linuxx86-64").configure { - it.linker.args.add('-lstdc++fs') // links in C++ filesystem library - } +```groovy +nativeUtils.platformConfigs.named("linuxx86-64").configure { + it.linker.args.add('-lstdc++fs') // links in C++ filesystem library +} +``` -Creating a Vendordep -^^^^^^^^^^^^^^^^^^^^ +### Creating a Vendordep -Please follow the instructions in the `vendordep repository `__. +Please follow the instructions in the [vendordep repository](https://github.com/wpilibsuite/vendor-template). diff --git a/source/docs/software/advanced-gradlerio/gradlew-tasks.rst b/source/docs/software/advanced-gradlerio/gradlew-tasks.rst index e96b7b301e..1b27039155 100644 --- a/source/docs/software/advanced-gradlerio/gradlew-tasks.rst +++ b/source/docs/software/advanced-gradlerio/gradlew-tasks.rst @@ -1,40 +1,37 @@ -Gradlew Tasks -============= +# Gradlew Tasks This article aims to highlight the gradle commands supported by the WPILib team for user use. These commands can be viewed by typing ``./gradlew tasks`` at the root of your robot project. Not all commands shown in ``./gradlew tasks`` and unsupported commands will not be documented here. -Build tasks ------------ +## Build tasks ``./gradlew build`` - Assembles and tests this project. Useful for prebuilding your project without deploying to the roboRIO. + ``./gradlew clean`` - Deletes the build directory. -CompileCommands tasks ---------------------- +## CompileCommands tasks + +``./gradlew generateCompileCommands`` - Generate compile_commands.json for C++ programs. This is a configuration file that is used by clangd, which is supported by many Integrated Development Environments and build tools. This file will be saved in ``build/TargetedCompileCommands/targetplatformandbuildtype`` but needs to be moved to the root of your project. Many IDE's will have a way to specify where this file is found, which you can use instead if you desire. Make sure you have gitignored this file. -``./gradlew generateCompileCommands`` - Generate compile_commands.json. This is a configuration file that is supported by many Integrated Development Environments. +.. warning:: + Using compile commands isn't recommended for most users and has a variety of pitfalls compared to the built-in intellisense. -EmbeddedTools tasks -------------------- +## DeployUtils tasks ``./gradlew deploy`` - Deploy all artifacts on all targets. This will deploy your robot project to the available targets (IE, roboRIO). ``./gradlew discoverRoborio`` - Determine the address(es) of target roboRIO. This will print out the IP address of a connected roboRIO. -GradleRIO tasks ---------------- - -``./gradlew downloadAll`` - Download all dependencies that may be used by this project +## GradleRIO tasks ``./gradlew $TOOL$`` - Runs the tool ``$TOOL$`` (Replace ``$TOOL$`` with the name of the tool. IE, Glass, Shuffleboard, etc) -``./gradlew $TOOL$Install`` - Installs the tool ``$TOOL$`` (Replace ``$TOOL$`` with the name of the tool. IE, Glass, Shuffleboard, etc) +``./gradlew $TOOL$Install`` - Installs the java tool ``$TOOL$`` (Replace ``$TOOL$`` with the name of the tool. IE, Shuffleboard, SmartDashboard, etc) ``./gradlew InstallAllTools`` - Installs all available tools. This excludes the development environment such as Visual Studio Code. It's the users requirement to ensure the required dependencies (Java) is installed. Only recommended for advanced users! -``./gradlew simulateExternalCpp`` - Simulate External Task for native executable. Exports a JSON file for use by editors / tools +``./gradlew simulateExternalNativeRelease`` - Simulate External Task for native executable. Exports a JSON file for use by editors / tools -``./gradlew simulateExternalJava`` - Simulate External Task for Java/Kotlin/JVM. Exports a JSON file for use by editors / tools +``./gradlew simulateExternalJavaRelease`` - Simulate External Task for Java/Kotlin/JVM. Exports a JSON file for use by editors / tools ``./gradlew simulateJava`` - Launches simulation for the Java projects diff --git a/source/docs/software/advanced-gradlerio/images/robot-ci/actions.png b/source/docs/software/advanced-gradlerio/images/robot-ci/actions.png index 43790322b6..88ac954864 100644 Binary files a/source/docs/software/advanced-gradlerio/images/robot-ci/actions.png and b/source/docs/software/advanced-gradlerio/images/robot-ci/actions.png differ diff --git a/source/docs/software/advanced-gradlerio/images/robot-ci/badge.png b/source/docs/software/advanced-gradlerio/images/robot-ci/badge.png index 69a3f56ec8..f51589e41e 100644 Binary files a/source/docs/software/advanced-gradlerio/images/robot-ci/badge.png and b/source/docs/software/advanced-gradlerio/images/robot-ci/badge.png differ diff --git a/source/docs/software/advanced-gradlerio/images/robot-ci/commit-new-file.png b/source/docs/software/advanced-gradlerio/images/robot-ci/commit-new-file.png index ead3113cb7..bb1ceeb961 100644 Binary files a/source/docs/software/advanced-gradlerio/images/robot-ci/commit-new-file.png and b/source/docs/software/advanced-gradlerio/images/robot-ci/commit-new-file.png differ diff --git a/source/docs/software/advanced-gradlerio/images/robot-ci/repository.png b/source/docs/software/advanced-gradlerio/images/robot-ci/repository.png index 4d60d5f195..72434a3bd8 100644 Binary files a/source/docs/software/advanced-gradlerio/images/robot-ci/repository.png and b/source/docs/software/advanced-gradlerio/images/robot-ci/repository.png differ diff --git a/source/docs/software/advanced-gradlerio/images/robot-ci/setup.png b/source/docs/software/advanced-gradlerio/images/robot-ci/setup.png index 4ace90cdec..8b3eeacd88 100644 Binary files a/source/docs/software/advanced-gradlerio/images/robot-ci/setup.png and b/source/docs/software/advanced-gradlerio/images/robot-ci/setup.png differ diff --git a/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-addconn.png b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-addconn.png new file mode 100644 index 0000000000..4de132fe2d Binary files /dev/null and b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-addconn.png differ diff --git a/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-dash.png b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-dash.png new file mode 100644 index 0000000000..1ae01c69ef Binary files /dev/null and b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-dash.png differ diff --git a/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-dialog.png b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-dialog.png new file mode 100644 index 0000000000..627d6c9f2d Binary files /dev/null and b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-dialog.png differ diff --git a/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-function-profiling.png b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-function-profiling.png new file mode 100644 index 0000000000..663edf7627 Binary files /dev/null and b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-function-profiling.png differ diff --git a/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-objects1.png b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-objects1.png new file mode 100644 index 0000000000..42a4d645e5 Binary files /dev/null and b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-objects1.png differ diff --git a/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-objects2.png b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-objects2.png new file mode 100644 index 0000000000..ecb2f3d1de Binary files /dev/null and b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-objects2.png differ diff --git a/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-perform-heapdump.png b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-perform-heapdump.png new file mode 100644 index 0000000000..62563a21e4 Binary files /dev/null and b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-perform-heapdump.png differ diff --git a/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-viewing-dump.png b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-viewing-dump.png new file mode 100644 index 0000000000..22623e9f1b Binary files /dev/null and b/source/docs/software/advanced-gradlerio/images/visualvm/visualvm-viewing-dump.png differ diff --git a/source/docs/software/advanced-gradlerio/index.rst b/source/docs/software/advanced-gradlerio/index.rst index 83a32d5811..ae75b98836 100644 --- a/source/docs/software/advanced-gradlerio/index.rst +++ b/source/docs/software/advanced-gradlerio/index.rst @@ -1,5 +1,4 @@ -Advanced GradleRIO -================== +# Advanced GradleRIO GradleRIO is the mechanism that powers the deployment of robot code to the roboRIO. GradleRIO is built on the popular Gradle dependency and build management system. This section highlights **advanced** configurations that teams can use to enhance their workflow. @@ -11,3 +10,6 @@ GradleRIO is the mechanism that powers the deployment of robot code to the roboR code-formatting gradlew-tasks deploy-git-data + compiler-args + profiling-with-visualvm + diff --git a/source/docs/software/advanced-gradlerio/profiling-with-visualvm.rst b/source/docs/software/advanced-gradlerio/profiling-with-visualvm.rst new file mode 100644 index 0000000000..a915c42ea3 --- /dev/null +++ b/source/docs/software/advanced-gradlerio/profiling-with-visualvm.rst @@ -0,0 +1,134 @@ +# Profiling with VisualVM + +This document is intended to familiarize the reader with the diagnostic tool that is [VisualVM](https://visualvm.github.io/) for debugging Java robot programs. VisualVM is a tool for profiling JVM based applications, such as viewing why an application is using a large amount of memory. This document assumes the reader is familiar with the *risks* associated with modifying their robot ``build.gradle``. This tutorial also assumes that the user knows basic terminal/commandline knowledge. + +## Unpacking VisualVM + +To begin, [download VisualVM](https://visualvm.github.io/download.html) and unpack it to the WPILib installation folder. The folder is located at ``~/wpilib/`` where ``~`` indicates the users home directory. On Windows, this is ``C:\Users\Public\wpilib``. + +## Setting up Gradle + +GradleRIO supports passing JVM launch arguments, and this is what is necessary to enable remote debugging. Remote debugging is a feature that allows a local machine (such as the user's desktop) to view important information about a remote target (in our case, a roboRIO). To begin, locate the ``frcJava`` code block located in the projects ``build.gradle``. Below is what is looks like. + +.. rli:: https://raw.githubusercontent.com/wpilibsuite/vscode-wpilib/v2024.3.2/vscode-wpilib/resources/gradle/java/build.gradle + :language: groovy + :lines: 15-40 + :linenos: + :lineno-start: 15 + :emphasize-lines: 15-16 + + +We will be replacing the highlighted lines with: + +```groovy +frcJava(getArtifactTypeClass('FRCJavaArtifact')) { + // Enable VisualVM connection + jvmArgs.add("-Dcom.sun.management.jmxremote=true") + jvmArgs.add("-Dcom.sun.management.jmxremote.port=1198") + jvmArgs.add("-Dcom.sun.management.jmxremote.local.only=false") + jvmArgs.add("-Dcom.sun.management.jmxremote.ssl=false") + jvmArgs.add("-Dcom.sun.management.jmxremote.authenticate=false") + jvmArgs.add("-Djava.rmi.server.hostname=10.TE.AM.2") // Replace TE.AM with team number +} +``` + +We are adding a few arguments here. In order: + +* Enable remote debugging +* Set the remote debugging port to 1198 +* Allow listening from remote targets +* Disable SSL authentication being required +* Set the hostname to the roboRIOs team number. Be sure to replace this. (:ref:`TE.AM IP Notation `) + +.. important:: The hostname when connected via USB-B should be ``172.22.11.2``. + +## Running VisualVM + +Launching VisualVM is done via the commandline with a few parameters. First, we navigate to the directory containing VisualVM. Then, launch it with parameters, passing it the WPILib JDK path. On a Windows machine, it looks like the following: + +```bash +cd "C:\Users\Public\wpilib\visualvm_217\bin" +./visualvm --jdkhome "C:\Users\Public\wpilib\2024\jdk" +``` + +.. important:: The exact path ``visualvm_217`` may vary and depends on the version of VisualVM downloaded. + +This should launch VisualVM. Once launched, open the :guilabel:`Add JMX Connection` dialog. + +.. image:: images/visualvm/visualvm-addconn.png + :alt: Add visualvm connection menu option + :width: 700 + +Once opened, configure the connection details and hostname. Ensure that :guilabel:`Do not require SSL connection` is ticked. + +.. image:: images/visualvm/visualvm-dialog.png + :alt: VisualVM connection dialog is ticked + :width: 700 + +If correctly done, a new menu option in the left-hand sidebar will appear. Clicking on it will show you a detailed dashboard of the running JVM application. + +.. image:: images/visualvm/visualvm-dash.png + :alt: VisualVM diagnostics dashboard + :width: 700 + +## Analyzing Function Timings + +An important feature of VisualVM is the ability to view how much time a specific function is taking up. This is *without* having a code debugger attached. To begin, click on the :guilabel:`Sampler` tab and then click on :guilabel:`CPU`. This will immediately give a breakdown of what functions are taking CPU time. + +.. image:: images/visualvm/visualvm-function-profiling.png + :alt: Analyzing the VisualVM function timing tree + :width: 700 + +The above screenshot shows a breakdown of the total time a specific function takes. You can see that ``totallyNotSlowFunction()`` accounts for ``61.9%`` of the robot program CPU time. We can then correlate this to our robot program. In ``totallyNotSlowFunction()``, we have the following code. + +```Java +public static void totallyNotSlowFunction() { + for (int i = 0; i < 2000; i++) { + System.out.println("HAHAHAHA"); + } +} +``` + +In this code snippet, we can identify 2 major causes of concern. A long running ``for`` loop blocks the rest of the robot program from running. Additionally, ``System.out.println()`` calls on the roboRIO are typically quite expensive. We found this information by profiling the Java application on the roboRIO! + +## Creating a Heap Dump + +Besides viewing the remote systems CPU and memory usage, VisualVM is most useful by creating a **Heap Dump**. When a Java object is created, it resides in an area of memory called the heap. When the heap is full, a process called [garbage collection](https://www.geeksforgeeks.org/garbage-collection-java/) begins. Garbage collection can be a common cause of loop overruns in a traditional Java robot program. + +To begin, ensure you are on the :guilabel:`Monitor` tab and click :guilabel:`Heap Dump`. + +.. image:: images/visualvm/visualvm-perform-heapdump.png + :alt: Location of heap dump button in VisualVM + :width: 700 + +This heap dump will be stored on the target system (roboRIO) and must be retrieved using SFTP. See :doc:`this article ` for information on retrieving the dump from the roboRIO. + +Once downloaded, the dump can be analyzed with VisualVM. + +.. tip:: You can also :ref:`configure the JVM to take a heap dump automatically when your robot code runs out of memory `. + +## Analyzing a Heap Dump + +Reopen VisualVM if closed using the previous instructions. Then click on :guilabel:`File` and :guilabel:`Load`. Navigate to the retrieved dump file and load it. + +.. image:: images/visualvm/visualvm-viewing-dump.png + :alt: Viewing a dump in VisualVM + :width: 700 + +Clicking on :guilabel:`Summary` and selecting :guilabel:`Objects` instead will show a breakdown of objects by quantity. The below screenshot showcases a completely empty robot program, and then one that creates an million large ``ArrayList`` of integers. + +Blank robot program: + +.. image:: images/visualvm/visualvm-objects1.png + :alt: List of objects in a blank robot program + :width: 700 + +with an ``ArrayList`` of ~10000 integers. + +.. image:: images/visualvm/visualvm-objects2.png + :alt: List of objects in a modified robot program + :width: 700 + +## Additional Info + +For more information on VisualVM, check out the [VisualVM documentation pages](https://visualvm.github.io/documentation.html). diff --git a/source/docs/software/advanced-gradlerio/robot-code-ci.rst b/source/docs/software/advanced-gradlerio/robot-code-ci.rst index 78ad86618e..0e784f6bcb 100644 --- a/source/docs/software/advanced-gradlerio/robot-code-ci.rst +++ b/source/docs/software/advanced-gradlerio/robot-code-ci.rst @@ -1,14 +1,12 @@ -Setting up CI for Robot Code using GitHub Actions -================================================= +# Setting up CI for Robot Code using GitHub Actions An important aspect of working in a team environment is being able to test code that is pushed to a central repository such as GitHub. For example, a project manager or lead developer might want to run a set of unit tests before merging a pull request or might want to ensure that all code on the main branch of a repository is in working order. -`GitHub Actions `_ is a service that allows for teams and individuals to build and run unit tests on code on various branches and on pull requests. These types of services are more commonly known as "Continuous Integration" services. This tutorial will show you how to setup GitHub Actions on robot code projects. +[GitHub Actions](https://github.com/features/actions) is a service that allows for teams and individuals to build and run unit tests on code on various branches and on pull requests. These types of services are more commonly known as "Continuous Integration" services. This tutorial will show you how to setup GitHub Actions on robot code projects. .. note:: This tutorial assumes that your team's robot code is being hosted on GitHub. For an introduction to Git and GitHub, please see this :doc:`introduction guide `. -Creating the Action -------------------- +## Creating the Action The instructions for carrying out the CI process are stored in a YAML file. To create this, click on the "Actions" tab at the top of your repository. Then click on the "set up a workflow yourself" hyperlink. .. image:: images/robot-ci/setup.png @@ -16,46 +14,38 @@ The instructions for carrying out the CI process are stored in a YAML file. To c You will now be greeted with a text editor. Replace all the default text with the following: -.. code-block:: yaml - - # This is a basic workflow to build robot code. - - name: CI - - # Controls when the action will run. Triggers the workflow on push or pull request - # events but only for the main branch. - on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - - # A workflow run is made up of one or more jobs that can run sequentially or in parallel - jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # This grabs the WPILib docker container - container: wpilib/roborio-cross-ubuntu:2023-22.04 - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - - # Declares the repository safe and not under dubious ownership. - - name: Add repository to git safe directories - run: git config --global --add safe.directory $GITHUB_WORKSPACE - - # Grant execute permission for gradlew - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - # Runs a single command using the runners shell - - name: Compile and run tests on robot code - run: ./gradlew build +```yaml +# This is a basic workflow to build robot code. +name: CI +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the main branch. +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + # This grabs the WPILib docker container + container: wpilib/roborio-cross-ubuntu:2024-22.04 + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v4 + # Declares the repository safe and not under dubious ownership. + - name: Add repository to git safe directories + run: git config --global --add safe.directory $GITHUB_WORKSPACE + # Grant execute permission for gradlew + - name: Grant execute permission for gradlew + run: chmod +x gradlew + # Runs a single command using the runners shell + - name: Compile and run tests on robot code + run: ./gradlew build +``` Then, save changes by clicking the "Start commit" button on the top-right corner of the screen. You can amend the default commit message if you wish to do so. Then, click the green "Commit new file" button. @@ -68,61 +58,55 @@ GitHub will now automatically run a build whenever a commit is pushed to main or .. image:: images/robot-ci/actions.png :alt: View of the actions tab -A Breakdown of the Actions YAML File ------------------------------------- +## A Breakdown of the Actions YAML File Here is a breakdown of the YAML file above. Although a strict understanding of each line is not required, some level of understanding will help you add more features and debug potential issues that may arise. -.. code-block:: yaml - - # Controls when the action will run. Triggers the workflow on push or pull request - # events but only for the main branch. - on: - push: - branches: [ main ] - pull_request: - branches: [ main ] +```yaml +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the main branch. +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] +``` This block of code dictates when the Action will run. Currently, the action will run when commits are pushed to main or when pull requests are opened against main. -.. code-block:: yaml - - # A workflow run is made up of one or more jobs that can run sequentially or in parallel - jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # This grabs the WPILib docker container - container: wpilib/roborio-cross-ubuntu:2023-22.04 +```yaml +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + # This grabs the WPILib docker container + container: wpilib/roborio-cross-ubuntu:2024-22.04 +``` Each Action workflow is made of a one or more jobs that run either sequentially (one after another) or in parallel (at the same time). In our workflow, there is only one "build" job. -We specify that we want the job to run on an Ubuntu virtual machine and in a virtualized `Docker container `_ that contains the JDK, C++ compiler and roboRIO toolchains. - -.. code-block:: yaml - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - - # Declares the repository safe and not under dubious ownership. - - name: Add repository to git safe directories - run: git config --global --add safe.directory $GITHUB_WORKSPACE - - # Grant execute permission for gradlew - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - # Runs a single command using the runners shell - - name: Compile and run tests on robot code - run: ./gradlew build - -Each job has certain steps that will be executed. This job has four steps. The first step involves checking out the repository to access the robot code. The second step is a workaround for a `GitHub Actions issue `__. The third step involves giving the virtual machine permission to execute gradle tasks using ``./gradlew``. The final step runs ``./gradlew build`` to compile robot code and run any unit tests. - -Adding a Build Status Badge to a README.md File ------------------------------------------------ +We specify that we want the job to run on an Ubuntu virtual machine and in a virtualized [Docker container ](https://www.docker.com/resources/what-container) that contains the JDK, C++ compiler and roboRIO toolchains. + +```yaml +# Steps represent a sequence of tasks that will be executed as part of the job +steps: +# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it +- uses: actions/checkout@v4 +# Declares the repository safe and not under dubious ownership. +- name: Add repository to git safe directories + run: git config --global --add safe.directory $GITHUB_WORKSPACE +# Grant execute permission for gradlew +- name: Grant execute permission for gradlew + run: chmod +x gradlew +# Runs a single command using the runners shell +- name: Compile and run tests on robot code + run: ./gradlew build +``` + +Each job has certain steps that will be executed. This job has four steps. The first step involves checking out the repository to access the robot code. The second step is a workaround for a [GitHub Actions issue](https://github.com/actions/runner/issues/2033). The third step involves giving the virtual machine permission to execute gradle tasks using ``./gradlew``. The final step runs ``./gradlew build`` to compile robot code and run any unit tests. + +## Adding a Build Status Badge to a README.md File It is helpful to add a CI status badge to the top of your repository's README file to quickly check the status of the latest build on main. To do this, click on the "Actions" tab at the top of the screen and select the "CI" tab on the left side of the screen. Then, click on the "Create status badge" button on the top right and copy the status badge Markdown code. .. image:: images/robot-ci/badge.png diff --git a/source/docs/software/basic-programming/alliancecolor.rst b/source/docs/software/basic-programming/alliancecolor.rst new file mode 100644 index 0000000000..87ea884f42 --- /dev/null +++ b/source/docs/software/basic-programming/alliancecolor.rst @@ -0,0 +1,52 @@ +# Get Alliance Color + +The ``DriverStation`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/DriverStation.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_driver_station.html), :py:class:`Python `) has many useful features for getting data from the Driver Station computer. One of the most important features is ``getAlliance`` (Java & Python) / ``GetAlliance`` (C++). + +Note that there are three cases: red, blue, and no color yet. It is important that code handles the third case correctly because the alliance color will not be available until the Driver Station connects. In particular, code should not assume that the alliance color will be available during constructor methods or `robotInit`, but it should be available by the time `autoInit` or `teleopInit` is called. FMS will set the alliance color automatically; when not connected to FMS, the alliance color can be set from the Driver Station (see :ref:`"Team Station" on the Operation Tab `). + +## Getting your Alliance Color and Doing an Action + +.. tab-set-code:: + + ```java + Optional ally = DriverStation.getAlliance(); + if (ally.isPresent()) { + if (ally.get() == Alliance.Red) { + + } + if (ally.get() == Alliance.Blue) { + + } + } + else { + + } + ``` + + ```c++ + using frc::DriverStation::Alliance; + if (auto ally = frc::DriverStation::GetAlliance()) { + if (ally.value() == Alliance::kRed) { + + } + if (ally.value() == Alliance::kBlue) { + + } + } + else { + + } + ``` + + ```Python + from wpilib import DriverStation + ally = DriverStation.getAlliance() + if ally is not None: + if ally == DriverStation.Alliance.kRed: + + elif ally == DriverStation.Alliance.kBlue: + + else: + + ``` + diff --git a/source/docs/software/basic-programming/coordinate-system.rst b/source/docs/software/basic-programming/coordinate-system.rst new file mode 100644 index 0000000000..e10316af13 --- /dev/null +++ b/source/docs/software/basic-programming/coordinate-system.rst @@ -0,0 +1,321 @@ +# Coordinate System + +Coordinate systems are used in FRC programming in several places. A few of the common places are: robot movement, joystick input, :term:`pose` estimation, AprilTags, and path planning. + +It is important to understand the basics of the coordinate system used throughout WPILib and other common tools for programming an FRC robot, such as PathPlanner. Many teams intuitively think of a coordinate system that is different from what is used in WPILib, and this leads to problems that need to be tracked down throughout the season. It is worthwhile to take a few minutes to understand the coordinate system, and come back here as a reference when programming. It's not very difficult to get robot movement with a joystick working without getting the coordinate system right, but it will be much more difficult to build on code using a different coordinate system to add :term:`pose estimation` with :term:`AprilTags` and path planning for autonomous. + +## WPILib coordinate system + +In most cases, WPILib uses the NWU axes convention (North-West-Up as external reference in the world frame.) In the NWU axes convention, where the positive X axis points ahead, the positive Y axis points left, and the positive Z axis points up referenced from the floor. When viewed with each positive axis pointing toward you, counter-clockwise (CCW) is a positive value and clockwise (CW) is a negative value. + +.. figure:: images/coordinate-system/robot-3d.svg + :scale: 200 + :alt: NWU axes convention in three dimensions + + Robot coordinate system in three dimensions + +The figure above shows the coordinate system in relation to an FRC robot. The figure below shows this same coordinate system when viewed from the top (with the Z axis pointing toward you.) This is how you can think of the robot's coordinates in 2D. + +.. figure:: images/coordinate-system/robot-2d.svg + :scale: 200 + :alt: NWU axes convention in two dimensions + + Robot coordinate system in two dimensions + +## Rotation conventions + +In most cases in WPILib programming, 0° is aligned with the positive X axis, and 180° is aligned with the negative X axis. CCW rotation is positive, so 90° is aligned with the positive Y axis, and -90° is aligned with the negative Y axis. + +.. figure:: images/coordinate-system/rotation.svg + :scale: 200 + :alt: Unit circle + + Unit circle with common angles + +The figure above shows the unit circle with common angles labeled in degrees (°) and radians (rad). Notice that rotation to the right is negative, and the range for the whole unit circle is -180° to 180° (-Pi radians to Pi radians). + +.. note:: The range is (-180, 180], meaning it is exclusive of -180° and inclusive of 180°. + +There are some places you may choose to use a different range, such as 0° to 360° or 0 to 1 rotation, but be aware that many core WPILib classes and FRC tools are built with the unit circle above. + +.. warning:: Some :term:`gyroscope` and :term:`IMU` models use CW positive rotation, such as the NavX IMU. Care must be taken to handle rotation properly, sensor values may need to be inverted. Read the documentation and verify that rotation is CCW positive. + +.. warning:: Many sensors that read rotation around an axis, such as encoders and IMU's, read continuously. This means they read more than one rotation, so when rotating past 180° they read 181°, not -179°. Some sensors have configuration settings where you can choose their wrapping behavior and range, while others need to be handled in your code. Careful attention should be paid to make sure sensor readings are consistent and your control loop handles wrapping in the same way as your sensor. + +## Joystick and controller coordinate system + +Joysticks, including the sticks on controllers, don't use the same NWU coordinate system. They use the NED (North-East-Down) convention, where the positive X axis points ahead, the positive Y axis points right, and the positive Z axis points down. When viewed with each positive axis pointing toward you, counter-clockwise (CCW) is a positive value and clockwise (CW) is a negative value. + +.. figure:: images/coordinate-system/joystick-3d.svg + :scale: 200 + :alt: NED axes convention + + Joystick coordinate system + +It's important to note that joystick input values are rotations around an axis, not translations. In practical terms, this means: + +- pushing forward on the joystick (toward the positive X axis) is a CW rotation around the Y axis, so you get a negative Y value. +- pushing to the right (toward the positive Y axis) is a CCW rotation around the X axis, so you get a positive X value. +- twisting the joystick CW (toward the positive Y axis) is a CCW rotation around the Z axis, so you get a positive Z value. + +## Using Joystick and controller input to drive a robot + +You may have noticed, the coordinate system used by WPILib for the robot is not the same as the coordinate system used for joysticks and controllers. Care needs to be taken to understand the difference, and properly pass driver input to the drive subsystem. + +### Differential drivetrain example + +Differential drivetrains are non-holonomic, which means the robot drivetrain cannot move side-to-side (strafe). This type of drivetrain can move forward and backward along the X axis, and rotate around the Z axis. Consider a common arcade drive scheme using a single joystick where the driver pushes the joystick forward/backward for forward/backward robot movement, and push the joystick left/right to rotate the robot left/right. + +The code snippet below uses the ``DifferentialDrive`` and ``Joystick`` classes to drive the robot with the arcade scheme described above. ``DifferentialDrive`` uses the robot coordinate system defined above, and ``Joystick`` uses the joystick coordinate system. + +.. tab-set-code:: + + ```java + public void teleopPeriodic() { + // Arcade drive with a given forward and turn rate + myDrive.arcadeDrive(-driveStick.getY(), -driveStick.getX()); + } + ``` + + ```c++ + void TeleopPeriodic() override { + // Arcade drive with a given forward and turn rate + myDrive.ArcadeDrive(-driveStick.GetY(), -driveStick.GetX()); + } + ``` + + ```python + def teleopPeriodic(self): + # Arcade drive with a given forward and turn rate + self.myDrive.arcadeDrive(-self.driveStick.getY(), -self.driveStick.getX()) + ``` + +The code calls the ``DifferentialDrive.arcadeDrive(xSpeed, zRotation)`` method, with values it gets from the ``Joystick`` class: + +- The first argument is ``xSpeed`` + + - Robot: ``xSpeed`` is the speed along the robot's X axis, which is forward/backward. + - Joystick: The driver sets forward/backward speed by rotating the joystick along its Y axis, which is pushing the joystick forward/backward. + - Code: Moving the joystick forward is negative Y rotation, whereas moving the robot forward is along the positive X axis. This means the joystick value needs to be inverted by placing a - (minus sign) in front of the value. + +- The second argument is ``zRotation`` + + - Robot: ``zRotation`` is the speed of rotation along the robot's Z axis, which is rotating left/right. + - Joystick: The driver sets rotation speed by rotating the joystick along its X axis, which is pushing the joystick left/right. + - Code: Moving the joystick to the right is positive X rotation, whereas robot rotation is CCW positive. This means the joystick value needs to be inverted by placing a - (minus sign) in front of the value. + +### Mecanum drivetrain example + +Mecanum drivetrains are holonomic, meaning they have the ability to move side-to-side. This type of drivetrain can move forward/backward and rotate around the Z axis like differential drivetrains, but it can also move side-to-side along the robot's Y axis. Consider a common arcade drive scheme using a single joystick where the driver pushes the joystick forward/backward for forward/backward robot movement, pushes the joystick left/right to move side-to-side, and twists the joystick to rotate the robot. + +.. tab-set-code:: + + ```java + public void teleopPeriodic() { + // Drive using the X, Y, and Z axes of the joystick. + m_robotDrive.driveCartesian(-m_stick.getY(), -m_stick.getX(), -m_stick.getZ()); + } + ``` + + ```c++ + void TeleopPeriodic() override { + // Drive using the X, Y, and Z axes of the joystick. + m_robotDrive.driveCartesian(-m_stick.GetY(), -m_stick.GetX(), -m_stick.GetZ()); + } + ``` + + ```python + def teleopPeriodic(self): + // Drive using the X, Y, and Z axes of the joystick. + self.robotDrive.driveCartesian(-self.stick.getY(), -self.stick.getX(), -self.stick.getZ()) + ``` + +The code calls the ``MecanumDrive.driveCartesian(xSpeed, ySpeed, zRotation)`` method, with values it gets from the ``Joystick`` class: + +- The first argument is ``xSpeed`` + + - Robot: ``xSpeed`` is the speed along the robot's X axis, which is forward/backward. + - Joystick: The driver sets forward/backward speed by rotating the joystick along its Y axis, which is pushing the joystick forward/backward. + - Code: Moving the joystick forward is negative Y rotation, whereas robot forward is along the positive X axis. This means the joystick value needs to be inverted by placing a - (minus sign) in front of the value. + + +- The second argument is ``ySpeed`` + + - Robot: ``ySpeed`` is the speed along the robot's Y axis, which is left/right. + - Joystick: The driver sets left/right speed by rotating the joystick along its X axis, which is pushing the joystick left/right. + - Code: Moving the joystick to the right is positive X rotation, whereas robot right is along the negative Y axis. This means the joystick value needs to be inverted by placing a - (minus sign) in front of the value. + +- The third argument is ``zRotation`` + + - Robot: ``zRotation`` is the speed of rotation along the robot's Z axis, which is rotating left/right. + - Joystick: The driver sets rotation speed by twisting the joystick along its Z axis, which is twisting the joystick left/right. + - Code: Twisting the joystick to the right is positive Z rotation, whereas robot rotation is CCW positive. This means the joystick value needs to be inverted by placing a - (minus sign) in front of the value. + +### Swerve drivetrain example + +Like mecanum drivetrains, swerve drivetrains are holonomic and have the ability to move side-to-side. Joystick control can be handled the same way for all holonomic drivetrains, but WPILib doesn't have a built-in robot drive class for swerve. Swerve coding is described in other sections of this documentation, but an example of using joystick input to set ``ChassisSpeeds`` values is included below. Consider the same common arcade drive scheme described in the mecanum section above. The scheme uses a single joystick where the driver pushes the joystick forward/backward for forward/backward robot movement, pushes the joystick left/right to move side-to-side, and twists the joystick to rotate the robot. + +.. tab-set-code:: + + ```java + // Drive using the X, Y, and Z axes of the joystick. + var speeds = new ChassisSpeeds(-m_stick.getY(), -m_stick.getX(), -m_stick.getZ()); + ``` + + ```c++ + // Drive using the X, Y, and Z axes of the joystick. + frc::ChassisSpeeds speeds{-m_stick.GetY(), -m_stick.GetX(), -m_stick.GetZ()}; + ``` + + ```python + # Drive using the X, Y, and Z axes of the joystick. + speeds = ChassisSpeeds(-self.stick.getY(), -self.stick.getX(), -self.stick.getZ()) + ``` + +The three arguments to the ``ChassisSpeeds`` constructor are the same as ``driveCartesian`` in the mecanum section above; ``xSpeed``, ``ySpeed``, and ``zRotation``. See the description of the arguments, and their joystick input in the section above. + +## Robot drive kinematics + +:doc:`Kinematics is a topic that is covered in a different section `, but it's worth discussing here in relation to the coordinate system. It is critically important that kinematics is configured using the coordinate system described above. Kinematics is a common starting point for coordinate system errors that then cascade to basic drivetrain control, field oriented driving, pose estimation, and path planning. + +When you construct a ``SwerveDriveKinematics`` or ``MecanumDriveKinematics`` object, you specify a translation from the center of your robot to each wheel. These translations use the coordinate system above, with the origin in the center of your robot. + +.. figure:: images/coordinate-system/kinematics.svg + :alt: Kinematics with translation signs + + Kinematics with translation signs + +For the robot in the diagram above, let's assume the distance between the front and rear wheels (wheelbase) is 2'. Let's also assume the distance between the left and right wheels (trackwidth) is also 2'. Our translations (x, y) would be like this: + +- Front left: (1', 1') +- Front right: (1', -1') +- Rear left: (-1', 1') +- Rear right: (-1', -1') + +.. warning:: A common error is to use an incorrect coordinate system where the positive Y axis points forward on the robot. The correct coordinate system has the positive X axis pointing forward. + +## Field coordinate systems + +The field coordinate system (or global coordinate system) is an absolute coordinate system where a point on the field is designated as the origin. Two common uses of the field coordinate system will be explored in this document: + +- Field oriented driving is a drive scheme for holonomic drivetrains, where the driver moves the controls relative to their perspective of the field, and the robot moves in that direction regardless of where the front of the robot is facing. For example, a driver on the red alliance pushes the joystick forward, the robot will move downfield toward the blue alliance wall, even if the robot's front is facing the driver. +- Pose estimation with odometry and/or AprilTags are used to estimate the robot's pose on the field. + +### Mirrored field vs. rotated field + +Historically, FRC has used two types of field layouts in relation to the red and blue alliance. + +Games such as Rapid React in 2022 used a rotated layout. A rotated layout means that, from your perspective from behind your alliance wall, your field elements and your opponent's elements are in the same location. Notice in the Rapid React field layout diagram below, whether you are on the red or blue alliance, your human player station is on your right and your hanger is on your left. + +.. figure:: images/coordinate-system/rapid-react-field.jpg + :alt: Rotated Rapid React field from 2022 + + Rotated field from RAPID REACT in 2022 [#]_ + +Games such as CHARGED UP in 2023 and CRESCENDO in 2024 used a mirrored layout. A mirrored layout means that the red and blue alliance layout are mirrored across the centerpoint of the field. Refer to the CHARGED UP field diagram below. When you are standing behind the blue alliance wall, the charge station is on the right side of the field from your perspective. However, standing behind the red alliance wall, the charge station is on the left side of the field from your perspective. + +.. figure:: images/coordinate-system/charged-up-field.jpg + :alt: Mirrored CHARGED UP field from 2023 + + Mirrored field from CHARGED UP in 2023 [#]_ + +### Dealing with red or blue alliance + +There are two primary ways many teams choose to define the field coordinate system. In both methods, positive rotation (theta) is in the counter-clockwise (CCW) direction. + +.. warning:: There are cases where your alliance may change (or appear to change) after the code is initialized. When you are not connected to the :term:`FMS` at a competition, you can change your alliance station in the Driver Station application at any time. Even when you are at a competition, your robot will usually initialize before connecting to the FMS so you will not have alliance information. + +.. note:: At competition events, the FMS will automatically report your Team Station and alliance color. When you are not connected to an FMS, you can choose your Team Station and alliance color on the Driver Station :ref:`docs/software/driverstation/driver-station:operation tab`. + +#### Always blue origin + +You may choose to define the origin of the field on the blue side, and keep it there regardless of your alliance color. With this solution, positive x-axis points away from the blue alliance wall. + +.. figure:: images/coordinate-system/field-blue-alliance.svg + :alt: CHARGED UP with blue origin + :scale: 200 + + CHARGED UP with blue origin + +Some advantages to this approach are: + +- Pose estimation with AprilTags is simplified. AprilTags throughout the field are unique. If you keep the coordinate system the same regardless of alliance, there is no need for special logic to deal with the location of AprilTags on the field relative to your alliance. +- Many of the tools and libraries used in FRC follow this convention. Some of the tools include: PathPlanner, Choreo, and the ShuffleBoard and Glass Field2d widget. + +In order to use this approach for field oriented driving, driver input needs to consider the alliance color. When your alliance is red and the driver is standing behind the red alliance wall, they will want the robot to move downfield toward the blue alliance wall. However, when your alliance is blue, the driver will want the robot to go downfield toward the red alliance wall. + +A simple way to deal with field oriented driving is to check the alliance color reported by the `DriverStation` class, and invert the driver's controls based on the alliance. As noted above, your alliance color can change so it needs to be checked on every robot iteration. + +.. tab-set-code:: + + ```java + // The origin is always blue. When our alliance is red, X and Y need to be inverted + var alliance = DriverStation.getAlliance(); + var invert = 1; + if (alliance.isPresent() && alliance.get() == Alliance.Red) { + invert = -1; + } + // Create field relative ChassisSpeeds for controlling Swerve + var chassisSpeeds = ChassisSpeeds + .fromFieldRelativeSpeeds(xSpeed * invert, ySpeed * invert, zRotation, imu.getRotation2d()); + // Control a mecanum drivetrain + m_robotDrive.driveCartesian(xSpeed * invert, ySpeed * invert, zRotation, imu.getRotation2d()); + ``` + + ```c++ + // The origin is always blue. When our alliance is red, X and Y need to be inverted + int invert = 1; + if (frc::DriverStation::GetAlliance() == frc::DriverStation::Alliance::kRed) { + invert = -1; + } + // Create field relative ChassisSpeeds for controlling Swerve + frc::ChassisSpeeds chassisSpeeds = + frc::ChassisSpeeds::FromFieldRelativeSpeeds(xSpeed * invert, ySpeed * invert, zRotation, imu.GetRotation2d()); + // Control a mecanum drivetrain + m_robotDrive.driveCartesian(xSpeed * invert, ySpeed * invert, zRotation, imu.GetRotation2d()); + ``` + + ```python + # The origin is always blue. When our alliance is red, X and Y need to be inverted + invert = 1 + if wpilib.DriverStation.getAlliance() == wpilib.DriverStation.Alliance.kRed: + invert = -1 + # Create field relative ChassisSpeeds for controlling Swerve + chassis_speeds = wpilib.ChassisSpeeds.FromFieldRelativeSpeeds( + xSpeed * invert, ySpeed * invert, zRotation, self.imu.GetAngle() + ) + # Control a mecanum drivetrain + self.robotDrive.driveCartesian(xSpeed * invert, ySpeed * invert, zRotation, self.imu.GetAngle()) + ``` + +#### Origin follows your alliance + +You may choose to define the origin of the field based on the alliance you are one. With this approach, the positive x-axis always points away from your alliance wall. + +When you are on the blue alliance, your origin looks like this: + +.. figure:: images/coordinate-system/field-blue-alliance.svg + :alt: CHARGED UP with alliance as origin + :scale: 200 + + CHARGED UP field with blue alliance as origin + +When you are on the red alliance, your origin looks like this: + +.. figure:: images/coordinate-system/field-red-alliance.svg + :alt: CHARGED UP with alliance as origin + :scale: 200 + + CHARGED UP field with red alliance as origin + +This approach has a few more complications than the previous approach, especially in years when the field layout is mirrored between alliances. + +In years when the field layout is rotated, this is a simple approach if you are not using AprilTags for pose estimation or doing other advanced techniques. When the field layout is rotated, the field elements appear at the same coordinates regardless of your alliance. + +Some things you need to consider when using this approach are: + +- As warned above, your alliance color can change after initialization. If you are not using AprilTags, you may not have anything to adjust when the alliance changes. However, if you are using AprilTags and your robot has seen a tag and used it for pose estimation, you will need to adjust your origin and reset your estimated pose. +- The field image in the ShuffleBoard and Glass Field2d widget follows the *Always blue origin* approach. Special handling is needed to display your robot pose correctly when your alliance is red. You will need to change the origin for your estimated pose to the blue alliance coordinate system before sending it to the dashboard. + +.. [#] Rapid React field image from MikLast on Chiefdelphi ``__ +.. [#] CHARGED UP field image from MikLast on Chiefdelphi ``__ diff --git a/source/docs/software/basic-programming/cpp-units.rst b/source/docs/software/basic-programming/cpp-units.rst index ebc765b22d..760275837c 100644 --- a/source/docs/software/basic-programming/cpp-units.rst +++ b/source/docs/software/basic-programming/cpp-units.rst @@ -1,188 +1,173 @@ -The C++ Units Library -===================== +# The C++ Units Library -WPILib is coupled with a `Units `_ library for C++ teams. This library leverages the C++ `type system `__ to enforce proper dimensionality for method parameters, automatically perform unit conversions, and even allow users to define arbitrary defined unit types. Since the C++ type system is enforced at compile-time, the library has essentially no runtime cost. +WPILib is coupled with a [Units](https://github.com/nholthaus/units) library for C++ teams. This library leverages the C++ [type system](https://docs.microsoft.com/en-us/cpp/cpp/cpp-type-system-modern-cpp?view=msvc-170&viewFallbackFrom=vs-2019) to enforce proper dimensionality for method parameters, automatically perform unit conversions, and even allow users to define arbitrary defined unit types. Since the C++ type system is enforced at compile-time, the library has essentially no runtime cost. -Using the Units Library ------------------------ +## Using the Units Library The units library is a header-only library. You must include the relevant header in your source files for the units you want to use. Here's a list of available units. -.. code-block:: c++ - - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include +```c++ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +``` The ``units/math.h`` header provides unit-aware functions like ``units::math::abs()``. -Unit Types and Container Types -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Unit Types and Container Types The C++ units library is based around two sorts of type definitions: unit types and container types. -Unit Types -~~~~~~~~~~ +#### Unit Types Unit types correspond to the abstract concept of a unit, without any actual stored value. Unit types are the fundamental "building block" of the units library - all unit types are defined constructively (using the ``compound_unit`` template) from a small number of "basic" unit types (such as ``meters``, ``seconds``, etc). -While unit types cannot contain numerical values, their use in building other unit types means that when a type or method uses a `template parameter `__ to specify its dimensionality, that parameter will be a unit type. +While unit types cannot contain numerical values, their use in building other unit types means that when a type or method uses a [template parameter](https://cplusplus.com/doc/oldtutorial/templates/) to specify its dimensionality, that parameter will be a unit type. -Container Types -~~~~~~~~~~~~~~~ +#### Container Types Container types correspond to an actual quantity dimensioned according to some unit - that is, they are what actually hold the numerical value. Container types are constructed from unit types with the ``unit_t`` template. Most unit types have a corresponding container type that has the same name suffixed by ``_t`` - for example, the unit type ``units::meter`` corresponds to the container type ``units::meter_t``. Whenever a specific quantity of a unit is used (as a variable or a method parameter), it will be an instance of the container type. By default, container types will store the actual value as a ``double`` - advanced users may change this by calling the ``unit_t`` template manually. -A full list of unit and container types can be found in the `documentation `__. +A full list of unit and container types can be found in the [documentation](https://github.com/nholthaus/units#namespaces). -Creating Instances of Units -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Creating Instances of Units To create an instance of a specific unit, we create an instance of its container type: -.. code-block:: c++ +```c++ +// The variable speed has a value of 5 meters per second. +units::meter_per_second_t speed{5.0}; +``` - // The variable speed has a value of 5 meters per second. - units::meter_per_second_t speed{5.0}; +Alternatively, the units library has [type literals](https://en.cppreference.com/w/cpp/language/user_literal) defined for some of the more common container types. These can be used in conjunction with type inference via ``auto`` to define a unit more succinctly: -Alternatively, the units library has `type literals `__ defined for some of the more common container types. These can be used in conjunction with type inference via ``auto`` to define a unit more succinctly: - -.. code-block:: c++ - - // The variable speed has a value of 5 meters per second. - auto speed = 5_mps; +```c++ +// The variable speed has a value of 5 meters per second. +auto speed = 5_mps; +``` Units can also be initialized using a value of an another container type, as long as the types can be converted between one another. For example, a ``meter_t`` value can be created from a ``foot_t`` value. -.. code-block:: c++ - - auto feet = 6_ft; - units::meter_t meters{feet}; +```c++ +auto feet = 6_ft; +units::meter_t meters{feet}; +``` In fact, all container types representing convertible unit types are *implicitly convertible*. Thus, the following is perfectly legal: -.. code-block:: c++ - - units::meter_t distance = 6_ft; +```c++ +units::meter_t distance = 6_ft; +``` In short, we can use *any* unit of length in place of *any other* unit of length, anywhere in our code; the units library will automatically perform the correct conversion for us. -Performing Arithmetic with Units -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Performing Arithmetic with Units Container types support all of the ordinary arithmetic operations of their underlying data type, with the added condition that the operation must be *dimensionally* sound. Thus, addition must always be performed on two compatible container types: -.. code-block:: c++ - - // Add two meter_t values together - auto sum = 5_m + 7_m; // sum is 12_m - - // Adds meters to feet; both are length, so this is fine - auto sum = 5_m + 7_ft; - - // Tries to add a meter_t to a second_t, will throw a compile-time error - auto sum = 5_m + 7_s; +```c++ +// Add two meter_t values together +auto sum = 5_m + 7_m; // sum is 12_m +// Adds meters to feet; both are length, so this is fine +auto sum = 5_m + 7_ft; +// Tries to add a meter_t to a second_t, will throw a compile-time error +auto sum = 5_m + 7_s; +``` Multiplication may be performed on any pair of container types, and yields the container type of a compound unit: .. note:: When a calculation yields a compound unit type, this type will only be checked for validity at the point of operation if the result type is specified explicitly. If ``auto`` is used, this check will not occur. For example, when we divide distance by time, we may want to ensure the result is, indeed, a velocity (i.e. ``units::meter_per_second_t``). If the return type is declared as ``auto``, this check will not be made. -.. code-block:: c++ - - // Multiply two meter_t values, result is square_meter_t - auto product = 5_m * 7_m; // product is 35_sq_m - -.. code-block:: c++ +```c++ +// Multiply two meter_t values, result is square_meter_t +auto product = 5_m * 7_m; // product is 35_sq_m +``` - // Divide a meter_t value by a second_t, result is a meter_per_second_t - units::meter_per_second_t speed = 6_m / 0.5_s; // speed is 12_mps +```c++ +// Divide a meter_t value by a second_t, result is a meter_per_second_t +units::meter_per_second_t speed = 6_m / 0.5_s; // speed is 12_mps +``` -```` Functions -^^^^^^^^^^^^^^^^^^^^^ +### ```` Functions Some ``std`` functions (such as ``clamp``) are templated to accept any type on which the arithmetic operations can be performed. Quantities stored as container types will work with these functions without issue. However, other ``std`` functions work only on ordinary numerical types (e.g. ``double``). The units library's ``units::math`` namespace contains wrappers for several of these functions that accept units. Examples of such functions include ``sqrt``, ``pow``, etc. -.. code-block:: c++ +```c++ +auto area = 36_sq_m; +units::meter_t sideLength = units::math::sqrt(area); +``` - auto area = 36_sq_m; - units::meter_t sideLength = units::math::sqrt(area); - -Removing the Unit Wrapper -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Removing the Unit Wrapper To convert a container type to its underlying value, use the ``value()`` method. This serves as an escape hatch from the units type system, which should be used only when necessary. -.. code-block:: c++ - - units::meter_t distance = 6.5_m; - double distanceMeters = distance.value(); +```c++ +units::meter_t distance = 6.5_m; +double distanceMeters = distance.value(); +``` - -Example of the Units Library in WPILib Code -------------------------------------------- +## Example of the Units Library in WPILib Code Several arguments for methods in new features of WPILib (ex. :ref:`kinematics `) use the units library. Here is an example of :ref:`sampling a trajectory `. -.. code-block:: c++ - - // Sample the trajectory at 1.2 seconds. This represents where the robot - // should be after 1.2 seconds of traversal. - Trajectory::State point = trajectory.Sample(1.2_s); - - // Since units of time are implicitly convertible, this is exactly equivalent to the above code - Trajectory::State point = trajectory.Sample(1200_ms); +```c++ +// Sample the trajectory at 1.2 seconds. This represents where the robot +// should be after 1.2 seconds of traversal. +Trajectory::State point = trajectory.Sample(1.2_s); +// Since units of time are implicitly convertible, this is exactly equivalent to the above code +Trajectory::State point = trajectory.Sample(1200_ms); +``` Some WPILib classes represent objects that could naturally work with multiple choices of unit types - for example, a motion profile might operate on either linear distance (e.g. meters) or angular distance (e.g. radians). For such classes, the unit type is required as a template parameter: -.. code-block:: c++ - - // Creates a new set of trapezoidal motion profile constraints - // Max velocity of 10 meters per second - // Max acceleration of 20 meters per second squared - frc::TrapezoidProfile::Constraints{10_mps, 20_mps_sq}; - - // Creates a new set of trapezoidal motion profile constraints - // Max velocity of 10 radians per second - // Max acceleration of 20 radians per second squared - frc::TrapezoidProfile::Constraints{10_rad_per_s, 20__rad_per_s / 1_s}; - -For more detailed documentation, please visit the official `GitHub page `_ for the units library. +```c++ +// Creates a new set of trapezoidal motion profile constraints +// Max velocity of 10 meters per second +// Max acceleration of 20 meters per second squared +frc::TrapezoidProfile::Constraints{10_mps, 20_mps_sq}; +// Creates a new set of trapezoidal motion profile constraints +// Max velocity of 10 radians per second +// Max acceleration of 20 radians per second squared +frc::TrapezoidProfile::Constraints{10_rad_per_s, 20__rad_per_s / 1_s}; +``` + +For more detailed documentation, please visit the official [GitHub page](https://github.com/nholthaus/units) for the units library. diff --git a/source/docs/software/basic-programming/functions-as-data.rst b/source/docs/software/basic-programming/functions-as-data.rst index 1bf2d4f753..a9dfb7823c 100644 --- a/source/docs/software/basic-programming/functions-as-data.rst +++ b/source/docs/software/basic-programming/functions-as-data.rst @@ -1,12 +1,10 @@ -Treating Functions as Data -========================== +# Treating Functions as Data Regardless of programming language, one of the first things anyone learns to do when programming a computer is to write a function (also known as a "method" or a "subroutine"). Functions are a fundamental part of organized code - writing functions lets us avoid duplicating the same piece of code over and over again. Instead of writing duplicated sections of code, we call a single function that contains the code we want to execute from multiple places (provided we named the function well, the function name is also easier to read than the code itself!). If the section of code needs some additional information about its surrounding context to run, we pass those to the function as "parameters", and if it needs to yield something back to the rest of the code once it finishes, we call that a "return value" (together, the parameters and return value are called the function's "signature"); Sometimes, we need to pass functions from one part of the code to another part of the code. This might seem like a strange concept, if we're used to thinking of functions as part of a class definition rather than objects in their own right. But at a basic level, functions are just data - in the same way we can store an ``integer`` or a ``double`` as a variable and pass it around our program, we can do the same thing with a function. A variable whose value is a function is called a "functional interface" in Java, and a "function pointer" or "functor" in C++. -Why Would We Want to Treat Functions as Data? ---------------------------------------------- +## Why Would We Want to Treat Functions as Data? Typically, code that calls a function is coupled to (depends on) the definition of the function. While this occurs all the time, it becomes problematic when the code *calling* the function (for example, WPILib) is developed independently and without direct knowledge of the code that *defines* the function (for example, code from an FRC team). Sometimes we solve this challenge through the use of class interfaces, which define collections of data and functions that are meant to be used together. However, often we really only have a dependency on a *single function*, rather than on an *entire class*. @@ -20,57 +18,53 @@ It's important that *passing* a function is not the same as *calling* a function Inside of code that passes a function, we will see some syntax that either refers to the name of an existing function in a special way, or else defines a new function to be passed inside of the call expression. The specific syntax needed (and the rules around it) depends on which programming language we are using. -Treating Functions as Data in Java ----------------------------------- +## Treating Functions as Data in Java -Java represents functions-as-data as instances of `functional interfaces `__. A "functional interface" is a special kind of class that has only a single method - since Java was originally designed strictly for object-oriented programming, it has no way of representing a single function detached from a class. Instead, it defines a particular group of classes that *only* represent single functions. Each type of function signature has its own functional interface, which is an interface with a single function definition of that signature. +Java represents functions-as-data as instances of [functional interfaces](https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html). A "functional interface" is a special kind of class that has only a single method - since Java was originally designed strictly for object-oriented programming, it has no way of representing a single function detached from a class. Instead, it defines a particular group of classes that *only* represent single functions. Each type of function signature has its own functional interface, which is an interface with a single function definition of that signature. This might sound complicated, but in the context of WPILib we don't really need to worry much about using the functional interfaces themselves - the code that does that is internal to WPILib. Instead, all we need to know is how to pass a function that we've written to a method that takes a functional interface as a parameter. For a simple example, consider the signature of ``Commands.runOnce`` (which creates an ``InstantCommand`` that, when scheduled, runs the given function once and then terminates): .. note:: The ``requirements`` parameter is explained in the :ref:`Command-based documentation `, and will not be discussed here. -.. code-block:: java - - public static Command runOnce(Runnable action, Subsystem... requirements) +```java +public static Command runOnce(Runnable action, Subsystem... requirements) +``` ``runOnce`` expects us to give it a ``Runnable`` parameter (named ``action``). A ``Runnable`` is the Java term for a function that takes no parameters and returns no value. When we call ``runOnce``, we need to give it a function with no parameters and no return value. There are two ways to do this: we can refer to some existing function using a "method reference", or we can define the function we want inline using a "lambda expression". -Method References -^^^^^^^^^^^^^^^^^ +### Method References A method reference lets us pass an already-existing function as our ``Runnable``: -.. code-block:: java - - // Create an InstantCommand that runs the `resetEncoders` method of the `drivetrain` object - Command disableCommand = runOnce(drivetrain::resetEncoders, drivetrain); +```java +// Create an InstantCommand that runs the `resetEncoders` method of the `drivetrain` object +Command disableCommand = runOnce(drivetrain::resetEncoders, drivetrain); +``` The expression ``drivetrain::resetEncoders`` is a reference to the ``resetEncoders`` method of the ``drivetrain`` object. It is not a method *call* - this line of code does not *itself* reset the encoders of the drivetrain. Instead, it returns a ``Command`` that will do so *when it is scheduled.* Remember that in order for this to work, ``resetEncoders`` must be a ``Runnable`` - that is, it must take no parameters and return no value. So, its signature must look like this: -.. code-block:: java - - // void because it returns no parameters, and has an empty parameter list - public void resetEncoders() +```java +// void because it returns no parameters, and has an empty parameter list +public void resetEncoders() +``` If the function signature does not match this, Java will not be able to interpret the method reference as a ``Runnable`` and the code will not compile. Note that all we need to do is make sure that the signature matches the signature of the single method in the ``Runnable`` functional interface - we don't need to *explicitly* name it as a ``Runnable``. -Lambda Expressions in Java -^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Lambda Expressions in Java If we do not already have a named function that does what we want, we can define a function "inline" - that means, right inside of the call to ``runOnce``! We do this by writing our function with a special syntax that uses an "arrow" symbol to link the argument list to the function body: -.. code-block:: java - - // Create an InstantCommand that runs the drive forward at half speed - Command driveHalfSpeed = runOnce(() -> { drivetrain.arcadeDrive(0.5, 0.0); }, drivetrain); +```java +// Create an InstantCommand that runs the drive forward at half speed +Command driveHalfSpeed = runOnce(() -> { drivetrain.arcadeDrive(0.5, 0.0); }, drivetrain); +``` Java calls ``() -> { drivetrain.arcadeDrive(0.5, 0.0); }`` a "lambda expression"; it may be less-confusingly called an "arrow function", "inline function", or "anonymous function" (because it has no name). While this may look a bit funky, it is just another way of writing a function - the parentheses before the arrow are the function's argument list, and the code contained in the brackets is the function body. The "lambda expression" here represents a function that calls ``drivetrain.arcadeDrive`` with a specific set of parameters - note again that this does not *call* the function, but merely defines it and passes it to the ``Command`` to be run later when the ``Command`` is scheduled. As with method references, we do not need to *explicitly* name the lambda expression as a ``Runnable`` - Java can infer that our lambda expression is a ``Runnable`` so long as its signature matches that of the single method in the ``Runnable`` interface. Accordingly, our lambda takes no arguments and has no return statement - if it did not match the ``Runnable`` contract, our code would fail to compile. -Capturing State in Java Lambda Expressions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Capturing State in Java Lambda Expressions In the above example, our function body references an object that lives outside of the function itself (namely, the ``drivetrain`` object). This is called a "capture" of a variable from the surrounding code (which is sometimes called the "outer scope" or "enclosing scope"). Usually the captured variables are either local variables from the enclosing method body in which the lambda expression is defined, or else fields of an enclosing class definition in which that method is defined. @@ -78,71 +72,65 @@ In Java capturing state is a fairly safe thing to do in general, with one major This means we can only capture primitive types (like ``int``, ``double``, and ``boolean``) if they're constants. If we want to capture a state variable that can change, it *must be wrapped in a mutable object*. -Syntactic Sugar for Java Lambda Expressions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Syntactic Sugar for Java Lambda Expressions The full lambda expression syntax can be needlessly verbose in some cases. To help with this, Java lets us take some shortcuts (called "syntactic sugar") in cases where some of the notation is redundant. -Omitting Function Body Brackets for One-Line Lambdas -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Omitting Function Body Brackets for One-Line Lambdas If the function body of our lambda expression is only one line, Java lets us omit the brackets around the function body. When omitting function brackets, we also omit trailing semicolons And the `return` keyword. So, our ``Runnable`` lambda above could instead be written: -.. code-block:: java +```java +// Create an InstantCommand that runs the drive forward at half speed +Command driveHalfSpeed = runOnce(() -> drivetrain.arcadeDrive(0.5, 0.0), drivetrain); +``` - // Create an InstantCommand that runs the drive forward at half speed - Command driveHalfSpeed = runOnce(() -> drivetrain.arcadeDrive(0.5, 0.0), drivetrain); - -Omitting Parentheses around Single Lambda Parameters -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Omitting Parentheses around Single Lambda Parameters If the lambda expression is for a functional interface that takes only a single argument, we can omit the parenthesis around the parameter list: -.. code-block:: java - - // We can write this lambda with no parenthesis around its single argument - IntConsumer exampleLambda = (a -> System.out.println(a)); +```java +// We can write this lambda with no parenthesis around its single argument +IntConsumer exampleLambda = (a -> System.out.println(a)); +``` -Treating Functions as Data in C++ ---------------------------------- +## Treating Functions as Data in C++ C++ has a number of ways to treat functions as data. For the sake of this article, we'll only talk about the parts that are relevant to using WPILibC. -In WPILibC, function types are represented with the ``std::function`` class (https://en.cppreference.com/w/cpp/utility/functional/function). This standard library class is templated on the function's signature - that means we have to provide it a `function type `__ as a template parameter to specify the signature of the function (compare this to :ref:`Java ` above, where we have a separate interface type for each kind of signature). +In WPILibC, function types are represented with the ``std::function`` class (https://en.cppreference.com/w/cpp/utility/functional/function). This standard library class is templated on the function's signature - that means we have to provide it a [function type](https://stackoverflow.com/questions/17446220/c-function-types) as a template parameter to specify the signature of the function (compare this to :ref:`Java ` above, where we have a separate interface type for each kind of signature). This sounds a lot more complicated than it is to use in practice. Let's look at the call signature of ``cmd::RunOnce`` (which creates an ``InstantCommand`` that, when scheduled, runs the given function once and then terminates): .. note:: The ``requirements`` parameter is explained in the :ref:`Command-based documentation `, and will not be discussed here. -.. code-block:: cpp - - CommandPtr RunOnce( - std::function action, - Requirements requirements); +```c++ +CommandPtr RunOnce( + std::function action, + Requirements requirements); +``` ``runOnce`` expects us to give it a ``std::function`` parameter (named ``action``). A ``std::function`` is the C++ type for a ``std::function`` that takes no parameters and returns no value (the template parameter, ``void()``, is a function type with no parameters and no return value). When we call ``runOnce``, we need to give it a function with no parameters and no return value. C++ lacks a clean way to refer to existing class methods in a way that can automatically be converted to a ``std::function``, so the typical way to do this is to define a new function inline with a "lambda expression". -Lambda Expressions in C++ -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Lambda Expressions in C++ To pass a function to ``runOnce``, we need to write a short inline function expression using a special syntax that resembles ordinary C++ function declarations, but varies in a few important ways: -.. code-block:: cpp - - // Create an InstantCommand that runs the drive forward at half speed - CommandPtr driveHalfSpeed = cmd::RunOnce([this] { drivetrain.ArcadeDrive(0.5, 0.0); }, {drivetrain}); +```c++ +// Create an InstantCommand that runs the drive forward at half speed +CommandPtr driveHalfSpeed = cmd::RunOnce([this] { drivetrain.ArcadeDrive(0.5, 0.0); }, {drivetrain}); +``` C++ calls ``[captures] (params) { body; }`` a "lambda expression". It has three parts: a *capture list* (square brackets), an optional *parameter list* (parentheses), and a *function body* (curly brackets). It may look a little strange, but the only real difference between a lambda expression and an ordinary function (apart from the lack of a function name) is the addition of the capture list. Since ``RunOnce`` wants a function with no parameters and no return value, our lambda expression has no parameter list and no return statement. The "lambda expression" here represents a function that calls ``drivetrain.ArcadeDrive`` with a specific set of parameters - note again that the above code does not *call* the function, but merely defines it and passes it to the ``Command`` to be run later when the ``Command`` is scheduled. -Capturing State in C++ Lambda Expressions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Capturing State in C++ Lambda Expressions In the above example, our function body references an object that lives outside of the function itself (namely, the ``drivetrain`` object). This is called a "capture" of a variable from the surrounding code (which is sometimes called the "outer scope" or "enclosing scope"). Usually the captured variables are either local variables from the enclosing method body in which the lambda expression is defined, or else fields of an enclosing class definition in which that method is defined. C++ has somewhat more-powerful semantics than Java. One cost of this is that we generally need to give the C++ compiler some help to figure out *how exactly* we want it to capture state from the enclosing scope. This is the purpose of the *capture list*. For the purposes of using the WPILibC Command-based framework, it is usually sufficient to use a capture list of ``[this]``, which gives access to members of the enclosing class by capturing the enclosing class's ``this`` pointer by value. -Method locals cannot be captured with the ``this`` pointer, and must be captured explicitly either by reference or by value by including them in the capture list (or by implicitly by instead specifying a default capture semantics). It is typically safer to capture locals by-value, since a lambda can outlive the lifespan of an object it captures by reference. For more details, consult the `C++ standard library documentation on capture semantics `__. +Method locals cannot be captured with the ``this`` pointer, and must be captured explicitly either by reference or by value by including them in the capture list (or by implicitly by instead specifying a default capture semantics). It is typically safer to capture locals by-value, since a lambda can outlive the lifespan of an object it captures by reference. For more details, consult the [C++ standard library documentation on capture semantics](https://en.cppreference.com/w/cpp/language/lambda#Lambda_capture). diff --git a/source/docs/software/basic-programming/git-getting-started.rst b/source/docs/software/basic-programming/git-getting-started.rst index 9ff426728c..65e17d733a 100644 --- a/source/docs/software/basic-programming/git-getting-started.rst +++ b/source/docs/software/basic-programming/git-getting-started.rst @@ -1,32 +1,29 @@ .. include:: -Git Version Control Introduction -================================ +# Git Version Control Introduction -.. important:: A more in-depth guide on Git is available on the `Git website `__. +.. important:: A more in-depth guide on Git is available on the [Git website](https://git-scm.com/book/en/v2). -`Git `_ is a Distributed Version Control System (VCS) created by Linus Torvalds, also known for creating and maintaining the Linux kernel. Version Control is a system for tracking changes of code for developers. The advantages of Git Version Control are: +[Git](https://git-scm.com/about) is a Distributed Version Control System (VCS) created by Linus Torvalds, also known for creating and maintaining the Linux kernel. Version Control is a system for tracking changes of code for developers. The advantages of Git Version Control are: - Separation of testing environments into *branches* - Ability to navigate to a particular *commit* without removing history - Ability to manage *commits* in various ways, including combining them -- Various other features, see `here `__ +- Various other features, see [here](https://git-scm.com/about) -Prerequisites -------------- +## Prerequisites .. important:: This tutorial uses the Windows operating system You have to download and install Git from the following links: -- `Windows `_ -- `macOS `_ -- `Linux `_ +- [Windows](https://git-scm.com/download/win) +- [macOS](https://git-scm.com/download/mac) +- [Linux](https://git-scm.com/download/linux) -.. note:: You may need to add Git to your `path `__ +.. note:: You may need to add Git to your [path](https://www.google.com/search?q=adding+git+to+path) -Git Vocabulary --------------- +## Git Vocabulary Git revolves around several core data structures and commands: @@ -39,8 +36,7 @@ Git revolves around several core data structures and commands: - **Fork:** duplicate a pre-existing repository to modify, and to compare against the original - **Merge:** combine various changes from different branches/commits/forks into a single history -Repository ----------- +## Repository A Git repository is a data structure containing the structure, history, and files of a project. @@ -50,23 +46,20 @@ Git repositories usually consist of: - A ``.gitignore`` file. This file contains the files or directories that you do *not* want included when you commit. - Files and folders. This is the main content of the repository. -Creating the repository -^^^^^^^^^^^^^^^^^^^^^^^ +### Creating the repository -You can store the repository locally, or through a remote -- a remote being the cloud, or possibly another storage medium or server that hosts your repository. `GitHub `_ is a popular free hosting service. Numerous developers use it, and that's what this tutorial will use. +You can store the repository locally, or through a remote -- a remote being the cloud, or possibly another storage medium or server that hosts your repository. [GitHub](https://github.com/) is a popular free hosting service. Numerous developers use it, and that's what this tutorial will use. -.. note:: There are various providers that can host repositories. `Gitlab `_ and `Bitbucket `_ are a few alternatives to Github. +.. note:: There are various providers that can host repositories. [Gitlab](https://about.gitlab.com) and [Bitbucket](https://bitbucket.org/) are a few alternatives to Github. -Creating a GitHub Account -~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Creating a GitHub Account Go ahead and create a GitHub account by visiting the `website `_ and following the on-screen prompts. .. image:: images/git-getting-started/create-account.png :alt: How to create a new GitHub account. -Local Creation -~~~~~~~~~~~~~~ +#### Local Creation After creating and verifying your account, you'll want to visit the homepage. It'll look similar to the shown image. @@ -95,7 +88,7 @@ You should see a screen similar to this .. note:: The keyboard shortcut :kbd:`Ctrl+~` can be used to open a terminal in Visual Studio Code for Windows. -Now you'll want to open a PowerShell window and navigate to your project directory. An excellent tutorial on PowerShell can be found `here `__. Please consult your search engine on how to open a terminal on alternative operating systems. +Now you'll want to open a PowerShell window and navigate to your project directory. An excellent tutorial on PowerShell can be found [here](https://programminghistorian.org/en/lessons/intro-to-powershell). Please consult your search engine on how to open a terminal on alternative operating systems. .. image:: images/git-getting-started/powershell.png :alt: An empty powershell window. @@ -108,54 +101,52 @@ If a directory is empty, a file needs to be created in order for git to have som .. tab-item:: Empty Directory - .. code-block:: console - - > cd "C:\Users\ExampleUser9007\Documents\Example Folder" - > git init - Initialized empty Git repository in C:/Users/ExampleUser9007/Documents/Example Folder/.git/ - > echo "# ExampleRepo" >> README.md - > git add README.md - > git commit -m "First commit" - [main (root-commit) fafafa] First commit - 1 file changed, 1 insertions(+), 0 deletions(-) - create mode 100644 README.md - > git remote add origin https://github.com/ExampleUser9007/ExampleRepo.git - > git push -u origin main + ```console + > cd "C:\Users\ExampleUser9007\Documents\Example Folder" + > git init + Initialized empty Git repository in C:/Users/ExampleUser9007/Documents/Example Folder/.git/ + > echo "# ExampleRepo" >> README.md + > git add README.md + > git commit -m "First commit" + [main (root-commit) fafafa] First commit + 1 file changed, 1 insertions(+), 0 deletions(-) + create mode 100644 README.md + > git remote add origin https://github.com/ExampleUser9007/ExampleRepo.git + > git push -u origin main + ``` .. tab-item:: Existing Project - .. code-block:: console - - > cd "C:\Users\ExampleUser9007\Documents\Example Folder" - > git init - Initialized empty Git repository in C:/Users/ExampleUser9007/Documents/Example Folder/.git/ - > git add . - > git commit -m "First commit" - [main (root-commit) fafafa] First commit - 1 file changed, 1 insertions(+), 0 deletions(-) - create mode 100644 README.md - > git remote add origin https://github.com/ExampleUser9007/ExampleRepo.git - > git push -u origin main - -Commits -------- + ```console + > cd "C:\Users\ExampleUser9007\Documents\Example Folder" + > git init + Initialized empty Git repository in C:/Users/ExampleUser9007/Documents/Example Folder/.git/ + > git add . + > git commit -m "First commit" + [main (root-commit) fafafa] First commit + 1 file changed, 1 insertions(+), 0 deletions(-) + create mode 100644 README.md + > git remote add origin https://github.com/ExampleUser9007/ExampleRepo.git + > git push -u origin main + ``` + +## Commits Repositories are primarily composed of commits. Commits are saved states or *versions* of code. In the previous example, we created a file called README.md. Open that file in your favorite text editor and edit a few lines. After tinkering with the file for a bit, simply save and close. Navigate to PowerShell and type the following commands. -.. code-block:: console +```console +> git add README.md +> git commit -m "Adds a description to the repository" +[main bcbcbc] Adds a description to the repository + 1 file changed, 2 insertions(+), 0 deletions(-) +> git push +``` - > git add README.md - > git commit -m "Adds a description to the repository" - [main bcbcbc] Adds a description to the repository - 1 file changed, 2 insertions(+), 0 deletions(-) - > git push +.. note:: Writing good commit messages is a key part of a maintainable project. A guide on writing commit messages can be found [here](https://cbea.ms/git-commit/). -.. note:: Writing good commit messages is a key part of a maintainable project. A guide on writing commit messages can be found `here `_. - -Git Pull -^^^^^^^^ +### Git Pull .. note:: ``git fetch`` can be used when the user does not wish to automatically merge into the current working branch @@ -163,8 +154,7 @@ This command retrieves the history or commits from the remote repository. When t Run: ``git pull`` -Git Add -^^^^^^^ +### Git Add This command "stages" the specified file(s) so that they will be included in the next commit. @@ -172,8 +162,7 @@ For a single file, run ``git add FILENAME.txt`` where FILENAME.txt is the name a To add every file/folder that isn't excluded via *gitignore*, run ``git add .``. When run in the root of the repository this command will stage every untracked, unexcluded file. -Git Commit -^^^^^^^^^^ +### Git Commit This command creates the commit and stores it locally. This saves the state and adds it to the repository's history. The commit will consist of whatever changes ("diffs") were made to the staged files since the last commit. @@ -181,15 +170,13 @@ It is required to specify a "commit message" explaining why you changed this set Run: ``git commit -m "type message here"`` -Git Push -^^^^^^^^ +### Git Push Upload (Push) your local changes to the remote (Cloud) Run: ``git push`` -Branches --------- +## Branches Branches in Git are similar to parallel worlds. They start off the same, and then they can "branch" out into different varying paths. Consider the Git control flow to look similar to this. @@ -198,20 +185,17 @@ Branches in Git are similar to parallel worlds. They start off the same, and the In the above example, main was branched (or duplicated) into the branch Feature 1 and someone checked out the branch, creating a local copy. Then, someone committed (or uploaded) their changes, merging them into the branch Feature 1. You are "merging" the changes from one branch into another. -Creating a Branch -^^^^^^^^^^^^^^^^^ +### Creating a Branch Run: ``git branch branch-name`` where branch-name is the name of the branch to create. The new branch history will be created from the current active branch. -Entering a Branch -^^^^^^^^^^^^^^^^^ +### Entering a Branch Once a branch is created, you have to then enter the branch. Run: ``git checkout branch-name`` where branch-name is the branch that was previously created. -Merging -------- +## Merging In scenarios where you want to copy one branches history into another, you can merge them. A merge is done by calling ``git merge branch-name`` with branch-name being the name of the branch to merge from. It is automatically merged into the current active branch. @@ -222,67 +206,58 @@ It's common for a remote repository to contain work (history) that you do not ha However, in the above example, what if File A was modified by both branch Feature1 and Feature2? This is called a **merge conflict**. A merge conflict can be resolved by editing the conflicting file. In the example, we would need to edit File A to keep the history or changes that we want. After that has been done, simply re-add, re-commit, and then push your changes. -Resets ------- +## Resets Sometimes history needs to be reset, or a commit needs to be undone. This can be done multiple ways. -Reverting the Commit -^^^^^^^^^^^^^^^^^^^^ +### Reverting the Commit .. note:: You cannot revert a merge, as git does not know which branch or origin it should choose. To revert history leading up to a commit run ``git revert commit-id``. The commit IDs can be shown using the ``git log`` command. -Resetting the Head -^^^^^^^^^^^^^^^^^^ +### Resetting the Head .. warning:: Forcibly resetting the head is a dangerous command. It permanently erases all history past the target. You have been warned! Run: ``git reset --hard commit-id``. -Forks ------ +## Forks Forks can be treated similarly to branches. You can merge the upstream (original repository) into the origin (forked repository). -Cloning an Existing Repo -^^^^^^^^^^^^^^^^^^^^^^^^ +### Cloning an Existing Repo In the situation that a repository is already created and stored on a remote, you can clone it using -.. code-block:: console - - git clone https://github.com/myrepo.git +```console +git clone https://github.com/myrepo.git +``` where ``myrepo.git`` is replaced with your git repo. If you follow this, you can skip to :ref:`commits `. -Updating a Fork -^^^^^^^^^^^^^^^ +### Updating a Fork 1. Add the upstream: ``git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git`` 2. Confirm it was added via: ``git remote -v`` 3. Pull changes from upstream: ``git fetch upstream`` 4. Merge the changes into head: ``git merge upstream/upstream-branch-name`` -Gitignore ---------- +## Gitignore .. important:: It is extremely important that teams **do not** modify the ``.gitignore`` file that is included with their robot project. This can lead to offline deployment not working. -A ``.gitignore`` file is commonly used as a list of files to not automatically commit with ``git add``. Any files or directory listed in this file will **not** be committed. They will also not show up with `git status `_. +A ``.gitignore`` file is commonly used as a list of files to not automatically commit with ``git add``. Any files or directory listed in this file will **not** be committed. They will also not show up with [git status](https://git-scm.com/docs/git-status). -Additional Information can be found `here `__. +Additional Information can be found [here](https://www.atlassian.com/git/tutorials/saving-changes/gitignore). -Hiding a Folder -^^^^^^^^^^^^^^^ +### Hiding a Folder Simply add a new line containing the folder to hide, with a forward slash at the end EX: ``directory-to-exclude/`` -Hiding a File -^^^^^^^^^^^^^ +### Hiding a File Add a new line with the name of the file to hide, including any prepending directory relative to the root of the repository. @@ -290,9 +265,8 @@ EX: ``directory/file-to-hide.txt`` EX: ``file-to-hide2.txt`` -Additional Information ----------------------- +## Additional Information -A much more in-depth tutorial can be found at the official `git `__ website. +A much more in-depth tutorial can be found at the official [git](https://git-scm.com/docs/gittutorial) website. -A guide for correcting common mistakes can be found at the git `flight rules `_ repository. +A guide for correcting common mistakes can be found at the git [flight rules](https://github.com/k88hudson/git-flight-rules/blob/master/README.md) repository. diff --git a/source/docs/software/basic-programming/images/coordinate-system/charged-up-field.jpg b/source/docs/software/basic-programming/images/coordinate-system/charged-up-field.jpg new file mode 100644 index 0000000000..4eb620e194 Binary files /dev/null and b/source/docs/software/basic-programming/images/coordinate-system/charged-up-field.jpg differ diff --git a/source/docs/software/basic-programming/images/coordinate-system/field-blue-alliance.svg b/source/docs/software/basic-programming/images/coordinate-system/field-blue-alliance.svg new file mode 100644 index 0000000000..ba2940e4b7 --- /dev/null +++ b/source/docs/software/basic-programming/images/coordinate-system/field-blue-alliance.svg @@ -0,0 +1,2157 @@ + + + + + + + + + + + + + + + + + + + + + Page-1 + + + Sheet.15 + + + + + + Sheet.2 + + + + Sheet.3 + + + + Sheet.4 + +Y + + + + +Y + + Sheet.8 + +X + + + + +X + + Circular Arrow + + + + + + + + + + + + + + + + + + + + + diff --git a/source/docs/software/basic-programming/images/coordinate-system/field-red-alliance.svg b/source/docs/software/basic-programming/images/coordinate-system/field-red-alliance.svg new file mode 100644 index 0000000000..53eba080e2 --- /dev/null +++ b/source/docs/software/basic-programming/images/coordinate-system/field-red-alliance.svg @@ -0,0 +1,2157 @@ + + + + + + + + + + + + + + + + + + + + + Page-1 + + + Sheet.20 + + + + + + Sheet.15 + + + + Sheet.16 + + + + Sheet.17 + +Y + + + + +Y + + Sheet.18 + +X + + + + +X + + Circular Arrow.19 + + + + + + + + + + + + + + + + + + + + + diff --git a/source/docs/software/basic-programming/images/coordinate-system/joystick-3d.svg b/source/docs/software/basic-programming/images/coordinate-system/joystick-3d.svg new file mode 100644 index 0000000000..b3b3bd9225 --- /dev/null +++ b/source/docs/software/basic-programming/images/coordinate-system/joystick-3d.svg @@ -0,0 +1,1750 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Page-1 + + + Circular Arrow + + + + + + + + + + + + + + + + + + + + + Sheet.6 + + + + Sheet.5 + + + + Sheet.15 + +X + + + + +X + + Sheet.19 + -X + + + + -X + + Sheet.27 + -Y + + + + -Y + + Sheet.28 + +Y + + + + +Y + + Sheet.29 + -Z + + + + -Z + + Sheet.30 + +Z + + + + +Z + + Circular Arrow.13 + + + + + + + + + + + + + + + + + + + + + Sheet.4 + + + + Circular Arrow.14 + + + + + + + + + + + + + + + + + + + + + Sheet.39 + + + + + + diff --git a/source/docs/software/basic-programming/images/coordinate-system/kinematics.svg b/source/docs/software/basic-programming/images/coordinate-system/kinematics.svg new file mode 100644 index 0000000000..27cde6d23b --- /dev/null +++ b/source/docs/software/basic-programming/images/coordinate-system/kinematics.svg @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Page-1 + + + Sheet.2 + + + + Sheet.3 + + + + Sheet.4 + +X + + + + +X + + Sheet.6 + -X + + + + -X + + Sheet.7 + +Y + + + + +Y + + Sheet.8 + -Y + + + + -Y + + Sheet.9 + (+, +) + + + + (+, +) + + Sheet.10 + (+, -) + + + + (+, -) + + Sheet.11 + (-, +) + + + + (-, +) + + Sheet.12 + (-, -) + + + + (-, -) + + Sheet.23 + + Sheet.15 + FRONT + + + + FRONT + + Square + + + + + + + Rectangle + + + + + + + Rectangle.17 + + + + + + + Rectangle.18 + + + + + + + Rectangle.19 + + + + + + + + diff --git a/source/docs/software/basic-programming/images/coordinate-system/rapid-react-field.jpg b/source/docs/software/basic-programming/images/coordinate-system/rapid-react-field.jpg new file mode 100644 index 0000000000..996d88e907 Binary files /dev/null and b/source/docs/software/basic-programming/images/coordinate-system/rapid-react-field.jpg differ diff --git a/source/docs/software/basic-programming/images/coordinate-system/robot-2d.svg b/source/docs/software/basic-programming/images/coordinate-system/robot-2d.svg new file mode 100644 index 0000000000..844a16c118 --- /dev/null +++ b/source/docs/software/basic-programming/images/coordinate-system/robot-2d.svg @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Page-1 + + + Sheet.2 + + + + Sheet.3 + + + + Sheet.4 + +X + + + + +X + + Sheet.6 + -X + + + + -X + + Sheet.7 + +Y + + + + +Y + + Sheet.8 + -Y + + + + -Y + + Circular Arrow + + + + + + + + + + + + + + + + + + + + + Sheet.15 + FRONT + + + + FRONT + + Square + + + + + + + Rectangle + + + + + + + Rectangle.17 + + + + + + + Rectangle.18 + + + + + + + Rectangle.19 + + + + + + + Rectangle.21 + + + + + + + Rectangle.22 + + + + + + + diff --git a/source/docs/software/basic-programming/images/coordinate-system/robot-3d.svg b/source/docs/software/basic-programming/images/coordinate-system/robot-3d.svg new file mode 100644 index 0000000000..a533ef1217 --- /dev/null +++ b/source/docs/software/basic-programming/images/coordinate-system/robot-3d.svg @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Page-1 + + + Circular Arrow + + + + + + + + + + + + + + + + + + + + + Sheet.6 + + + + Sheet.5 + + + + Sheet.15 + +X + + + + +X + + Sheet.19 + -X + + + + -X + + Sheet.27 + +Y + + + + +Y + + Sheet.28 + -Y + + + + -Y + + Sheet.29 + +Z + + + + +Z + + Sheet.30 + -Z + + + + -Z + + Sheet.35 + + + + Circular Arrow.13 + + + + + + + + + + + + + + + + + + + + + Sheet.32 + + + + Sheet.4 + + + + Sheet.33 + + + + Sheet.34 + + + + Circular Arrow.14 + + + + + + + + + + + + + + + + + + + + + Sheet.38 + + + + + + diff --git a/source/docs/software/basic-programming/images/coordinate-system/rotation.svg b/source/docs/software/basic-programming/images/coordinate-system/rotation.svg new file mode 100644 index 0000000000..05d2af4cee --- /dev/null +++ b/source/docs/software/basic-programming/images/coordinate-system/rotation.svg @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Page-1 + + + Sheet.21 + + + + Sheet.22 + + + + Circle + + + + + + + Sheet.8 + 0° 0 rad + + + + 0 rad + + Sheet.10 + 90° ½π rad + + + + 90°½π rad + + Sheet.11 + 180° π rad + + + + 180°π rad + + Sheet.12 + -90° -½π rad + + + + -90°-½π rad + + Sheet.13 + + + + Sheet.14 + + + + Sheet.15 + + + + Sheet.16 + + + + Sheet.17 + 45° ¼π rad + + + + 45°¼π rad + + Sheet.18 + -45° -¼π rad + + + + -45°-¼π rad + + Sheet.19 + -135° -¾π rad + + + + -135°-¾π rad + + Sheet.20 + 135° ¾π rad + + + + 135°¾π rad + + Circular Arrow + + + + + + + + + + + + + + + + + + + + + diff --git a/source/docs/software/basic-programming/images/git-getting-started/create-account.png b/source/docs/software/basic-programming/images/git-getting-started/create-account.png index cb6ae97651..1071820ac9 100644 Binary files a/source/docs/software/basic-programming/images/git-getting-started/create-account.png and b/source/docs/software/basic-programming/images/git-getting-started/create-account.png differ diff --git a/source/docs/software/basic-programming/images/git-getting-started/create-repository.png b/source/docs/software/basic-programming/images/git-getting-started/create-repository.png index 254a98678c..580c38acd0 100644 Binary files a/source/docs/software/basic-programming/images/git-getting-started/create-repository.png and b/source/docs/software/basic-programming/images/git-getting-started/create-repository.png differ diff --git a/source/docs/software/basic-programming/images/git-getting-started/homepage.png b/source/docs/software/basic-programming/images/git-getting-started/homepage.png index 37dcfdc945..baeb6fcaab 100644 Binary files a/source/docs/software/basic-programming/images/git-getting-started/homepage.png and b/source/docs/software/basic-programming/images/git-getting-started/homepage.png differ diff --git a/source/docs/software/basic-programming/images/git-getting-started/new-repository.png b/source/docs/software/basic-programming/images/git-getting-started/new-repository.png index e87ddfe826..cb98e24c52 100644 Binary files a/source/docs/software/basic-programming/images/git-getting-started/new-repository.png and b/source/docs/software/basic-programming/images/git-getting-started/new-repository.png differ diff --git a/source/docs/software/basic-programming/images/git-getting-started/plus.png b/source/docs/software/basic-programming/images/git-getting-started/plus.png index 7cffbc8224..c20687a002 100644 Binary files a/source/docs/software/basic-programming/images/git-getting-started/plus.png and b/source/docs/software/basic-programming/images/git-getting-started/plus.png differ diff --git a/source/docs/software/basic-programming/images/git-getting-started/powershell.png b/source/docs/software/basic-programming/images/git-getting-started/powershell.png index c49cd144a6..2f0abbc64a 100644 Binary files a/source/docs/software/basic-programming/images/git-getting-started/powershell.png and b/source/docs/software/basic-programming/images/git-getting-started/powershell.png differ diff --git a/source/docs/software/basic-programming/images/git-getting-started/quick-setup.png b/source/docs/software/basic-programming/images/git-getting-started/quick-setup.png index 9a02a6fea0..360bc8c979 100644 Binary files a/source/docs/software/basic-programming/images/git-getting-started/quick-setup.png and b/source/docs/software/basic-programming/images/git-getting-started/quick-setup.png differ diff --git a/source/docs/software/basic-programming/images/joystick/dpadangles.png b/source/docs/software/basic-programming/images/joystick/dpadangles.png index debf1a774f..fb91a2f357 100644 Binary files a/source/docs/software/basic-programming/images/joystick/dpadangles.png and b/source/docs/software/basic-programming/images/joystick/dpadangles.png differ diff --git a/source/docs/software/basic-programming/images/joystick/joystick.png b/source/docs/software/basic-programming/images/joystick/joystick.png index bbe8af5399..c4145d5cd2 100644 Binary files a/source/docs/software/basic-programming/images/joystick/joystick.png and b/source/docs/software/basic-programming/images/joystick/joystick.png differ diff --git a/source/docs/software/basic-programming/images/reading-stacktraces/bad-gradlew-project.png b/source/docs/software/basic-programming/images/reading-stacktraces/bad-gradlew-project.png index 70b6eaef55..2521b293b3 100644 Binary files a/source/docs/software/basic-programming/images/reading-stacktraces/bad-gradlew-project.png and b/source/docs/software/basic-programming/images/reading-stacktraces/bad-gradlew-project.png differ diff --git a/source/docs/software/basic-programming/images/reading-stacktraces/cpp_div_zero_stacktrace.png b/source/docs/software/basic-programming/images/reading-stacktraces/cpp_div_zero_stacktrace.png index 247004ef36..4933124f42 100644 Binary files a/source/docs/software/basic-programming/images/reading-stacktraces/cpp_div_zero_stacktrace.png and b/source/docs/software/basic-programming/images/reading-stacktraces/cpp_div_zero_stacktrace.png differ diff --git a/source/docs/software/basic-programming/images/reading-stacktraces/cpp_null_stacktrace.png b/source/docs/software/basic-programming/images/reading-stacktraces/cpp_null_stacktrace.png index d8013ff383..94c71e08b7 100644 Binary files a/source/docs/software/basic-programming/images/reading-stacktraces/cpp_null_stacktrace.png and b/source/docs/software/basic-programming/images/reading-stacktraces/cpp_null_stacktrace.png differ diff --git a/source/docs/software/basic-programming/images/reading-stacktraces/cpp_vscode_dbg_tab.png b/source/docs/software/basic-programming/images/reading-stacktraces/cpp_vscode_dbg_tab.png index 1a8dc26b20..65fb698c9f 100644 Binary files a/source/docs/software/basic-programming/images/reading-stacktraces/cpp_vscode_dbg_tab.png and b/source/docs/software/basic-programming/images/reading-stacktraces/cpp_vscode_dbg_tab.png differ diff --git a/source/docs/software/basic-programming/images/robot-preferences/preferences-widget-shuffleboard.png b/source/docs/software/basic-programming/images/robot-preferences/preferences-widget-shuffleboard.png index 36f6922b44..e9a9b2ac19 100644 Binary files a/source/docs/software/basic-programming/images/robot-preferences/preferences-widget-shuffleboard.png and b/source/docs/software/basic-programming/images/robot-preferences/preferences-widget-shuffleboard.png differ diff --git a/source/docs/software/basic-programming/images/robot-preferences/preferences-widget-smartdashboard.png b/source/docs/software/basic-programming/images/robot-preferences/preferences-widget-smartdashboard.png index b78267d72f..86da03db1e 100644 Binary files a/source/docs/software/basic-programming/images/robot-preferences/preferences-widget-smartdashboard.png and b/source/docs/software/basic-programming/images/robot-preferences/preferences-widget-smartdashboard.png differ diff --git a/source/docs/software/basic-programming/images/robot-preferences/view-edit-preferences-values-shuffleboard.png b/source/docs/software/basic-programming/images/robot-preferences/view-edit-preferences-values-shuffleboard.png index a1eddfcc66..cca5a31ae9 100644 Binary files a/source/docs/software/basic-programming/images/robot-preferences/view-edit-preferences-values-shuffleboard.png and b/source/docs/software/basic-programming/images/robot-preferences/view-edit-preferences-values-shuffleboard.png differ diff --git a/source/docs/software/basic-programming/images/robot-preferences/view-edit-preferences-values-smartdashboard.png b/source/docs/software/basic-programming/images/robot-preferences/view-edit-preferences-values-smartdashboard.png index c8c595ef02..9c773e6a50 100644 Binary files a/source/docs/software/basic-programming/images/robot-preferences/view-edit-preferences-values-smartdashboard.png and b/source/docs/software/basic-programming/images/robot-preferences/view-edit-preferences-values-smartdashboard.png differ diff --git a/source/docs/software/basic-programming/index.rst b/source/docs/software/basic-programming/index.rst index 65a5e883dc..a077d7096e 100644 --- a/source/docs/software/basic-programming/index.rst +++ b/source/docs/software/basic-programming/index.rst @@ -1,5 +1,4 @@ -Basic Programming -================= +# Basic Programming .. toctree:: :maxdepth: 1 @@ -8,8 +7,10 @@ Basic Programming cpp-units java-units joystick + coordinate-system robot-preferences using-test-mode reading-stacktraces functions-as-data + alliancecolor java-gc diff --git a/source/docs/software/basic-programming/java-gc.rst b/source/docs/software/basic-programming/java-gc.rst index 74d4baa70f..c1a331d2e7 100644 --- a/source/docs/software/basic-programming/java-gc.rst +++ b/source/docs/software/basic-programming/java-gc.rst @@ -1,31 +1,172 @@ -Java Garbage Collection -======================= +# Java Garbage Collection Java garbage collection is the process of automatically managing memory for Java objects. The Java Virtual Machine (JVM) is responsible for creating and destroying objects, and the garbage collector is responsible for identifying and reclaiming unused objects. Java garbage collection is an automatic process, which means that the programmer does not need to explicitly deallocate memory. The garbage collector keeps track of which objects are in use and which are not, and it periodically reclaims unused objects. -Object Creation ---------------- +## Object Creation Creating a large number of objects in Java can lead to memory and performance issues. While the Java Garbage Collector (GC) is designed to handle memory management efficiently, creating too many objects can overwhelm the GC and cause performance degradation. -Memory Concerns -^^^^^^^^^^^^^^^ +### Memory Concerns When a large number of objects are created, it increases the overall memory footprint of the application. While the overhead for a single object may be insignificant, it can become substantial when multiplied by a large number of objects. -Performance Concerns -^^^^^^^^^^^^^^^^^^^^ +.. note:: :doc:`VisualVM ` can be used to see where memory is allocated. + +### Performance Concerns The GC's job is to periodically identify and reclaim unused objects in memory. While garbage collection is running on an FRC robot coded in Java, execution of the robot program is paused. When the GC has to collect a large number of objects, it has to pause the application to run more frequently or for longer periods of time. This is because the GC has to perform more work to collect and process each object. GC-related performance degradation in robot programs can manifest as occasional pauses, freezes, or loop overruns as the GC works to reclaim memory. -Design Considerations -^^^^^^^^^^^^^^^^^^^^^ +### Design Considerations If you anticipate your application creating a large number of short-lived objects, it is important to consider design strategies to mitigate the potential memory and performance issues. Here are some strategies to consider: - Minimize object creation: Carefully evaluate the need for each object creation. If possible, reuse existing objects or use alternative data structures, such as arrays or primitives, to avoid creating new objects. - Efficient data structures: Use data structures that are well-suited for the type of data you are working with. For example, if you are dealing with a large number of primitive values, consider using arrays or collections specifically designed for primitives. + +## Diagnosing Out of Memory Errors with Heap Dumps + +All objects in Java are retained in a section of memory called the *heap*. As objects typically consume the greatest amount of memory in a Java program, it is often useful to take a snapshot of the state of the heap---a heap dump---to analyze memory issues. Heap dumps only capture the state of a program's heap at a single point in time, so they are unlikely to be useful if not captured exactly at the time the program is experiencing memory issues. + +Since ``OutOfMemoryError``\ s both crash the program and are a common reason to want a heap dump, the JVM can be configured to automatically take a heap dump the moment an ``OutOfMemoryError`` is caught by the JVM. To configure these options, locate the ``frcJava`` code block in your project's ``build.gradle``: + +.. rli:: https://raw.githubusercontent.com/wpilibsuite/vscode-wpilib/v2024.3.1/vscode-wpilib/resources/gradle/java/build.gradle + :language: groovy + :lines: 15-40 + :linenos: + :lineno-start: 15 + :emphasize-lines: 15-16 + +Add to the code block so that it contains two ``jvmArgs`` commands, as shown below: + +```groovy +frcJava(getArtifactTypeClass('FRCJavaArtifact')) { + // If you have other configuration here, you do not need to remove it. + // Enable automatic heap dumps on OutOfMemoryError + // Note: the heap dump path here is a path on a USB flash drive, see below + jvmArgs.add("-XX:+HeapDumpOnOutOfMemoryError") + jvmArgs.add("-XX:HeapDumpPath=/u/frc-usercode.hprof") +} +``` + +This will cause the JVM to write heap dumps to a file named ``frc-usercode.hprof`` at the root of a USB flash drive attached to the roboRIO when the code runs out of memory. It is recommended to save these heap dumps to a USB flash drive because heap dumps intrinsically consume the same amount of space on disk as the program heap did in memory when the program crashed, and are likely to be larger than the roboRIO's internal storage has capacity for. Once you have reproduced the ``OutOfMemoryError``, redeploy your code without these options enabled, and use the USB flash drive to transfer the heap dump to a computer for analysis in a memory profiler such as :ref:`VisualVM `. + +.. warning:: Configuring the JVM this way requires that the flash drive remain connected to the roboRIO while your code is running. + +Larger SD cards may provide enough onboard storage to allow the use of these options on the roboRIO 2 without a USB flash drive. To do this, set the ``-XX:HeapDumpPath`` option to reference a path on the SD card, and use :doc:`FTP/SFTP to transfer the heap dump to a computer ` before deleting it from the SD card. + +Note that the JVM will **not** overwrite heap dumps with the exact path and filename specified by ``-XX:HeapDumpPath`` if they already exist, nor will it dump the process heap to a file with a different name. If a path to a directory is supplied instead of a path to a file, the JVM will instead write out heap dumps with unique filenames within the specified directory, with the name ``java_pidNNNN.hprof``, where ``NNNN`` is the process ID of the JVM that ran out of memory. Note that this can cause large files to build up on disk if they are not cleaned out, so if you configure the JVM this way, be sure to frequently copy heap dumps to a computer and delete them from the flash drive/SD card afterward. + +.. caution:: Always be vigilant about the amount of available space on the underlying storage medium while you use this feature. + + Use of this feature is not recommended during competitive play. + +## System Memory Tuning + +If the JVM cannot allocate memory, the program will be terminated. As an embedded system with only a small amount of memory available (256 MB on the roboRIO 1, 512 MB on the roboRIO 2), the roboRIO is particularly susceptible to running out of memory. + +.. admonition :: No amount of system tuning can fix out of memory errors caused by out-of-control allocations. + + If you are running out of memory, always investigate allocations with heap dumps and/or :doc:`VisualVM ` first. + +If you continue to run out of memory even after investigating with VisualVM and taking steps to minimize the number of allocated objects, a few different options are available to make additional memory available to the robot program. + +- Disabling the system web server +- Setting sysctls (Linux kernel options) +- Periodically calling the garbage collector +- Setting up swap on a USB flash drive + +Implementing most of these options require :doc:`connecting with SSH ` to the roboRIO and running commands. If run incorrectly, it may require a reimage to recover, so be careful when following the instructions. + +### Disabling the System Web Server + +The built-in NI system web server provides the webpage (the :doc:`roboRIO Web Dashboard `) seen when using a web browser to connect to the roboRIO, e.g. to change IP address settings. It also is used by the Driver Station's data log download functionality. However, it consumes several MB of RAM, so disabling it will free up that memory for the robot program to use. There are several ways to disable the web server: + +The first and easiest is to use the :doc:`RoboRIO Team Number Setter ` tool. Versions 2024.2.1 and later of the tool have a button to disable or enable the web server. However, a few teams have reported that this does not work or does not persist between reboots. There are two alternate ways to disable the web server; both require connecting to the roboRIO with SSH and logging in as the ``admin`` user. + +1. Run ``/etc/init.d/systemWebServer stop; update-rc.d -f systemWebServer remove; sync`` + +2. Run ``chmod a-x /usr/local/natinst/etc/init.d/systemWebServer; sync`` + +To revert the alternate ways and re-enable the web server, take the corresponding step: + +1. Run ``update-rc.d -f systemWebServer defaults; /etc/init.d/systemWebServer start; sync`` + +2. Run ``chmod a+x /usr/local/natinst/etc/init.d/systemWebServer; sync`` + +### Setting sysctls + +Several Linux kernel options (called sysctls) can be set to tweak how the kernel allocates memory. Several options have been found to reduce out-of-memory errors: + +- Setting ``vm.overcommit_memory`` to 1 (the default value is 2). This causes the kernel to always pretend there is enough memory for a requested memory allocation at the time of allocation; the default setting always checks to see if there's actually enough memory to back an allocation at the time of allocation, not when the memory is actually used. +- Setting ``vm.vfs_cache_pressure`` to 1000 (the default value is 100). Increasing this causes the kernel to much more aggressively reclaim file system object caches; it may slightly degrade performance. +- Setting ``vm.swappiness`` to 100 (the default value is 60). This causes the kernel to more aggressively swap process memory to the swap file. Changing this option has no effect unless you add a swap file. + +You can set some or all of these options; the most important one is ``vm.overcommit_memory``. Setting these options requires connecting to the roboRIO with SSH and logging in as the ``admin`` user, then running the following commands: + +```text +echo "vm.overcommit_memory=1" >> /etc/sysctl.conf +echo "vm.vfs_cache_pressure=1000" >> /etc/sysctl.conf +echo "vm.swappiness=100" >> /etc/sysctl.conf +sync +``` + +The ``/etc/sysctl.conf`` file should contain the following lines at the end when done (to check, you can run the command ``cat /etc/sysctl.conf``): + +```text +vm.overcommit_memory=1 +vm.vfs_cache_pressure=1000 +vm.swappiness=100 +``` + +To revert the change, edit ``/etc/sysctl.conf`` (this will require the use of the vi editor) and remove these 3 lines. + +### Periodically Calling the Garbage Collector + +Sometimes the garbage collector won't run frequently enough to keep up with the quantity of allocations. As Java provides a way to trigger a garbage collection to occur, running it on a periodic basis may reduce peak memory usage. This can be done by adding a ``Timer`` and a periodic check: + +```java +Timer m_gcTimer = new Timer(); +public void robotInit() { + m_gcTimer.start(); +} +public void periodic() { + // run the garbage collector every 5 seconds + if (m_gcTimer.advanceIfElapsed(5)) { + System.gc(); + } +} +``` + +### Setting Up Swap on a USB Flash Drive + +A swap file on a Linux system provides disk-backed space that can be used by the system as additional virtual memory to put infrequently used data and programs when they aren't being used, freeing up physical RAM for active use such as the robot program. It is strongly recommended to not use the built-in non-replaceable flash storage on the roboRIO 1 for a swap file, as it has very limited write cycles and may wear out quickly. Instead, however, a FAT32-formatted USB flash drive may be used for this purpose. This does require the USB flash drive to always be plugged into the roboRIO before boot. + +.. caution:: Having a swap file on a USB stick means it's critical the USB stick stay connected to the roboRIO at all times it is powered. + + This should be used as a last resort if none of the other steps above help. Generally needing swap is indicative of some other allocation issue, so use VisualVM first to optimize allocations. + +A swap file can be set up by plugging the USB flash drive into the roboRIO USB port, connecting to the roboRIO with SSH and logging in as the ``admin`` user, and running the following commands. Note the vi step requires knowledge of how to edit and save a file in vi. + +```text +fallocate -l 100M /u/swapfile +mkswap /u/swapfile +swapon /u/swapfile +vi /etc/init.d/addswap.sh +chmod a+x /etc/init.d/addswap.sh +update-rc.d -v addswap.sh defaults +sync +``` + +The ``/etc/init.d/addswap.sh`` file contents should look like this: + +```text +#!/bin/sh +[ -x /sbin/swapon ] && swapon -e /u/swapfile +: exit 0 +``` + +To revert the change, run ``update-rc.d -f addswap.sh remove; rm /etc/init.d/addswap.sh; sync; reboot``. diff --git a/source/docs/software/basic-programming/java-units.rst b/source/docs/software/basic-programming/java-units.rst index 9a6d38ec67..865dde2528 100644 --- a/source/docs/software/basic-programming/java-units.rst +++ b/source/docs/software/basic-programming/java-units.rst @@ -1,5 +1,6 @@ -The Java Units Library -====================== +# The Java Units Library + +.. note:: New for 2025: The units library has been refactored to have unit-specific measurement classes instead of a single generic ``Measure`` class. The new measurement classes have clearer names (``Distance`` instead of ``Measure``, or ``LinearAcceleration`` instead of ``Measure>>``), and implement math operations to return the most specific result types possible instead of a wildcard ``Measure``. The units library is a tool that helps programmers avoid mistakes related to units of measurement. It does this by keeping track of the units of measurement, and by ensuring that all operations are performed with the correct units. This can help to prevent errors that can lead to incorrect results, such as adding a distance in inches to a distance in meters. @@ -12,8 +13,7 @@ The units library has a number of features: - Support for performing arithmetic and comparisons on quantities with units. - Support for displaying quantities with units in a human-readable format. -Terminology ------------ +## Terminology Dimension Dimensions represent the nature of a physical quantity, such as length, time, or mass. They are independent of any specific unit system. For example, the dimension of meters is length, regardless of whether the length is expressed in meters, millimeters, or inches. @@ -25,122 +25,122 @@ Measure These concepts are used within the Units Library. For example, the **measure** *10 seconds* has a magnitude of 10, the **dimension** is time, and the **unit** is seconds. -Using the Units Library ------------------------ +## Using the Units Library The Java units library is available in the ``edu.wpi.first.units`` package. The most relevant classes are: -- The various classes for predefined dimensions, such as `Distance `__ and `Time `__ -- `Units `__, which contains a set of predefined units. Take a look a the `Units javadoc `__ to browse the available units and their types. -- `Measure `__, which is used to tag a value with a unit. +- The various classes for predefined dimensions, such as [DistanceUnit](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/units/DistanceUnit.html) and [TimeUnit](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/units/TimeUnit.html) +- [Units](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/units/Units.html), which contains a set of predefined units. Take a look a the [Units javadoc](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/units/Units.html) to browse the available units and their types. +- [Measure](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/units/Measure.html), which is used to tag a value with a unit, and the dimension-specific implementations like [Distance](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/units/measure/Distance.html) and [Time](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/units/measure/Time.html) .. note:: It is recommended to static import ``edu.wpi.first.units.Units.*`` to get full access to all the predefined units. -Java Generics -^^^^^^^^^^^^^ -Units of measurement can be complex expressions involving various dimension, such as distance, time, and velocity. Nested `generic type parameters `__ allow for the definition of units that can represent such complex expressions. Generics are used to keep the library concise, reusable, and extensible, but it tends to be verbose due to the syntax for Java generics. - -For instance, consider the type ``Measure>``. This type represents a measurement for velocity, where the velocity itself is expressed as a unit of distance per unit of time. This nested structure allows for the representation of units like meters per second or feet per minute. Similarly, the type ``Measure>>`` represents a measurement for a ratio of voltage to velocity. This type is useful for representing quantities like volts per meter per second, the unit of measure for some :ref:`feedforward` gains. - -It's important to note that not all measurements require such complex nested types. For example, the type ``Measure`` is sufficient for representing simple units like meters or feet. However, for more complex units, the use of nested generic type parameters is essential. +### Creating Measures -For local variables, you may choose to use Java's `var` keyword instead of including the full type name. For example, these are equivalent: +Every dimension has a measurement class with the corresponding name - for example, a ``Distance`` measures distance, ``Time`` measures time, and ``LinearVelocity`` measures linear velocity. To instantiate one of these measurements, call the ``Unit.of`` method on the appropriate unit object. For example, to create a ``Distance`` object representing a distance of 6 inches, you would write: -.. code-block:: java +```java +Distance wheelDiameter = Inches.of(6); +``` - Measure>> v = VoltsPerMeterPerSecond.of(8); - var v = VoltsPerMeterPerSecond.of(8); - -Creating Measures -^^^^^^^^^^^^^^^^^ +Other measures can also be created using their ``Unit.of`` method: -The ``Measure`` class is a generic type that represents a magnitude (physical quantity) with its corresponding unit. It provides a consistent and type-safe way to handle different dimensions of measurements, such as distance, angle, and velocity, but abstracts away the particular unit (e.g. meter vs. inch). To create a ``Measure`` object, you call the ``Unit.of`` method on the appropriate unit object. For example, to create a ``Measure`` object representing a distance of 6 inches, you would write: +```java +Mass kArmMass = Kilograms.of(1.423); +Distance kArmLength = Inches.of(32.25); +Angle kMinArmAngle = Degrees.of(5); +Angle kArmMaxTravel = Rotations.of(0.45); +LinearVelocity kMaxSpeed = MetersPerSecond.of(2.5); +``` -.. code-block:: java +.. warning:: Composite units with ``PerUnit`` and ``MultUnit`` have special requirements, and the ``of`` method is not recommended to be used with them - Measure wheelDiameter = Inches.of(6); +#### Using Composite Unit Types -Other measures can also be created using their ``Unit.of`` method: +Due to requirements of inheritance in Java's type system, ``PerUnit`` and ``MultUnit`` cannot return a normal ``Per`` or ``Mult`` object from their ``of`` factory methods. Instead, they need to return a bounded wildcard ``Measure>`` or ``Measure>`` to allow subclasses like ``LinearVelocity`` to return a compatible type. New ``ofNative`` methods are provided to be able to work with known ``Per`` and ``Mult`` objects -.. code-block:: java +```java +// Using ofNative: +Per kP = VoltsPerMeter.ofNative(1); +kP.in(VoltsPerMeter); // 1.0 - Measure kArmMass = Kilograms.of(1.423); - Measure kArmLength = Inches.of(32.25); - Measure kMinArmAngle = Degrees.of(5); - Measure kArmMaxTravel = Rotations.of(0.45); - Measure kMaxSpeed = MetersPerSecond.of(2.5); +Measure output = kP.timesDivisor(Meters.of(1)); +output.in(Volts); // 1.0 -Performing Calculations -^^^^^^^^^^^^^^^^^^^^^^^ +// Without ofNative +Measure> kP = VoltsPerMeter.of(1); +kP.in(VoltsPerMeter); // Compilation error! -The ``Measure`` class also supports arithmetic operations, such as addition, subtraction, multiplication, and division. These are done by calling methods on the objects. These operations always ensure that the units are compatible before performing the calculation, and they return a new ``Measure`` object. For example, you can add two ``Measure`` objects together, even if they have different units: +Measure output = kP.times(Meters.of(1)); // The compiler can't know what unit this is +output.in(Volts); // Compilation error! +``` -.. code-block:: java +### Performing Calculations - Measure distance1 = Inches.of(10); - Measure distance2 = Meters.of(0.254); +The ``Measure`` class also supports arithmetic operations, such as addition, subtraction, multiplication, and division. These are done by calling methods on the objects. These operations always ensure that the units are compatible before performing the calculation, and they return a new ``Measure`` object. For example, you can add two ``Distance`` objects together, even if they have different units: - Measure totalDistance = distance1.plus(distance2); +```java +Distance distance1 = Inches.of(10); +Distance distance2 = Meters.of(0.254); +Distance totalDistance = distance1.plus(distance2); +``` -In this code, the units library will automatically convert the measures to the same unit before adding the two distances. The resulting ``totalDistance`` object will be a new ``Measure`` object that has a value of 0.508 meters, or 20 inches. +In this code, the units library will automatically convert the measures to the same unit before adding the two distances. The resulting ``totalDistance`` object will be a new ``Distance`` object that has a value of 0.508 meters, or 20 inches. -This example combines the wheel diameter and gear ratio to calcualate the distance per rotation of the wheel: +.. note:: Mathematical operations are type safe. It is impossible to add a distance to a time, or subtract an angle from a voltage. However, multiplication and division operations make a best-effort attempt to return results in the most appropriate unit type; dividing a distance by time results in a ``LinearVelocity`` measurement, and multiplying it by time returns a ``Distance``. -.. code-block:: java +This example combines the wheel diameter and gear ratio to calculate the distance per rotation of the wheel: - Measure wheelDiameter = Inches.of(3); - double gearRatio = 10.48; - Measure distancePerRotation = wheelDiameter.times(Math.PI).divide(gearRatio); +```java +Distance wheelDiameter = Inches.of(3); +double gearRatio = 10.48; +Distance distancePerRotation = wheelDiameter.times(Math.PI).divide(gearRatio); +``` .. warning:: By default, arithmetic operations create new ``Measure`` instances for their results. See :ref:`Java Garbage Collection` for discussion on creating a large number of short-lived objects. See also, the `Mutability and Object Creation`_ section below for a possible workaround. -Converting Units -^^^^^^^^^^^^^^^^ +### Converting Units Unit conversions can be done by calling ``Measure.in(Unit)``. The Java type system will prevent units from being converted between incompatible types, such as distances to angles. The returned values will be bare ``double`` values without unit information - it is up to you, the programmer, to interpret them correctly! It is strongly recommended to only use unit conversions when interacting with APIs that do not support the units library. -.. code-block:: java +```java +LinearVelocity kMaxVelocity = FeetPerSecond.of(12.5); +LinearAcceleration kMaxAcceleration = FeetPerSecond.per(Second).of(22.9); +kMaxVelocity.in(MetersPerSecond); // => OK! Returns 3.81 +kMaxVelocity.in(RadiansPerSecond); // => Compile error! LinearVelocity cannot be converted to AngularVelocity - Measure> kMaxVelocity = FeetPerSecond.of(12.5); - Measure>> kMaxAcceleration = FeetPerSecond.per(Second).of(22.9); +// The WPILib math libraries use SI metric units, so we have to convert to meters: +TrapezoidProfile.Constraints kDriveConstraints = new TrapezoidProfile.Constraints( + maxVelocity.in(MetersPerSecond), + maxAcceleration.in(MetersPerSecondPerSecond) +); +``` - kMaxVelocity.in(MetersPerSecond); // => OK! Returns 3.81 - kMaxVelocity.in(RadiansPerSecond); // => Compile error! Velocity cannot be converted to Unit> - - // The WPILib math libraries use SI metric units, so we have to convert to meters: - TrapezoidProfile.Constraints kDriveConstraints = new TrapezoidProfile.Constraints( - maxVelocity.in(MetersPerSecond), - maxAcceleration.in(MetersPerSecondPerSecond) - ); - -Usage Example -^^^^^^^^^^^^^ +### Usage Example Pulling all of the concepts together, we can create an example that calculates the end effector position of an arm mechanism: -.. code-block:: java - - Measure armLength = Feet.of(3).plus(Inches.of(4.25)); - Measure endEffectorX = armLength.times(Math.cos(getArmAngle().in(Radians))); - Measure endEffectorY = armLength.times(Math.sin(getArmAngle().in(Radians))); +```java +Distance armLength = Feet.of(3).plus(Inches.of(4.25)); +Distance endEffectorX = armLength.times(Math.cos(getArmAngle().in(Radians))); +Distance endEffectorY = armLength.times(Math.sin(getArmAngle().in(Radians))); +``` -Human-readable Formatting -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Human-readable Formatting The ``Measure`` class has methods that can be used to get a human-readable representation of the measure. This feature is useful to display a measure on a dashboard or in logs. - ``toString()`` and ``toShortString()`` return a string representation of the measure in a shorthand form. The symbol of the backing unit is used, rather than the full name, and the magnitude is represented in scientific notation. For example, 1.234e+04 V/m - ``toLongString()`` returns a string representation of the measure in a longhand form. The name of the backing unit is used, rather than its symbol, and the magnitude is represented in a full string, not scientific notation. For example, 1234 Volt per Meter -Mutability and Object Creation ------------------------------- +## Mutability and Object Creation -To reduce the number of object instances you create, and reduce memory usage, a special ``MutableMeasure`` class is available. You may want to consider using mutable objects if you are using the units library repeatedly, such as in the robot's periodic loop. See :ref:`Java Garbage Collection` for more discussion on creating a large number of short-lived objects. +To reduce the number of object instances you create, and reduce memory usage, a special ``MutableMeasure`` class is available, with unit-specific subtypes like ``MutDistance`` and ``MutTime``. You may want to consider using mutable objects if you are using the units library repeatedly, such as in the robot's periodic loop. See :ref:`Java Garbage Collection` for more discussion on creating a large number of short-lived objects. Mutable measures can be created in a similar way to regular, immutable measures using the ``Unit.mutable`` method (instead of ``Unit.of``). ``MutableMeasure`` allows the internal state of the object to be updated, such as with the results of arithmetic operations, to avoid allocating new objects. Special care needs to be taken when mutating a measure because it will change the value every place that instance is referenced. If the object will be exposed as part of a public method, have that method return a regular ``Measure`` in its signature to prevent the caller from modifying your internal state. Extra methods are available on ``MutableMeasure`` for updating the internal value. Note that these methods all begin with the ``mut_`` prefix - this is to make it obvious that these methods will be mutating the object and are potentially unsafe! -For the full list of methods and API documentation, see `the MutableMeasure API documentation `__ +For the full list of methods and API documentation, see [the MutableMeasure API documentation](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/units/MutableMeasure.html) +-------------------------------+--------------------------------------------------------------------------------------------------+ | ``mut_plus(double, Unit)`` | Increments the internal value by an amount in another unit. The internal unit will stay the same | @@ -162,134 +162,125 @@ For the full list of methods and API documentation, see `the MutableMeasure API | ``mut_setMagnitude(double)`` | Overrides the internal value, keeping the internal unit. Be careful when using this! | +-------------------------------+--------------------------------------------------------------------------------------------------+ -.. code-block:: java - - MutableMeasure measure = MutableMeasure.zero(Feet); - measure.mut_plus(10, Inches); // 0.8333 feet - measure.mut_plus(Inches.of(10)); // 1.6667 feet - measure.mut_minus(5, Inches); // 1.25 feet - measure.mut_minus(Inches.of(5)); // 0.8333 feet - measure.mut_times(6); // 0.8333 * 6 = 5 feet - measure.mut_divide(5); // 5 / 5 = 1 foot - measure.mut_replace(6.2, Meters) // 6.2 meters - note the unit changed! - measure.mut_replace(Millimeters.of(14.2)) // 14.2mm - the unit changed again! - measure.mut_setMagnitude(72) // 72mm +```java +MutDistance measure = Feet.mutable(0); +measure.mut_plus(10, Inches); // 0.8333 feet +measure.mut_plus(Inches.of(10)); // 1.6667 feet +measure.mut_minus(5, Inches); // 1.25 feet +measure.mut_minus(Inches.of(5)); // 0.8333 feet +measure.mut_times(6); // 0.8333 * 6 = 5 feet +measure.mut_divide(5); // 5 / 5 = 1 foot +measure.mut_replace(6.2, Meters) // 6.2 meters - note the unit changed! +measure.mut_replace(Millimeters.of(14.2)) // 14.2mm - the unit changed again! +measure.mut_setMagnitude(72) // 72mm +``` Revisiting the arm example from above, we can use ``mut_replace`` - and, optionally, ``mut_times`` - to calculate the end effector position -.. code-block:: java - - import edu.wpi.first.units.Measure; - import edu.wpi.first.units.MutableMeasure; - import static edu.wpi.first.units.Units.*; - - public class Arm { - // Note the two ephemeral object allocations for the Feet.of and Inches.of calls. - // Because this is a constant value computed just once, they will easily be garbage collected without - // any problems with memory use or loop timing jitter. - private static final Measure kArmLength = Feet.of(3).plus(Inches.of(4.25)); - - // Angle and X/Y locations will likely be called in the main robot loop, let's store them in a MutableMeasure - // to avoid allocating lots of short-lived objects - private final MutableMeasure m_angle = MutableMeasure.zero(Degrees); - private final MutableMeasure m_endEffectorX = MutableMeasure.zero(Feet); - private final MutableMeasure m_endEffectorY = MutableMeasure.zero(Feet); - - private final Encoder m_encoder = new Encoder(...); - - public Measure getEndEffectorX() { - m_endEffectorX.mut_replace( - Math.cos(getAngle().in(Radians)) * kArmLength.in(Feet), // the new magnitude to store - Feet // the units of the new magnitude - ); - return m_endEffectorX; - } - - public Measure getEndEffectorY() { - // An alternative approach so we don't have to unpack and repack the units - m_endEffectorY.mut_replace(kArmLength); - m_endEffectorY.mut_times(Math.sin(getAngle().in(Radians))); - return m_endEffectorY; - } - - public Measure getAngle() { - double rawAngle = m_encoder.getPosition(); - m_angle.mut_replace(rawAngle, Degrees); // NOTE: the encoder must be configured with distancePerPulse in terms of degrees! - return m_angle; - } - } +```java +import edu.wpi.first.units.Measure; +import edu.wpi.first.units.MutableMeasure; +import static edu.wpi.first.units.Units.*; +public class Arm { + // Note the two ephemeral object allocations for the Feet.of and Inches.of calls. + // Because this is a constant value computed just once, they will easily be garbage collected without + // any problems with memory use or loop timing jitter. + private static final Distance kArmLength = Feet.of(3).plus(Inches.of(4.25)); + + // Angle and X/Y locations will likely be called in the main robot loop, let's store them in a MutableMeasure + // to avoid allocating lots of short-lived objects + private final MutAngle m_angle = Degrees.mutable(0); + private final MutDistance m_endEffectorX = Feet.mutable(0); + private final MutDistance m_endEffectorY = Feet.mutable(0); + private final Encoder m_encoder = new Encoder(...); + + public Distance getEndEffectorX() { + return m_endEffectorX.mut_replace( + Math.cos(getAngle().in(Radians)) * kArmLength.in(Feet), // the new magnitude to store + Feet // the units of the new magnitude + ); + } + + public Distance getEndEffectorY() { + // An alternative approach so we don't have to unpack and repack the units + m_endEffectorY.mut_replace(kArmLength); + m_endEffectorY.mut_times(Math.sin(getAngle().in(Radians))); + return m_endEffectorY; + } + + public Angle getAngle() { + double rawAngle = m_encoder.getPosition(); + m_angle.mut_replace(rawAngle, Degrees); // NOTE: the encoder must be configured with distancePerPulse in terms of degrees! + return m_angle; + } +} +``` .. warning:: ``MutableMeasure`` objects can - by definition - change their values at any time! It is unsafe to keep a stateful reference to them - prefer to extract a value using the ``Measure.in`` method, or create a copy with ``Measure.copy`` that can be safely stored. For the same reason, library authors must also be careful about methods accepting ``Measure``. Can you spot the bug in this code? -.. code-block:: java - - private Measure m_lastDistance; - - public Measure calculateDelta(Measure currentDistance) { - if (m_lastDistance == null) { - m_lastDistance = currentDistance; - return currentDistance; - } else { - Measure delta = currentDistance.minus(m_lastDistance); - m_lastDistance = currentDistance; - return delta; - } - } +```java +private Distance m_lastDistance; +public Distance calculateDelta(Distance currentDistance) { + if (m_lastDistance == null) { + m_lastDistance = currentDistance; + return currentDistance; + } else { + Distance delta = currentDistance.minus(m_lastDistance); + m_lastDistance = currentDistance; + return delta; + } +} +``` If we run the ``calculateDelta`` method a few times, we can see a pattern: -.. code-block:: java +```java +MutDistance distance = Inches.mutable(0); +distance.mut_plus(10, Inches); +calculateDelta(distance); // expect 10 inches and get 10 - good! +distance.mut_plus(2, Inches); +calculateDelta(distance); // expect 2 inches, but get 0 instead! +distance.mut_plus(8, Inches); +calculateDelta(distance); // expect 8 inches, but get 0 instead! +``` - MutableMeasure distance = MutableMeasure.zero(Inches); - distance.mut_plus(10, Inches); - calculateDelta(distance); // expect 10 inches and get 10 - good! - - distance.mut_plus(2, Inches); - calculateDelta(distance); // expect 2 inches, but get 0 instead! - - distance.mut_plus(8, Inches); - calculateDelta(distance); // expect 8 inches, but get 0 instead! - -This is because the ``m_lastDistance`` field is a reference to the *same* ``MutableMeasure`` object as the input! Effectively, the delta is calculated as (currentDistance - currentDistance) on every call after the first, which naturally always returns zero. One solution would be to track ``m_lastDistance`` as a *copy* of the input measure to take a snapshot; however, this approach does incur one extra object allocation for the copy. If you need to be careful about object allocations, ``m_lastDistance`` could also be stored as a ``MutableMeasure``. +This is because the ``m_lastDistance`` field is a reference to the *same* ``MutDistance`` object as the input! Effectively, the delta is calculated as (currentDistance - currentDistance) on every call after the first, which naturally always returns zero. One solution would be to track ``m_lastDistance`` as a *copy* of the input measure to take a snapshot; however, this approach does incur one extra object allocation for the copy. If you need to be careful about object allocations, ``m_lastDistance`` could also be stored as a ``MutDistance``. .. tab-set:: .. tab-item:: Immutable Copies - .. code-block:: java - - private Measure m_lastDistance; - - public Measure calculateDelta(Measure currentDistance) { - if (m_lastDistance == null) { - m_lastDistance = currentDistance.copy(); - return currentDistance; - } else { - var delta = currentDistance.minus(m_lastDistance); - m_lastDistance = currentDistance.copy(); - return delta; - } - } + ```java + private Distance m_lastDistance; + public Distance calculateDelta(Distance currentDistance) { + if (m_lastDistance == null) { + m_lastDistance = currentDistance.copy(); + return currentDistance; + } else { + var delta = currentDistance.minus(m_lastDistance); + m_lastDistance = currentDistance.copy(); + return delta; + } + } + ``` .. tab-item:: Zero-allocation Mutables - .. code-block:: java - - private final MutableMeasure m_lastDistance = MutableMeasure.zero(Meters); - private final MutableMeasure m_delta = MutableMeasure.zero(Meters); + ```java + private final MutDistance m_lastDistance = Meters.mutable(0); + private final MutDistance m_delta = Meters.mutable(0); + public Distance calculateDelta(Distance currentDistance) { + // m_delta = currentDistance - m_lastDistance + m_delta.mut_replace(currentDistance); + m_delta.mut_minus(m_lastDistance); + m_lastDistance.mut_replace(currentDistance); + return m_delta; + } + ``` - public Measure calculateDelta(Measure currentDistance) { - // m_delta = currentDistance - m_lastDistance - m_delta.mut_replace(currentDistance); - m_delta.mut_minus(m_lastDistance); - m_lastDistance.mut_replace(currentDistance); - return m_delta; - } - -Defining New Units ------------------- +## Defining New Units There are four ways to define a new unit that isn't already present in the library: @@ -300,41 +291,82 @@ There are four ways to define a new unit that isn't already present in the libra New units can be defined as combinations of existing units using the ``Unit.mult`` and ``Unit.per`` methods. -.. code-block:: java - - Per VoltsPerInch = Volts.per(Inch); - Velocity KgPerSecond = Kilograms.per(Second); - Mult> Newtons = Kilograms.mult(MetersPerSecondSquared); +```java +PerUnit VoltsPerInch = Volts.per(Inch); +VelocityUnit KgPerSecond = Kilograms.per(Second); // Could also be declared as PerUnit +DistanceUnit FootMinutesPerSecond = FeetPerSecond.mult(Minutes); +``` Using ``mult`` and ``per`` will store the resulting unit. Every call will return the same object to avoid unnecessary allocations and garbage collector pressure. -.. code-block:: java - - @Override - public void robotPeriodic() { - // Feet.per(Millisecond) creates a new unit on the first loop, - // which will be reused on every successive loop - SmartDashboard.putNumber("Speed", m_drivebase.getSpeed().in(Feet.per(Millisecond))); - } +```java +@Override +public void robotPeriodic() { + // Feet.per(Millisecond) creates a new unit on the first loop, + // which will be reused on every successive loop + SmartDashboard.putNumber("Speed", m_drivebase.getSpeed().in(Feet.per(Millisecond))); +} +``` .. note:: Calling ``Unit.per(Time)`` will return a ``Velocity`` unit, which is different from and incompatible with a ``Per`` unit! -New dimensions can also be created by subclassing ``Unit`` and implementing the two constructors. Note that ``Unit`` is also a parameterized generic type, where the generic type argument is self-referential; ``Distance`` is a ``Unit``. This is what allows us to have stronger guarantees in the type system to prevent conversions between unrelated dimensions. - -.. code-block:: java - - public class ElectricCharge extends Unit { - public ElectricCharge(double baseUnitEquivalent, String name, String symbol) { - super(ElectricCharge.class, baseUnitEquivalent, name, symbol); - } - - // required for derivation with Milli, Kilo, etc. - public ElectricCharge(UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { - super(ElectricCharge.class, toBaseConverter, fromBaseConverter, name, symbol); - } - } +New dimensions can also be created by subclassing ``Unit`` and implementing the two constructors. Dimension-specific measurement types are recommended, but take considerable effort to implement all the unit-specific math operations. + +```java +public class ElectricChargeUnit extends Unit { + public ElectricCharge(double baseUnitEquivalent, String name, String symbol) { + super(ElectricCharge.class, baseUnitEquivalent, name, symbol); + } + // required for derivation with Milli, Kilo, etc. + public ElectricCharge(UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { + super(ElectricCharge.class, toBaseConverter, fromBaseConverter, name, symbol); + } + + @Override + public ElectricChargeUnit getBaseUnit() { + // The base method must be overridden in order to return the correct type + return (ElectricChargeUnit) super.getBaseUnit(); + } + + @Override + public Measure of(double magnitude) { + return ImmutableMeasure.ofRelativeUnits(magnitude, this); + } + + @Override + public Measure ofBaseUnits(double baseUnitMagnitude) { + return ImmutableMeasure.ofBaseUnits(baseUnitMagnitude, this); + } + + @Override + public Measure zero() { + return (Measure) super.zero(); + } + + @Override + public Measure one() { + return (Measure) super.one(); + } + + @Override + public MutableMeasure mutable(double magnitude) { + return new GenericMutableMeasureImpl(magnitude, toBaseUnits(magnitude), this); + } + + @Override + public VelocityUnit per(TimeUnit period) { + // Note: technically, this would return a CurrentUnit, since electric charge per time is current (measured in Amperes) + return VelocityUnit.combine(this, period); + } + + public double convertFrom(double magnitude, ElectricChargeUnit otherUnit) { + return fromBaseUnits(otherUnit.toBaseUnits(magnitude)); + } +} + +public static final ElectricCharge Coulomb = new ElectricCharge(1, "Coulomb", "C"); +public static final ElectricCharge ElectronCharge = new ElectricCharge(1.60217646e-19, "Electron Charge", "e"); +public static final ElectricCharge AmpHour = new ElectricCharge(3600, "Amp Hour", "Ah"); +public static final ElectricCharge MilliampHour = Milli(AmpHour); +``` - public static final ElectricCharge Coulomb = new ElectricCharge(1, "Coulomb", "C"); - public static final ElectricCharge ElectronCharge = new ElectricCharge(1.60217646e-19, "Electron Charge", "e"); - public static final ElectricCharge AmpHour = new ElectricCharge(3600, "Amp Hour", "Ah"); - public static final ElectricCharge MilliampHour = Milli(AmpHour); diff --git a/source/docs/software/basic-programming/joystick.rst b/source/docs/software/basic-programming/joystick.rst index 661483fd7d..9391316028 100644 --- a/source/docs/software/basic-programming/joystick.rst +++ b/source/docs/software/basic-programming/joystick.rst @@ -1,14 +1,12 @@ .. include:: -Joysticks -========= +# Joysticks A joystick can be used with the Driver Station program to control the robot. Almost any "controller" that can be recognized by Windows can be used as a joystick. Joysticks are accessed using the ``GenericHID`` class. This class has three relevant subclasses for preconfigured joysticks. You may also implement your own for other controllers by extending ``GenericHID``. The first is ``Joystick`` which is useful for standard flight joysticks. The second is ``XboxController`` which works for the Xbox 360, Xbox One, or Logitech F310 (in XInput mode). Finally, the ``PS4Controller`` class is ideal for using that controller. Each axis of the controller ranges from -1 to 1. The command based way to use the these classes is detailed in the section: :ref:`docs/software/commandbased/binding-commands-to-triggers:Binding Commands to Triggers`. -Driver Station Joysticks ------------------------- +## Driver Station Joysticks .. image:: /docs/software/driverstation/images/driver-station/ds-usb-tab.png :alt: The 4th tab down on the left hand side is the USB devices tab. @@ -26,8 +24,7 @@ When the Driver Station is in disabled mode, it is routinely looking for status When the robot is connected to the Field Management System at competition, the Driver Station mode is dictated by the :term:`FMS`. This means that you cannot disable your robot and the DS cannot disable itself in order to detect joystick changes. A manual complete refresh of the joysticks can be initiated by pressing the F1 key on the keyboard. Note that this will close and re-open all devices, so all devices should be in their center position as noted above. -``Joystick`` Class ------------------- +## ``Joystick`` Class .. image:: images/joystick/joystick.png :alt: A Logitech flight stick with an explanation of the axis values and buttons. @@ -35,46 +32,46 @@ When the robot is connected to the Field Management System at competition, the D .. tab-set-code:: - .. code-block:: java + ```java + Joystick exampleJoystick = new Joystick(0); // 0 is the USB Port to be used as indicated on the Driver Station + ``` - Joystick exampleJoystick = new Joystick(0); // 0 is the USB Port to be used as indicated on the Driver Station + ```c++ + Joystick exampleJoystick{0}; // 0 is the USB Port to be used as indicated on the Driver Station + ``` - .. code-block:: c++ - - Joystick exampleJoystick{0}; // 0 is the USB Port to be used as indicated on the Driver Station - - .. code-block:: python - - exampleJoystick = wpilib.Joystick(0) # 0 is the USB Port to be used as indicated on the Driver Station + ```python + exampleJoystick = wpilib.Joystick(0) # 0 is the USB Port to be used as indicated on the Driver Station + ``` The ``Joystick`` class is designed to make using a flight joystick to operate the robot significantly easier. Depending on the flight joystick, the user may need to set the specific X, Y, Z, and Throttle channels that your flight joystick uses. This class offers special methods for accessing the angle and magnitude of the flight joystick. -.. important:: Forward on joysticks is the negative direction. Teams often negate the values when reading joystick axes to account for this. +.. important:: Due to differences in coordinate systems, teams usually negate the values when reading joystick axes. See the :ref:`docs/software/basic-programming/coordinate-system:Joystick and controller coordinate system` section for more detail. -``XboxController`` Class ------------------------- +## ``XboxController`` Class .. image:: images/joystick/xbox.jpg :alt: Original Xbox Controller. .. tab-set-code:: - .. code-block:: java - - XboxController exampleXbox = new XboxController(0); // 0 is the USB Port to be used as indicated on the Driver Station + ```java + XboxController exampleXbox = new XboxController(0); // 0 is the USB Port to be used as indicated on the Driver Station + ``` - .. code-block:: c++ + ```c++ + XboxController exampleXbox{0}; // 0 is the USB Port to be used as indicated on the Driver Station + ``` - XboxController exampleXbox{0}; // 0 is the USB Port to be used as indicated on the Driver Station - - .. code-block:: python - - exampleXbox = wpilib.XboxController(0) # 0 is the USB Port to be used as indicated on the Driver Station + ```python + exampleXbox = wpilib.XboxController(0) # 0 is the USB Port to be used as indicated on the Driver Station + ``` The ``XboxController`` class provides named methods (e.g. ``getXButton``, ``getXButtonPressed``, ``getXButtonReleased``) for each of the buttons, and the indices can be accessed with ``XboxController.Button.kX.value``. The rumble feature of the controller can be controlled by using ``XboxController.setRumble(GenericHID.RumbleType.kRightRumble, value)``. Many users do a split stick arcade drive that uses the left stick for just forwards / backwards and the right stick for left / right turning. -``PS4Controller`` Class ------------------------ +.. important:: Due to differences in coordinate systems, teams usually negate the values when reading joystick axes. See the :ref:`docs/software/basic-programming/coordinate-system:Joystick and controller coordinate system` section for more detail. + +## ``PS4Controller`` Class .. image:: images/joystick/ps4.jpg :alt: PlayStation 4 controller. @@ -82,22 +79,23 @@ The ``XboxController`` class provides named methods (e.g. ``getXButton``, ``getX .. tab-set-code:: - .. code-block:: java + ```java + PS4Controller examplePS4 = new PS4Controller(0); // 0 is the USB Port to be used as indicated on the Driver Station + ``` - PS4Controller examplePS4 = new PS4Controller(0); // 0 is the USB Port to be used as indicated on the Driver Station + ```c++ + PS4Controller examplePS4{0}; // 0 is the USB Port to be used as indicated on the Driver Station + ``` - .. code-block:: c++ - - PS4Controller examplePS4{0}; // 0 is the USB Port to be used as indicated on the Driver Station - - .. code-block:: python - - examplePS4 = wpilib.PS4Controller(0) # 0 is the USB Port to be used as indicated on the Driver Station + ```python + examplePS4 = wpilib.PS4Controller(0) # 0 is the USB Port to be used as indicated on the Driver Station + ``` The ``PS4Controller`` class provides named methods (e.g. ``getSquareButton``, ``getSquareButtonPressed``, ``getSquareButtonReleased``) for each of the buttons, and the indices can be accessed with ``PS4Controller.Button.kSquare.value``. The rumble feature of the controller can be controlled by using ``PS4Controller.setRumble(GenericHID.RumbleType.kRightRumble, value)``. -POV ---- +.. important:: Due to differences in coordinate systems, teams usually negate the values when reading joystick axes. See the :ref:`docs/software/basic-programming/coordinate-system:Joystick and controller coordinate system` section for more detail. + +## POV .. image:: images/joystick/dpadangles.png :alt: The angles used by the code of the POV/D-pad with 0 at the top and continuing clockwise. @@ -105,43 +103,38 @@ POV On joysticks, the POV is a directional hat that can select one of 8 different angles or read -1 for unpressed. The XboxController/PS4Controller D-pad works the same as a POV. Be careful when using a POV with exact angle requirements as it is hard for the user to ensure they select exactly the angle desired. -``GenericHID`` Usage --------------------- +## ``GenericHID`` Usage An axis can be used with ``.getRawAxis(int index)`` (if not using any of the classes above) that returns the current value. Zero and one in this example are each the index of an axis as found in the Driver Station mentioned above. .. tab-set-code:: - .. code-block:: java - - private final PWMSparkMax m_leftMotor = new PWMSparkMax(Constants.kLeftMotorPort); - private final PWMSparkMax m_rightMotor = new PWMSparkMax(Constants.kRightMotorPort); - private final DifferentialDrive m_robotDrive = new DifferentialDrive(m_leftMotor, m_rightMotor); - private final GenericHID m_stick = new GenericHID(Constants.kJoystickPort); - - m_robotDrive.arcadeDrive(-m_stick.getRawAxis(0), m_stick.getRawAxis(1)); - - .. code-block:: c++ - - frc::PWMVictorSPX m_leftMotor{Constants::kLeftMotorPort}; - frc::PWMVictorSPX m_rightMotor{Constants::kRightMotorPort}; - frc::DifferentialDrive m_robotDrive{m_leftMotor, m_rightMotor}; - frc::GenericHID m_stick{Constants::kJoystickPort}; - - m_robotDrive.ArcadeDrive(-m_stick.GetRawAxis(0), m_stick.GetRawAxis(1)); - - .. code-block:: python - - leftMotor = wpilib.PWMVictorSPX(LEFT_MOTOR_PORT) - rightMotor = wpilib.PWMVictorSPX(RIGHT_MOTOR_PORT) - self.robotDrive = wpilib.drive.DifferentialDrive(leftMotor, rightMotor) - self.stick = wpilib.GenericHID(JOYSTICK_PORT) - - self.robotDrive.arcadeDrive(-self.stick.getRawAxis(0), self.stick.getRawAxis(1)) - - -Button Usage ------------- + ```java + private final PWMSparkMax m_leftMotor = new PWMSparkMax(Constants.kLeftMotorPort); + private final PWMSparkMax m_rightMotor = new PWMSparkMax(Constants.kRightMotorPort); + private final DifferentialDrive m_robotDrive = new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); + private final GenericHID m_stick = new GenericHID(Constants.kJoystickPort); + m_robotDrive.arcadeDrive(-m_stick.getRawAxis(0), m_stick.getRawAxis(1)); + ``` + + ```c++ + frc::PWMVictorSPX m_leftMotor{Constants::kLeftMotorPort}; + frc::PWMVictorSPX m_rightMotor{Constants::kRightMotorPort}; + frc::DifferentialDrive m_robotDrive{[&](double output) { m_leftMotor.Set(output); }, + [&](double output) { m_rightMotor.Set(output); }}; + frc::GenericHID m_stick{Constants::kJoystickPort}; + m_robotDrive.ArcadeDrive(-m_stick.GetRawAxis(0), m_stick.GetRawAxis(1)); + ``` + + ```python + leftMotor = wpilib.PWMVictorSPX(LEFT_MOTOR_PORT) + rightMotor = wpilib.PWMVictorSPX(RIGHT_MOTOR_PORT) + self.robotDrive = wpilib.drive.DifferentialDrive(leftMotor, rightMotor) + self.stick = wpilib.GenericHID(JOYSTICK_PORT) + self.robotDrive.arcadeDrive(-self.stick.getRawAxis(0), self.stick.getRawAxis(1)) + ``` + +## Button Usage .. note:: Usage such as the following is for code not using the command-based framework. For button usage in the command-based framework, see :ref:`docs/software/commandbased/binding-commands-to-triggers:Binding Commands to Triggers`. @@ -149,101 +142,92 @@ Unlike an axis, you will usually want to use the ``pressed`` and ``released`` me .. tab-set-code:: - .. code-block:: java + ```java + if (joystick.getRawButtonPressed(0)) { + turnIntakeOn(); // When pressed the intake turns on + } + if (joystick.getRawButtonReleased(0)) { + turnIntakeOff(); // When released the intake turns off + } + // OR + if (joystick.getRawButton(0)) { + turnIntakeOn(); + } else { + turnIntakeOff(); + } + ``` + + ```c++ + if (joystick.GetRawButtonPressed(0)) { + turnIntakeOn(); // When pressed the intake turns on + } + if (joystick.GetRawButtonReleased(0)) { + turnIntakeOff(); // When released the intake turns off + } + // OR + if (joystick.GetRawButton(0)) { + turnIntakeOn(); + } else { + turnIntakeOff(); + } + ``` + + ```python + if joystick.getRawButtonPressed(0): + turnIntakeOn() # When pressed the intake turns on + if joystick.getRawButtonReleased(0): + turnIntakeOff() # When released the intake turns off + # OR + if joystick.getRawButton(0): + turnIntakeOn() + else: + turnIntakeOff() + ``` - if (joystick.getRawButtonPressed(0)) { - turnIntakeOn(); // When pressed the intake turns on - } - if (joystick.getRawButtonReleased(0)) { - turnIntakeOff(); // When released the intake turns off - } +A common request is to toggle something on and off with the press of a button. Toggles should be used with caution, as they require the user to keep track of the robot state. - OR +.. tab-set-code:: - if (joystick.getRawButton(0)) { - turnIntakeOn(); + ```java + boolean toggle = false; + if (joystick.getRawButtonPressed(0)) { + if (toggle) { + // Current state is true so turn off + retractIntake(); + toggle = false; } else { - turnIntakeOff(); + // Current state is false so turn on + deployIntake(); + toggle = true; } - - .. code-block:: c++ - - if (joystick.GetRawButtonPressed(0)) { - turnIntakeOn(); // When pressed the intake turns on - } - if (joystick.GetRawButtonReleased(0)) { - turnIntakeOff(); // When released the intake turns off - } - - OR - - if (joystick.GetRawButton(0)) { - turnIntakeOn(); + } + ``` + + ```c++ + bool toggle{false}; + if (joystick.GetRawButtonPressed(0)) { + if (toggle) { + // Current state is true so turn off + retractIntake(); + toggle = false; } else { - turnIntakeOff(); + // Current state is false so turn on + deployIntake(); + toggle = true; } - - .. code-block:: python - - if joystick.getRawButtonPressed(0): - turnIntakeOn() # When pressed the intake turns on - - if joystick.getRawButtonReleased(0): - turnIntakeOff() # When released the intake turns off - - # OR - - if joystick.getRawButton(0): - turnIntakeOn() + } + ``` + + ```python + toggle = False + if joystick.getRawButtonPressed(0): + if toggle: + # current state is True so turn off + retractIntake() + toggle = False else: - turnIntakeOff() - -A common request is to toggle something on and off with the press of a button. Toggles should be used with caution, as they require the user to keep track of the robot state. - -.. tab-set-code:: - - .. code-block:: java - - boolean toggle = false; - - if (joystick.getRawButtonPressed(0)) { - if (toggle) { - // Current state is true so turn off - retractIntake(); - toggle = false; - } else { - // Current state is false so turn on - deployIntake(); - toggle = true; - } - } - - .. code-block:: c++ - - bool toggle{false}; - - if (joystick.GetRawButtonPressed(0)) { - if (toggle) { - // Current state is true so turn off - retractIntake(); - toggle = false; - } else { - // Current state is false so turn on - deployIntake(); - toggle = true; - } - } - - .. code-block:: python - - toggle = False + # Current state is False so turn on + deployIntake() + toggle = True + ``` - if joystick.getRawButtonPressed(0): - if toggle: - # current state is True so turn off - retractIntake() - toggle = False - else: - # Current state is False so turn on - deployIntake() - toggle = True diff --git a/source/docs/software/basic-programming/reading-stacktraces.rst b/source/docs/software/basic-programming/reading-stacktraces.rst index 46991a814e..61109685b0 100644 --- a/source/docs/software/basic-programming/reading-stacktraces.rst +++ b/source/docs/software/basic-programming/reading-stacktraces.rst @@ -1,5 +1,4 @@ -Reading Stacktraces -=================== +# Reading Stacktraces ``An unexpected error has occurred.`` @@ -9,8 +8,7 @@ When an unhandled exception occurs, it means that your code has one or more bugs This article will explore some of the tools and techniques involved in finding and fixing those bugs. -What's a "Stack Trace"? ------------------------ +## What's a "Stack Trace"? The ``unexpected error has occurred`` message is a signal that a *stack trace* has been printed out. @@ -18,18 +16,15 @@ In Java and C++, the :term:`call stack` data structure is used to store informat A *stack trace* prints information about what was on this stack when the unhandled exception occurred. This points you to the lines of code which were running just before the problem happened. While it doesn't always point you to the exact *root cause* of your issue, it's usually the best place to start looking. -What's an "Unhandled Exception"? --------------------------------- +## What's an "Unhandled Exception"? An unrecoverable error is any condition which arises where the processor cannot continue executing code. It almost always implies that, even though the code compiled and started running, it no longer makes sense for execution to continue. In almost all cases, the root cause of an unhandled exception is code that isn't correctly implemented. It almost never implies that any hardware has malfunctioned. -So How Do I Fix My Issue? -------------------------- +## So How Do I Fix My Issue? -Read the Stack Trace -^^^^^^^^^^^^^^^^^^^^ +### Read the Stack Trace To start, search above the ``unexpected error has occurred`` for the stack trace. @@ -40,14 +35,14 @@ To start, search above the ``unexpected error has occurred`` for the stack trace In Java, it should look something like this: - .. code-block:: text - - Error at frc.robot.Robot.robotInit(Robot.java:24): Unhandled exception: java.lang.NullPointerException - at frc.robot.Robot.robotInit(Robot.java:24) - at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:94) - at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:335) - at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$0(RobotBase.java:387) - at java.base/java.lang.Thread.run(Thread.java:834) + ```text + Error at frc.robot.Robot.robotInit(Robot.java:24): Unhandled exception: java.lang.NullPointerException + at frc.robot.Robot.robotInit(Robot.java:24) + at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:94) + at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:335) + at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$0(RobotBase.java:387) + at java.base/java.lang.Thread.run(Thread.java:834) + ``` There's a few important things to pick out of here: @@ -69,17 +64,17 @@ To start, search above the ``unexpected error has occurred`` for the stack trace For example, If the error happened deep inside your codebase, you might see more entries on the stack: - .. code-block:: text - - Error at frc.robot.Robot.buggyMethod(TooManyBugs.java:1138): Unhandled exception: java.lang.NullPointerException - at frc.robot.Robot.buggyMethod(TooManyBugs.java:1138) - at frc.robot.Robot.barInit(Bar.java:21) - at frc.robot.Robot.fooInit(Foo.java:34) - at frc.robot.Robot.robotInit(Robot.java:24) - at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:94) - at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:335) - at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$0(RobotBase.java:387) - at java.base/java.lang.Thread.run(Thread.java:834) + ```text + Error at frc.robot.Robot.buggyMethod(TooManyBugs.java:1138): Unhandled exception: java.lang.NullPointerException + at frc.robot.Robot.buggyMethod(TooManyBugs.java:1138) + at frc.robot.Robot.barInit(Bar.java:21) + at frc.robot.Robot.fooInit(Foo.java:34) + at frc.robot.Robot.robotInit(Robot.java:24) + at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:94) + at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:335) + at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$0(RobotBase.java:387) + at java.base/java.lang.Thread.run(Thread.java:834) + ``` In this case: ``robotInit`` called ``fooInit``, which in turn called ``barInit``, which in turn called ``buggyMethod``. Then, during the execution of ``buggyMethod``, the ``NullPointerException`` occurred. @@ -118,8 +113,7 @@ To start, search above the ``unexpected error has occurred`` for the stack trace The examples in this page assume you are running code examples in simulation, with the debugger connected and watching for unexpected errors. Similar techniques should apply while running on a real robot. -Perform Code Analysis -^^^^^^^^^^^^^^^^^^^^^ +### Perform Code Analysis Once you've found the stack trace, and found the lines of code which are triggering the unhandled exception, you can start the process of determining root cause. @@ -134,31 +128,26 @@ A key strategy for analyzing code is to ask the following questions: Frequent testing and careful code changes help make this particular strategy more effective. -Run the Single Step Debugger -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Run the Single Step Debugger Sometimes, just looking at code isn't enough to spot the issue. The :ref:`single-step debugger ` is a great option in this case - it allows you to inspect the series of events leading up to the unhandled exception. -Search for More Information -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Search for More Information -`Google `_ is a phenomenal resource for understanding the root cause of errors. Searches involving the programming language and the name of the exception will often yield good results on more explanations for what the error means, how it comes about, and potential fixes. +[Google](https://www.google.com/) is a phenomenal resource for understanding the root cause of errors. Searches involving the programming language and the name of the exception will often yield good results on more explanations for what the error means, how it comes about, and potential fixes. -Seeking Outside Help -^^^^^^^^^^^^^^^^^^^^ +### Seeking Outside Help If all else fails, you can seek out advice and help from others (both in-person and online). When working with folks who aren't familiar with your codebase, it's very important to provide the following information: * Access to your source code, (EX: :ref:`on github.com `) * The **full text** of the error, including the full stack trace. -Common Examples & Patterns --------------------------- +## Common Examples & Patterns There are a number of common issues which result in runtime exceptions. -Null Pointers and References -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Null Pointers and References Both C++ and Java have the concept of "null" - they use it to indicate something which has not yet been initialized, and does not refer to anything meaningful. @@ -169,31 +158,27 @@ For example, consider the following code: .. tab-set-code:: - .. code-block:: Java - :lineno-start: 19 - - PWMSparkMax armMotorCtrl; - - @Override - public void robotInit() { - armMotorCtrl.setInverted(true); - } - - - .. code-block:: C++ - :lineno-start: 17 - - class Robot : public frc::TimedRobot { - public: - void RobotInit() override { - motorRef->SetInverted(false); - } - - private: - frc::PWMVictorSPX m_armMotor{0}; - frc::PWMVictorSPX* motorRef; - }; - + ```Java + :lineno-start: 19 + PWMSparkMax armMotorCtrl; + @Override + public void robotInit() { + armMotorCtrl.setInverted(true); + } + ``` + + ```C++ + :lineno-start: 17 + class Robot : public frc::TimedRobot { + public: + void RobotInit() override { + motorRef->SetInverted(false); + } + private: + frc::PWMVictorSPX m_armMotor{0}; + frc::PWMVictorSPX* motorRef; + }; + ``` When run, you'll see output that looks like this: @@ -202,21 +187,19 @@ When run, you'll see output that looks like this: .. tab-item:: Java :sync: tabcode-java - .. code-block:: text - - ********** Robot program starting ********** - Error at frc.robot.Robot.robotInit(Robot.java:23): Unhandled exception: java.lang.NullPointerException - at frc.robot.Robot.robotInit(Robot.java:23) - at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:107) - at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:373) - at edu.wpi.first.wpilibj.RobotBase.startRobot(RobotBase.java:463) - at frc.robot.Main.main(Main.java:23) - - Warning at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:388): The robot program quit unexpectedly. This is usually due to a code error. - The above stacktrace can help determine where the error occurred. - See https://wpilib.org/stacktrace for more information. - Error at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:395): The startCompetition() method (or methods called by it) should have handled the exception above. - + ```text + ********** Robot program starting ********** + Error at frc.robot.Robot.robotInit(Robot.java:23): Unhandled exception: java.lang.NullPointerException + at frc.robot.Robot.robotInit(Robot.java:23) + at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:107) + at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:373) + at edu.wpi.first.wpilibj.RobotBase.startRobot(RobotBase.java:463) + at frc.robot.Main.main(Main.java:23) + Warning at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:388): The robot program quit unexpectedly. This is usually due to a code error. + The above stacktrace can help determine where the error occurred. + See https://wpilib.org/stacktrace for more information. + Error at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:395): The startCompetition() method (or methods called by it) should have handled the exception above. + ``` Reading the stack trace, you can see that the issue happened inside of the ``robotInit()`` function, on line 23, and the exception involved "Null Pointer". @@ -227,11 +210,11 @@ When run, you'll see output that looks like this: .. tab-item:: C++ :sync: tabcode-c++ - .. code-block:: text - - Exception has occurred: W32/0xc0000005 - Unhandled exception thrown: read access violation. - this->motorRef was nullptr. + ```text + Exception has occurred: W32/0xc0000005 + Unhandled exception thrown: read access violation. + this->motorRef was nullptr. + ``` In Simulation, this will show up in a debugger window that points to line 20 in the above buggy code. @@ -244,8 +227,7 @@ When run, you'll see output that looks like this: The exception states its type was ``nullptr``. -Fixing Null Object Issues -~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Fixing Null Object Issues Generally, you will want to ensure each reference has been initialized before using it. In this case, there is a missing line of code to instantiate the ``armMotorCtrl`` before calling the ``setInverted()`` method. @@ -254,37 +236,31 @@ A functional implementation could look like this: .. tab-set-code:: - .. code-block:: Java - :lineno-start: 19 - - PWMSparkMax armMotorCtrl; - - @Override - public void robotInit() { - armMotorCtrl = new PWMSparkMax(0); - armMotorCtrl.setInverted(true); - } - - - .. code-block:: C++ - :lineno-start: 17 - - class Robot : public frc::TimedRobot { - public: - void RobotInit() override { - motorRef = &m_armMotor; - motorRef->SetInverted(false); - } - - private: - frc::PWMVictorSPX m_armMotor{0}; - frc::PWMVictorSPX* motorRef; - }; - - - -Divide by Zero -^^^^^^^^^^^^^^ + ```Java + :lineno-start: 19 + PWMSparkMax armMotorCtrl; + @Override + public void robotInit() { + armMotorCtrl = new PWMSparkMax(0); + armMotorCtrl.setInverted(true); + } + ``` + + ```C++ + :lineno-start: 17 + class Robot : public frc::TimedRobot { + public: + void RobotInit() override { + motorRef = &m_armMotor; + motorRef->SetInverted(false); + } + private: + frc::PWMVictorSPX m_armMotor{0}; + frc::PWMVictorSPX* motorRef; + }; + ``` + +### Divide by Zero It is not generally possible to divide an integer by zero, and expect reasonable results. Most processors (including the roboRIO) will raise an Unhandled Exception. @@ -293,34 +269,30 @@ For example, consider the following code: .. tab-set-code:: - .. code-block:: Java - :lineno-start: 18 - - int armLengthRatio; - int elbowToWrist_in = 39; - int shoulderToElbow_in = 0; //TODO - - @Override - public void robotInit() { - armLengthRatio = elbowToWrist_in / shoulderToElbow_in; - } - - - .. code-block:: C++ - :lineno-start: 17 - - class Robot : public frc::TimedRobot { - public: - void RobotInit() override { - armLengthRatio = elbowToWrist_in / shoulderToElbow_in; - } - - private: - int armLengthRatio; - int elbowToWrist_in = 39; - int shoulderToElbow_in = 0; //TODO - - }; + ```Java + :lineno-start: 18 + int armLengthRatio; + int elbowToWrist_in = 39; + int shoulderToElbow_in = 0; //TODO + @Override + public void robotInit() { + armLengthRatio = elbowToWrist_in / shoulderToElbow_in; + } + ``` + + ```C++ + :lineno-start: 17 + class Robot : public frc::TimedRobot { + public: + void RobotInit() override { + armLengthRatio = elbowToWrist_in / shoulderToElbow_in; + } + private: + int armLengthRatio; + int elbowToWrist_in = 39; + int shoulderToElbow_in = 0; //TODO + }; + ``` When run, you'll see output that looks like this: @@ -329,21 +301,19 @@ When run, you'll see output that looks like this: .. tab-item:: Java :sync: tabcode-java - .. code-block:: text - - - ********** Robot program starting ********** - Error at frc.robot.Robot.robotInit(Robot.java:24): Unhandled exception: java.lang.ArithmeticException: / by zero - at frc.robot.Robot.robotInit(Robot.java:24) - at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:107) - at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:373) - at edu.wpi.first.wpilibj.RobotBase.startRobot(RobotBase.java:463) - at frc.robot.Main.main(Main.java:23) - - Warning at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:388): The robot program quit unexpectedly. This is usually due to a code error. - The above stacktrace can help determine where the error occurred. - See https://wpilib.org/stacktrace for more information. - Error at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:395): The startCompetition() method (or methods called by it) should have handled the exception above. + ```text + ********** Robot program starting ********** + Error at frc.robot.Robot.robotInit(Robot.java:24): Unhandled exception: java.lang.ArithmeticException: / by zero + at frc.robot.Robot.robotInit(Robot.java:24) + at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:107) + at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:373) + at edu.wpi.first.wpilibj.RobotBase.startRobot(RobotBase.java:463) + at frc.robot.Main.main(Main.java:23) + Warning at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:388): The robot program quit unexpectedly. This is usually due to a code error. + The above stacktrace can help determine where the error occurred. + See https://wpilib.org/stacktrace for more information. + Error at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:395): The startCompetition() method (or methods called by it) should have handled the exception above. + ``` Looking at the stack trace, we can see a ``java.lang.ArithmeticException: / by zero`` exception has occurred on line 24. If you look at the two variables which are used on the right-hand side of the ``=`` operator, you might notice one of them has been initialized to zero. Looks like someone forgot to update it! Furthermore, the zero-value variable is used in the denominator of a division operation. Hence, the divide by zero error happens. @@ -352,10 +322,10 @@ When run, you'll see output that looks like this: .. tab-item:: C++ :sync: tabcode-c++ - .. code-block:: text - - Exception has occurred: W32/0xc0000094 - Unhandled exception at 0x00007FF71B223CD6 in frcUserProgram.exe: 0xC0000094: Integer division by zero. + ```text + Exception has occurred: W32/0xc0000094 + Unhandled exception at 0x00007FF71B223CD6 in frcUserProgram.exe: 0xC0000094: Integer division by zero. + ``` In Simulation, this will show up in a debugger window that points to line 20 in the above buggy code. @@ -370,8 +340,7 @@ When run, you'll see output that looks like this: -Fixing Divide By Zero Issues -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Fixing Divide By Zero Issues Divide By Zero issues can be fixed in a number of ways. It's important to start by thinking about what a zero in the denominator of your calculation *means*. Is it plausible? Why did it happen in the particular case you saw? @@ -381,45 +350,37 @@ A functional implementation could look like this: .. tab-set-code:: - .. code-block:: Java - :lineno-start: 18 - - int armLengthRatio; - int elbowToWrist_in = 39; - int shoulderToElbow_in = 3; - - @Override - public void robotInit() { - - armLengthRatio = elbowToWrist_in / shoulderToElbow_in; - - } - - - - .. code-block:: C++ - :lineno-start: 17 - - class Robot : public frc::TimedRobot { - public: - void RobotInit() override { - armLengthRatio = elbowToWrist_in / shoulderToElbow_in; - } - - private: - int armLengthRatio; - int elbowToWrist_in = 39; - int shoulderToElbow_in = 3 - - }; + ```Java + :lineno-start: 18 + int armLengthRatio; + int elbowToWrist_in = 39; + int shoulderToElbow_in = 3; + @Override + public void robotInit() { + armLengthRatio = elbowToWrist_in / shoulderToElbow_in; + } + ``` + + ```C++ + :lineno-start: 17 + class Robot : public frc::TimedRobot { + public: + void RobotInit() override { + armLengthRatio = elbowToWrist_in / shoulderToElbow_in; + } + private: + int armLengthRatio; + int elbowToWrist_in = 39; + int shoulderToElbow_in = 3 + }; + ``` Alternatively, if zero *is* a valid value, adding ``if/else`` statements around the calculation can help you define alternate behavior to avoid making the processor perform a division by zero. Finally, changing variable types to be ``float`` or ``double`` can help you get around the issue - floating-point numbers have special values like ``NaN`` to represent the results of a divide-by-zero operation. However, you may still have to handle this in code which consumes that calculation's value. -HAL Resource Already Allocated -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### HAL Resource Already Allocated A very common FRC-specific error occurs when the code attempts to put two hardware-related entities on the same HAL resource (usually, roboRIO IO pin). @@ -428,35 +389,30 @@ For example, consider the following code: .. tab-set-code:: - .. code-block:: Java - :lineno-start: 19 - - PWMSparkMax leftFrontMotor; - PWMSparkMax leftRearMotor; - - @Override - public void robotInit() { - leftFrontMotor = new PWMSparkMax(0); - leftRearMotor = new PWMSparkMax(0); - } - - - .. code-block:: C++ - :lineno-start: 17 - - class Robot : public frc::TimedRobot { - public: - void RobotInit() override { - m_frontLeftMotor.Set(0.5); - m_rearLeftMotor.Set(0.25); - } - - private: - frc::PWMVictorSPX m_frontLeftMotor{0}; - frc::PWMVictorSPX m_rearLeftMotor{0}; - - }; - + ```Java + :lineno-start: 19 + PWMSparkMax leftFrontMotor; + PWMSparkMax leftRearMotor; + @Override + public void robotInit() { + leftFrontMotor = new PWMSparkMax(0); + leftRearMotor = new PWMSparkMax(0); + } + ``` + + ```C++ + :lineno-start: 17 + class Robot : public frc::TimedRobot { + public: + void RobotInit() override { + m_frontLeftMotor.Set(0.5); + m_rearLeftMotor.Set(0.25); + } + private: + frc::PWMVictorSPX m_frontLeftMotor{0}; + frc::PWMVictorSPX m_rearLeftMotor{0}; + }; + ``` When run, you'll see output that looks like this: @@ -465,33 +421,31 @@ When run, you'll see output that looks like this: .. tab-item:: Java :sync: tabcode-java - .. code-block:: text - - ********** Robot program starting ********** - Error at frc.robot.Robot.robotInit(Robot.java:25): Unhandled exception: edu.wpi.first.hal.util.AllocationException: Code: -1029 - PWM or DIO 0 previously allocated. - Location of the previous allocation: - at frc.robot.Robot.robotInit(Robot.java:24) - at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:107) - at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:373) - at edu.wpi.first.wpilibj.RobotBase.startRobot(RobotBase.java:463) - at frc.robot.Main.main(Main.java:23) - - Location of the current allocation: - at edu.wpi.first.hal.PWMJNI.initializePWMPort(Native Method) - at edu.wpi.first.wpilibj.PWM.(PWM.java:66) - at edu.wpi.first.wpilibj.motorcontrol.PWMMotorController.(PWMMotorController.java:27) - at edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax.(PWMSparkMax.java:35) - at frc.robot.Robot.robotInit(Robot.java:25) - at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:107) - at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:373) - at edu.wpi.first.wpilibj.RobotBase.startRobot(RobotBase.java:463) - at frc.robot.Main.main(Main.java:23) - - Warning at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:388): The robot program quit unexpectedly. This is usually due to a code error. - The above stacktrace can help determine where the error occurred. - See https://wpilib.org/stacktrace for more information. - Error at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:395): The startCompetition() method (or methods called by it) should have handled the exception above. + ```text + ********** Robot program starting ********** + Error at frc.robot.Robot.robotInit(Robot.java:25): Unhandled exception: edu.wpi.first.hal.util.AllocationException: Code: -1029 + PWM or DIO 0 previously allocated. + Location of the previous allocation: + at frc.robot.Robot.robotInit(Robot.java:24) + at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:107) + at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:373) + at edu.wpi.first.wpilibj.RobotBase.startRobot(RobotBase.java:463) + at frc.robot.Main.main(Main.java:23) + Location of the current allocation: + at edu.wpi.first.hal.PWMJNI.initializePWMPort(Native Method) + at edu.wpi.first.wpilibj.PWM.(PWM.java:66) + at edu.wpi.first.wpilibj.motorcontrol.PWMMotorController.(PWMMotorController.java:27) + at edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax.(PWMSparkMax.java:35) + at frc.robot.Robot.robotInit(Robot.java:25) + at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:107) + at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:373) + at edu.wpi.first.wpilibj.RobotBase.startRobot(RobotBase.java:463) + at frc.robot.Main.main(Main.java:23) + Warning at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:388): The robot program quit unexpectedly. This is usually due to a code error. + The above stacktrace can help determine where the error occurred. + See https://wpilib.org/stacktrace for more information. + Error at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:395): The startCompetition() method (or methods called by it) should have handled the exception above. + ``` This stack trace shows that a ``edu.wpi.first.hal.util.AllocationException`` has occurred. It also gives the helpful message: ``PWM or DIO 0 previously allocated.``. @@ -504,95 +458,80 @@ When run, you'll see output that looks like this: In C++, you won't specifically see a stacktrace from this issue. Instead, you'll get messages which look like the following: - .. code-block:: text - - Error at PWM [C::31]: PWM or DIO 0 previously allocated. - Location of the previous allocation: - at frc::PWM::PWM(int, bool) + 0x50 [0xb6f01b68] - at frc::PWMMotorController::PWMMotorController(std::basic_string_view >, int) + 0x70 [0xb6ef7d50] - at frc::PWMVictorSPX::PWMVictorSPX(int) + 0x3c [0xb6e9af1c] - at void frc::impl::RunRobot(wpi::priority_mutex&, Robot**) + 0xa8 [0x13718] - at int frc::StartRobot() + 0x3d4 [0x13c9c] - at __libc_start_main + 0x114 [0xb57ec580] - - Location of the current allocation:: Channel 0 - at + 0x5fb5c [0xb6e81b5c] - at frc::PWM::PWM(int, bool) + 0x334 [0xb6f01e4c] - at frc::PWMMotorController::PWMMotorController(std::basic_string_view >, int) + 0x70 [0xb6ef7d50] - at frc::PWMVictorSPX::PWMVictorSPX(int) + 0x3c [0xb6e9af1c] - at void frc::impl::RunRobot(wpi::priority_mutex&, Robot**) + 0xb4 [0x13724] - at int frc::StartRobot() + 0x3d4 [0x13c9c] - at __libc_start_main + 0x114 [0xb57ec580] - - Error at RunRobot: Error: The robot program quit unexpectedly. This is usually due to a code error. - The above stacktrace can help determine where the error occurred. - See https://wpilib.org/stacktrace for more information. - - at void frc::impl::RunRobot(wpi::priority_mutex&, Robot**) + 0x1c8 [0x13838] - at int frc::StartRobot() + 0x3d4 [0x13c9c] - at __libc_start_main + 0x114 [0xb57ec580] - - terminate called after throwing an instance of 'frc::RuntimeError' - what(): PWM or DIO 0 previously allocated. - Location of the previous allocation: - at frc::PWM::PWM(int, bool) + 0x50 [0xb6f01b68] - at frc::PWMMotorController::PWMMotorController(std::basic_string_view >, int) + 0x70 [0xb6ef7d50] - at frc::PWMVictorSPX::PWMVictorSPX(int) + 0x3c [0xb6e9af1c] - at void frc::impl::RunRobot(wpi::priority_mutex&, Robot**) + 0xa8 [0x13718] - at int frc::StartRobot() + 0x3d4 [0x13c9c] - at __libc_start_main + 0x114 [0xb57ec580] - - Location of the current allocation:: Channel 0 - + ```text + Error at PWM [C::31]: PWM or DIO 0 previously allocated. + Location of the previous allocation: + at frc::PWM::PWM(int, bool) + 0x50 [0xb6f01b68] + at frc::PWMMotorController::PWMMotorController(std::basic_string_view >, int) + 0x70 [0xb6ef7d50] + at frc::PWMVictorSPX::PWMVictorSPX(int) + 0x3c [0xb6e9af1c] + at void frc::impl::RunRobot(wpi::priority_mutex&, Robot**) + 0xa8 [0x13718] + at int frc::StartRobot() + 0x3d4 [0x13c9c] + at __libc_start_main + 0x114 [0xb57ec580] + Location of the current allocation:: Channel 0 + at + 0x5fb5c [0xb6e81b5c] + at frc::PWM::PWM(int, bool) + 0x334 [0xb6f01e4c] + at frc::PWMMotorController::PWMMotorController(std::basic_string_view >, int) + 0x70 [0xb6ef7d50] + at frc::PWMVictorSPX::PWMVictorSPX(int) + 0x3c [0xb6e9af1c] + at void frc::impl::RunRobot(wpi::priority_mutex&, Robot**) + 0xb4 [0x13724] + at int frc::StartRobot() + 0x3d4 [0x13c9c] + at __libc_start_main + 0x114 [0xb57ec580] + Error at RunRobot: Error: The robot program quit unexpectedly. This is usually due to a code error. + The above stacktrace can help determine where the error occurred. + See https://wpilib.org/stacktrace for more information. + at void frc::impl::RunRobot(wpi::priority_mutex&, Robot**) + 0x1c8 [0x13838] + at int frc::StartRobot() + 0x3d4 [0x13c9c] + at __libc_start_main + 0x114 [0xb57ec580] + terminate called after throwing an instance of 'frc::RuntimeError' + what(): PWM or DIO 0 previously allocated. + Location of the previous allocation: + at frc::PWM::PWM(int, bool) + 0x50 [0xb6f01b68] + at frc::PWMMotorController::PWMMotorController(std::basic_string_view >, int) + 0x70 [0xb6ef7d50] + at frc::PWMVictorSPX::PWMVictorSPX(int) + 0x3c [0xb6e9af1c] + at void frc::impl::RunRobot(wpi::priority_mutex&, Robot**) + 0xa8 [0x13718] + at int frc::StartRobot() + 0x3d4 [0x13c9c] + at __libc_start_main + 0x114 [0xb57ec580] + Location of the current allocation:: Channel 0 + ``` The key thing to notice here is the string, ``PWM or DIO 0 previously allocated.``. That string is your primary clue that something in code has incorrectly "doubled up" on pin 0 usage. The message example above was generated on a roboRIO. If you are running in simulation, it might look different. -Fixing HAL Resource Already Allocated Issues -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Fixing HAL Resource Already Allocated Issues ``HAL: Resource already allocated`` are some of the most straightforward errors to fix. Just spend a bit of time looking at the electrical wiring on the robot, and compare that to what's in code. -In the example, the left motor controllers are plugged into PWM ports ``0`` and ``1``. Therefore, corrected code would look like this: +In the example, the left motor controllers are plugged into :term:`PWM` ports ``0`` and ``1``. Therefore, corrected code would look like this: .. tab-set-code:: - .. code-block:: Java - :lineno-start: 19 - - PWMSparkMax leftFrontMotor; - PWMSparkMax leftRearMotor; - - @Override - public void robotInit() { - - leftFrontMotor = new PWMSparkMax(0); - leftRearMotor = new PWMSparkMax(1); - - } - - - .. code-block:: C++ - - :lineno-start: 17 - - class Robot : public frc::TimedRobot { - public: - void RobotInit() override { - m_frontLeftMotor.Set(0.5); - m_rearLeftMotor.Set(0.25); - } - - private: - frc::PWMVictorSPX m_frontLeftMotor{0}; - frc::PWMVictorSPX m_rearLeftMotor{1}; - - }; - -gradlew is not recognized... -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ```Java + :lineno-start: 19 + PWMSparkMax leftFrontMotor; + PWMSparkMax leftRearMotor; + @Override + public void robotInit() { + leftFrontMotor = new PWMSparkMax(0); + leftRearMotor = new PWMSparkMax(1); + } + ``` + + ```C++ + :lineno-start: 17 + class Robot : public frc::TimedRobot { + public: + void RobotInit() override { + m_frontLeftMotor.Set(0.5); + m_rearLeftMotor.Set(0.25); + } + private: + frc::PWMVictorSPX m_frontLeftMotor{0}; + frc::PWMVictorSPX m_rearLeftMotor{1}; + }; + ``` + +### gradlew is not recognized... ``gradlew is not recognized as an internal or external command`` is a common error that can occur when the project or directory that you are currently in does not contain a ``gradlew`` file. This usually occurs when you open the wrong directory. @@ -610,8 +549,7 @@ If you do not see any one of the above files in your project directory, then you - A corrupt or bad project. - You are in the wrong directory. -Fixing gradlew is not recognized... -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Fixing gradlew is not recognized... ``gradlew is not recognized...`` is a fairly easy problem to fix. First identify the problem source: diff --git a/source/docs/software/basic-programming/robot-preferences.rst b/source/docs/software/basic-programming/robot-preferences.rst index 0ff6d3988c..14bc537ad5 100644 --- a/source/docs/software/basic-programming/robot-preferences.rst +++ b/source/docs/software/basic-programming/robot-preferences.rst @@ -1,63 +1,78 @@ -Setting Robot Preferences -========================= +# Setting Robot Preferences -The Robot Preferences (`Java `__, `C++ `__) class is used to store values in the flash memory on the roboRIO. The values might be for remembering preferences on the robot such as calibration settings for potentiometers, PID values, setpoints, etc. that you would like to change without having to rebuild the program. The values can be viewed on SmartDashboard or Shuffleboard and read and written by the robot program. +The Robot Preferences ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/Preferences.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_preferences.html)) class is used to store values in the flash memory on the roboRIO. The values might be for remembering preferences on the robot such as calibration settings for potentiometers, PID values, setpoints, etc. that you would like to change without having to rebuild the program. The values can be viewed on SmartDashboard or Shuffleboard and read and written by the robot program. -This example shows how to utilize Preferences to change the setpoint of a PID controller and the P constant. The code examples are adapted from the Arm Simulation example (`Java `__, `C++ `__). You can run the Arm Simulation example in the Robot Simulator to see how to use the preference class and interact with it using the dashboards without needing a robot. +This example shows how to utilize Preferences to change the setpoint of a PID controller and the P constant. The code examples are adapted from the Arm Simulation example ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java), [C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp)). You can run the Arm Simulation example in the Robot Simulator to see how to use the preference class and interact with it using the dashboards without needing a robot. -Initializing Preferences ------------------------- +## Initializing Preferences .. tab-set:: .. tab-item:: Java :sync: Java - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Constants.java + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Constants.java :language: java :lines: 15-20 - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/subsystems/Arm.java + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/subsystems/Arm.java :language: java :lines: 28-30, 73-74, 80-83 .. tab-item:: C++ :sync: C++ - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/Constants.h - :language: cpp + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/ArmSimulation/include/Constants.h + :language: c++ :lines: 30-34 - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/subsystems/Arm.cpp - :language: cpp + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/subsystems/Arm.cpp + :language: c++ :lines: 12, 18-22 + .. tab-item:: Python + :sync: Python + + .. rli:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/ArmSimulation/constants.py + :language: python + :lines: 18-23 + + .. rli:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/ArmSimulation/subsytems/arm.py + :language: python + :lines: 20-23,37-41 + Preferences are stored using a name, the key. It's helpful to store the key in a constant, like ``kArmPositionKey`` and ``kArmPKey`` in the code above to avoid typing it multiple times and avoid typos. We also declare variables, ``kArmKp`` and ``armPositionDeg`` to hold the data retrieved from preferences. In ``robotInit``, each key is checked to see if it already exists in the Preferences database. The ``containsKey`` method takes one parameter, the key to check if data for that key already exists in the preferences database. If it doesn't exist, a default value is written. The ``setDouble`` method takes two parameters, the key to write and the data to write. There are similar methods for other data types like booleans, ints, and strings. If using the Command Framework, this type of code could be placed in the constructor of a Subsystem or Command. -Reading Preferences -------------------- +## Reading Preferences .. tab-set:: .. tab-item:: Java :sync: Java - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/subsystems/Arm.java + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/subsystems/Arm.java :language: java :lines: 105-112 .. tab-item:: C++ :sync: C++ - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/subsystems/Arm.cpp - :language: cpp + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/subsystems/Arm.cpp + :language: c++ :lines: 44-52 + .. tab-item:: Python + :sync: Python + + .. rli:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/ArmSimulation/subsytems/arm.py + :language: python + :lines: 43-50 + Reading a preference is easy. The ``getDouble`` method takes two parameters, the key to read, and a default value to use in case the preference doesn't exist. There are similar methods for other data types like booleans, ints, and strings. Depending on the data that is stored in preferences, you can use it when you read it, such as the proportional constant above. Or you can store it in a variable and use it later, such as the setpoint, which is used in ``telopPeriodic`` below. @@ -67,55 +82,59 @@ Depending on the data that is stored in preferences, you can use it when you rea .. tab-item:: Java :sync: Java - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java :language: java :lines: 29-38 - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/subsystems/Arm.java + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/subsystems/Arm.java :language: java :lines: 114-120 .. tab-item:: C++ :sync: C++ - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp - :language: cpp + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp + :language: c++ :lines: 15-24 - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/subsystems/Arm.cpp - :language: cpp + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/subsystems/Arm.cpp + :language: c++ :lines: 54-60 -Using Preferences in SmartDashboard ------------------------------------ + .. tab-item:: Python + :sync: Python + + .. rli:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/ArmSimulation/robot.py + :language: python + :lines: 22-28 + .. rli:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/ArmSimulation/subsytems/arm.py + :language: python + :lines: 52-57 + +## Using Preferences in SmartDashboard -Displaying Preferences in SmartDashboard -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Displaying Preferences in SmartDashboard .. image:: images/robot-preferences/preferences-widget-smartdashboard.png :alt: Adding preferences from the Smartdashboard menu In the SmartDashboard, the Preferences display can be added to the display by selecting :guilabel:`View` then :guilabel:`Add...` then :guilabel:`Robot Preferences`. This reveals the contents of the preferences file stored in the roboRIO flash memory. -Editing Preferences in SmartDashboard -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Editing Preferences in SmartDashboard .. image:: images/robot-preferences/view-edit-preferences-values-smartdashboard.png :alt: Editing the robot preferences via the SmartDashboard widget. The values are shown here with the default values from the code. If the values need to be adjusted they can be edited here and saved. -Using Preferences in Shuffleboard ---------------------------------- +## Using Preferences in Shuffleboard -Displaying Preferences in Shuffleboard -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Displaying Preferences in Shuffleboard .. image:: images/robot-preferences/preferences-widget-shuffleboard.png :alt: Adding preferences from the sources window in Shuffleboard In Shuffleboard, the Preferences display can be added to the display by dragging the preferences field from the sources window. This reveals the contents of the preferences file stored in the roboRIO flash memory. -Editing Preferences in Shuffleboard -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Editing Preferences in Shuffleboard .. image:: images/robot-preferences/view-edit-preferences-values-shuffleboard.png :alt: Editing the robot preferences via the Shuffleboard widget. diff --git a/source/docs/software/basic-programming/using-test-mode.rst b/source/docs/software/basic-programming/using-test-mode.rst index d6f89b33b2..ae7ec8f168 100644 --- a/source/docs/software/basic-programming/using-test-mode.rst +++ b/source/docs/software/basic-programming/using-test-mode.rst @@ -1,26 +1,22 @@ -Using Test Mode -=============== +# Using Test Mode Test mode is designed to enable programmers to have a place to put code to verify that all systems on the robot are functioning. In each of the robot program templates there is a place to add test code to the robot. -Enabling Test Mode ------------------- +## Enabling Test Mode .. image:: /docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/enabling-test-mode/setting-test-mode-driver-station.png :alt: Selecting the "Test" button on the Driver Station and then "Enable". Test mode on the robot can be enabled from the Driver Station just like autonomous or teleop. To enable test mode in the Driver Station, select the "Test" button and enable the robot. The test mode code will then run. -LiveWindow in Test Mode ------------------------ - -With LiveWindow, all actuator outputs can be controlled on the Dashboard and all sensor values can be seen. PID Controllers can also be tuned. The sensors and actuators are added automatically, no code is necessary. See :doc:`/docs/software/dashboards/smartdashboard/test-mode-and-live-window/index` for more details. - -Adding Test mode code to your robot code ----------------------------------------- +## Adding Test mode code to your robot code When in test mode, the ``testInit`` method is run once, and the ``testPeriodic`` method is run once per tick, in addition to ``robotPeriodic``, similar to teleop and autonomous control modes. Adding test mode can be as painless as calling your already written Teleop methods from Test. Or you can write special code to try out a new feature that is only run in Test mode, before integrating it into your teleop or autonomous code. You could even write code to move all motors and check all sensors to help the pit crew! -.. warning:: If you write your own test code, it may interfere with the LiveWindow code that can control actuators and is enabled automatically. You may need to call ``LiveWindow.setEnabled(false)`` in your testInit method to avoid this. +## LiveWindow in Test Mode + +.. important:: Since 2024, LiveWindow in Test Mode is disabled by default! See :ref:`docs/software/dashboards/smartdashboard/test-mode-and-live-window/enabling-test-mode:Enabling LiveWindow in Test Mode` to enable it. + +With LiveWindow, all actuator outputs can be controlled on the Dashboard and all sensor values can be seen. PID Controllers can also be tuned. The sensors and actuators are added automatically, no code is necessary. See :doc:`/docs/software/dashboards/smartdashboard/test-mode-and-live-window/index` for more details. diff --git a/source/docs/software/can-devices/can-addressing.rst b/source/docs/software/can-devices/can-addressing.rst index e070f660ab..1818f4c789 100644 --- a/source/docs/software/can-devices/can-addressing.rst +++ b/source/docs/software/can-devices/can-addressing.rst @@ -1,20 +1,17 @@ .. include:: -FRC CAN Device Specifications -============================= +# FRC CAN Device Specifications This document seeks to describe the basic functions of the current FRC\ |reg| -CAN system and the requirements for any new CAN devices seeking to work +:term:`CAN` system and the requirements for any new CAN devices seeking to work with the system. -Addressing ----------- +## Addressing FRC CAN nodes assign arbitration IDs based on a pre-defined scheme that breaks the ID into 5 components: -Device Type -~~~~~~~~~~~ +#### Device Type This is a 5-bit value describing the type of device being addressed. A table of currently assigned device types can be found below. If you wish @@ -40,8 +37,7 @@ Reserved 12-30 Firmware Update 31 ========================= ===== -Manufacturer -~~~~~~~~~~~~ +#### Manufacturer This is an 8-bit value indicating the manufacturer of the CAN device. Currently assigned values can be found in the table below. If you wish @@ -71,8 +67,7 @@ Vivid Hosting 16 Reserved 17-255 ===================== ========== -API/Message Identifier -~~~~~~~~~~~~~~~~~~~~~~ +#### API/Message Identifier The API or Message Identifier is a 10-bit value that identifies a particular command or message type. These identifiers are unique for @@ -84,8 +79,7 @@ for a CTR Power Distribution Module). The Message identifier is further broken down into 2 sub-fields: the 6-bit API Class and the 4-bit API Index. -API Class -~~~~~~~~~ +#### API Class The API Class is a 6-bit identifier for an API grouping. Similar messages are grouped into a single API Class. An example of the API @@ -105,8 +99,7 @@ Configuration 7 Ack 8 ========================= = -API Index -~~~~~~~~~ +#### API Index The API Index is a 4-bit identifier for a particular message within an API Class. An example of the API Index values for the Jaguar Motor @@ -128,8 +121,7 @@ Trusted Set Setpoint No Ack 10 Set Setpoint No Ack 11 =========================== == -Device Number -~~~~~~~~~~~~~ +#### Device Number Device Number is a 6-bit quantity indicating the number of the device of a particular type. Devices should default to device ID 0 to match other @@ -139,16 +131,14 @@ device specific broadcast messages. .. image:: images/can-addressing/can-id-example.png :alt: CAN addressing bit mapping. -Protected Frames ----------------- +## Protected Frames FRC CAN Nodes which implement actuator control capability (motor controllers, relays, pneumatics controllers, etc.) must implement a way to verify that the robot is enabled and that commands originate with the main robot controller (i.e. the roboRIO). -Broadcast Messages ------------------- +## Broadcast Messages Broadcast messages are messages sent to all nodes by setting the device type and manufacturer fields to 0. The API Class for broadcast messages @@ -174,8 +164,7 @@ System Resume 10 Devices should disable immediately when receiving the Disable message (arbID 0). Implementation of other broadcast messages is optional. -Requirements for FRC CAN Nodes ------------------------------- +## Requirements for FRC CAN Nodes For CAN Nodes to be accepted for use in the FRC System, they must: - Communicate using Arbitration IDs which match the prescribed FRC @@ -190,8 +179,7 @@ For CAN Nodes to be accepted for use in the FRC System, they must: - If controlling actuators, utilize a scheme to assure that the robot is issuing commands, is enabled, and is still present. - Provide software library support for LabVIEW, C++, and Java or arrange with *FIRST*\ |reg| or FIRST's Control System Partners to provide such interfaces. -Universal Heartbeat -------------------- +## Universal Heartbeat The roboRIO provides a universal CAN heartbeat that any device on the bus can listen and react to. This heartbeat is sent every 20 ms. The heartbeat has a full CAN ID of ``0x01011840`` (which is the NI Manufacturer ID, RobotController type, Device ID 0 and API ID ``0x061``). It is an 8 byte CAN packet with the following bitfield layout. @@ -229,25 +217,25 @@ The roboRIO provides a universal CAN heartbeat that any device on the bus can li | Time of day (hours) | 1 | 5 | +-----------------------+------+--------------+ -.. code-block:: c++ - - struct [[gnu::packed]] RobotState { - uint64_t matchTimeSeconds : 8; - uint64_t matchNumber : 10; - uint64_t replayNumber : 6; - uint64_t redAlliance : 1; - uint64_t enabled : 1; - uint64_t autonomous : 1; - uint64_t testMode : 1; - uint64_t systemWatchdog : 1; - uint64_t tournamentType : 3; - uint64_t timeOfDay_yr : 6; - uint64_t timeOfDay_month : 4; - uint64_t timeOfDay_day : 5; - uint64_t timeOfDay_sec : 6; - uint64_t timeOfDay_min : 6; - uint64_t timeOfDay_hr : 5; - }; +```c++ +struct [[gnu::packed]] RobotState { + uint64_t matchTimeSeconds : 8; + uint64_t matchNumber : 10; + uint64_t replayNumber : 6; + uint64_t redAlliance : 1; + uint64_t enabled : 1; + uint64_t autonomous : 1; + uint64_t testMode : 1; + uint64_t systemWatchdog : 1; + uint64_t tournamentType : 3; + uint64_t timeOfDay_yr : 6; + uint64_t timeOfDay_month : 4; + uint64_t timeOfDay_day : 5; + uint64_t timeOfDay_sec : 6; + uint64_t timeOfDay_min : 6; + uint64_t timeOfDay_hr : 5; +}; +``` If the ``System watchdog`` flag is set, motor controllers are enabled. If 100 ms has passed since this packet was received, the robot program can be considered hung, and devices should act as if the robot has been disabled. diff --git a/source/docs/software/can-devices/images/can-addressing/can-id-example.png b/source/docs/software/can-devices/images/can-addressing/can-id-example.png index 88ab7e8606..f9e1e516e8 100644 Binary files a/source/docs/software/can-devices/images/can-addressing/can-id-example.png and b/source/docs/software/can-devices/images/can-addressing/can-id-example.png differ diff --git a/source/docs/software/can-devices/index.rst b/source/docs/software/can-devices/index.rst index bbe1124a8b..9cd0fdf8c9 100644 --- a/source/docs/software/can-devices/index.rst +++ b/source/docs/software/can-devices/index.rst @@ -1,5 +1,4 @@ -CAN Devices -=========== +# CAN Devices .. toctree:: :maxdepth: 1 diff --git a/source/docs/software/can-devices/pneumatic-hub.rst b/source/docs/software/can-devices/pneumatic-hub.rst index ec3d02e0c6..4a094aa5eb 100644 --- a/source/docs/software/can-devices/pneumatic-hub.rst +++ b/source/docs/software/can-devices/pneumatic-hub.rst @@ -1,13 +1,12 @@ -Pneumatic Hub -============= +# Pneumatic Hub .. image:: /docs/controls-overviews/images/control-system-hardware/pneumatic-hub.png :alt: The Pneumatic Hub (PH) :width: 400 -The Pneumatic Hub (PH) is a CAN-based device that provides complete control over the compressor and up to 16 solenoids per module. The PH is integrated into WPILib through a series of classes that make it simple to use. +The Pneumatic Hub (:term:`PH`) is a :term:`CAN`-based device that provides complete control over the compressor and up to 16 solenoids per module. The PH is integrated into WPILib through a series of classes that make it simple to use. -The closed loop control of the Compressor and Pressure switch is handled by the :code:`Compressor` class (`Java `__, `C++ `__), and the Solenoids are handled by the :code:`Solenoid` (`Java `__, `C++ `__) and :code:`DoubleSolenoid` (`Java `__, `C++ `__) classes. +The closed loop control of the Compressor and Pressure switch is handled by the :code:`Compressor` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/Compressor.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_compressor.html), :external:py:class:[Python](wpilib.Compressor>`), and the Solenoids are handled by the :code:`Solenoid` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/Solenoid.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_solenoid.html), :external:py:class:[Python](wpilib.Solenoid>`) and :code:`DoubleSolenoid` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/DoubleSolenoid.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_double_solenoid.html), :external:py:class:`Python `) classes. An additional PH module can be used where the module's corresponding solenoids are differentiated by the module number in the constructors of the Solenoid and Compressor classes. diff --git a/source/docs/software/can-devices/pneumatics-control-module.rst b/source/docs/software/can-devices/pneumatics-control-module.rst index 3cfdd0ede3..2f62b5c989 100644 --- a/source/docs/software/can-devices/pneumatics-control-module.rst +++ b/source/docs/software/can-devices/pneumatics-control-module.rst @@ -1,13 +1,12 @@ -Pneumatics Control Module -========================= +# Pneumatics Control Module .. image:: /docs/controls-overviews/images/control-system-hardware/pneumatics-control-module.png :alt: The Pneumatics Control Module (PCM) :width: 400 -The Pneumatics Control Module (PCM) is a CAN-based device that provides complete control over the compressor and up to 8 solenoids per module. The PCM is integrated into WPILib through a series of classes that make it simple to use. +The Pneumatics Control Module (:term:`PCM`) is a :term:`CAN`-based device that provides complete control over the compressor and up to 8 solenoids per module. The PCM is integrated into WPILib through a series of classes that make it simple to use. -The closed loop control of the Compressor and Pressure switch is handled by the :code:`Compressor` class (`Java `__, `C++ `__), and the Solenoids are handled by the :code:`Solenoid` (`Java `__, `C++ `__) and :code:`DoubleSolenoid` (`Java `__, `C++ `__) classes. +The closed loop control of the Compressor and Pressure switch is handled by the :code:`Compressor` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/Compressor.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_compressor.html), :external:py:class:[Python](wpilib.Compressor>`), and the Solenoids are handled by the :code:`Solenoid` ([Java])https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/Solenoid.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_solenoid.html), :external:py:class:[Python](wpilib.Solenoid>`) and :code:`DoubleSolenoid` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/DoubleSolenoid.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_double_solenoid.html), :external:py:class:`Python `) classes. An additional PCM module can be used where the module's corresponding solenoids are differentiated by the module number in the constructors of the Solenoid and Compressor classes. diff --git a/source/docs/software/can-devices/power-distribution-module.rst b/source/docs/software/can-devices/power-distribution-module.rst index 56964a3a70..66f6265ac0 100644 --- a/source/docs/software/can-devices/power-distribution-module.rst +++ b/source/docs/software/can-devices/power-distribution-module.rst @@ -1,124 +1,156 @@ -Power Distribution Module -========================= +# Power Distribution Module -The CTRE Power Distribution Panel (PDP) and Rev Power Distribution Hub (PDH) can use their CAN connectivity to communicate a wealth of status information regarding the robot's power use to the roboRIO, for use in user code. This has the capability to report current temperature, the bus voltage, the total robot current draw, the total robot energy use, and the individual current draw of each device power channel. This data can be used for a number of advanced control techniques, such as motor :term:`torque` limiting and brownout avoidance. +The CTRE Power Distribution Panel (:term:`PDP`) and Rev Power Distribution Hub (:term:`PDH`) can use their :term:`CAN` connectivity to communicate a wealth of status information regarding the robot's power use to the roboRIO, for use in user code. This has the capability to report current temperature, the bus voltage, the total robot current draw, the total robot energy use, and the individual current draw of each device power channel. This data can be used for a number of advanced control techniques, such as motor :term:`torque` limiting and brownout avoidance. -Creating a Power Distribution Object ------------------------------------- +## Creating a Power Distribution Object -To use the either Power Distribution module, create an instance of the :code:`PowerDistribution` class (`Java `__, `C++ `__). With no arguments, the Power Distribution object will be detected, and must use CAN ID of 0 for CTRE or 1 for REV. If the CAN ID is non-default, additional constructors are available to specify the CAN ID and type. +To use the either Power Distribution module, create an instance of the :code:`PowerDistribution` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj/PowerDistribution.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_power_distribution.html), :external:py:class:`Python `). With no arguments, the Power Distribution object will be detected, and must use CAN ID of 0 for CTRE or 1 for REV. If the CAN ID is non-default, additional constructors are available to specify the CAN ID and type. .. tab-set-code:: - .. code-block:: java - - PowerDistribution examplePD = new PowerDistribution(); - PowerDistribution examplePD = new PowerDistribution(0, ModuleType.kCTRE); - PowerDistribution examplePD = new PowerDistribution(1, ModuleType.kRev); - - .. code-block:: c++ - - PowerDistribution examplePD{}; - PowerDistribution examplePD{0, frc::PowerDistribution::ModuleType::kCTRE}; - PowerDistribution examplePD{1, frc::PowerDistribution::ModuleType::kRev}; + ```java + PowerDistribution examplePD = new PowerDistribution(); + PowerDistribution examplePD = new PowerDistribution(0, ModuleType.kCTRE); + PowerDistribution examplePD = new PowerDistribution(1, ModuleType.kRev); + ``` + + ```c++ + PowerDistribution examplePD{}; + PowerDistribution examplePD{0, frc::PowerDistribution::ModuleType::kCTRE}; + PowerDistribution examplePD{1, frc::PowerDistribution::ModuleType::kRev}; + ``` + + ```python + from wpilib import PowerDistribution + examplePD = PowerDistribution() + examplePD = PowerDistribution(0, PowerDistribution.ModuleType.kCTRE) + examplePD = PowerDistribution(1, PowerDistribution.ModuleType.kRev) + ``` Note: it is not necessary to create a PowerDistribution object unless you need to read values from it. The board will work and supply power on all the channels even if the object is never created. .. warning:: To enable voltage and current logging in the Driver Station, the CAN ID for the CTRE Power Distribution Panel *must* be 0, and for the REV Power Distribution Hub it *must* be 1. -Reading the Bus Voltage ------------------------ +## Reading the Bus Voltage .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/canpdp/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/canpdp/Robot.java :language: java :lines: 32-35 :linenos: :lineno-start: 32 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/CANPDP/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/CANPDP/cpp/Robot.cpp + :language: c++ :lines: 28-31 :linenos: :lineno-start: 28 + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/CANPDP/robot.py + :language: python + :lines: 34-37 + :linenos: + :lineno-start: 34 + + Monitoring the bus voltage can be useful for (among other things) detecting when the robot is near a brownout, so that action can be taken to avoid brownout in a controlled manner. See the :doc:`roboRIO Brownouts document` for more information. -Reading the Temperature ------------------------ +## Reading the Temperature .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/canpdp/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/canpdp/Robot.java :language: java :lines: 37-39 :linenos: :lineno-start: 37 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/CANPDP/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/CANPDP/cpp/Robot.cpp + :language: c++ :lines: 33-35 :linenos: :lineno-start: 33 + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/CANPDP/robot.py + :language: python + :lines: 39-41 + :linenos: + :lineno-start: 39 + Monitoring the temperature can be useful for detecting if the robot has been drawing too much power and needs to be shut down for a while, or if there is a short or other wiring problem. -Reading the Total Current, Power, and Energy --------------------------------------------- +## Reading the Total Current, Power, and Energy .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/canpdp/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/canpdp/Robot.java :language: java :lines: 41-53 :linenos: :lineno-start: 41 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/CANPDP/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/CANPDP/cpp/Robot.cpp + :language: c++ :lines: 37-49 :linenos: :lineno-start: 37 + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/CANPDP/robot.py + :language: python + :lines: 43-55 + :linenos: + :lineno-start: 43 + Monitoring the total current, power and energy can be useful for controlling how much power is being drawn from the battery, both for preventing brownouts and ensuring that mechanisms have sufficient power available to perform the actions required. Power is the bus voltage multiplied by the current with the units Watts. Energy is the power summed over time with units Joules. -Reading Individual Channel Currents ------------------------------------ +## Reading Individual Channel Currents The PDP/PDH also allows users to monitor the current drawn by the individual device power channels. You can read the current on any of the 16 PDP channels (0-15) or 24 PDH channels (0-23). .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/canpdp/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/canpdp/Robot.java :language: java :lines: 26-30 :linenos: :lineno-start: 26 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/CANPDP/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/CANPDP/cpp/Robot.cpp + :language: c++ :lines: 22-26 :linenos: :lineno-start: 22 + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/CANPDP/robot.py + :language: python + :lines: 28-32 + :linenos: + :lineno-start: 28 + Monitoring individual device current draws can be useful for detecting shorts or stalled motors. -Using the Switchable Channel (PDH) ----------------------------------- +## Using the Switchable Channel (PDH) The REV PDH has one channel that can be switched on or off to control custom circuits. .. tab-set-code:: - .. code-block:: java + ```java + examplePD.setSwitchableChannel(true); + examplePD.setSwitchableChannel(false); + ``` - examplePD.setSwitchableChannel(true); - examplePD.setSwitchableChannel(false); + ```c++ + examplePD.SetSwitchableChannel(true); + examplePD.SetSwitchableChannel(false); + ``` - .. code-block:: c++ + ```python + examplePD.setSwitchableChannel(True) + examplePD.setSwitchableChannel(False) + ``` - examplePD.SetSwitchableChannel(true); - examplePD.SetSwitchableChannel(false); diff --git a/source/docs/software/can-devices/third-party-devices.rst b/source/docs/software/can-devices/third-party-devices.rst index 6d3543c73a..e738cc88c8 100644 --- a/source/docs/software/can-devices/third-party-devices.rst +++ b/source/docs/software/can-devices/third-party-devices.rst @@ -1,9 +1,8 @@ .. include:: -Third-Party CAN Devices -======================= +# Third-Party CAN Devices -A number of FRC\ |reg| vendors offer their own CAN peripherals. As CAN devices +A number of FRC\ |reg| vendors offer their own :term:`CAN` peripherals. As CAN devices offer expansive feature-sets, vendor CAN devices require similarly expansive code libraries to operate. As a result, these libraries are not maintained as an official part of WPILib, but are instead maintained @@ -12,113 +11,112 @@ libraries, see :ref:`3rd Party Libraries `__ + [Phoenix Device Software Documentation](https://docs.ctr-electronics.com/) -CTRE Motor Controllers -^^^^^^^^^^^^^^^^^^^^^^ +### CTRE Motor Controllers - **Talon FX (with Falcon 500 Motor)** - - API Documentation (v5: `Java `__, `C++ `__ | Pro: `Java `__, `C++ `__) - - `Hardware User's Manual `__ - - Software Documentation (`v5 `__, `Pro `__) + - API Documentation (v5: [Java](https://api.ctr-electronics.com/phoenix/release/java/com/ctre/phoenix/motorcontrol/can/WPI_TalonFX.html), [C++](https://api.ctr-electronics.com/phoenix/release/cpp/classctre_1_1phoenix_1_1motorcontrol_1_1can_1_1_w_p_i___talon_f_x.html) | Pro: [Java](https://api.ctr-electronics.com/phoenixpro/release/java/com/ctre/phoenixpro/hardware/TalonFX.html), [C++](https://api.ctr-electronics.com/phoenixpro/release/cpp/classctre_1_1phoenixpro_1_1hardware_1_1_talon_f_x.html)) + - [Hardware User's Manual](https://store.ctr-electronics.com/content/user-manual/Falcon%20500%20User%20Guide.pdf) + - Software Documentation ([v5](https://v5.docs.ctr-electronics.com/en/stable/ch13_MC.html), [Pro](https://pro.docs.ctr-electronics.com/en/stable/docs/api-reference/examples/quickstart.html)) - **Talon SRX** - - API Documentation (`Java `__, `C++ `__) - - `Hardware User's Manual `__ - - `Software Documentation `__ + - API Documentation ([Java](https://api.ctr-electronics.com/phoenix/release/java/com/ctre/phoenix/motorcontrol/can/WPI_TalonSRX.html), [C++](https://api.ctr-electronics.com/phoenix/release/cpp/classctre_1_1phoenix_1_1motorcontrol_1_1can_1_1_w_p_i___talon_s_r_x.html)) + - [Hardware User's Manual](https://store.ctr-electronics.com/content/user-manual/Talon%20SRX%20User's%20Guide.pdf) + - [Software Documentation](https://v5.docs.ctr-electronics.com/en/stable/ch13_MC.html) - **Victor SPX** - - API Documentation (`Java `__, `C++ `__) - - `Hardware User's Manual `__ - - `Software Documentation `__ + - API Documentation ([Java](https://api.ctr-electronics.com/phoenix/release/java/com/ctre/phoenix/motorcontrol/can/WPI_VictorSPX.html), [C++](https://api.ctr-electronics.com/phoenix/release/cpp/classctre_1_1phoenix_1_1motorcontrol_1_1can_1_1_w_p_i___victor_s_p_x.html)) + - [Hardware User's Manual](https://store.ctr-electronics.com/content/user-manual/Victor%20SPX%20User's%20Guide.pdf) + - [Software Documentation](https://v5.docs.ctr-electronics.com/en/stable/ch13_MC.html) -CTRE Sensors -^^^^^^^^^^^^ +### CTRE Sensors - **CANcoder** - - API Documentation (v5: `Java `__, `C++ `__ | Pro: `Java `__, `C++ `__) - - `Hardware User's Manual `__ - - Software Documentation (`v5 `__, `Pro `__) + - API Documentation (v5: [Java](https://api.ctr-electronics.com/phoenix/release/java/com/ctre/phoenix/sensors/WPI_CANCoder.html), [C++](https://api.ctr-electronics.com/phoenix/release/cpp/classctre_1_1phoenix_1_1sensors_1_1_w_p_i___c_a_n_coder.html) | Pro: [Java](https://api.ctr-electronics.com/phoenixpro/release/java/com/ctre/phoenixpro/hardware/CANcoder.html), [C++](https://api.ctr-electronics.com/phoenixpro/release/cpp/classctre_1_1phoenixpro_1_1hardware_1_1_c_a_ncoder.html)) + - [Hardware User's Manual](https://store.ctr-electronics.com/content/user-manual/CANCoder%20User's%20Guide.pdf) + - Software Documentation ([v5](https://v5.docs.ctr-electronics.com/en/stable/ch12a_BringUpCANCoder.html), [Pro](https://pro.docs.ctr-electronics.com/en/stable/docs/api-reference/api-usage/index.html)) - **Pigeon 2.0** - - API Documentation (v5: `Java `__, `C++ `__ | Pro: `Java `__, `C++ `__) - - `Hardware User's Manual `__ - - Software Documentation (`v5 `__, `Pro `__) + - API Documentation (v5: [Java](https://api.ctr-electronics.com/phoenix/release/java/com/ctre/phoenix/sensors/WPI_Pigeon2.html), [C++](https://api.ctr-electronics.com/phoenix/release/cpp/classctre_1_1phoenix_1_1sensors_1_1_w_p_i___pigeon2.html) | Pro: [Java](https://api.ctr-electronics.com/phoenixpro/release/java/com/ctre/phoenixpro/hardware/Pigeon2.html), [C++](https://api.ctr-electronics.com/phoenixpro/release/cpp/classctre_1_1phoenixpro_1_1hardware_1_1_pigeon2.html)) + - [Hardware User's Manual](https://store.ctr-electronics.com/content/user-manual/Pigeon2%20User's%20Guide.pdf) + - Software Documentation ([v5](https://v5.docs.ctr-electronics.com/en/stable/ch11a_BringUpPigeon2.html), [Pro](https://pro.docs.ctr-electronics.com/en/stable/docs/api-reference/api-usage/index.html)) - **Pigeon IMU** - - API Documentation (`Java `__, `C++ `__) - - `Hardware User's Manual `__ - - `Software Documentation `__ + - API Documentation ([Java](https://api.ctr-electronics.com/phoenix/release/java/com/ctre/phoenix/sensors/WPI_PigeonIMU.html), [C++](https://api.ctr-electronics.com/phoenix/release/cpp/classctre_1_1phoenix_1_1sensors_1_1_w_p_i___pigeon_i_m_u.html)) + - [Hardware User's Manual](https://store.ctr-electronics.com/content/user-manual/Pigeon%20IMU%20User's%20Guide.pdf) + - [Software Documentation](https://v5.docs.ctr-electronics.com/en/stable/ch11_BringUpPigeon.html) - **CANifier** - - API Documentation (`Java `__, `C++ `__) - - `Hardware User's Manual `__ - - `Software Documentation `__ + - API Documentation ([Java](https://api.ctr-electronics.com/phoenix/release/java/com/ctre/phoenix/CANifier.html), [C++](https://api.ctr-electronics.com/phoenix/release/cpp/classctre_1_1phoenix_1_1_c_a_nifier.html)) + - [Hardware User's Manual](https://store.ctr-electronics.com/content/user-manual/CANifier%20User%27s%20Guide.pdf) + - [Software Documentation](https://v5.docs.ctr-electronics.com/en/stable/ch12_BringUpCANifier.html) -CTRE Other CAN Devices -^^^^^^^^^^^^^^^^^^^^^^ +### CTRE Other CAN Devices - **CANdle LED Controller** - - API Documentation (`Java `__, `C++ `__) - - `Hardware User's Manual `__ - - `Software Documentation `__ + - API Documentation ([Java](https://api.ctr-electronics.com/phoenix/release/java/com/ctre/phoenix/led/CANdle.html), [C++](https://api.ctr-electronics.com/phoenix/release/cpp/classctre_1_1phoenix_1_1led_1_1_c_a_ndle.html)) + - [Hardware User's Manual](https://store.ctr-electronics.com/content/user-manual/CANdle%20User's%20Guide.pdf) + - [Software Documentation](https://v5.docs.ctr-electronics.com/en/stable/ch12b_BringUpCANdle.html) -REV Robotics ------------- +## REV Robotics -REV Robotics currently offers the SPARK MAX motor controller, which has a similar feature-set to the Talon SRX. +REV Robotics currently offers the SPARK MAX and SPARK Flex motor controllers which can be used for brushed and REV brushless (NEO, NEO 550, and NEO Vortex) motors. -REV Motor Controllers -^^^^^^^^^^^^^^^^^^^^^ +### REV Motor Controllers - **SPARK MAX** - - API Documentation (`Java `__, `C++ `__) - - `Technical Manual `__ + - API Documentation ([Java](https://codedocs.revrobotics.com/java/com/revrobotics/cansparkmax), [C++](https://codedocs.revrobotics.com/cpp/classrev_1_1_c_a_n_spark_max)) + - [Technical Manual](https://docs.revrobotics.com/brushless/spark-max/overview) -Playing With Fusion -------------------- +- **SPARK Flex** + + - API Documentation ([Java](https://codedocs.revrobotics.com/java/com/revrobotics/cansparkflex), [C++](https://codedocs.revrobotics.com/cpp/classrev_1_1_c_a_n_spark_flex)) + - [Technical Manual](https://docs.revrobotics.com/brushless/spark-flex/overview) + +## Playing With Fusion Playing With Fusion (PWF) offers the Venom integrated motor/controller as well as a Time-of-Flight distance sensor: -PWF Motor Controllers -^^^^^^^^^^^^^^^^^^^^^ +### PWF Motor Controllers - **Venom** - - API Documentation (`Java `__, `C++ `__) - - `Technical Manual `__ + - API Documentation ([Java](https://www.playingwithfusion.com/frc/2020/javadoc/com/playingwithfusion/package-summary.html), [C++](https://www.playingwithfusion.com/frc/2020/cppdoc/html/annotated.html)) + - [Technical Manual](https://www.playingwithfusion.com/include/getfile.php?fileid=7086) -PWF Sensors -^^^^^^^^^^^ +### PWF Sensors - **Time of Flight Sensor** - - API Documentation (`Java `__, `C++ `__) - - `Technical Manual `__ + - API Documentation ([Java](https://www.playingwithfusion.com/frc/2020/javadoc/com/playingwithfusion/package-summary.html), [C++](https://www.playingwithfusion.com/frc/2020/cppdoc/html/annotated.html)) + - [Technical Manual](https://www.playingwithfusion.com/include/getfile.php?fileid=7091) -Redux Robotics --------------- +## Redux Robotics -Redux Robotics currently offers the Canandcoder CAN + PWM magnetic encoder. +Redux Robotics currently offers the Canandcoder :term:`CAN` + :term:`PWM` magnetic encoder and the Canandcolor :term:`CAN`-enabled color sensor. -Redux Sensors -^^^^^^^^^^^^^ +### Redux Sensors - **Canandcoder** - - API Documentation (`Java `__, `C++ `__) - - `Technical Manual `__ + - API Documentation ([Java](https://apidocs.reduxrobotics.com/current/java/com/reduxrobotics/sensors/canandcoder/Canandcoder.html), [C++](https://apidocs.reduxrobotics.com/current/cpp/classredux_1_1sensors_1_1canandcoder_1_1Canandcoder.html)) + - [Technical Manual](https://docs.reduxrobotics.com/canandcoder/index.html) + +- **Canandcolor** + + - API Documentation ([Java](https://apidocs.reduxrobotics.com/current/java/com/reduxrobotics/sensors/canandcolor/Canandcolor.html), [C++](https://apidocs.reduxrobotics.com/current/cpp/classredux_1_1sensors_1_1canandcolor_1_1Canandcolor.html)) + - [Technical Manual](https://docs.reduxrobotics.com/canandcolor/index.html) diff --git a/source/docs/software/can-devices/using-can-devices.rst b/source/docs/software/can-devices/using-can-devices.rst index abb5ac9f19..1d2686f458 100644 --- a/source/docs/software/can-devices/using-can-devices.rst +++ b/source/docs/software/can-devices/using-can-devices.rst @@ -1,11 +1,10 @@ -Using CAN Devices -================= +# Using CAN Devices -CAN has many advantages over other methods of connection between the robot controller and peripheral devices. +:term:`CAN` has many advantages over other methods of connection between the robot controller and peripheral devices. - CAN connections are daisy-chained from device to device, which often results in much shorter wire runs than having to wire each device to the RIO itself. -- Much more data can be sent over a CAN connection than over a PWM connection - thus, CAN motor controllers are capable of a much more expansive feature-set than are PWM motor controllers. +- Much more data can be sent over a CAN connection than over a :term:`PWM` connection - thus, CAN motor controllers are capable of a much more expansive feature-set than are PWM motor controllers. - CAN is bi-directional, so CAN motor controllers can send data back to the RIO, again facilitating a more expansive feature-set than can be offered by PWM Controllers. diff --git a/source/docs/software/commandbased/binding-commands-to-triggers.rst b/source/docs/software/commandbased/binding-commands-to-triggers.rst index ddfd0f08f0..cd7a140c13 100644 --- a/source/docs/software/commandbased/binding-commands-to-triggers.rst +++ b/source/docs/software/commandbased/binding-commands-to-triggers.rst @@ -1,74 +1,66 @@ -Binding Commands to Triggers -============================ +# Binding Commands to Triggers Apart from autonomous commands, which are scheduled at the start of the autonomous period, and default commands, which are automatically scheduled whenever their subsystem is not currently in-use, the most common way to run a command is by binding it to a triggering event, such as a button being pressed by a human operator. The command-based paradigm makes this extremely easy to do. As mentioned earlier, command-based is a :term:`declarative programming` paradigm. Accordingly, binding buttons to commands is done declaratively; the association of a button and a command is "declared" once, during robot initialization. The library then does all the hard work of checking the button state and scheduling (or canceling) the command as needed, behind-the-scenes. Users only need to worry about designing their desired UI setup - not about implementing it! -Command binding is done through the ``Trigger`` class (`Java `__, `C++ `__). +Command binding is done through the ``Trigger`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/button/Trigger.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_trigger.html)). -Getting a Trigger Instance --------------------------- +## Getting a Trigger Instance To bind commands to conditions, we need a ``Trigger`` object. There are three ways to get a Trigger object: -HID Factories -^^^^^^^^^^^^^ +### HID Factories -The command-based HID classes contain factory methods returning a ``Trigger`` for a given button. ``CommandGenericHID`` has an index-based ``button(int)`` factory (`Java `__, `C++ `__), and its subclasses ``CommandXboxController`` (`Java `__, `C++ `__), ``CommandPS4Controller`` (`Java `__, `C++ `__), and ``CommandJoystick`` (`Java `__, `C++ `__) have named factory methods for each button. +The command-based HID classes contain factory methods returning a ``Trigger`` for a given button. ``CommandGenericHID`` has an index-based ``button(int)`` factory ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/button/CommandGenericHID.html#button(int)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_generic_h_i_d.html#a661f49794a913615c94fba927e1072a8)), and its subclasses ``CommandXboxController`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/button/CommandXboxController.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_xbox_controller.html)), ``CommandPS4Controller`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/button/CommandPS4Controller.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_p_s4_controller.html)), and ``CommandJoystick`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/button/CommandJoystick.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_joystick.html)) have named factory methods for each button. .. tab-set-code:: - .. code-block:: java + ```java + CommandXboxController exampleCommandController = new CommandXboxController(1); // Creates a CommandXboxController on port 1. + Trigger xButton = exampleCommandController.x(); // Creates a new Trigger object for the `X` button on exampleCommandController + ``` - CommandXboxController exampleCommandController = new CommandXboxController(1); // Creates a CommandXboxController on port 1. - Trigger xButton = exampleCommandController.x(); // Creates a new Trigger object for the `X` button on exampleCommandController + ```c++ + frc2::CommandXboxController exampleCommandController{1} // Creates a CommandXboxController on port 1 + frc2::Trigger xButton = exampleCommandController.X() // Creates a new Trigger object for the `X` button on exampleCommandController + ``` - .. code-block:: c++ +### JoystickButton - frc2::CommandXboxController exampleCommandController{1} // Creates a CommandXboxController on port 1 - frc2::Trigger xButton = exampleCommandController.X() // Creates a new Trigger object for the `X` button on exampleCommandController - -JoystickButton -^^^^^^^^^^^^^^ - -Alternatively, the :ref:`regular HID classes ` can be used and passed to create an instance of ``JoystickButton`` (`Java `__, `C++ `__), a constructor-only subclass of ``Trigger``: +Alternatively, the :ref:`regular HID classes ` can be used and passed to create an instance of ``JoystickButton`` [Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/button/JoystickButton.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_joystick_button.html)), a constructor-only subclass of ``Trigger``: .. tab-set-code:: - .. code-block:: java - - XboxController exampleController = new XboxController(2); // Creates an XboxController on port 2. - Trigger yButton = new JoystickButton(exampleController, XboxController.Button.kY.value); // Creates a new JoystickButton object for the `Y` button on exampleController + ```java + XboxController exampleController = new XboxController(2); // Creates an XboxController on port 2. + Trigger yButton = new JoystickButton(exampleController, XboxController.Button.kY.value); // Creates a new JoystickButton object for the `Y` button on exampleController + ``` - .. code-block:: c++ + ```c++ + frc::XboxController exampleController{2} // Creates an XboxController on port 2 + frc2::JoystickButton yButton(&exampleStick, frc::XboxController::Button::kY); // Creates a new JoystickButton object for the `Y` button on exampleController + ``` - frc::XboxController exampleController{2} // Creates an XboxController on port 2 - frc2::JoystickButton yButton(&exampleStick, frc::XboxController::Button::kY); // Creates a new JoystickButton object for the `Y` button on exampleController - -Arbitrary Triggers -^^^^^^^^^^^^^^^^^^ +### Arbitrary Triggers While binding to HID buttons is by far the most common use case, users may want to bind commands to arbitrary triggering events. This can be done inline by passing a lambda to the constructor of ``Trigger``: .. tab-set-code:: - .. code-block:: java - - DigitalInput limitSwitch = new DigitalInput(3); // Limit switch on DIO 3 - - Trigger exampleTrigger = new Trigger(limitSwitch::get); + ```java + DigitalInput limitSwitch = new DigitalInput(3); // Limit switch on DIO 3 + Trigger exampleTrigger = new Trigger(limitSwitch::get); + ``` - .. code-block:: c++ + ```c++ + frc::DigitalInput limitSwitch{3}; // Limit switch on DIO 3 + frc2::Trigger exampleTrigger([&limitSwitch] { return limitSwitch.Get(); }); + ``` - frc::DigitalInput limitSwitch{3}; // Limit switch on DIO 3 +## Trigger Bindings - frc2::Trigger exampleTrigger([&limitSwitch] { return limitSwitch.Get(); }); - -Trigger Bindings ----------------- - -.. note:: The C++ command-based library offers two overloads of each button binding method - one that takes an `rvalue reference `__ (``CommandPtr&&``), and one that takes a raw pointer (``Command*``). The rvalue overload moves ownership to the scheduler, while the raw pointer overload leaves the user responsible for the lifespan of the command object. It is recommended that users preferentially use the rvalue reference overload unless there is a specific need to retain a handle to the command in the calling code. +.. note:: The C++ command-based library offers two overloads of each button binding method - one that takes an [rvalue reference](http://thbecker.net/articles/rvalue_references/section_01.html) (``CommandPtr&&``), and one that takes a raw pointer (``Command*``). The rvalue overload moves ownership to the scheduler, while the raw pointer overload leaves the user responsible for the lifespan of the command object. It is recommended that users preferentially use the rvalue reference overload unless there is a specific need to retain a handle to the command in the calling code. There are a number of bindings available for the ``Trigger`` class. All of these bindings will automatically schedule a command when a certain trigger activation event occurs - however, each binding has different specific behavior. @@ -76,134 +68,127 @@ There are a number of bindings available for the ``Trigger`` class. All of these .. note:: The ``Button`` subclass is deprecated, and usage of its binding methods should be replaced according to the respective deprecation messages in the API docs. -onTrue -^^^^^^ +### onTrue This binding schedules a command when a trigger changes from ``false`` to ``true`` (or, accordingly, when a button changes is initially pressed). The command will be scheduled on the iteration when the state changes, and will not be scheduled again unless the trigger becomes ``false`` and then ``true`` again (or the button is released and then re-pressed). .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/RobotContainer.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/RapidReactCommandBot.java :language: java - :lines: 52-53 + :lines: 63-64 :linenos: - :lineno-start: 52 + :lineno-start: 63 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/cpp/RobotContainer.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/RapidReactCommandBot.cpp :language: c++ - :lines: 24-25 + :lines: 28-29 :linenos: - :lineno-start: 24 + :lineno-start: 28 The ``onFalse`` binding is identical, only that it schedules on ``false`` instead of on ``true``. -whileTrue -^^^^^^^^^ +### whileTrue This binding schedules a command when a trigger changes from ``false`` to ``true`` (or, accordingly, when a button is initially pressed) and cancels it when the trigger becomes ``false`` again (or the button is released). The command will *not* be re-scheduled if it finishes while the trigger is still ``true``. For the command to restart if it finishes while the trigger is ``true``, wrap the command in a ``RepeatCommand``, or use a ``RunCommand`` instead of an ``InstantCommand``. .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/RobotContainer.java :language: java - :lines: 114-116 + :lines: 49-51 :linenos: - :lineno-start: 114 + :lineno-start: 49 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/RobotContainer.cpp :language: c++ - :lines: 75-78 + :lines: 27-29 :linenos: - :lineno-start: 75 + :lineno-start: 27 The ``whileFalse`` binding is identical, only that it schedules on ``false`` and cancels on ``true``. -toggleOnTrue -^^^^^^^^^^^^ +### toggleOnTrue -This binding toggles a command, scheduling it when a trigger changes from ``false`` to ``true`` (or a button is initially pressed), and canceling it under the same condition if the command is currently running. Note that while this functionality is supported, toggles are not a highly-recommended option for user control, as they require the driver to keep track of the robot state. The preferred method is to use two buttons; one to turn on and another to turn off. Using a `StartEndCommand `__ or a `ConditionalCommand `__ is a good way to specify the commands that you want to be want to be toggled between. +This binding toggles a command, scheduling it when a trigger changes from ``false`` to ``true`` (or a button is initially pressed), and canceling it under the same condition if the command is currently running. Note that while this functionality is supported, toggles are not a highly-recommended option for user control, as they require the driver to keep track of the robot state. The preferred method is to use two buttons; one to turn on and another to turn off. Using a [StartEndCommand](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/StartEndCommand.html) or a [ConditionalCommand](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/ConditionalCommand.html) is a good way to specify the commands that you want to be want to be toggled between. .. tab-set-code:: - .. code-block:: java - - myButton.toggleOnTrue(Commands.startEnd(mySubsystem::onMethod, - mySubsystem::offMethod, - mySubsystem)); - - .. code-block:: c++ + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/RapidReactCommandBot.java + :language: java + :lines: 76-77 + :linenos: + :lineno-start: 76 - myButton.ToggleOnTrue(frc2::cmd::StartEnd([&] { mySubsystem.OnMethod(); }, - [&] { mySubsystem.OffMethod(); }, - {&mySubsystem})); + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/RapidReactCommandBot.cpp + :language: c++ + :lines: 41-43 + :linenos: + :lineno-start: 41 The ``toggleOnFalse`` binding is identical, only that it toggles on ``false`` instead of on ``true``. -Chaining Calls --------------- +## Chaining Calls It is useful to note that the command binding methods all return the trigger that they were called on, and thus can be chained to bind multiple commands to different states of the same trigger. For example: .. tab-set-code:: - .. code-block:: java - - exampleButton - // Binds a FooCommand to be scheduled when the button is pressed - .onTrue(new FooCommand()) - // Binds a BarCommand to be scheduled when that same button is released - .onFalse(new BarCommand()); - - .. code-block:: c++ + ```java + exampleButton + // Binds a FooCommand to be scheduled when the button is pressed + .onTrue(new FooCommand()) + // Binds a BarCommand to be scheduled when that same button is released + .onFalse(new BarCommand()); + ``` - exampleButton - // Binds a FooCommand to be scheduled when the button is pressed - .OnTrue(FooCommand().ToPtr()) - // Binds a BarCommand to be scheduled when that same button is released - .OnFalse(BarCommand().ToPtr()); + ```c++ + exampleButton + // Binds a FooCommand to be scheduled when the button is pressed + .OnTrue(FooCommand().ToPtr()) + // Binds a BarCommand to be scheduled when that same button is released + .OnFalse(BarCommand().ToPtr()); + ``` -Composing Triggers ------------------- +## Composing Triggers The ``Trigger`` class can be composed to create composite triggers through the ``and()``, ``or()``, and ``negate()`` methods (or, in C++, the ``&&``, ``||``, and ``!`` operators). For example: .. tab-set-code:: - .. code-block:: java + ```java + // Binds an ExampleCommand to be scheduled when both the 'X' and 'Y' buttons of the driver gamepad are pressed + exampleCommandController.x() + .and(exampleCommandController.y()) + .onTrue(new ExampleCommand()); + ``` - // Binds an ExampleCommand to be scheduled when both the 'X' and 'Y' buttons of the driver gamepad are pressed - exampleCommandController.x() - .and(exampleCommandController.y()) - .onTrue(new ExampleCommand()); + ```c++ + // Binds an ExampleCommand to be scheduled when both the 'X' and 'Y' buttons of the driver gamepad are pressed + (exampleCommandController.X() + && exampleCommandController.Y()) + .OnTrue(ExampleCommand().ToPtr()); + ``` - .. code-block:: c++ - - // Binds an ExampleCommand to be scheduled when both the 'X' and 'Y' buttons of the driver gamepad are pressed - (exampleCommandController.X() - && exampleCommandController.Y()) - .OnTrue(ExampleCommand().ToPtr()); - -Debouncing Triggers -------------------- +## Debouncing Triggers To avoid rapid repeated activation, triggers (especially those originating from digital inputs) can be debounced with the :ref:`WPILib Debouncer class ` using the `debounce` method: .. tab-set-code:: - .. code-block:: java - - // debounces exampleButton with a 0.1s debounce time, rising edges only - exampleButton.debounce(0.1).onTrue(new ExampleCommand()); - - // debounces exampleButton with a 0.1s debounce time, both rising and falling edges - exampleButton.debounce(0.1, Debouncer.DebounceType.kBoth).onTrue(new ExampleCommand()); - - .. code-block:: c++ + ```java + // debounces exampleButton with a 0.1s debounce time, rising edges only + exampleButton.debounce(0.1).onTrue(new ExampleCommand()); + // debounces exampleButton with a 0.1s debounce time, both rising and falling edges + exampleButton.debounce(0.1, Debouncer.DebounceType.kBoth).onTrue(new ExampleCommand()); + ``` - // debounces exampleButton with a 100ms debounce time, rising edges only - exampleButton.Debounce(100_ms).OnTrue(ExampleCommand().ToPtr()); + ```c++ + // debounces exampleButton with a 100ms debounce time, rising edges only + exampleButton.Debounce(100_ms).OnTrue(ExampleCommand().ToPtr()); + // debounces exampleButton with a 100ms debounce time, both rising and falling edges + exampleButton.Debounce(100_ms, Debouncer::DebounceType::Both).OnTrue(ExampleCommand().ToPtr()); + ``` - // debounces exampleButton with a 100ms debounce time, both rising and falling edges - exampleButton.Debounce(100_ms, Debouncer::DebounceType::Both).OnTrue(ExampleCommand().ToPtr()); diff --git a/source/docs/software/commandbased/command-compositions.rst b/source/docs/software/commandbased/command-compositions.rst index af51271fd0..c222c62710 100644 --- a/source/docs/software/commandbased/command-compositions.rst +++ b/source/docs/software/commandbased/command-compositions.rst @@ -1,5 +1,4 @@ -Command Compositions -==================== +# Command Compositions Individual commands are capable of accomplishing a large variety of robot tasks, but the simple three-state format can quickly become cumbersome when more advanced functionality requiring extended sequences of robot tasks or coordination of multiple robot subsystems is required. In order to accomplish this, users are encouraged to use the powerful command composition functionality included in the command-based library. @@ -9,265 +8,329 @@ Most importantly, however, command compositions are themselves commands - they e .. tab-set-code:: - .. code-block:: java - + ```java // Will run fooCommand, and then a race between barCommand and bazCommand button.onTrue(fooCommand.andThen(barCommand.raceWith(bazCommand))); + ``` - .. code-block:: c++ - + ```c++ // Will run fooCommand, and then a race between barCommand and bazCommand button.OnTrue(std::move(fooCommand).AndThen(std::move(barCommand).RaceWith(std::move(bazCommand)))); + ``` + + ```python + # Will run fooCommand, and then a race between barCommand and bazCommand + button.onTrue(fooCommand.andThen(barCommand.raceWith(bazCommand))) + ``` As a rule, command compositions require all subsystems their components require, may run when disabled if all their component set ``runsWhenDisabled`` as ``true``, and are ``kCancelIncoming`` if all their components are ``kCancelIncoming`` as well. -Command instances that have been passed to a command composition cannot be independently scheduled or passed to a second command composition. Attempting to do so will throw an exception and crash the user program. This is because composition members are run through their encapsulating command composition, and errors could occur if those same command instances were independently scheduled at the same time as the group - the command would be being run from multiple places at once, and thus could end up with inconsistent internal state, causing unexpected and hard-to-diagnose behavior. The C++ command-based library uses ``CommandPtr``, a class with move-only semantics, so this type of mistake is easier to avoid. +Command instances that have been passed to a command composition cannot be independently scheduled or passed to a second command composition. Attempting to do so will throw an exception and crash the user program. This is because composition members are run through their encapsulating command composition, and errors could occur if those same command instances were independently scheduled at the same time as the composition - the command would be being run from multiple places at once, and thus could end up with inconsistent internal state, causing unexpected and hard-to-diagnose behavior. The C++ command-based library uses ``CommandPtr``, a class with move-only semantics, so this type of mistake is easier to avoid. -Composition Types ------------------ +## Composition Types The command-based library includes various composition types. All of them can be constructed using factories that accept the member commands, and some can also be constructed using decorators: methods that can be called on a command object, which is transformed into a new object that is returned. .. important:: After calling a decorator or being passed to a composition, the command object cannot be reused! Use only the command object returned from the decorator. -Repeating -^^^^^^^^^ +### Repeating -The ``repeatedly()`` decorator (`Java `__, `C++ `__), backed by the ``RepeatCommand`` class (`Java `__, `C++ `__) restarts the command each time it ends, so that it runs until interrupted. +The ``repeatedly()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#repeatedly()), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#acc156a5299699110729918c3aa2b2694), :external:py:meth:[Python](commands2.Command.repeatedly>`), backed by the ``RepeatCommand`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/RepeatCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_repeat_command.html), :external:py:class:`Python `) restarts the command each time it ends, so that it runs until interrupted. .. tab-set-code:: - .. code-block:: java - - // Will run forever unless externally interrupted, restarting every time command.isFinished() returns true - Command repeats = command.repeatedly(); + ```java + // Will run forever unless externally interrupted, restarting every time command.isFinished() returns true + Command repeats = command.repeatedly(); + ``` - .. code-block:: c++ + ```c++ + // Will run forever unless externally interrupted, restarting every time command.IsFinished() returns true + frc2::CommandPtr repeats = std::move(command).Repeatedly(); + ``` - // Will run forever unless externally interrupted, restarting every time command.IsFinished() returns true - frc2::CommandPtr repeats = std::move(command).Repeatedly(); + ```python + # Will run forever unless externally interrupted, restarting every time command.IsFinished() returns true + repeats = commands2.cmd.repeatedly() + ``` -Sequence -^^^^^^^^ +### Sequence -The ``Sequence`` factory (`Java `__, `C++ `__), backed by the ``SequentialCommandGroup`` class (`Java `__, `C++ `__), runs a list of commands in sequence: the first command will be executed, then the second, then the third, and so on until the list finishes. The sequential group finishes after the last command in the sequence finishes. It is therefore usually important to ensure that each command in the sequence does actually finish (if a given command does not finish, the next command will never start!). +The ``Sequence`` factory ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Commands.html#sequence(edu.wpi.first.wpilibj2.command.Command...)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/namespacefrc2_1_1cmd.html#ac588bdc52a86a4683b89c28dcadea458), :external:py:func:[Python](commands2.cmd.sequence>`), backed by the ``SequentialCommandGroup`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/SequentialCommandGroup.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_sequential_command_group.html), :external:py:class:`Python `), runs a list of commands in sequence: the first command will be executed, then the second, then the third, and so on until the list finishes. The sequential group finishes after the last command in the sequence finishes. It is therefore usually important to ensure that each command in the sequence does actually finish (if a given command does not finish, the next command will never start!). -The ``andThen()`` (`Java `__, `C++ `__) and ``beforeStarting()`` (`Java `__, `C++ `__) decorators can be used to construct a sequence composition with infix syntax. +The ``andThen()`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#andThen(edu.wpi.first.wpilibj2.command.Command...)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#a4ea952f52baf9fb157bb42801be602c0), :external:py:meth:[Python](commands2.Command.andThen>`) and ``beforeStarting()`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#beforeStarting(edu.wpi.first.wpilibj2.command.Command)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#a61e9a735d7b48dafd4b7499af8ff0c23), :external:py:meth:`Python `) decorators can be used to construct a sequence composition with infix syntax. .. tab-set-code:: - .. code-block:: java + ```java + fooCommand.andThen(barCommand) + ``` - fooCommand.andThen(barCommand) + ```c++ + std::move(fooCommand).AndThen(std::move(barCommand)) + ``` - .. code-block:: c++ + ```python + fooCommand.andThen(barCommand) + ``` - std::move(fooCommand).AndThen(std::move(barCommand)) +### Repeating Sequence -Repeating Sequence -^^^^^^^^^^^^^^^^^^ +As it's a fairly common combination, the ``RepeatingSequence`` factory ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Commands.html#repeatingSequence(edu.wpi.first.wpilibj2.command.Command...)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/namespacefrc2_1_1cmd.html#ac6b82e1b867c16264aa4e4bd79724d8c), :external:py:func:`Python `) creates a `Repeating`_ `Sequence`_ that runs until interrupted, restarting from the first command each time the last command finishes. -As it's a fairly common combination, the ``RepeatingSequence`` factory (`Java `__, `C++ `__) creates a `Repeating`_ `Sequence`_ that runs until interrupted, restarting from the first command each time the last command finishes. - -Parallel -^^^^^^^^ +### Parallel There are three types of parallel compositions, differing based on when the composition finishes: -- The ``Parallel`` factory (`Java `__, `C++ `__), backed by the ``ParallelCommandGroup`` class (`Java `__, `C++ `__), constructs a parallel composition that finishes when all members finish. The ``alongWith`` decorator (`Java `__, `C++ `__) does the same in infix notation. -- The ``Race`` factory (`Java `__, `C++ `__), backed by the ``ParallelRaceGroup`` class (`Java `__, `C++ `__), constructs a parallel composition that finishes as soon as any member finishes; all other members are interrupted at that point. The ``raceWith`` decorator (`Java `__, `C++ `__) does the same in infix notation. -- The ``Deadline`` factory (`Java `__, `C++ `__), ``ParallelDeadlineGroup`` (`Java `__, `C++ `__) finishes when a specific command (the "deadline") ends; all other members still running at that point are interrupted. The ``deadlineWith`` decorator (`Java `__, `C++ `__) does the same in infix notation; the comand the decorator was called on is the deadline. +- The ``Parallel`` factory ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Commands.html#parallel(edu.wpi.first.wpilibj2.command.Command...)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/namespacefrc2_1_1cmd.html#ac98ed0faaf370bde01be52bd631dc4e8), :external:py:func:[Python](commands2.cmd.parallel>`), backed by the ``ParallelCommandGroup`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/ParallelCommandGroup.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_parallel_command_group.html), :external:py:class:[Python](commands2.ParallelCommandGroup>`), constructs a parallel composition that finishes when all members finish. The ``alongWith`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#alongWith(edu.wpi.first.wpilibj2.command.Command...)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#a6b9700cd25277a3ac558d63301985f40), :external:py:meth:`Python `) does the same in infix notation. +- The ``Race`` factory ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Commands.html#race(edu.wpi.first.wpilibj2.command.Command...)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/namespacefrc2_1_1cmd.html#a5253e241cf1e19eddfb79e2311068ac5), :external:py:func:[Python](commands2.cmd.race>`), backed by the ``ParallelRaceGroup`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/ParallelRaceGroup.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_parallel_race_group.html), :external:py:class:[Python](commands2.ParallelRaceGroup>`), constructs a parallel composition that finishes as soon as any member finishes; all other members are interrupted at that point. The ``raceWith`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#raceWith(edu.wpi.first.wpilibj2.command.Command...)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#a4d6c1761cef10bb79a727e43e89643d0), :external:py:meth:`Python `) does the same in infix notation. +- The ``Deadline`` factory ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Commands.html#deadline(edu.wpi.first.wpilibj2.command.Command,edu.wpi.first.wpilibj2.command.Command...)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/namespacefrc2_1_1cmd.html#a91073d40910a70f1e2d02c7ce320196a), :external:py:func:[Python](commands2.cmd.deadline>`), ``ParallelDeadlineGroup`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/ParallelDeadlineGroup.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_parallel_deadline_group.html), :external:py:class:[Python](commands2.ParallelDeadlineGroup>`) finishes when a specific command (the "deadline") ends; all other members still running at that point are interrupted. The ``deadlineWith`` decorator (`Java `) does the same in infix notation; the command the decorator was called on is the deadline. .. tab-set-code:: - .. code-block:: java - - // Will be a parallel command group that ends after three seconds with all three commands running their full duration. + ```java + // Will be a parallel command composition that ends after three seconds with all three commands running their full duration. button.onTrue(Commands.parallel(twoSecCommand, oneSecCommand, threeSecCommand)); - - // Will be a parallel race group that ends after one second with the two and three second commands getting interrupted. + // Will be a parallel race composition that ends after one second with the two and three second commands getting interrupted. button.onTrue(Commands.race(twoSecCommand, oneSecCommand, threeSecCommand)); - - // Will be a parallel deadline group that ends after two seconds (the deadline) with the three second command getting interrupted (one second command already finished). + // Will be a parallel deadline composition that ends after two seconds (the deadline) with the three second command getting interrupted (one second command already finished). button.onTrue(Commands.deadline(twoSecCommand, oneSecCommand, threeSecCommand)); + ``` - .. code-block:: c++ - - // Will be a parallel command group that ends after three seconds with all three commands running their full duration. + ```c++ + // Will be a parallel command composition that ends after three seconds with all three commands running their full duration. button.OnTrue(frc2::cmd::Parallel(std::move(twoSecCommand), std::move(oneSecCommand), std::move(threeSecCommand))); - - // Will be a parallel race group that ends after one second with the two and three second commands getting interrupted. + // Will be a parallel race composition that ends after one second with the two and three second commands getting interrupted. button.OnTrue(frc2::cmd::Race(std::move(twoSecCommand), std::move(oneSecCommand), std::move(threeSecCommand))); - - // Will be a parallel deadline group that ends after two seconds (the deadline) with the three second command getting interrupted (one second command already finished). + // Will be a parallel deadline composition that ends after two seconds (the deadline) with the three second command getting interrupted (one second command already finished). button.OnTrue(frc2::cmd::Deadline(std::move(twoSecCommand), std::move(oneSecCommand), std::move(threeSecCommand))); + ``` -Adding Command End Conditions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ```python + # Will be a parallel command composition that ends after three seconds with all three commands running their full duration. + button.onTrue(commands2.cmd.parallel(twoSecCommand, oneSecCommand, threeSecCommand)) + # Will be a parallel race composition that ends after one second with the two and three second commands getting interrupted. + button.onTrue(commands2.cmd.race(twoSecCommand, oneSecCommand, threeSecCommand)) + # Will be a parallel deadline composition that ends after two seconds (the deadline) with the three second command getting interrupted (one second command already finished). + button.onTrue(commands2.cmd.deadline(twoSecCommand, oneSecCommand, threeSecCommand)) + ``` -The ``until()`` (`Java `__, `C++ `__) decorator composes the command with an additional end condition. Note that the command the decorator was called on will see this end condition as an interruption. +### Adding Command End Conditions -.. tab-set-code:: +The ``until()`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#until(java.util.function.BooleanSupplier)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#a4ffddf195a71e71d80e62df95fffdfcf), :external:py:meth:`Python `) decorator composes the command with an additional end condition. Note that the command the decorator was called on will see this end condition as an interruption. - .. code-block:: java +.. tab-set-code:: - // Will be interrupted if m_limitSwitch.get() returns true - button.onTrue(command.until(m_limitSwitch::get)); + ```java + // Will be interrupted if m_limitSwitch.get() returns true + button.onTrue(command.until(m_limitSwitch::get)); + ``` - .. code-block:: c++ + ```c++ + // Will be interrupted if m_limitSwitch.get() returns true + button.OnTrue(command.Until([&m_limitSwitch] { return m_limitSwitch.Get(); })); + ``` - // Will be interrupted if m_limitSwitch.get() returns true - button.OnTrue(command.Until([&m_limitSwitch] { return m_limitSwitch.Get(); })); + ```python + # Will be interrupted if limitSwitch.get() returns true + button.onTrue(commands2.cmd.until(limitSwitch.get)) + ``` -The ``withTimeout()`` decorator (`Java `__, `C++ `__) is a specialization of ``until`` that uses a timeout as the additional end condition. +The ``withTimeout()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#withTimeout(double)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#ac6b2e1e4f55ed905ec7d189b9288e3d0), :external:py:meth:`Python `) is a specialization of ``until`` that uses a timeout as the additional end condition. .. tab-set-code:: - .. code-block:: java + ```java + // Will time out 5 seconds after being scheduled, and be interrupted + button.onTrue(command.withTimeout(5)); + ``` - // Will time out 5 seconds after being scheduled, and be interrupted - button.onTrue(command.withTimeout(5)); + ```c++ + // Will time out 5 seconds after being scheduled, and be interrupted + button.OnTrue(command.WithTimeout(5.0_s)); + ``` - .. code-block:: c++ + ```python + # Will time out 5 seconds after being scheduled, and be interrupted + button.onTrue(commands2.cmd.withTimeout(5.0)) + ``` - // Will time out 5 seconds after being scheduled, and be interrupted - button.OnTrue(command.WithTimeout(5.0_s)); +### Adding End Behavior -Adding End Behavior -^^^^^^^^^^^^^^^^^^^ +The ``finallyDo()`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#finallyDo(edu.wpi.first.util.function.BooleanConsumer)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#abd0ae6c855d7cf1f1a33cda5575a7b8f), :external:py:meth:`Python `) decorator composes the command with an a lambda that will be called after the command's ``end()`` method, with the same boolean parameter indicating whether the command finished or was interrupted. -The ``finallyDo()`` (`Java `__, `C++ `__) decorator composes the command with an a lambda that will be called after the command's ``end()`` method, with the same boolean parameter indicating whether the command finished or was interrupted. +The ``handleInterrupt()`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#handleInterrupt(java.lang.Runnable)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#a2a5580e71dfe356d2b261efe213f7c67), :external:py:meth:`Python `) decorator composes the command with an a lambda that will be called only when the command is interrupted. -The ``handleInterrupt()`` (`Java `__, `C++ `__) decorator composes the command with an a lambda that will be called only when the command is interrupted. - -Selecting Compositions -^^^^^^^^^^^^^^^^^^^^^^ +### Selecting Compositions Sometimes it's desired to run a command out of a few options based on sensor feedback or other data known only at runtime. This can be useful for determining an auto routine, or running a different command based on whether a game piece is present or not, and so on. -The ``Select`` factory (`Java `__, `C++ `__), backed by the ``SelectCommand`` class (`Java `__, `C++ `__), executes one command from a map, based on a selector function called when scheduled. +The ``Select`` factory ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Commands.html#select(java.util.Map,java.util.function.Supplier)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/namespacefrc2_1_1cmd.html#ae9a028777063223108f78c7a0c4e8746), :external:py:func:[Python](commands2.cmd.select>`), backed by the ``SelectCommand`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/SelectCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_select_command.html), :external:py:class:`Python `), executes one command from a map, based on a selector function called when scheduled. .. tab-set:: - .. tab-item:: Java - :sync: Java - + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/selectcommand/RobotContainer.java - :language: java - :lines: 20-45 - :linenos: - :lineno-start: 20 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/selectcommand/RobotContainer.java + :language: java + :lines: 20-45 + :linenos: + :lineno-start: 20 - .. tab-item:: C++ (Header) - :sync: C++ (Header) + .. tab-item:: C++ (Header) + :sync: C++ (Header) + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/SelectCommand/include/RobotContainer.h + :language: c++ + :lines: 26-43 + :linenos: + :lineno-start: 26 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/SelectCommand/include/RobotContainer.h - :language: c++ - :lines: 24-43 - :linenos: - :lineno-start: 24 - -The ``Either`` factory (`Java `__, `C++ `__), backed by the ``ConditionalCommand`` class (`Java `__, `C++ `__), is a specialization accepting two commands and a boolean selector function. +The ``Either`` factory ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Commands.html#either(edu.wpi.first.wpilibj2.command.Command,edu.wpi.first.wpilibj2.command.Command,java.util.function.BooleanSupplier)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/namespacefrc2_1_1cmd.html#a389d1d0055c3be03a852bfc88aaa2ee5), :external:py:func:[Python](commands2.cmd.either>`), backed by the ``ConditionalCommand`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/ConditionalCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_conditional_command.html), :external:py:class:`Python `), is a specialization accepting two commands and a boolean selector function. .. tab-set-code:: - .. code-block:: java - - // Runs either commandOnTrue or commandOnFalse depending on the value of m_limitSwitch.get() - new ConditionalCommand(commandOnTrue, commandOnFalse, m_limitSwitch::get) + ```java + // Runs either commandOnTrue or commandOnFalse depending on the value of m_limitSwitch.get() + new ConditionalCommand(commandOnTrue, commandOnFalse, m_limitSwitch::get) + ``` - .. code-block:: c++ + ```c++ + // Runs either commandOnTrue or commandOnFalse depending on the value of m_limitSwitch.get() + frc2::ConditionalCommand(commandOnTrue, commandOnFalse, [&m_limitSwitch] { return m_limitSwitch.Get(); }) + ``` - // Runs either commandOnTrue or commandOnFalse depending on the value of m_limitSwitch.get() - frc2::ConditionalCommand(commandOnTrue, commandOnFalse, [&m_limitSwitch] { return m_limitSwitch.Get(); }) + ```python + # Runs either commandOnTrue or commandOnFalse depending on the value of limitSwitch.get() + ConditionalCommand(commandOnTrue, commandOnFalse, limitSwitch.get) + ``` -The ``unless()`` decorator (`Java `__, `C++ `__) composes a command with a condition that will prevent it from running. +The ``unless()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#unless(java.util.function.BooleanSupplier)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#a2be7f65d40f68581104ab1f6a1ba5e93), :external:py:meth:`Python `) composes a command with a condition that will prevent it from running. .. tab-set-code:: - .. code-block:: java + ```java + // Command will only run if the intake is deployed. If the intake gets deployed while the command is running, the command will not stop running + button.onTrue(command.unless(() -> !intake.isDeployed())); + ``` - // Command will only run if the intake is deployed. If the intake gets deployed while the command is running, the command will not stop running - button.onTrue(command.unless(() -> !intake.isDeployed())); + ```c++ + // Command will only run if the intake is deployed. If the intake gets deployed while the command is running, the command will not stop running + button.OnTrue(command.Unless([&intake] { return !intake.IsDeployed(); })); + ``` - .. code-block:: c++ + ```python + # Command will only run if the intake is deployed. If the intake gets deployed while the command is running, the command will not stop running + button.onTrue(command.unless(lambda: not intake.isDeployed())) + ``` - // Command will only run if the intake is deployed. If the intake gets deployed while the command is running, the command will not stop running - button.OnTrue(command.Unless([&intake] { return !intake.IsDeployed(); })); +``ProxyCommand`` described below also has a constructor overload ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/ProxyCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_proxy_command.html), :external:py:class:`Python `) that calls a command-returning lambda at schedule-time and runs the returned command by proxy. -``ProxyCommand`` described below also has a constructor overload (`Java `__, `C++ `__) that calls a command-returning lambda at schedule-time and runs the returned command by proxy. +### Scheduling Other Commands -Scheduling Other Commands -^^^^^^^^^^^^^^^^^^^^^^^^^ +By default, composition members are run through the command composition, and are never themselves seen by the scheduler. Accordingly, their requirements are added to the composition's requirements. While this is usually fine, sometimes it is undesirable for the entire command composition to gain the requirements of a single command. A good solution is to "fork off" from the command composition and schedule that command separately. However, this requires synchronization between the composition and the individually-scheduled command. -By default, composition members are run through the command composition, and are never themselves seen by the scheduler. Accordingly, their requirements are added to the group's requirements. While this is usually fine, sometimes it is undesirable for the entire command group to gain the requirements of a single command. A good solution is to "fork off" from the command group and schedule that command separately. However, this requires synchronization between the composition and the individually-scheduled command. +``ProxyCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/ProxyCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_proxy_command.html), :external:py:class:[Python](commands2.ProxyCommand>`), also creatable using the ``.asProxy()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#asProxy()), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_ptr.html#aa45784053431393e3277e5bc5ae7f751), :external:py:meth:`Python `), schedules a command "by proxy": the command is scheduled when the proxy is scheduled, and the proxy finishes when the command finishes. In the case of "forking off" from a command composition, this allows the composition to track the command's progress without it being in the composition. -``ProxyCommand`` (`Java `__, `C++ `__), also creatable using the ``.asProxy()`` decorator (`Java `__, `C++ `__), schedules a command "by proxy": the command is scheduled when the proxy is scheduled, and the proxy finishes when the command finishes. In the case of "forking off" from a command composition, this allows the group to track the command's progress without it being in the composition. -.. tab-set-code:: - - .. code-block:: java +Command compositions inherit the union of their compoments' requirements and requirements are immutable. Therefore, a ``SequentialCommandGroup`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/SequentialCommandGroup.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_sequential_command_group.html), :external:py:class:`Python `) that intakes a game piece, indexes it, aims a shooter, and shoots it would reserve all three subsystems (the intake, indexer, and shooter), precluding any of those subsystems from performing other operations in their "downtime". If this is not desired, the subsystems that should only be reserved for the composition while they are actively being used by it should have their commands proxied. - // The sequence continues only after the proxied command ends - Commands.waitSeconds(5.0).asProxy() - .andThen(Commands.print("This will only be printed after the 5-second delay elapses!")) +.. warning:: Do not use ``ProxyCommand`` unless you are sure of what you are doing and there is no other way to accomplish your need! Proxying is only intended for use as an escape hatch from command composition requirement unions. - .. code-block:: c++ +.. note:: Because proxied commands still require their subsystem, despite not leaking that requirement to the composition, all of the commands that require a given subsystem must be proxied if one of them is. Otherwise, when the proxied command is scheduled its requirement will conflict with that of the composition, canceling the composition. - // The sequence continues only after the proxied command ends - frc2::cmd::Wait(5.0_s).AsProxy() - .AndThen(frc2::cmd::Print("This will only be printed after the 5-second delay elapses!")) +.. tab-set-code:: -For cases that don't need to track the proxied command, ``ScheduleCommand`` (`Java `__, `C++ `__) schedules a specified command and ends instantly. + ```java + // composition requirements are indexer and shooter, intake still reserved during its command but not afterwards + Commands.sequence( + intake.intakeGamePiece().asProxy(), // we want to let the intake intake another game piece while we are processing this one + indexer.processGamePiece(), + shooter.aimAndShoot() + ); + ``` + + ```c++ + // composition requirements are indexer and shooter, intake still reserved during its command but not afterwards + frc2::cmd::Sequence( + intake.IntakeGamePiece().AsProxy(), // we want to let the intake intake another game piece while we are processing this one + indexer.ProcessGamePiece(), + shooter.AimAndShoot() + ); + ``` + + ```python + # composition requirements are indexer and shooter, intake still reserved during its command but not afterwards + commands2.cmd.sequence( + intake.intakeGamePiece().asProxy(), # we want to let the intake intake another game piece while we are processing this one + indexer.processGamePiece(), + shooter.aimAndShoot() + ) + ``` + +For cases that don't need to track the proxied command, ``ScheduleCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/ScheduleCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_schedule_command.html), :external:py:class:`Python `) schedules a specified command and ends instantly. .. tab-set-code:: - .. code-block:: java + ```java + // ScheduleCommand ends immediately, so the sequence continues + new ScheduleCommand(Commands.waitSeconds(5.0)) + .andThen(Commands.print("This will be printed immediately!")) + ``` - // ScheduleCommand ends immediately, so the sequence continues - new ScheduleCommand(Commands.waitSeconds(5.0)) - .andThen(Commands.print("This will be printed immediately!")) + ```c++ + // ScheduleCommand ends immediately, so the sequence continues + frc2::ScheduleCommand(frc2::cmd::Wait(5.0_s)) + .AndThen(frc2::cmd::Print("This will be printed immediately!")) + ``` - .. code-block:: c++ + ```python + # ScheduleCommand ends immediately, so the sequence continues + ScheduleCommand(commands2.cmd.waitSeconds(5.0)) + .andThen(commands2.cmd.print("This will be printed immediately!")) + ``` - // ScheduleCommand ends immediately, so the sequence continues - frc2::ScheduleCommand(frc2::cmd::Wait(5.0_s)) - .AndThen(frc2::cmd::Print("This will be printed immediately!")) +## Subclassing Compositions -Subclassing Compositions ------------------------- - -Command compositions can also be written as a constructor-only subclass of the most exterior composition type, passing the composition members to the superclass constructor. Consider the following from the Hatch Bot example project (`Java `__, `C++ `__): +Command compositions can also be written as a constructor-only subclass of the most exterior composition type, passing the composition members to the superclass constructor. Consider the following from the Hatch Bot example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional)): .. tab-set:: - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/ComplexAuto.java - :language: java - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Header) - :sync: C++ (Header) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/ComplexAuto.h - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Source) - :sync: C++ (Source) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/ComplexAuto.cpp - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 + .. tab-item:: Java + :sync: Java + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/ComplexAuto.java + :language: java + :lines: 5- + :linenos: + :lineno-start: 5 + + .. tab-item:: C++ (Header) + :sync: C++ (Header) + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/ComplexAuto.h + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 + + .. tab-item:: C++ (Source) + :sync: C++ (Source) + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/ComplexAuto.cpp + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 + + .. tab-item:: Python + :sync: Python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/main/HatchbotTraditional/commands/complexauto.py + :language: python + :lines: 7- + :linenos: + :lineno-start: 5 The advantages and disadvantages of this subclassing approach in comparison to others are discussed in :ref:`docs/software/commandbased/organizing-command-based:Subclassing Command Groups`. diff --git a/source/docs/software/commandbased/command-scheduler.rst b/source/docs/software/commandbased/command-scheduler.rst index ef3114a58e..fadf53458d 100644 --- a/source/docs/software/commandbased/command-scheduler.rst +++ b/source/docs/software/commandbased/command-scheduler.rst @@ -1,12 +1,10 @@ -The Command Scheduler -===================== +# The Command Scheduler -The ``CommandScheduler`` (`Java `__, `C++ `__) is the class responsible for actually running commands. Each iteration (ordinarily once per 20ms), the scheduler polls all registered buttons, schedules commands for execution accordingly, runs the command bodies of all scheduled commands, and ends those commands that have finished or are interrupted. +The ``CommandScheduler`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/CommandScheduler.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_scheduler.html)) is the class responsible for actually running commands. Each iteration (ordinarily once per 20ms), the scheduler polls all registered buttons, schedules commands for execution accordingly, runs the command bodies of all scheduled commands, and ends those commands that have finished or are interrupted. The ``CommandScheduler`` also runs the ``periodic()`` method of each registered ``Subsystem``. -Using the Command Scheduler ---------------------------- +## Using the Command Scheduler The ``CommandScheduler`` is a *singleton*, meaning that it is a globally-accessible class with only one instance. Accordingly, in order to access the scheduler, users must call the ``CommandScheduler.getInstance()`` command. @@ -14,81 +12,77 @@ For the most part, users do not have to call scheduler methods directly - almost However, there is one exception: users *must* call ``CommandScheduler.getInstance().run()`` from the ``robotPeriodic()`` method of their ``Robot`` class. If this is not done, the scheduler will never run, and the command framework will not work. The provided command-based project template has this call already included. -The ``schedule()`` Method -------------------------- +## The ``schedule()`` Method -To schedule a command, users call the ``schedule()`` method (`Java `__, `C++ `__). This method takes a command, and attempts to add it to list of currently-running commands, pending whether it is already running or whether its requirements are available. If it is added, its ``initialize()`` method is called. +To schedule a command, users call the ``schedule()`` method ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/CommandScheduler.html#schedule(edu.wpi.first.wpilibj2.command.Command...)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_scheduler.html#a9f3f7bb5c1a3cf57592fe5cfadb6a57d)). This method takes a command, and attempts to add it to list of currently-running commands, pending whether it is already running or whether its requirements are available. If it is added, its ``initialize()`` method is called. This method walks through the following steps: #. Verifies that the command isn't in a composition. -#. No-op if scheduler is disabled, command is already scheduled, or robot is disabled and command doesn't . +#. :term:`No-op` if scheduler is disabled, command is already scheduled, or robot is disabled and command doesn't :ref:`docs/software/commandbased/commands:runsWhenDisabled`. #. If requirements are in use: + * If all conflicting commands are interruptible, cancel them. * If not, don't schedule the new command. #. Call ``initialize()``. .. tab-set:: - .. tab-item:: Java - :sync: tabcode-java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java - :language: java - :lines: 202-245 - :linenos: - :lineno-start: 202 + .. tab-item:: Java + :sync: tabcode-java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java - :language: java - :lines: 181-191 - :linenos: - :lineno-start: 181 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java + :language: java + :lines: 202-245 + :linenos: + :lineno-start: 202 - .. tab-item:: C++ (Source) - :sync: tabcode-cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java + :language: java + :lines: 181-191 + :linenos: + :lineno-start: 181 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp - :language: c++ - :lines: 114-159 - :linenos: - :lineno-start: 114 + .. tab-item:: C++ (Source) + :sync: tabcode-c++ + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp + :language: c++ + :lines: 114-159 + :linenos: + :lineno-start: 114 -The Scheduler Run Sequence --------------------------- +## The Scheduler Run Sequence .. note:: The ``initialize()`` method of each ``Command`` is called when the command is scheduled, which is not necessarily when the scheduler runs (unless that command is bound to a button). -What does a single iteration of the scheduler's ``run()`` method (`Java `__, `C++ `__) actually do? The following section walks through the logic of a scheduler iteration. For the full implementation, see the source code (`Java `__, `C++ `__). +What does a single iteration of the scheduler's ``run()`` method ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/CommandScheduler.html#run()), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_scheduler.html#aa5000fa52e320da7ba72c196f34aa0f5)) actually do? The following section walks through the logic of a scheduler iteration. For the full implementation, see the source code ([Java](https://github.com/wpilibsuite/allwpilib/blob/v2024.3.2/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java#L252-L331), [C++](https://github.com/wpilibsuite/allwpilib/blob/v2024.3.2/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp#L173-L253)). -Step 1: Run Subsystem Periodic Methods -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Step 1: Run Subsystem Periodic Methods First, the scheduler runs the ``periodic()`` method of each registered ``Subsystem``. In simulation, each subsystem's ``simulationPeriodic()`` method is called as well. .. tab-set:: - .. tab-item:: Java - :sync: tabcode-java + .. tab-item:: Java + :sync: tabcode-java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java - :language: java - :lines: 278-285 - :linenos: - :lineno-start: 278 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java + :language: java + :lines: 278-285 + :linenos: + :lineno-start: 278 - .. tab-item:: C++ (Source) - :sync: tabcode-cpp + .. tab-item:: C++ (Source) + :sync: tabcode-c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp - :language: c++ - :lines: 183-190 - :linenos: - :lineno-start: 183 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp + :language: c++ + :lines: 183-190 + :linenos: + :lineno-start: 183 -Step 2: Poll Command Scheduling Triggers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Step 2: Poll Command Scheduling Triggers .. note:: For more information on how trigger bindings work, see :doc:`binding-commands-to-triggers` @@ -96,26 +90,25 @@ Secondly, the scheduler polls the state of all registered triggers to see if any .. tab-set:: - .. tab-item:: Java - :sync: tabcode-java + .. tab-item:: Java + :sync: tabcode-java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java - :language: java - :lines: 290-292 - :linenos: - :lineno-start: 290 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java + :language: java + :lines: 290-292 + :linenos: + :lineno-start: 290 - .. tab-item:: C++ (Source) - :sync: tabcode-cpp + .. tab-item:: C++ (Source) + :sync: tabcode-c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp - :language: c++ - :lines: 195-197 - :linenos: - :lineno-start: 195 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp + :language: c++ + :lines: 195-197 + :linenos: + :lineno-start: 195 -Step 3: Run/Finish Scheduled Commands -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Step 3: Run/Finish Scheduled Commands Thirdly, the scheduler calls the ``execute()`` method of each currently-scheduled command, and then checks whether the command has finished by calling the ``isFinished()`` method. If the command has finished, the ``end()`` method is also called, and the command is de-scheduled and its required subsystems are freed. @@ -123,89 +116,86 @@ Note that this sequence of calls is done in order for each command - thus, one c .. tab-set:: - .. tab-item:: Java - :sync: tabcode-java + .. tab-item:: Java + :sync: tabcode-java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java - :language: java - :lines: 295-325 - :linenos: - :lineno-start: 295 - :emphasize-lines: 16,21-22 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java + :language: java + :lines: 295-325 + :linenos: + :lineno-start: 295 + :emphasize-lines: 16,21-22 - .. tab-item:: C++ (Source) - :sync: tabcode-cpp + .. tab-item:: C++ (Source) + :sync: tabcode-c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp - :language: c++ - :lines: 201-226 - :linenos: - :lineno-start: 201 - :emphasize-lines: 7,13-14 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp + :language: c++ + :lines: 201-226 + :linenos: + :lineno-start: 201 + :emphasize-lines: 7,13-14 -Step 4: Schedule Default Commands -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Step 4: Schedule Default Commands Finally, any registered ``Subsystem`` has its default command scheduled (if it has one). Note that the ``initialize()`` method of the default command will be called at this time. .. tab-set:: - .. tab-item:: Java - :sync: tabcode-java + .. tab-item:: Java + :sync: tabcode-java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java - :language: java - :lines: 340-346 - :linenos: - :lineno-start: 340 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java + :language: java + :lines: 340-346 + :linenos: + :lineno-start: 340 - .. tab-item:: C++ (Source) - :sync: tabcode-cpp + .. tab-item:: C++ (Source) + :sync: tabcode-c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp - :language: c++ - :lines: 240-246 - :linenos: - :lineno-start: 240 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2023.4.3/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp + :language: c++ + :lines: 240-246 + :linenos: + :lineno-start: 240 -Disabling the Scheduler ------------------------ +## Disabling the Scheduler The scheduler can be disabled by calling ``CommandScheduler.getInstance().disable()``. When disabled, the scheduler's ``schedule()`` and ``run()`` commands will not do anything. The scheduler may be re-enabled by calling ``CommandScheduler.getInstance().enable()``. -Command Event Methods ---------------------- +## Command Event Methods Occasionally, it is desirable to have the scheduler execute a custom action whenever a certain command event (initialization, execution, or ending) occurs. This can be done with the following methods: -- ``onCommandInitialize`` (`Java `__, `C++ `__) runs a specified action whenever a command is initialized. +- ``onCommandInitialize`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/CommandScheduler.html#onCommandInitialize(java.util.function.Consumer)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_scheduler.html#a5f983f0e45b0500c96eebe52780324d4)) runs a specified action whenever a command is initialized. -- ``onCommandExecute`` (`Java `__, `C++ `__) runs a specified action whenever a command is executed. +- ``onCommandExecute`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/CommandScheduler.html#onCommandExecute(java.util.function.Consumer)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_scheduler.html#a58c538f4b8dd95e266e4a99167aa7f99)) runs a specified action whenever a command is executed. -- ``onCommandFinish`` (`Java `__, `C++ `__) runs a specified action whenever a command finishes normally (i.e. the ``isFinished()`` method returned true). +- ``onCommandFinish`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/CommandScheduler.html#onCommandFinish(java.util.function.Consumer)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_scheduler.html#a068e61446afe2341cc0651f0dfd2a55f)) runs a specified action whenever a command finishes normally (i.e. the ``isFinished()`` method returned true). -- ``onCommandInterrupt`` (`Java `__, `C++ `__) runs a specified action whenever a command is interrupted (i.e. by being explicitly canceled or by another command that shares one of its requirements). +- ``onCommandInterrupt`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/CommandScheduler.html#onCommandInterrupt(java.util.function.Consumer)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_scheduler.html#ab5ba99a542aa778a76726d7c68461bf0)) runs a specified action whenever a command is interrupted (i.e. by being explicitly canceled or by another command that shares one of its requirements). -A typical use-case for these methods is adding markers in an event log whenever a command scheduling event takes place, as demonstrated in the following code from the HatchbotInlined example project (`Java `__, `C++ `__): +A typical use-case for these methods is adding markers in an event log whenever a command scheduling event takes place, as demonstrated in the following code from the HatchbotInlined example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/HatchbotInlined)): .. tab-set:: - .. tab-item:: Java - :sync: tabcode-java + .. tab-item:: Java + :sync: tabcode-java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/RobotContainer.java - :language: java - :lines: 73-88 - :linenos: - :lineno-start: 73 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/RobotContainer.java + :language: java + :lines: 73-88 + :linenos: + :lineno-start: 73 - .. tab-item:: C++ (Source) - :sync: tabcode-cpp + .. tab-item:: C++ (Source) + :sync: tabcode-c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-2/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/RobotContainer.cpp - :language: c++ - :lines: 23-47 - :linenos: - :lineno-start: 23 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-2/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/RobotContainer.cpp + :language: c++ + :lines: 23-47 + :linenos: + :lineno-start: 23 diff --git a/source/docs/software/commandbased/commands.rst b/source/docs/software/commandbased/commands.rst index ffc843ddd6..75e5892127 100644 --- a/source/docs/software/commandbased/commands.rst +++ b/source/docs/software/commandbased/commands.rst @@ -1,334 +1,387 @@ -Commands -======== +# Commands -**Commands** represent actions the robot can take. Commands run when scheduled, until they are interrupted or their end condition is met. Commands are represented in the command-based library by the ``Command`` class (`Java `__, `C++ `__). +**Commands** represent actions the robot can take. Commands run when scheduled, until they are interrupted or their end condition is met. Commands are represented in the command-based library by the ``Command`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command.html)) or the ``Command`` class in ``commands2`` library (:external:py:class:`Python `). -The Structure of a Command --------------------------- +## The Structure of a Command Commands specify what the command will do in each of its possible states. This is done by overriding the ``initialize()``, ``execute()``, and ``end()`` methods. Additionally, a command must be able to tell the scheduler when (if ever) it has finished execution - this is done by overriding the ``isFinished()`` method. All of these methods are defaulted to reduce clutter in user code: ``initialize()``, ``execute()``, and ``end()`` are defaulted to simply do nothing, while ``isFinished()`` is defaulted to return false (resulting in a command that never finishes naturally, and will run until interrupted). -Initialization -^^^^^^^^^^^^^^ +### Initialization -The ``initialize()`` method (`Java `__, `C++ `__) marks the command start, and is called exactly once per time a command is scheduled. The ``initialize()`` method should be used to place the command in a known starting state for execution. Command objects may be reused and scheduled multiple times, so any state or resources needed for the command's functionality should be initialized or opened in ``initialize`` (which will be called at the start of each use) rather than the constructor (which is invoked only once on object allocation). It is also useful for performing tasks that only need to be performed once per time scheduled, such as setting motors to run at a constant speed or setting the state of a solenoid actuator. +The ``initialize()`` method ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#initialize()), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command.html#ad3f1971a1b44ecdd4683d766f831bccd), :external:py:meth:`Python `) marks the command start, and is called exactly once per time a command is scheduled. The ``initialize()`` method should be used to place the command in a known starting state for execution. Command objects may be reused and scheduled multiple times, so any state or resources needed for the command's functionality should be initialized or opened in ``initialize`` (which will be called at the start of each use) rather than the constructor (which is invoked only once on object allocation). It is also useful for performing tasks that only need to be performed once per time scheduled, such as setting motors to run at a constant speed or setting the state of a solenoid actuator. -Execution -^^^^^^^^^ +### Execution -The ``execute()`` method (`Java `__, `C++ `__) is called repeatedly while the command is scheduled; this is when the scheduler’s ``run()`` method is called (this is generally done in the main robot periodic method, which runs every 20ms by default). The execute block should be used for any task that needs to be done continually while the command is scheduled, such as updating motor outputs to match joystick inputs, or using the output of a control loop. +The ``execute()`` method ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#execute()), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command.html#a7d7ea1271f7dcc65c0ba3221d179b510), :external:py:meth:`Python `) is called repeatedly while the command is scheduled; this is when the scheduler’s ``run()`` method is called (this is generally done in the main robot periodic method, which runs every 20ms by default). The execute block should be used for any task that needs to be done continually while the command is scheduled, such as updating motor outputs to match joystick inputs, or using the output of a control loop. -Ending -^^^^^^ +### Ending -The ``end(bool interrupted)`` method (`Java `__, `C++ `__) is called once when the command ends, whether it finishes normally (i.e. ``isFinished()`` returned true) or it was interrupted (either by another command or by being explicitly canceled). The method argument specifies the manner in which the command ended; users can use this to differentiate the behavior of their command end accordingly. The end block should be used to "wrap up" command state in a neat way, such as setting motors back to zero or reverting a solenoid actuator to a "default" state. Any state or resources initialized in ``initialize()`` should be closed in ``end()``. +The ``end(bool interrupted)`` method ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#end(boolean)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command.html#a134eda3756f00c667bb5415b23ee920c), :external:py:meth:`Python `) is called once when the command ends, whether it finishes normally (i.e. ``isFinished()`` returned true) or it was interrupted (either by another command or by being explicitly canceled). The method argument specifies the manner in which the command ended; users can use this to differentiate the behavior of their command end accordingly. The end block should be used to "wrap up" command state in a neat way, such as setting motors back to zero or reverting a solenoid actuator to a "default" state. Any state or resources initialized in ``initialize()`` should be closed in ``end()``. -Specifying end conditions -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Specifying end conditions -The ``isFinished()`` method (`Java `__, `C++ `__) is called repeatedly while the command is scheduled, whenever the scheduler’s ``run()`` method is called. As soon as it returns true, the command’s ``end()`` method is called and it ends. The ``isFinished()`` method is called after the ``execute()`` method, so the command will execute once on the same iteration that it ends. +The ``isFinished()`` method ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#end(boolean)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command.html#af5e8c12152d195a4f3c06789366aac88), :external:py:meth:`Python `) is called repeatedly while the command is scheduled, whenever the scheduler’s ``run()`` method is called. As soon as it returns true, the command’s ``end()`` method is called and it ends. The ``isFinished()`` method is called after the ``execute()`` method, so the command will execute once on the same iteration that it ends. -Command Properties ------------------- +## Command Properties In addition to the four lifecycle methods described above, each ``Command`` also has three properties, defined by getter methods that should always return the same value with no side affects. -getRequirements -^^^^^^^^^^^^^^^ +### getRequirements Each command should declare any subsystems it controls as requirements. This backs the scheduler's resource management mechanism, ensuring that no more than one command requires a given subsystem at the same time. This prevents situations such as two different pieces of code attempting to set the same motor controller to different output values. -Declaring requirements is done by overriding the ``getRequirements()`` method in the relevant command class, by calling ``addRequirements()``, or by using the ``requirements`` vararg (Java) / ``Requirements`` struct (C++) parameter at the end of the parameter list of most command constructors and factories in the library: +Declaring requirements is done by overriding the ``getRequirements()`` method in the relevant command class, by calling ``addRequirements()``, or by using the ``requirements`` vararg (Java) / ``Requirements`` struct (C++) parameter / ``requirements`` argument (Python) at the end of the parameter list of most command constructors and factories in the library: .. tab-set-code:: - .. code-block:: java + ```java + Commands.run(intake::activate, intake); + ``` - Commands.run(intake::activate, intake); + ```c++ + frc2::cmd::Run([&intake] { intake.Activate(); }, {&intake}); + ``` - .. code-block:: c++ - - frc2::cmd::Run([&intake] { intake.Activate(); }, {&intake}); + ```python + commands2.cmd.run(intake.activate, intake) + ``` As a rule, command compositions require all subsystems their components require. -runsWhenDisabled -^^^^^^^^^^^^^^^^ +### runsWhenDisabled -The ``runsWhenDisabled()`` method (`Java `__, `C++ `__) returns a ``boolean``/``bool`` specifying whether the command may run when the robot is disabled. With the default of returning ``false``, the command will be canceled when the robot is disabled and attempts to schedule it will do nothing. Returning ``true`` will allow the command to run and be scheduled when the robot is disabled. +The ``runsWhenDisabled()`` method ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#runsWhenDisabled()), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command.html#a5113cbf3655ce8679dd48bf22700b2f4), :external:py:meth:`Python `) returns a ``boolean``/``bool`` specifying whether the command may run when the robot is disabled. With the default of returning ``false``, the command will be canceled when the robot is disabled and attempts to schedule it will do nothing. Returning ``true`` will allow the command to run and be scheduled when the robot is disabled. -.. important:: When the robot is disabled, PWM outputs are disabled and CAN motor controllers may not apply voltage, regardless of ``runsWhenDisabled``! +.. important:: When the robot is disabled, :term:`PWM` outputs are disabled and CAN motor controllers may not apply voltage, regardless of ``runsWhenDisabled``! -This property can be set either by overriding the ``runsWhenDisabled()`` method in the relevant command class, or by using the ``ignoringDisable`` decorator (`Java `__, `C++ `__): +This property can be set either by overriding the ``runsWhenDisabled()`` method in the relevant command class, or by using the ``ignoringDisable`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#ignoringDisable(boolean)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command.html#acc67b15e71a66aafb7523ccdd0a7a834), :external:py:meth:`Python `): .. tab-set-code:: - .. code-block:: java - - Command mayRunDuringDisabled = Commands.run(() -> updateTelemetry()).ignoringDisable(true); + ```java + Command mayRunDuringDisabled = Commands.run(() -> updateTelemetry()).ignoringDisable(true); + ``` - .. code-block:: c++ + ```c++ + frc2::CommandPtr mayRunDuringDisabled = frc2::cmd::Run([] { UpdateTelemetry(); }).IgnoringDisable(true); + ``` - frc2::CommandPtr mayRunDuringDisabled = frc2::cmd::Run([] { UpdateTelemetry(); }).IgnoringDisable(true); + ```python + may_run_during_disabled = commands2.cmd.run(lambda: update_telemetry()).ignoring_disable(True) + ``` As a rule, command compositions may run when disabled if all their component commands set ``runsWhenDisabled`` as ``true``. -getInterruptionBehavior -^^^^^^^^^^^^^^^^^^^^^^^ +### getInterruptionBehavior -The ``getInterruptionBehavior()`` method (`Java `__, `C++ `__) defines what happens if another command sharing a requirement is scheduled while this one is running. In the default behavior, ``kCancelSelf``, the current command will be canceled and the incoming command will be scheduled successfully. If ``kCancelIncoming`` is returned, the incoming command's scheduling will be aborted and this command will continue running. Note that ``getInterruptionBehavior`` only affects resolution of requirement conflicts: all commands can be canceled, regardless of ``getInterruptionBehavior``. +The ``getInterruptionBehavior()`` method ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#getInterruptionBehavior()), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command.html#ab1e027e86fc5c9132914ca566a9845a8), :external:py:meth:`Python `) defines what happens if another command sharing a requirement is scheduled while this one is running. In the default behavior, ``kCancelSelf``, the current command will be canceled and the incoming command will be scheduled successfully. If ``kCancelIncoming`` is returned, the incoming command's scheduling will be aborted and this command will continue running. Note that ``getInterruptionBehavior`` only affects resolution of requirement conflicts: all commands can be canceled, regardless of ``getInterruptionBehavior``. .. note:: This was previously controlled by the ``interruptible`` parameter passed when scheduling a command, and is now a property of the command object. -This property can be set either by overriding the ``getInterruptionBehavior`` method in the relevant command class, or by using the `withInterruptBehavior()` decorator (`Java `__, `C++ `__): +This property can be set either by overriding the ``getInterruptionBehavior`` method in the relevant command class, or by using the `withInterruptBehavior()` decorator ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html#withInterruptBehavior(edu.wpi.first.wpilibj2.command.Command.InterruptionBehavior)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command.html#a6583f966509478a29e7764a72c4bf177), :external:py:meth:`Python `) .. tab-set-code:: - .. code-block:: java + ```java + Command noninteruptible = Commands.run(intake::activate, intake).withInterruptBehavior(Command.InterruptBehavior.kCancelIncoming); + ``` - Command noninteruptible = Commands.run(intake::activate, intake).withInterruptBehavior(Command.InterruptBehavior.kCancelIncoming); + ```c++ + frc2::CommandPtr noninterruptible = frc2::cmd::Run([&intake] { intake.Activate(); }, {&intake}).WithInterruptBehavior(Command::InterruptBehavior::kCancelIncoming); + ``` - .. code-block:: c++ - - frc2::CommandPtr noninterruptible = frc2::cmd::Run([&intake] { intake.Activate(); }, {&intake}).WithInterruptBehavior(Command::InterruptBehavior::kCancelIncoming); + ```python + non_interruptible = commands2.cmd.run(intake.activate, intake).with_interrupt_behavior(Command.InterruptBehavior.kCancelIncoming) + ``` As a rule, command compositions are ``kCancelIncoming`` if all their components are ``kCancelIncoming`` as well. -Included Command Types ----------------------- +## Included Command Types -The command-based library includes many pre-written command types. Through the use of :ref:`lambdas `, these commands can cover almost all use cases and teams should rarely need to write custom command classes. Many of these commands are provided via static factory functions in the ``Commands`` utility class (Java) or in the ``frc2::cmd`` namespace defined in the ``Commands.h`` header (C++). Classes inheriting from ``Subsystem`` also have instance methods that implicitly require ``this``. +The command-based library includes many pre-written command types. Through the use of :ref:`lambdas `, these commands can cover almost all use cases and teams should rarely need to write custom command classes. Many of these commands are provided via static factory functions in the ``Commands`` utility class (Java), in the ``frc2::cmd`` namespace defined in the ``Commands.h`` header (C++), or in the ``commands2.cmd`` namespace (Python). In Java and C++, classes inheriting from ``Subsystem`` also have instance methods that implicitly require ``this``. -Running Actions -^^^^^^^^^^^^^^^ +### Running Actions The most basic commands are actions the robot takes: setting voltage to a motor, changing a solenoid's direction, etc. For these commands, which typically consist of a method call or two, the command-based library offers several factories to be construct commands inline with one or more lambdas to be executed. -The ``runOnce`` factory, backed by the ``InstantCommand`` (`Java `__, `C++ `__) class, creates a command that calls a lambda once, and then finishes. +The ``runOnce`` factory, backed by the ``InstantCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/InstantCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_instant_command.html), :external:py:class:`Python `) class, creates a command that calls a lambda once, and then finishes. .. tab-set:: .. tab-item:: Java :sync: tabcode-java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/HatchSubsystem.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/HatchSubsystem.java :language: java :lines: 25-35 :linenos: :lineno-start: 25 .. tab-item:: C++ (Header) - :sync: tabcode-cpp + :sync: tabcode-c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/HatchSubsystem.h + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/HatchSubsystem.h :language: c++ :lines: 20-28 :linenos: :lineno-start: 20 .. tab-item:: C++ (Source) - :sync: tabcode-cpp-source + :sync: tabcode-c++-source - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/HatchSubsystem.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/HatchSubsystem.cpp :language: c++ :lines: 15-25 :linenos: :lineno-start: 15 -The ``run`` factory, backed by the ``RunCommand`` (`Java `__, `C++ `__) class, creates a command that calls a lambda repeatedly, until interrupted. - -.. tab-set-code:: - - .. code-block:: java - - // A split-stick arcade command, with forward/backward controlled by the left - // hand, and turning controlled by the right. - new RunCommand(() -> m_robotDrive.arcadeDrive( - -driverController.getLeftY(), - driverController.getRightX()), - m_robotDrive) - - .. code-block:: c++ + .. tab-item:: Python + :sync: tabcode-python - // A split-stick arcade command, with forward/backward controlled by the left - // hand, and turning controlled by the right. - frc2::RunCommand( - [this] { - m_drive.ArcadeDrive( - -m_driverController.GetLeftY(), - m_driverController.GetRightX()); - }, - {&m_drive})) + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/main/HatchbotInlined/subsystems/hatchsubsystem.py + :language: python + :lines: 24-34 + :linenos: + :lineno-start: 24 -The ``startEnd`` factory, backed by the ``StartEndCommand`` (`Java `__, `C++ `__) class, calls one lambda when scheduled, and then a second lambda when interrupted. +The ``run`` factory, backed by the ``RunCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/RunCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_run_command.html), :external:py:class:`Python `) class, creates a command that calls a lambda repeatedly, until interrupted. .. tab-set-code:: - .. code-block:: java - - Commands.startEnd( - // Start a flywheel spinning at 50% power - () -> m_shooter.shooterSpeed(0.5), - // Stop the flywheel at the end of the command - () -> m_shooter.shooterSpeed(0.0), - // Requires the shooter subsystem - m_shooter - ) + ```java + // A split-stick arcade command, with forward/backward controlled by the left + // hand, and turning controlled by the right. + new RunCommand(() -> m_robotDrive.arcadeDrive( + -driverController.getLeftY(), + driverController.getRightX()), + m_robotDrive) + ``` + + ```c++ + // A split-stick arcade command, with forward/backward controlled by the left + // hand, and turning controlled by the right. + frc2::RunCommand( + [this] { + m_drive.ArcadeDrive( + -m_driverController.GetLeftY(), + m_driverController.GetRightX()); + }, + {&m_drive})) + ``` + + ```python + # A split-stick arcade command, with forward/backward controlled by the left + # hand, and turning controlled by the right. + commands2.cmd.run(lambda: robot_drive.arcade_drive( + -driver_controller.get_left_y(), + driver_controller.get_right_x()), + robot_drive) + ``` + +The ``startEnd`` factory, backed by the ``StartEndCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/StartEndCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_start_end_command.html), :external:py:class:`Python `) class, calls one lambda when scheduled, and then a second lambda when interrupted. - .. code-block:: c++ +.. tab-set-code:: - frc2::cmd::StartEnd( + ```java + Commands.startEnd( // Start a flywheel spinning at 50% power - [this] { m_shooter.shooterSpeed(0.5); }, + () -> m_shooter.shooterSpeed(0.5), // Stop the flywheel at the end of the command - [this] { m_shooter.shooterSpeed(0.0); }, + () -> m_shooter.shooterSpeed(0.0), // Requires the shooter subsystem - {&m_shooter} - ) - -``FunctionalCommand`` (`Java `__, `C++ `__) accepts four lambdas that constitute the four command lifecycle methods: a ``Runnable``/``std::function`` for each of ``initialize()`` and ``execute()``, a ``BooleanConsumer``/``std::function`` for ``end()``, and a ``BooleanSupplier``/``std::function`` for ``isFinished()``. + m_shooter + ) + ``` + + ```c++ + frc2::cmd::StartEnd( + // Start a flywheel spinning at 50% power + [this] { m_shooter.shooterSpeed(0.5); }, + // Stop the flywheel at the end of the command + [this] { m_shooter.shooterSpeed(0.0); }, + // Requires the shooter subsystem + {&m_shooter} + ) + ``` + + ```python + commands2.cmd.start_end( + # Start a flywheel spinning at 50% power + lambda: shooter.shooter_speed(0.5), + # Stop the flywheel at the end of the command + lambda: shooter.shooter_speed(0.0), + # Requires the shooter subsystem + shooter) + ``` + +``FunctionalCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/FunctionalCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_functional_command.html), :external:py:class:`Python `) accepts four lambdas that constitute the four command lifecycle methods: a ``Runnable``/``std::function/Callable`` for each of ``initialize()`` and ``execute()``, a ``BooleanConsumer``/``std::function/Callable[bool,[]]`` for ``end()``, and a ``BooleanSupplier``/``std::function/Callable[[],bool]`` for ``isFinished()``. .. tab-set-code:: - .. code-block:: java - - new FunctionalCommand( - // Reset encoders on command start - m_robotDrive::resetEncoders, - // Start driving forward at the start of the command - () -> m_robotDrive.arcadeDrive(kAutoDriveSpeed, 0), - // Stop driving at the end of the command - interrupted -> m_robotDrive.arcadeDrive(0, 0), - // End the command when the robot's driven distance exceeds the desired value - () -> m_robotDrive.getAverageEncoderDistance() >= kAutoDriveDistanceInches, - // Require the drive subsystem - m_robotDrive - ) - - .. code-block:: c++ - - frc2::FunctionalCommand( + ```java + new FunctionalCommand( // Reset encoders on command start - [this] { m_drive.ResetEncoders(); }, + m_robotDrive::resetEncoders, // Start driving forward at the start of the command - [this] { m_drive.ArcadeDrive(ac::kAutoDriveSpeed, 0); }, + () -> m_robotDrive.arcadeDrive(kAutoDriveSpeed, 0), // Stop driving at the end of the command - [this] (bool interrupted) { m_drive.ArcadeDrive(0, 0); }, + interrupted -> m_robotDrive.arcadeDrive(0, 0), // End the command when the robot's driven distance exceeds the desired value - [this] { return m_drive.GetAverageEncoderDistance() >= kAutoDriveDistanceInches; }, - // Requires the drive subsystem - {&m_drive} - ) - -To print a string and ending immediately, the library offers the ``Commands.print(String)``/``frc2::cmd::Print(std::string_view)`` factory, backed by the ``PrintCommand`` (`Java `__, `C++ `__) subclass of ``InstantCommand``. - -Waiting -^^^^^^^ + () -> m_robotDrive.getAverageEncoderDistance() >= kAutoDriveDistanceInches, + // Require the drive subsystem + m_robotDrive + ) + ``` + + ```c++ + frc2::FunctionalCommand( + // Reset encoders on command start + [this] { m_drive.ResetEncoders(); }, + // Start driving forward at the start of the command + [this] { m_drive.ArcadeDrive(ac::kAutoDriveSpeed, 0); }, + // Stop driving at the end of the command + [this] (bool interrupted) { m_drive.ArcadeDrive(0, 0); }, + // End the command when the robot's driven distance exceeds the desired value + [this] { return m_drive.GetAverageEncoderDistance() >= kAutoDriveDistanceInches; }, + // Requires the drive subsystem + {&m_drive} + ) + .. code-block:: python + commands2.cmd.functional_command( + # Reset encoders on command start + lambda: robot_drive.reset_encoders(), + # Start driving forward at the start of the command + lambda: robot_drive.arcade_drive(ac.kAutoDriveSpeed, 0), + # Stop driving at the end of the command + lambda interrupted: robot_drive.arcade_drive(0, 0), + # End the command when the robot's driven distance exceeds the desired value + lambda: robot_drive.get_average_encoder_distance() >= ac.kAutoDriveDistanceInches, + # Require the drive subsystem + robot_drive) + ``` + +To print a string and ending immediately, the library offers the ``Commands.print(String)``/``frc2::cmd::Print(std::string_view)``/``commands2.cmd.print(String)`` factory, backed by the ``PrintCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/PrintCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_print_command.html), :external:py:class:`Python `) subclass of ``InstantCommand``. + +### Waiting Waiting for a certain condition to happen or adding a delay can be useful to synchronize between different commands in a command composition or between other robot actions. -To wait and end after a specified period of time elapses, the library offers the ``Commands.waitSeconds(double)``/``frc2::cmd::Wait(units::second_t)`` factory, backed by the ``WaitCommand`` (`Java `__, `C++ `__) class. +To wait and end after a specified period of time elapses, the library offers the ``Commands.waitSeconds(double)``/``frc2::cmd::Wait(units::second_t)``/``commands2.cmd.wait(float)`` factory, backed by the ``WaitCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/WaitCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_wait_command.html), :external:py:class:`Python `) class. .. tab-set-code:: - .. code-block:: java + ```java + // Ends 5 seconds after being scheduled + new WaitCommand(5.0) + ``` - // Ends 5 seconds after being scheduled - new WaitCommand(5.0) + ```c++ + // Ends 5 seconds after being scheduled + frc2::WaitCommand(5.0_s) + ``` - .. code-block:: c++ + ```python + # Ends 5 seconds after being scheduled + commands2.cmd.wait(5.0) + ``` - // Ends 5 seconds after being scheduled - frc2::WaitCommand(5.0_s) - -To wait until a certain condition becomes ``true``, the library offers the ``Commands.waitUntil(BooleanSupplier)``/``frc2::cmd::WaitUntil(std::function)`` factory, backed by the ``WaitUntilCommand`` class (`Java `__, `C++ `__). +To wait until a certain condition becomes ``true``, the library offers the ``Commands.waitUntil(BooleanSupplier)``/``frc2::cmd::WaitUntil(std::function)`` factory, backed by the ``WaitUntilCommand`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/WaitUntilCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_wait_until_command.html), :external:py:class:`Python `). .. tab-set-code:: - .. code-block:: java - - // Ends after m_limitSwitch.get() returns true - new WaitUntilCommand(m_limitSwitch::get) + ```java + // Ends after m_limitSwitch.get() returns true + new WaitUntilCommand(m_limitSwitch::get) + ``` - .. code-block:: c++ + ```c++ + // Ends after m_limitSwitch.Get() returns true + frc2::WaitUntilCommand([&m_limitSwitch] { return m_limitSwitch.Get(); }) + ``` - // Ends after m_limitSwitch.Get() returns true - frc2::WaitUntilCommand([&m_limitSwitch] { return m_limitSwitch.Get(); }) + ```python + # Ends after limit_switch.get() returns True + commands2.cmd.wait_until(limit_switch.get) + ``` -Control Algorithm Commands -^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Control Algorithm Commands There are commands for various control setups: -- ``PIDCommand`` uses a PID controller. For more info, see :ref:`docs/software/commandbased/pid-subsystems-commands:PIDCommand`. - -- ``TrapezoidProfileCommand`` tracks a trapezoid motion profile. For more info, see :ref:`docs/software/commandbased/profile-subsystems-commands:TrapezoidProfileCommand`. +- ``TrapezoidProfile`` tracks a trapezoid motion profile. For more info, see :doc:`/docs/software/commandbased/profile-subsystems-commands`. - ``ProfiledPIDCommand`` combines PID control with trapezoid motion profiles. For more info, see :ref:`docs/software/commandbased/profilepid-subsystems-commands:ProfiledPIDCommand`. -- ``MecanumControllerCommand`` (`Java `__, `C++ `__) is useful for controlling mecanum drivetrains. See API docs and the **MecanumControllerCommand** (`Java `__, `C++ `__) example project for more info. +- ``MecanumControllerCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_mecanum_controller_command.html)) is useful for controlling mecanum drivetrains. See API docs and the **MecanumControllerCommand** ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand)) example project for more info. -- ``SwerveControllerCommand`` (`Java `__, `C++ `__) is useful for controlling swerve drivetrains. See API docs and the **SwerveControllerCommand** (`Java `__, `C++ `__) example project for more info. +- ``SwerveControllerCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_swerve_controller_command.html)) is useful for controlling swerve drivetrains. See API docs and the **SwerveControllerCommand** ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand)) example project for more info. -- ``RamseteCommand`` (`Java `__, `C++ `__) is useful for path following with differential drivetrains ("tank drive"). See API docs and the :ref:`Trajectory Tutorial` for more info. +- ``RamseteCommand`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/RamseteCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_ramsete_command.html)) is useful for path following with differential drivetrains ("tank drive"). See API docs and the :ref:`Trajectory Tutorial` for more info. -Custom Command Classes ----------------------- +## Custom Command Classes Users may also write custom command classes. As this is significantly more verbose, it's recommended to use the more concise factories mentioned above. -.. note:: In the C++ API, a :term:`CRTP` is used to allow certain Command methods to work with the object ownership model. Users should always extend the ``CommandHelper`` `class `__ when defining their own command classes, as is shown below. +.. note:: In the C++ API, a :term:`CRTP` is used to allow certain Command methods to work with the object ownership model. Users should always extend the ``CommandHelper`` [class](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_helper.html) when defining their own command classes, as is shown below. -To write a custom command class, subclass the abstract ``Command`` class (`Java `__) or ``CommandHelper`` (`C++ `__), as seen in the command-based template (`Java `__, `C++ `__): +To write a custom command class, subclass the abstract ``Command`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Command.html)) or ``CommandHelper`` ([C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command.html)), as seen in the command-based template ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/ExampleCommand.java), [C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/include/commands/ExampleCommand.h)): .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/ExampleCommand.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/commands/ExampleCommand.java :language: java :lines: 7-24 :linenos: :lineno-start: 7 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/commandbased/include/commands/ExampleCommand.h + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/commandbased/include/commands/ExampleCommand.h :language: c++ :lines: 5-31 :linenos: :lineno-start: 5 -Simple Command Example ----------------------- +## Simple Command Example -What might a functional command look like in practice? As before, below is a simple command from the HatchBot example project (`Java `__, `C++ `__) that uses the ``HatchSubsystem``: +What might a functional command look like in practice? As before, below is a simple command from the HatchBot example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional)) that uses the ``HatchSubsystem``: .. tab-set:: .. tab-item:: Java :sync: tabcode-java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/GrabHatch.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/GrabHatch.java :language: java :lines: 5- :linenos: :lineno-start: 5 .. tab-item:: C++ (Header) - :sync: tabcode-cpp + :sync: tabcode-c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/GrabHatch.h + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/GrabHatch.h :language: c++ :lines: 5- :linenos: :lineno-start: 5 .. tab-item:: C++ (Source) - :sync: tabcode-cpp-source + :sync: tabcode-c++-source - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/GrabHatch.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/GrabHatch.cpp :language: c++ :lines: 5- :linenos: :lineno-start: 5 + .. tab-item:: Python + :sync: tabcode-python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/main/HatchbotTraditional/commands/grabhatch.py + :language: python + :lines: 7- + :linenos: + :lineno-start: 7 + Notice that the hatch subsystem used by the command is passed into the command through the command’s constructor. This is a pattern called :term:`dependency injection`, and allows users to avoid declaring their subsystems as global variables. This is widely accepted as a best-practice - the reasoning behind this is discussed in a :doc:`later section `. Notice also that the above command calls the subsystem method once from initialize, and then immediately ends (as ``isFinished()`` simply returns true). This is typical for commands that toggle the states of subsystems, and as such it would be more succinct to write this command using the factories described above. @@ -340,58 +393,79 @@ What about a more complicated case? Below is a drive command, from the same exam .. tab-item:: Java :sync: tabcode-java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/DefaultDrive.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/commands/DefaultDrive.java :language: java :lines: 5- :linenos: :lineno-start: 5 .. tab-item:: C++ (Header) - :sync: tabcode-cpp + :sync: tabcode-c++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/DefaultDrive.h + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/commands/DefaultDrive.h :language: c++ :lines: 5- :linenos: :lineno-start: 5 .. tab-item:: C++ (Source) - :sync: tabcode-cpp-source + :sync: tabcode-c++-source - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/DefaultDrive.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/commands/DefaultDrive.cpp :language: c++ :lines: 5- :linenos: :lineno-start: 5 + .. tab-item:: Python + :sync: tabcode-python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/main/HatchbotTraditional/commands/defaultdrive.py + :language: python + :lines: 7- + :linenos: + :lineno-start: 7 + And then usage: .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java :language: java :lines: 59-67 :linenos: :lineno-start: 59 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp :language: c++ :lines: 57-60 :linenos: :lineno-start: 57 + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/main/HatchbotTraditional/robotcontainer.py + :language: python + :lines: 65-72 + :linenos: + :lineno-start: 65 + Notice that this command does not override ``isFinished()``, and thus will never end; this is the norm for commands that are intended to be used as default commands. Once more, this command is rather simple and calls the subsystem method only from one place, and as such, could be more concisely written using factories: .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/RobotContainer.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/RobotContainer.java :language: java :lines: 51-60 :linenos: :lineno-start: 51 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/RobotContainer.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/RobotContainer.cpp :language: c++ :lines: 52-58 :linenos: :lineno-start: 52 + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/main/HatchbotInlined/robotcontainer.py + :language: python + :lines: 53-65 + :linenos: + :lineno-start: 53 diff --git a/source/docs/software/commandbased/cpp-command-discussion.rst b/source/docs/software/commandbased/cpp-command-discussion.rst index a8a22ae285..c547bfa807 100644 --- a/source/docs/software/commandbased/cpp-command-discussion.rst +++ b/source/docs/software/commandbased/cpp-command-discussion.rst @@ -1,28 +1,25 @@ -A Technical Discussion on C++ Commands -====================================== +# A Technical Discussion on C++ Commands .. note:: This article assumes that you have a fair understanding of advanced C++ concepts, including templates, smart pointers, inheritance, rvalue references, copy semantics, move semantics, and CRTP. You do not need to understand the information within this article to use the command-based framework in your robot code. This article will help you understand the reasoning behind some of the decisions made in the 2020 command-based framework (such as the use of ``std::unique_ptr``, CRTP in the form of ``CommandHelper``, etc.). You do not need to understand the information within this article to use the command-based framework in your robot code. .. note:: The model was further changed in 2023, as described :ref:`below `. -Ownership Model ---------------- +## Ownership Model The old command-based framework employed the use of raw pointers, meaning that users had to use ``new`` (resulting in manual heap allocations) in their robot code. Since there was no clear indication on who owned the commands (the scheduler, the command groups, or the user themselves), it was not apparent who was supposed to take care of freeing the memory. Several examples in the old command-based framework involved code like this: -.. code-block:: c++ - - #include "PlaceSoda.h" - #include "Elevator.h" - #include "Wrist.h" - - PlaceSoda::PlaceSoda() { - AddSequential(new SetElevatorSetpoint(Elevator::TABLE_HEIGHT)); - AddSequential(new SetWristSetpoint(Wrist::PICKUP)); - AddSequential(new OpenClaw()); - } +```c++ +#include "PlaceSoda.h" +#include "Elevator.h" +#include "Wrist.h" +PlaceSoda::PlaceSoda() { + AddSequential(new SetElevatorSetpoint(Elevator::TABLE_HEIGHT)); + AddSequential(new SetWristSetpoint(Wrist::PICKUP)); + AddSequential(new OpenClaw()); +} +``` In the command-group above, the component commands of the command group were being heap allocated and passed into ``AddSequential`` all in the same line. This meant that user had no reference to that object in memory and therefore had no means of freeing the allocated memory once the command group ended. The command group itself never freed the memory and neither did the command scheduler. This led to memory leaks in robot programs (i.e. memory was allocated on the heap but never freed). @@ -30,137 +27,123 @@ This glaring problem was one of the reasons for the rewrite of the framework. A Default commands are owned by the command scheduler whereas component commands of command compositions are owned by the command composition. Other commands are owned by whatever the user decides they should be owned by (e.g. a subsystem instance or a ``RobotContainer`` instance). This means that the ownership of the memory allocated by any commands or command compositions is clearly defined. -``std::unique_ptr`` vs. ``std::shared_ptr`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### ``std::unique_ptr`` vs. ``std::shared_ptr`` Using ``std::unique_ptr`` allows us to clearly determine who owns the object. Because an ``std::unique_ptr`` cannot be copied, there will never be more than one instance of a ``std::unique_ptr`` that points to the same block of memory on the heap. For example, a constructor for ``SequentialCommandGroup`` takes in a ``std::vector>&&``. This means that it requires an rvalue reference to a vector of ``std::unique_ptr``. Let's go through some example code step-by-step to understand this better: -.. code-block:: c++ - - // Let's create a vector to store our commands that we want to run sequentially. - std::vector> commands; - - // Add an instant command that prints to the console. - commands.emplace_back(std::make_unique([]{ std::cout << "Hello"; }, requirements)); - - // Add some other command: this can be something that a user has created. - commands.emplace_back(std::make_unique(args, needed, for, this, command)); - - // Now the vector "owns" all of these commands. In its current state, when the vector is destroyed (i.e. - // it goes out of scope), it will destroy all of the commands we just added. - - // Let's create a SequentialCommandGroup that will run these two commands sequentially. - auto group = SequentialCommandGroup(std::move(commands)); - - // Note that we MOVED the vector of commands into the sequential command group, meaning that the - // command group now has ownership of our commands. When we call std::move on the vector, all of its - // contents (i.e. the unique_ptr instances) are moved into the command group. - - // Even if the vector were to be destroyed while the command group was running, everything would be OK - // since the vector does not own our commands anymore. +```c++ +// Let's create a vector to store our commands that we want to run sequentially. +std::vector> commands; +// Add an instant command that prints to the console. +commands.emplace_back(std::make_unique([]{ std::cout << "Hello"; }, requirements)); +// Add some other command: this can be something that a user has created. +commands.emplace_back(std::make_unique(args, needed, for, this, command)); +// Now the vector "owns" all of these commands. In its current state, when the vector is destroyed (i.e. +// it goes out of scope), it will destroy all of the commands we just added. +// Let's create a SequentialCommandGroup that will run these two commands sequentially. +auto group = SequentialCommandGroup(std::move(commands)); +// Note that we MOVED the vector of commands into the sequential command group, meaning that the +// command group now has ownership of our commands. When we call std::move on the vector, all of its +// contents (i.e. the unique_ptr instances) are moved into the command group. +// Even if the vector were to be destroyed while the command group was running, everything would be OK +// since the vector does not own our commands anymore. +``` With ``std::shared_ptr``, there is no clear ownership model because there can be multiple instances of a ``std::shared_ptr`` that point to the same block of memory. If commands were in ``std::shared_ptr`` instances, a command group or the command scheduler cannot take ownership and free the memory once the command has finished executing because the user might still unknowingly still have a ``std::shared_ptr`` instance pointing to that block of memory somewhere in scope. -Use of CRTP ------------ +## Use of CRTP You may have noticed that in order to create a new command, you must extend ``CommandHelper``, providing the base class (usually ``frc2::Command``) and the class that you just created. Let's take a look at the reasoning behind this: -Command Decorators -^^^^^^^^^^^^^^^^^^ +### Command Decorators The new command-based framework includes a feature known as "command decorators", which allows the user to something like this: -.. code-block:: c++ - - auto task = MyCommand().AndThen([] { std::cout << "This printed after my command ended."; }, - requirements); +```c++ +auto task = MyCommand().AndThen([] { std::cout << "This printed after my command ended."; }, + requirements); +``` When ``task`` is scheduled, it will first execute ``MyCommand()`` and once that command has finished executing, it will print the message to the console. The way this is achieved internally is by using a sequential command group. Recall from the previous section that in order to construct a sequential command group, we need a vector of unique pointers to each command. Creating the unique pointer for the print function is pretty trivial: -.. code-block:: c++ - - temp.emplace_back( - std::make_unique(std::move(toRun), requirements)); +```c++ +temp.emplace_back( + std::make_unique(std::move(toRun), requirements)); +``` Here ``temp`` is storing the vector of commands that we need to pass into the ``SequentialCommandGroup`` constructor. But before we add that ``InstantCommand``, we need to add ``MyCommand()`` to the ``SequentialCommandGroup``. How do we do that? -.. code-block:: c++ - - temp.emplace_back(std::make_unique(std::move(*this)); +```c++ +temp.emplace_back(std::make_unique(std::move(*this)); +``` You might think it would be this straightforward, but that is not the case. Because this decorator code is in the ``Command`` class, ``*this`` refers to the ``Command`` in the subclass that you are calling the decorator from and has the type of ``Command``. Effectively, you will be trying to move a ``Command`` instead of ``MyCommand``. We could cast the ``this`` pointer to a ``MyCommand*`` and then dereference it but we have no information about the subclass to cast to at compile-time. -Solutions to the Problem -^^^^^^^^^^^^^^^^^^^^^^^^ +### Solutions to the Problem Our initial solution to this was to create a virtual method in ``Command`` called ``TransferOwnership()`` that every subclass of ``Command`` had to override. Such an override would have looked like this: -.. code-block:: c++ - - std::unique_ptr TransferOwnership() && override { - return std::make_unique(std::move(*this)); - } +```c++ +std::unique_ptr TransferOwnership() && override { + return std::make_unique(std::move(*this)); +} +``` Because the code would be in the derived subclass, ``*this`` would actually point to the desired subclass instance and the user has the type info of the derived class to make the unique pointer. After a few days of deliberation, a CRTP method was proposed. Here, an intermediary derived class of ``Command`` called ``CommandHelper`` would exist. ``CommandHelper`` would have two template arguments, the original base class and the desired derived subclass. Let's take a look at a basic implementation of ``CommandHelper`` to understand this: -.. code-block:: c++ - - // In the real implementation, we use SFINAE to check that Base is actually a - // Command or a subclass of Command. - template - class CommandHelper : public Base { - // Here, we are just inheriting all of the superclass (base class) constructors. - using Base::Base; - - // Here, we will override the TransferOwnership() method mentioned above. - std::unique_ptr TransferOwnership() && override { - // Previously, we mentioned that we had no information about the derived class - // to cast to at compile-time, but because of CRTP we do! It's one of our template - // arguments! - return std::make_unique(std::move(*static_cast(this))); - } - }; +```c++ +// In the real implementation, we use SFINAE to check that Base is actually a +// Command or a subclass of Command. +template +class CommandHelper : public Base { + // Here, we are just inheriting all of the superclass (base class) constructors. + using Base::Base; + // Here, we will override the TransferOwnership() method mentioned above. + std::unique_ptr TransferOwnership() && override { + // Previously, we mentioned that we had no information about the derived class + // to cast to at compile-time, but because of CRTP we do! It's one of our template + // arguments! + return std::make_unique(std::move(*static_cast(this))); + } +}; +``` Thus, making your custom commands extend ``CommandHelper`` instead of ``Command`` will automatically implement this boilerplate for you and this is the reasoning behind asking teams to use what may seem to be a rather obscure way of doing things. Going back to our ``AndThen()`` example, we can now do the following: -.. code-block:: c++ - - // Because of how inheritance works, we will call the TransferOwnership() - // of the subclass. We are moving *this because TransferOwnership() can only - // be called on rvalue references. - temp.emplace_back(std::move(*this).TransferOwnership()); +```c++ +// Because of how inheritance works, we will call the TransferOwnership() +// of the subclass. We are moving *this because TransferOwnership() can only +// be called on rvalue references. +temp.emplace_back(std::move(*this).TransferOwnership()); +``` -Lack of Advanced Decorators ---------------------------- +## Lack of Advanced Decorators Most of the C++ decorators take in ``std::function`` instead of actual commands themselves. The idea of taking in actual commands in decorators such as ``AndThen()``, ``BeforeStarting()``, etc. was considered but then abandoned due to a variety of reasons. -Templating Decorators -^^^^^^^^^^^^^^^^^^^^^ +### Templating Decorators Because we need to know the types of the commands that we are adding to a command group at compile-time, we will need to use templates (variadic for multiple commands). However, this might not seem like a big deal. The constructors for command groups do this anyway: -.. code-block:: c++ - - template >...>>> - explicit SequentialCommandGroup(Types&&... commands) { - AddCommands(std::forward(commands)...); - } - - template >...>>> - void AddCommands(Types&&... commands) { - std::vector> foo; - ((void)foo.emplace_back(std::make_unique>( - std::forward(commands))), - ...); - AddCommands(std::move(foo)); - } +```c++ +template >...>>> +explicit SequentialCommandGroup(Types&&... commands) { + AddCommands(std::forward(commands)...); +} +template >...>>> +void AddCommands(Types&&... commands) { + std::vector> foo; + ((void)foo.emplace_back(std::make_unique>( + std::forward(commands))), + ...); + AddCommands(std::move(foo)); +} +``` .. note:: This is a secondary constructor for ``SequentialCommandGroup`` in addition to the vector constructor that we described above. @@ -168,20 +151,17 @@ However, when we make a templated function, its definition must be declared inli We use a forward declaration at the top of ``Command.h``: -.. code-block:: c++ - - class SequentialCommandGroup; - - class Command { ... }; +```c++ +class SequentialCommandGroup; +class Command { ... }; +``` And then we include ``SequentialCommandGroup.h`` in ``Command.cpp``. If these decorator functions were templated however, we cannot write definitions in the ``.cpp`` files, resulting in a circular dependency. -Java vs C++ Syntax -^^^^^^^^^^^^^^^^^^ +### Java vs C++ Syntax These decorators usually save more verbosity in Java (because Java requires raw ``new`` calls) than in C++, so in general, it does not make much of a syntanctic difference in C++ if you create the command group manually in user code. -2023 Updates ------------- +## 2023 Updates After a few years in the new command-based framework, the recommended way to create commands increasingly shifted towards inline commands, decorators, and factory methods. With this paradigm shift, it became evident that the C++ commands model introduced in 2020 and described above has some pain points when used according to the new recommendations. @@ -189,8 +169,7 @@ A significant root cause of most pain points was commands being passed by value Additionally, various decorators weren't supported in C++ due to reasons described :ref:`above `. As long as decorators were rarely used and were mainly to reduce verbosity (where Java was more verbose than C++), this was less of a problem. Once heavy usage of decorators was recommended, this became more of an issue. -``CommandPtr`` -^^^^^^^^^^^^^^ +### ``CommandPtr`` Let's recall the mention of ``std::unique_ptr`` far above: a value type with only move semantics. This is the ownership model we want! @@ -210,9 +189,9 @@ There are multiple ways to get a ``CommandPtr`` instance: - A ``ToPtr()`` method has been added to the CRTP, akin to ``TransferOwnership``. This is useful especially for user-defined command classes, as well as other command classes that don't have factories. -For instance, consider the following from the `HatchbotInlined example project `: +For instance, consider the following from the [HatchbotInlined example project](https://github.com/wpilibsuite/allwpilib/blob/v2023.2.1/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/): -.. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/commands/Autos.cpp +.. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/commands/Autos.cpp :language: c++ :lines: 33-73 :linenos: diff --git a/source/docs/software/commandbased/index.rst b/source/docs/software/commandbased/index.rst index acf476bdbe..7769449950 100644 --- a/source/docs/software/commandbased/index.rst +++ b/source/docs/software/commandbased/index.rst @@ -1,5 +1,4 @@ -Command-Based Programming -========================= +# Command-Based Programming This sequence of articles serves as an introduction to and reference for the WPILib command-based framework. @@ -21,24 +20,20 @@ For a collection of example projects using the command-based framework, see :ref profile-subsystems-commands profilepid-subsystems-commands -Passing Functions As Parameters -------------------------------- +## Passing Functions As Parameters In order to provide a concise inline syntax, the command-based library often accepts functions as parameters of constructors, factories, and decorators. Fortunately, both Java and C++ offer users the ability to :ref:`pass functions as objects `: -Method References (Java) -^^^^^^^^^^^^^^^^^^^^^^^^ +### Method References (Java) -In Java, a reference to a function that can be passed as a parameter is called a method reference. The general syntax for a method reference is ``object::method``. Note that no method parameters are included, since the method *itself* is passed. The method is not being called - it is being passed to another piece of code (in this case, a command) so that *that* code can call it when needed. For further information on method references, see :ref:`docs/software/basic-programming/functions-as-data:Method References`. +In Java, a reference to a function that can be passed as a parameter is called a method reference. The general syntax for a method reference is ``object::method`` or ``Class::staticMethod``. Note that no method parameters are included, since the method *itself* is passed. The method is not being called - it is being passed to another piece of code (in this case, a command) so that *that* code can call it when needed. For further information on method references, see :ref:`docs/software/basic-programming/functions-as-data:Method References`. -Lambda Expressions (Java) -^^^^^^^^^^^^^^^^^^^^^^^^^ +### Lambda Expressions (Java) While method references work well for passing a function that has already been written, often it is inconvenient/wasteful to write a function solely for the purpose of sending as a method reference, if that function will never be used elsewhere. To avoid this, Java also supports a feature called "lambda expressions." A lambda expression is an inline method definition - it allows a function to be defined *inside of a parameter list*. For specifics on how to write Java lambda expressions, see :ref:`docs/software/basic-programming/functions-as-data:Lambda Expressions in Java`. -Lambda Expressions (C++) -^^^^^^^^^^^^^^^^^^^^^^^^ +### Lambda Expressions (C++) -.. warning:: Due to complications in C++ semantics, capturing ``this`` in a C++ lambda can cause a null pointer exception if done from a component command of a command composition. Whenever possible, C++ users should capture relevant command members explicitly and by value. For more details, see `here `__. +.. warning:: Due to complications in C++ semantics, capturing ``this`` in a C++ lambda can cause a null pointer exception if done from a component command of a command composition. Whenever possible, C++ users should capture relevant command members explicitly and by value. For more details, see [here](https://github.com/wpilibsuite/allwpilib/issues/3109). C++ lacks a close equivalent to Java method references - pointers to member functions are generally not directly usable as parameters due to the presence of the implicit ``this`` parameter. However, C++ does offer lambda expressions - in addition, the lambda expressions offered by C++ are in many ways more powerful than those in Java. For specifics on how to write C++ lambda expressions, see :ref:`docs/software/basic-programming/functions-as-data:Lambda Expressions in C++`. diff --git a/source/docs/software/commandbased/organizing-command-based.rst b/source/docs/software/commandbased/organizing-command-based.rst index f93f386015..699022b797 100644 --- a/source/docs/software/commandbased/organizing-command-based.rst +++ b/source/docs/software/commandbased/organizing-command-based.rst @@ -1,5 +1,4 @@ -Organizing Command-Based Robot Projects -======================================= +# Organizing Command-Based Robot Projects As robot code becomes more complicated, navigating, understanding, and maintaining the code takes up more and more time and energy. Making changes to the code often becomes more difficult, sometimes for reasons that have very little to do with the actual complexity of the underlying logic. For a simplified example: putting the logic for many unrelated robot functions into a single 1000-line file makes it difficult to find a specific piece of code within that file, particularly under stress at a competition. But spreading out closely related logic across dozens of tiny files is often just as difficult to navigate. @@ -7,77 +6,67 @@ This is not a problem unique to FRC, and in fact, good organization only becomes This article discusses various facets of command-based robot program design that advanced FRC programmers may want to be aware of when writing code. It is not a prescriptive tutorial, though it presents some recommended best practices. If this level of choice seems daunting, however, many teams have been highly successful while sticking closely to WPILib's example code and guidelines. However, this discussion may be of interest to intermediate and advanced programmers who want to make their code not only effective, but flexible, easily changeable, and sometimes even beautiful. -Why Care About Organization? ----------------------------- +## Why Care About Organization? Good code organization will rarely make or break a team's competitive ability—but it does mean easier debugging, faster modifications, nicer-looking code, and happier programmers. While it's impossible to define "good" organization by way of what the code looks like from the inside, it's easier to define in terms of what the robot's software looks like from the outside. -What Good Organization Looks Like -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### What Good Organization Looks Like When code is well-designed and well-organized, the code's internal structure is intuitive and easily comprehensible. Cumbersome boilerplate is minimized, meaning that new robot functionality can often be added with just a few lines of code. When a constant value (such as the speed of the robot's intake) needs to be changed, it only needs to change in one place. If multiple programmers are working together, they can easily understand each others' work. Bugs are rare, since it is difficult to accidentally introduce unintended behavior (such as creating a command that does not require necessary subsystems). Implementing more advanced functions like unit tests is easier, since the code is abstracted away from the physical hardware. Programmers are happy (most of the time). -What Bad Organization Looks Like -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### What Bad Organization Looks Like Poorly organized code often has internal structure that makes little to no sense, even to whoever wrote it. When functionality has to be added or changed, it often breaks unrelated parts of the robot: adding automatic shooter control might introduce a bug in the climbing sequence for unclear reasons. Alternatively, the organizational framework might be so strict that it's impossible to implement necessary behavior, requiring nasty hacks or workarounds. Many lines of boilerplate code are needed for simple robot logic. Constants are scattered across the codebase, and changing basic behavior often requires making the same change to many different files. Collaboration among multiple programmers is difficult or impossible. -Defining Commands ------------------ +## Defining Commands In larger robot codebases, multiple copies of the same command need to be used in many different places. For instance, a command that runs a robot's intake might be used in teleop, bound to a certain button; as part of a complicated command group for an autonomous routine; and as part of a self-test sequence. As an example, let's look at some ways to define a simple command that simply runs the robot's intake forward at full power until canceled. -Inline Commands -^^^^^^^^^^^^^^^ +### Inline Commands The easiest and most expressive way to do this is with a ``StartEndCommand``: .. tab-set-code:: - .. code-block:: java + ```java + Command runIntake = Commands.startEnd(() -> intake.set(1), () -> intake.set(0), intake); + ``` - Command runIntake = Commands.startEnd(() -> intake.set(1), () -> intake.set(0), intake); - - .. code-block:: c++ - - frc2::CommandPtr runIntake = frc2::cmd::StartEnd([&intake] { intake.Set(1.0); }, [&intake] { intake.Set(0); }, {&intake}); + ```c++ + frc2::CommandPtr runIntake = frc2::cmd::StartEnd([&intake] { intake.Set(1.0); }, [&intake] { intake.Set(0); }, {&intake}); + ``` This is sufficient for commands that are only used once. However, for a command like this that might get used in many different autonomous routines and button bindings, inline commands everywhere means a lot of repetitive code: .. tab-set-code:: - .. code-block:: java - - // RobotContainer.java - intakeButton.whileTrue(Commands.startEnd(() -> intake.set(1.0), () -> intake.set(0), intake)); - - Command intakeAndShoot = Commands.startEnd(() -> intake.set(1.0), () -> intake.set(0), intake) - .alongWith(new RunShooter(shooter)); - - Command autonomousCommand = Commands.sequence( - Commands.startEnd(() -> intake.set(1.0), () -> intake.set(0.0), intake).withTimeout(5.0), - Commands.waitSeconds(3.0), - Commands.startEnd(() -> intake.set(1.0), () -> intake.set(0.0), intake).withTimeout(5.0) - ); - - .. code-block:: c++ - - intakeButton.WhileTrue(frc2::cmd::StartEnd([&intake] { intake.Set(1.0); }, [&intake] { intake.Set(0); }, {&intake})); - - frc2::CommandPtr intakeAndShoot = frc2::cmd::StartEnd([&intake] { intake.Set(1.0); }, [&intake] { intake.Set(0); }, {&intake}) - .AlongWith(RunShooter(&shooter).ToPtr()); - - frc2::CommandPtr autonomousCommand = frc2::cmd::Sequence( - frc2::cmd::StartEnd([&intake] { intake.Set(1.0); }, [&intake] { intake.Set(0); }, {&intake}).WithTimeout(5.0_s), - frc2::cmd::Wait(3.0_s), - frc2::cmd::StartEnd([&intake] { intake.Set(1.0); }, [&intake] { intake.Set(0); }, {&intake}).WithTimeout(5.0_s) - ); + ```java + // RobotContainer.java + intakeButton.whileTrue(Commands.startEnd(() -> intake.set(1.0), () -> intake.set(0), intake)); + Command intakeAndShoot = Commands.startEnd(() -> intake.set(1.0), () -> intake.set(0), intake) + .alongWith(new RunShooter(shooter)); + Command autonomousCommand = Commands.sequence( + Commands.startEnd(() -> intake.set(1.0), () -> intake.set(0.0), intake).withTimeout(5.0), + Commands.waitSeconds(3.0), + Commands.startEnd(() -> intake.set(1.0), () -> intake.set(0.0), intake).withTimeout(5.0) + ); + ``` + + ```c++ + intakeButton.WhileTrue(frc2::cmd::StartEnd([&intake] { intake.Set(1.0); }, [&intake] { intake.Set(0); }, {&intake})); + frc2::CommandPtr intakeAndShoot = frc2::cmd::StartEnd([&intake] { intake.Set(1.0); }, [&intake] { intake.Set(0); }, {&intake}) + .AlongWith(RunShooter(&shooter).ToPtr()); + frc2::CommandPtr autonomousCommand = frc2::cmd::Sequence( + frc2::cmd::StartEnd([&intake] { intake.Set(1.0); }, [&intake] { intake.Set(0); }, {&intake}).WithTimeout(5.0_s), + frc2::cmd::Wait(3.0_s), + frc2::cmd::StartEnd([&intake] { intake.Set(1.0); }, [&intake] { intake.Set(0); }, {&intake}).WithTimeout(5.0_s) + ); + ``` Creating one ``StartEndCommand`` instance and putting it in a variable won't work here, since once an instance of a command is added to a command group it is effectively "owned" by that command group and cannot be used in any other context. -Instance Command Factory Methods -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Instance Command Factory Methods One way to solve this quandary is using the "factory method" design pattern: a function that returns a new object every invocation, according to some specification. Using :ref:`command composition `, a factory method can construct a complex command object with merely a few lines of code. @@ -87,24 +76,23 @@ For example, a command like the intake-running command is conceptually related t .. tab-set-code:: - .. code-block:: java - - public class Intake extends SubsystemBase { - // [code for motor controllers, configuration, etc.] - // ... - - public Command runIntakeCommand() { - // implicitly requires `this` - return this.startEnd(() -> this.set(1.0), () -> this.set(0.0)); - } - } - - .. code-block:: c++ - - frc2::CommandPtr Intake::RunIntakeCommand() { + ```java + public class Intake extends SubsystemBase { + // [code for motor controllers, configuration, etc.] + // ... + public Command runIntakeCommand() { // implicitly requires `this` - return this->StartEnd([this] { this->Set(1.0); }, [this] { this->Set(0); }); + return this.startEnd(() -> this.set(1.0), () -> this.set(0.0)); } + } + ``` + + ```c++ + frc2::CommandPtr Intake::RunIntakeCommand() { + // implicitly requires `this` + return this->StartEnd([this] { this->Set(1.0); }, [this] { this->Set(0); }); + } + ``` Notice how since we are in the ``Intake`` class, we no longer refer to ``intake``; instead, we use the ``this`` keyword to refer to the current instance. @@ -114,68 +102,62 @@ Using this new factory method in command groups and button bindings is highly ex .. tab-set-code:: - .. code-block:: java - - intakeButton.whileTrue(intake.runIntakeCommand()); - - Command intakeAndShoot = intake.runIntakeCommand().alongWith(new RunShooter(shooter)); - - Command autonomousCommand = Commands.sequence( - intake.runIntakeCommand().withTimeout(5.0), - Commands.waitSeconds(3.0), - intake.runIntakeCommand().withTimeout(5.0) - ); - - .. code-block:: c++ - - intakeButton.WhileTrue(intake.RunIntakeCommand()); - - frc2::CommandPtr intakeAndShoot = intake.RunIntakeCommand().AlongWith(RunShooter(&shooter).ToPtr()); - - frc2::CommandPtr autonomousCommand = frc2::cmd::Sequence( - intake.RunIntakeCommand().WithTimeout(5.0_s), - frc2::cmd::Wait(3.0_s), - intake.RunIntakeCommand().WithTimeout(5.0_s) - ); + ```java + intakeButton.whileTrue(intake.runIntakeCommand()); + Command intakeAndShoot = intake.runIntakeCommand().alongWith(new RunShooter(shooter)); + Command autonomousCommand = Commands.sequence( + intake.runIntakeCommand().withTimeout(5.0), + Commands.waitSeconds(3.0), + intake.runIntakeCommand().withTimeout(5.0) + ); + ``` + + ```c++ + intakeButton.WhileTrue(intake.RunIntakeCommand()); + frc2::CommandPtr intakeAndShoot = intake.RunIntakeCommand().AlongWith(RunShooter(&shooter).ToPtr()); + frc2::CommandPtr autonomousCommand = frc2::cmd::Sequence( + intake.RunIntakeCommand().WithTimeout(5.0_s), + frc2::cmd::Wait(3.0_s), + intake.RunIntakeCommand().WithTimeout(5.0_s) + ); + ``` Adding a parameter to the ``runIntakeCommand`` method to provide the exact percentage to run the intake is easy and allows for even more flexibility. .. tab-set-code:: - .. code-block:: java - - public Command runIntakeCommand(double percent) { - return new StartEndCommand(() -> this.set(percent), () -> this.set(0.0), this); - } - - .. code-block:: c++ + ```java + public Command runIntakeCommand(double percent) { + return new StartEndCommand(() -> this.set(percent), () -> this.set(0.0), this); + } + ``` - frc2::CommandPtr Intake::RunIntakeCommand() { - // implicitly requires `this` - return this->StartEnd([this, percent] { this->Set(percent); }, [this] { this->Set(0); }); - } + ```c++ + frc2::CommandPtr Intake::RunIntakeCommand() { + // implicitly requires `this` + return this->StartEnd([this, percent] { this->Set(percent); }, [this] { this->Set(0); }); + } + ``` For instance, this code creates a command group that runs the intake forwards for two seconds, waits for two seconds, and then runs the intake backwards for five seconds. .. tab-set-code:: - .. code-block:: java - - Command intakeRunSequence = intake.runIntakeCommand(1.0).withTimeout(2.0) - .andThen(Commands.waitSeconds(2.0)) - .andThen(intake.runIntakeCommand(-1.0).withTimeout(5.0)); - - .. code-block:: c++ - - frc2::CommandPtr intakeRunSequence = intake.RunIntakeCommand(1.0).WithTimeout(2.0_s) - .AndThen(frc2::cmd::Wait(2.0_s)) - .AndThen(intake.RunIntakeCommand(-1.0).WithTimeout(5.0_s)); + ```java + Command intakeRunSequence = intake.runIntakeCommand(1.0).withTimeout(2.0) + .andThen(Commands.waitSeconds(2.0)) + .andThen(intake.runIntakeCommand(-1.0).withTimeout(5.0)); + ``` + ```c++ + frc2::CommandPtr intakeRunSequence = intake.RunIntakeCommand(1.0).WithTimeout(2.0_s) + .AndThen(frc2::cmd::Wait(2.0_s)) + .AndThen(intake.RunIntakeCommand(-1.0).WithTimeout(5.0_s)); + ``` This approach is recommended for commands that are conceptually related to only a single subsystem, and is very concise. However, it doesn't fare well with commands related to more than one subsystem: passing in other subsystem objects is unintuitive and can cause race conditions and circular dependencies, and thus should be avoided. Therefore, this approach is best suited for single-subsystem commands, and should be used only for those cases. -Static Command Factories -~~~~~~~~~~~~~~~~~~~~~~~~ +#### Static Command Factories Instance factory methods work great for single-subsystem commands. However, complicated robot actions (like the ones often required during the autonomous period) typically need to coordinate multiple subsystems at once. When we want to define an inline command that uses multiple subsystems, it doesn't make sense for the command factory to live in any single one of those subsystems. Instead, it can be cleaner to define the command factory methods statically in some external class: @@ -183,96 +165,86 @@ Instance factory methods work great for single-subsystem commands. However, com .. tab-set-code:: - .. code-block:: java - - public class AutoRoutines { - - public static Command driveAndIntake(Drivetrain drivetrain, Intake intake) { - return Commands.sequence( - Commands.parallel( - drivetrain.driveCommand(0.5, 0.5), - intake.runIntakeCommand(1.0) - ).withTimeout(5.0), - Commands.parallel( - drivetrain.stopCommand(); - intake.stopCommand(); - ) - ); - } - } - - .. code-block:: c++ - - // TODO - -Non-Static Command Factories -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ```java + public class AutoRoutines { + public static Command driveAndIntake(Drivetrain drivetrain, Intake intake) { + return Commands.sequence( + Commands.parallel( + drivetrain.driveCommand(0.5, 0.5), + intake.runIntakeCommand(1.0) + ).withTimeout(5.0), + Commands.parallel( + drivetrain.stopCommand(); + intake.stopCommand(); + ) + ); + } + } + ``` + + ```c++ + // TODO + ``` + +#### Non-Static Command Factories If we want to avoid the verbosity of adding required subsystems as parameters to our factory methods, we can instead construct an instance of our ``AutoRoutines`` class and inject our subsystems through the constructor: .. tab-set-code:: - .. code-block:: java - - public class AutoRoutines { - - private Drivetrain drivetrain; - - private Intake intake; - - public AutoRoutines(Drivetrain drivetrain, Intake intake) { + ```java + public class AutoRoutines { + private Drivetrain drivetrain; + private Intake intake; + public AutoRoutines(Drivetrain drivetrain, Intake intake) { this.drivetrain = drivetrain; this.intake = intake; - } - - public Command driveAndIntake() { - return Commands.sequence( - Commands.parallel( - drivetrain.driveCommand(0.5, 0.5), - intake.runIntakeCommand(1.0) - ).withTimeout(5.0), - Commands.parallel( + } + public Command driveAndIntake() { + return Commands.sequence( + Commands.parallel( + drivetrain.driveCommand(0.5, 0.5), + intake.runIntakeCommand(1.0) + ).withTimeout(5.0), + Commands.parallel( drivetrain.stopCommand(); intake.stopCommand(); - ) - ); - } - - public Command driveThenIntake() { - return Commands.sequence( - drivetrain.driveCommand(0.5, 0.5).withTimeout(5.0), - drivetrain.stopCommand(), - intake.runIntakeCommand(1.0).withTimeout(5.0), - intake.stopCommand() - ); - } - } - - .. code-block:: c++ - - // TODO + ) + ); + } + public Command driveThenIntake() { + return Commands.sequence( + drivetrain.driveCommand(0.5, 0.5).withTimeout(5.0), + drivetrain.stopCommand(), + intake.runIntakeCommand(1.0).withTimeout(5.0), + intake.stopCommand() + ); + } + } + ``` + + ```c++ + // TODO + ``` Then, elsewhere in our code, we can instantiate an single instance of this class and use it to produce several commands: .. tab-set-code:: - .. code-block:: java - - AutoRoutines autoRoutines = new AutoRoutines(this.drivetrain, this.intake); - - Command driveAndIntake = autoRoutines.driveAndIntake(); - Command driveThenIntake = autoRoutines.driveThenIntake(); - - Command drivingAndIntakingSequence = Commands.sequence( - autoRoutines.driveAndIntake(), - autoRoutines.driveThenIntake() - ); - - .. code-block:: c++ + ```java + AutoRoutines autoRoutines = new AutoRoutines(this.drivetrain, this.intake); + Command driveAndIntake = autoRoutines.driveAndIntake(); + Command driveThenIntake = autoRoutines.driveThenIntake(); + Command drivingAndIntakingSequence = Commands.sequence( + autoRoutines.driveAndIntake(), + autoRoutines.driveThenIntake() + ); + ``` - // TODO + ```c++ + // TODO + ``` -Capturing State in Inline Commands -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Capturing State in Inline Commands Inline commands are extremely concise and expressive, but do not offer explicit support for commands that have their own internal state (such as a drivetrain trajectory following command, which may encapsulate an entire controller). This is often accomplished by instead writing a Command class, which will be covered later in this article. @@ -282,99 +254,91 @@ However, it is still possible to ergonomically write a stateful command composit .. tab-set-code:: - .. code-block:: java - - public Command turnToAngle(double targetDegrees) { - // Create a controller for the inline command to capture - PIDController controller = new PIDController(Constants.kTurnToAngleP, 0, 0); - // We can do whatever configuration we want on the created state before returning from the factory - controller.setPositionTolerance(Constants.kTurnToAngleTolerance); - - // Try to turn at a rate proportional to the heading error until we're at the setpoint, then stop - return run(() -> arcadeDrive(0,-controller.calculate(gyro.getHeading(), targetDegrees))) - .until(controller::atSetpoint) - .andThen(runOnce(() -> arcadeDrive(0, 0))); - } - - .. code-block:: c++ - - // TODO + ```java + public Command turnToAngle(double targetDegrees) { + // Create a controller for the inline command to capture + PIDController controller = new PIDController(Constants.kTurnToAngleP, 0, 0); + // We can do whatever configuration we want on the created state before returning from the factory + controller.setPositionTolerance(Constants.kTurnToAngleTolerance); + // Try to turn at a rate proportional to the heading error until we're at the setpoint, then stop + return run(() -> arcadeDrive(0,-controller.calculate(gyro.getHeading(), targetDegrees))) + .until(controller::atSetpoint) + .andThen(runOnce(() -> arcadeDrive(0, 0))); + } + ``` + + ```c++ + // TODO + ``` This pattern works very well in Java so long as the captured state is "effectively final" - i.e., it is never reassigned. This means that we cannot directly define and capture primitive types (e.g. `int`, `double`, `boolean`) - to circumvent this, we need to wrap any state primitives in a mutable container type (the same way `PIDController` wraps its internal `kP`, `kI`, and `kD` values). -Writing Command Classes -^^^^^^^^^^^^^^^^^^^^^^^ +### Writing Command Classes Another possible way to define reusable commands is to write a class that represents the command. This is typically done by subclassing either ``Command`` or one of the ``CommandGroup`` classes. -Subclassing Command -~~~~~~~~~~~~~~~~~~~~~~~ +#### Subclassing Command Returning to our simple intake command from earlier, we could do this by creating a new subclass of ``Command`` that implements the necessary ``initialize`` and ``end`` methods. .. tab-set-code:: - .. code-block:: java - - public class RunIntakeCommand extends Command { - private Intake m_intake; - - public RunIntakeCommand(Intake intake) { - this.m_intake = intake; - addRequirements(intake); - } - - @Override - public void initialize() { - m_intake.set(1.0); - } - - @Override - public void end(boolean interrupted) { - m_intake.set(0.0); - } - - // execute() defaults to do nothing - // isFinished() defaults to return false - } - - .. code-block:: c++ - - // TODO + ```java + public class RunIntakeCommand extends Command { + private Intake m_intake; + public RunIntakeCommand(Intake intake) { + this.m_intake = intake; + addRequirements(intake); + } + @Override + public void initialize() { + m_intake.set(1.0); + } + @Override + public void end(boolean interrupted) { + m_intake.set(0.0); + } + // execute() defaults to do nothing + // isFinished() defaults to return false + } + ``` + + ```c++ + // TODO + ``` This, however, is just as cumbersome as the original repetitive code, if not more verbose. The only two lines that really matter in this entire file are the two calls to ``intake.set()``, yet there are over 20 lines of boilerplate code! Not to mention, doing this for a lot of robot actions quickly clutters up a robot project with dozens of small files. Nevertheless, this might feel more "natural," particularly for programmers who prefer to stick closely to an object-oriented model. This approach should be used for commands with internal state (not subsystem state!), as the class can have fields to manage said state. It may also be more intuitive to write commands with complex logic as classes, especially for those less experienced with command composition. As the command is detached from any specific subsystem class and the required subsystem objects are injected through the constructor, this approach deals well with commands involving multiple subsystems. -Subclassing Command Groups -~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Subclassing Command Groups If we wish to write composite commands as their own classes, we may write a constructor-only subclass of the most exterior group type. For example, an intake-then-outtake sequence (with single-subsystem commands defined as instance factory methods) can look like this: .. tab-set-code:: - .. code-block:: java - - public class IntakeThenOuttake extends SequentialCommandGroup { - public IntakeThenOuttake(Intake intake) { - super( - intake.runIntakeCommand(1.0).withTimeout(2.0), - new WaitCommand(2.0), - intake.runIntakeCommand(-1).withTimeout(5.0) - ); - } - } - .. code-block:: c++ - - // TODO + ```java + public class IntakeThenOuttake extends SequentialCommandGroup { + public IntakeThenOuttake(Intake intake) { + super( + intake.runIntakeCommand(1.0).withTimeout(2.0), + new WaitCommand(2.0), + intake.runIntakeCommand(-1).withTimeout(5.0) + ); + } + } + ``` + + ```c++ + // TODO + ``` This is relatively short and minimizes boilerplate. It is also comfortable to use in a purely object-oriented paradigm and may be more acceptable to novice programmers. However, it has some downsides. For one, it is not immediately clear exactly what type of command group this is from the constructor definition: it is better to define this in a more inline and expressive way, particularly when nested command groups start showing up. Additionally, it requires a new file for every single command group, even when the groups are conceptually related. As with factory methods, state can be defined and captured within the command group subclass constructor, if necessary. -Summary -^^^^^^^ +### Summary .. list-table:: :header-rows: 1 diff --git a/source/docs/software/commandbased/pid-subsystems-commands.rst b/source/docs/software/commandbased/pid-subsystems-commands.rst index 06fa236496..bd5bdd8bf6 100644 --- a/source/docs/software/commandbased/pid-subsystems-commands.rst +++ b/source/docs/software/commandbased/pid-subsystems-commands.rst @@ -1,289 +1,39 @@ .. include:: -PID Control through PIDSubsystems and PIDCommands +PID Control in Command-based ================================================= .. note:: For a description of the WPILib PID control features used by these command-based wrappers, see :ref:`docs/software/advanced-controls/controllers/pidcontroller:PID Control in WPILib`. -One of the most common control algorithms used in FRC\ |reg| is the :term:`PID` controller. WPILib offers its own :ref:`PIDController ` class to help teams implement this functionality on their robots. To further help teams integrate PID control into a command-based robot project, the command-based library includes two convenience wrappers for the ``PIDController`` class: ``PIDSubsystem``, which integrates the PID controller into a subsystem, and ``PIDCommand``, which integrates the PID controller into a command. - -PIDSubsystems -------------- - -The ``PIDSubsystem`` class (`Java `__, `C++ `__) allows users to conveniently create a subsystem with a built-in ``PIDController``. In order to use the ``PIDSubsystem`` class, users must create a subclass of it. - -Creating a PIDSubsystem -^^^^^^^^^^^^^^^^^^^^^^^ - -.. note:: If ``periodic`` is overridden when inheriting from ``PIDSubsystem``, make sure to call ``super.periodic()``! Otherwise, PID functionality will not work properly. - -When subclassing ``PIDSubsystem``, users must override two abstract methods to provide functionality that the class will use in its ordinary operation: - -getMeasurement() -~~~~~~~~~~~~~~~~ - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDSubsystem.java - :language: java - :lines: 84-84 - - .. tab-item:: C++ - :sync: C++ - - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibNewCommands/src/main/native/include/frc2/command/PIDSubsystem.h - :language: cpp - :lines: 80-80 - -The ``getMeasurement`` method returns the current measurement of the process variable. The ``PIDSubsystem`` will automatically call this method from its ``periodic()`` block, and pass its value to the control loop. - -Users should override this method to return whatever sensor reading they wish to use as their process variable measurement. - -useOutput() -~~~~~~~~~~~ +One of the most common control algorithms used in FRC\ |reg| is the :term:`PID` controller. WPILib offers its own :ref:`PIDController ` class to help teams implement this functionality on their robots. The following example is from the RapidReactCommandBot example project (`Java `__, `C++ `__) and shows how PIDControllers can be used within the command-based framework: .. tab-set:: .. tab-item:: Java - :sync: Java + :sync: Java - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDSubsystem.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/subsystems/Shooter.java :language: java - :lines: 77-77 + :lines: 5- + :linenos: + :lineno-start: 5 .. tab-item:: C++ - :sync: C++ - - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibNewCommands/src/main/native/include/frc2/command/PIDSubsystem.h - :language: cpp - :lines: 88-88 - - -The ``useOutput()`` method consumes the output of the PID controller, and the current setpoint (which is often useful for computing a feedforward). The ``PIDSubsystem`` will automatically call this method from its ``periodic()`` block, and pass it the computed output of the control loop. - -Users should override this method to pass the final computed control output to their subsystem's motors. - -Passing In the Controller -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Users must also pass in a ``PIDController`` to the ``PIDSubsystem`` base class through the superclass constructor call of their subclass. This serves to specify the PID gains, as well as the period (if the user is using a non-standard main robot loop period). - -Additional modifications (e.g. enabling continuous input) can be made to the controller in the constructor body by calling ``getController()``. - -Using a PIDSubsystem -^^^^^^^^^^^^^^^^^^^^ - -Once an instance of a ``PIDSubsystem`` subclass has been created, it can be used by commands through the following methods: - -setSetpoint() -~~~~~~~~~~~~~ - -The ``setSetpoint()`` method can be used to set the setpoint of the ``PIDSubsystem``. The subsystem will automatically track to the setpoint using the defined output: - -.. tab-set-code:: - - .. code-block:: java - - // The subsystem will track to a setpoint of 5. - examplePIDSubsystem.setSetpoint(5); - - .. code-block:: c++ - - // The subsystem will track to a setpoint of 5. - examplePIDSubsystem.SetSetpoint(5); - -enable() and disable() -~~~~~~~~~~~~~~~~~~~~~~ - -The ``enable()`` and ``disable()`` methods enable and disable the PID control of the ``PIDSubsystem``. When the subsystem is enabled, it will automatically run the control loop and track the setpoint. When it is disabled, no control is performed. - -Additionally, the ``enable()`` method resets the internal ``PIDController``, and the ``disable()`` method calls the user-defined `useOutput()`_ method with both output and setpoint set to ``0``. - -Full PIDSubsystem Example -^^^^^^^^^^^^^^^^^^^^^^^^^ - -What does a ``PIDSubsystem`` look like when used in practice? The following examples are taken from the FrisbeeBot example project (`Java `__, `C++ `__): - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/ShooterSubsystem.java - :language: java - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ - :sync: C++ (Header) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/ShooterSubsystem.h - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Source) - :sync: C++ (Source) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/Frisbeebot/cpp/subsystems/ShooterSubsystem.cpp - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - -Using a ``PIDSubsystem`` with commands can be very simple: - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/RobotContainer.java - :language: java - :lines: 26-27, 80-87 - - .. tab-item:: C++ - :sync: C++ (Header) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/RobotContainer.h - :language: c++ - :lines: 45-49 - :linenos: - :lineno-start: 45 - - .. tab-item:: C++ (Source) - :sync: C++ (Source) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/Frisbeebot/cpp/RobotContainer.cpp - :language: c++ - :lines: 25-31 - :linenos: - :lineno-start: 25 - -PIDCommand ----------- - -The ``PIDCommand`` class allows users to easily create commands with a built-in PIDController. - -Creating a PIDCommand -^^^^^^^^^^^^^^^^^^^^^ - -A ``PIDCommand`` can be created two ways - by subclassing the ``PIDCommand`` class, or by defining the command :ref:`inline `. Both methods ultimately extremely similar, and ultimately the choice of which to use comes down to where the user desires that the relevant code be located. - -.. note:: If subclassing ``PIDCommand`` and overriding any methods, make sure to call the ``super`` version of those methods! Otherwise, PID functionality will not work properly. - -In either case, a ``PIDCommand`` is created by passing the necessary parameters to its constructor (if defining a subclass, this can be done with a `super()` call): - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/PIDCommand.java - :language: java - :lines: 27-41 - :linenos: - :lineno-start: 27 - - .. tab-item:: C++ - :sync: C++ - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibNewCommands/src/main/native/include/frc2/command/PIDCommand.h - :language: c++ - :lines: 28-42 - :linenos: - :lineno-start: 28 - -controller -~~~~~~~~~~ - -The ``controller`` parameter is the ``PIDController`` object that will be used by the command. By passing this in, users can specify the PID gains and the period for the controller (if the user is using a nonstandard main robot loop period). - -When subclassing ``PIDCommand``, additional modifications (e.g. enabling continuous input) can be made to the controller in the constructor body by calling ``getController()``. - -measurementSource -~~~~~~~~~~~~~~~~~ - -The ``measurementSource`` parameter is a function (usually passed as a :ref:`lambda `) that returns the measurement of the process variable. Passing in the ``measurementSource`` function in ``PIDCommand`` is functionally analogous to overriding the `getMeasurement()`_ function in ``PIDSubsystem``. - -When subclassing ``PIDCommand``, advanced users may further modify the measurement supplier by modifying the class's ``m_measurement`` field. - -setpointSource -~~~~~~~~~~~~~~ - -The ``setpointSource`` parameter is a function (usually passed as a :ref:`lambda `) that returns the current setpoint for the control loop. If only a constant setpoint is needed, an overload exists that takes a constant setpoint rather than a supplier. - -When subclassing ``PIDCommand``, advanced users may further modify the setpoint supplier by modifying the class's ``m_setpoint`` field. - -useOutput -~~~~~~~~~ - -The ``useOutput`` parameter is a function (usually passed as a :ref:`lambda `) that consumes the output and setpoint of the control loop. Passing in the ``useOutput`` function in ``PIDCommand`` is functionally analogous to overriding the `useOutput()`_ function in ``PIDSubsystem``. - -When subclassing ``PIDCommand``, advanced users may further modify the output consumer by modifying the class's ``m_useOutput`` field. - -requirements -~~~~~~~~~~~~ - -Like all inlineable commands, ``PIDCommand`` allows the user to specify its subsystem requirements as a constructor parameter. - -Full PIDCommand Example -^^^^^^^^^^^^^^^^^^^^^^^ - -What does a ``PIDCommand`` look like when used in practice? The following examples are from the GyroDriveCommands example project (`Java `__, `C++ `__): - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/commands/TurnToAngle.java - :language: java - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ - :sync: C++ (Header) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/commands/TurnToAngle.h - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Source) - :sync: C++ (Source) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngle.cpp - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - -And, for an :ref:`inlined ` example: - -.. tab-set:: + :sync: C++ (Header) - .. tab-item:: Java - :sync: Java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/subsystems/Shooter.h + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/RobotContainer.java - :language: java - :lines: 64-79 - :linenos: - :lineno-start: 64 + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. tab-item:: C++ - :sync: C++ + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/cpp/subsystems/Shooter.cpp + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/RobotContainer.cpp - :language: c++ - :lines: 34-49 - :linenos: - :lineno-start: 34 +A ``PIDController`` is declared inside the ``Shooter`` subsystem. It is used by ``ShootCommand`` alongside a feedforward to spin the shooter flywheel to the specified velocity. Once the ``PIDController`` reaches the specified velocity, the ``ShootCommand`` runs the feeder. diff --git a/source/docs/software/commandbased/profile-subsystems-commands.rst b/source/docs/software/commandbased/profile-subsystems-commands.rst index 49b769e30c..cfa21cb119 100644 --- a/source/docs/software/commandbased/profile-subsystems-commands.rst +++ b/source/docs/software/commandbased/profile-subsystems-commands.rst @@ -1,259 +1,45 @@ -Motion Profiling through TrapezoidProfileSubsystems and TrapezoidProfileCommands +Motion Profiling in Command-based ================================================================================ .. note:: For a description of the WPILib motion profiling features used by these command-based wrappers, see :ref:`docs/software/advanced-controls/controllers/trapezoidal-profiles:Trapezoidal Motion Profiles in WPILib`. -.. note:: The ``TrapezoidProfile`` command wrappers are generally intended for composition with custom or external controllers. For combining trapezoidal motion profiling with WPILib's ``PIDController``, see :doc:`profilepid-subsystems-commands`. +.. note:: The ``TrapezoidProfile`` class, used on its own, is most useful when composed with external controllers, such as a "smart" motor controller with a built-in PID functionality. For combining trapezoidal motion profiling with WPILib's ``PIDController``, see :doc:`profilepid-subsystems-commands`. -When controlling a mechanism, is often desirable to move it smoothly between two positions, rather than to abruptly change its setpoint. This is called "motion-profiling," and is supported in WPILib through the ``TrapezoidProfile`` class (`Java `__, `C++ `__). +When controlling a mechanism, is often desirable to move it smoothly between two positions, rather than to abruptly change its setpoint. This is called "motion-profiling," and is supported in WPILib through the ``TrapezoidProfile`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/trajectory/TrapezoidProfile.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc_1_1_trapezoid_profile.html)). -To further help teams integrate motion profiling into their command-based robot projects, WPILib includes two convenience wrappers for the ``TrapezoidProfile`` class: ``TrapezoidProfileSubsystem``, which automatically generates and executes motion profiles in its ``periodic()`` method, and the ``TrapezoidProfileCommand``, which executes a single user-provided ``TrapezoidProfile``. +.. note:: In C++, the ``TrapezoidProfile`` class is templated on the unit type used for distance measurements, which may be angular or linear. The passed-in values *must* have units consistent with the distance units, or a compile-time error will be thrown. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. -TrapezoidProfileSubsystem -------------------------- - -.. note:: In C++, the ``TrapezoidProfileSubsystem`` class is templated on the unit type used for distance measurements, which may be angular or linear. The passed-in values *must* have units consistent with the distance units, or a compile-time error will be thrown. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. - -The ``TrapezoidProfileSubsystem`` class (`Java `__, `C++ `__) will automatically create and execute trapezoidal motion profiles to reach the user-provided goal state. To use the ``TrapezoidProfileSubsystem`` class, users must create a subclass of it. - -Creating a TrapezoidProfileSubsystem -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. note:: If ``periodic`` is overridden when inheriting from ``TrapezoidProfileSubsystem``, make sure to call ``super.periodic()``! Otherwise, motion profiling functionality will not work properly. - -When subclassing ``TrapezoidProfileSubsystem``, users must override a single abstract method to provide functionality that the class will use in its ordinary operation: - -useState() -~~~~~~~~~~ +The following examples are taken from the DriveDistanceOffboard example project (`Java `__, `C++ `__): .. tab-set:: .. tab-item:: Java - :sync: Java + :sync: Java - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2025.0.0-alpha-2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java :language: java - :lines: 105-105 + :lines: 5- + :linenos: + :lineno-start: 5 - .. tab-item:: C++ - :sync: C++ + .. tab-item:: C++ (Header) + :sync: C++ (Header) - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileSubsystem.h + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2025.0.0-alpha-2/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/subsystems/DriveSubsystem.h :language: c++ - :lines: 75-75 - - -The ``useState()`` method consumes the current state of the motion profile. The ``TrapezoidProfileSubsystem`` will automatically call this method from its ``periodic()`` block, and pass it the motion profile state corresponding to the subsystem's current progress through the motion profile. - -Users may do whatever they want with this state; a typical use case (as shown in the `Full TrapezoidProfileSubsystem Example`_) is to use the state to obtain a setpoint and a feedforward for an external "smart" motor controller. - -Constructor Parameters -~~~~~~~~~~~~~~~~~~~~~~ - -Users must pass in a set of ``TrapezoidProfile.Constraints`` to the ``TrapezoidProfileSubsystem`` base class through the superclass constructor call of their subclass. This serves to constrain the automatically-generated profiles to a given maximum velocity and acceleration. - -Users must also pass in an initial position for the mechanism. - -Advanced users may pass in an alternate value for the loop period, if a non-standard main loop period is being used. - -Using a TrapezoidProfileSubsystem -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Once an instance of a ``TrapezoidProfileSubsystem`` subclass has been created, it can be used by commands through the following methods: - -setGoal() -~~~~~~~~~ - -.. note:: If you wish to set the goal to a simple distance with an implicit target velocity of zero, an overload of ``setGoal()`` exists that takes a single distance value, rather than a full motion profile state. - -The ``setGoal()`` method can be used to set the goal state of the ``TrapezoidProfileSubsystem``. The subsystem will automatically execute a profile to the goal, passing the current state at each iteration to the provided `useState()`_ method. - -.. tab-set-code:: - - .. code-block:: java - - // The subsystem will execute a profile to a position of 5 and a velocity of 3. - examplePIDSubsystem.setGoal(new TrapezoidProfile.State(5, 3); - - .. code-block:: c++ - - // The subsystem will execute a profile to a position of 5 meters and a velocity of 3 mps. - examplePIDSubsyste.SetGoal({5_m, 3_mps}); - -enable() and disable() -~~~~~~~~~~~~~~~~~~~~~~ - -The ``enable()`` and ``disable()`` methods enable and disable the motion profiling control of the ``TrapezoidProfileSubsystem``. When the subsystem is enabled, it will automatically run the control loop and call ``useState()`` periodically. When it is disabled, no control is performed. - -Full TrapezoidProfileSubsystem Example -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -What does a ``TrapezoidProfileSubsystem`` look like when used in practice? The following examples are taking from the ArmbotOffobard example project (`Java `__, `C++ `__): - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/ArmSubsystem.java - :language: java - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Header) - :sync: C++ (Header) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/subsystems/ArmSubsystem.h - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Source) - :sync: C++ (Source) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/cpp/subsystems/ArmSubsystem.cpp - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - -Using a ``TrapezoidProfileSubsystem`` with commands can be quite simple: - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/RobotContainer.java - :language: java - :lines: 52-58 - :linenos: - :lineno-start: 52 + :lines: 5- + :linenos: + :lineno-start: 5 - .. tab-item:: C++ - :sync: C++ + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/cpp/RobotContainer.cpp - :language: c++ - :lines: 24-29 - :linenos: - :lineno-start: 24 - -TrapezoidProfileCommand ------------------------ - -.. note:: In C++, the ``TrapezoidProfileCommand`` class is templated on the unit type used for distance measurements, which may be angular or linear. The passed-in values *must* have units consistent with the distance units, or a compile-time error will be thrown. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. - -The ``TrapezoidProfileCommand`` class (`Java `__, `C++ `__) allows users to create a command that will execute a single ``TrapezoidProfile``, passing its current state at each iteration to a user-defined function. - -Creating a TrapezoidProfileCommand -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -A ``TrapezoidProfileCommand`` can be created two ways - by subclassing the ``TrapezoidProfileCommand`` class, or by defining the command :ref:`inline `. Both methods are ultimately extremely similar, and ultimately the choice of which to use comes down to where the user desires that the relevant code be located. - -.. note:: If subclassing ``TrapezoidProfileCommand`` and overriding any methods, make sure to call the ``super`` version of those methods! Otherwise, motion profiling functionality will not work properly. - -In either case, a ``TrapezoidProfileCommand`` is created by passing the necessary parameters to its constructor (if defining a subclass, this can be done with a `super()` call): - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/main/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileCommand.java - :language: java - :lines: 28-43 - :linenos: - :lineno-start: 28 - - .. tab-item:: C++ - :sync: C++ - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/main/wpilibNewCommands/src/main/native/include/frc2/command/TrapezoidProfileCommand.h - :language: c++ - :lines: 35-49 - :linenos: - :lineno-start: 35 - -profile -~~~~~~~ - -The ``profile`` parameter is the ``TrapezoidProfile`` object that will be executed by the command. By passing this in, users specify the motion constraints of the profile that the command will use. - -output -~~~~~~ - -The ``output`` parameter is a function (usually passed as a :ref:`lambda `) that consumes the output and setpoint of the control loop. Passing in the ``useOutput`` function in ``PIDCommand`` is functionally analogous to overriding the `useState()`_ function in ``PIDSubsystem``. - -goal -~~~~ - -The ``goal`` parameter is a function that supplies the desired state of the motion profile. This can be used to change the goal at runtime if desired. - -currentState -~~~~~~~~~~~~ - -The ``currentState`` parameter is a function that supplies the starting state of the motion profile. Combined with ``goal``, this can be used to dynamically generate and follow any motion profile at runtime. - -requirements -~~~~~~~~~~~~ - -Like all inlineable commands, ``TrapezoidProfileCommand`` allows the user to specify its subsystem requirements as a constructor parameter. - -Full TrapezoidProfileCommand Example -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -What does a ``TrapezoidProfileSubsystem`` look like when used in practice? The following examples are taking from the DriveDistanceOffboard example project (`Java `__, `C++ `__): - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/commands/DriveDistanceProfiled.java - :language: java - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Header) - :sync: C++ (Header) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/main/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/commands/DriveDistanceProfiled.h - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Source) - :sync: C++ (Source) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/main/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/commands/DriveDistanceProfiled.cpp - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - -And, for an :ref:`inlined ` example: - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/RobotContainer.java - :language: java - :lines: 66-85 - :linenos: - :lineno-start: 66 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2025.0.0-alpha-2/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/subsystems/DriveSubsystem.cpp + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 - .. tab-item:: C++ - :sync: C++ +There are two commands in this example. They function very similarly, with the main difference being that one resets encoders, and the other doesn't, which allows encoder data to be preserved. - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/main/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/cpp/RobotContainer.cpp - :language: c++ - :lines: 37-60 - :linenos: - :lineno-start: 37 +The subsystem contains a ``TrapezoidProfile`` with a ``Timer``. The timer is used along with a ``kDt`` constant of 0.02 seconds to calculate the current and next states from the ``TrapezoidProfile``. The current state is fed to the "smart" motor controller for PID control, while the current and next state are used to calculate feedforward outputs. Both commands end when ``isFinished(0)`` returns true, which means that the profile has reached the goal state. diff --git a/source/docs/software/commandbased/profilepid-subsystems-commands.rst b/source/docs/software/commandbased/profilepid-subsystems-commands.rst index 695657817b..ed26178c10 100644 --- a/source/docs/software/commandbased/profilepid-subsystems-commands.rst +++ b/source/docs/software/commandbased/profilepid-subsystems-commands.rst @@ -1,176 +1,18 @@ .. include:: -Combining Motion Profiling and PID in Command-Based -=================================================== +# Combining Motion Profiling and PID in Command-Based .. note:: For a description of the WPILib PID control features used by these command-based wrappers, see :ref:`docs/software/advanced-controls/controllers/pidcontroller:PID Control in WPILib`. -A common FRC\ |reg| controls solution is to pair a trapezoidal motion profile for setpoint generation with a PID controller for setpoint tracking. To facilitate this, WPILib includes its own :ref:`ProfiledPIDController ` class. To further aid teams in integrating this functionality into their robots, the command-based framework contains two convenience wrappers for the ``ProfiledPIDController`` class: ``ProfiledPIDSubsystem``, which integrates the controller into a subsystem, and ``ProfiledPIDCommand``, which integrates the controller into a command. +A common FRC\ |reg| controls solution is to pair a trapezoidal motion profile for setpoint generation with a PID controller for setpoint tracking. To facilitate this, WPILib includes its own :ref:`ProfiledPIDController ` class. To further aid teams in integrating this functionality into their robots, the command-based framework contains a convenience wrapper for the ``ProfiledPIDController`` class: ``ProfiledPIDCommand``, which integrates the controller into a command. -ProfiledPIDSubsystem --------------------- - -.. note:: In C++, the ``ProfiledPIDSubsystem`` class is templated on the unit type used for distance measurements, which may be angular or linear. The passed-in values *must* have units consistent with the distance units, or a compile-time error will be thrown. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. - -The ``ProfiledPIDSubsystem`` class (`Java `__, `C++ `__) allows users to conveniently create a subsystem with a built-in PIDController. In order to use the ``ProfiledPIDSubsystem`` class, users must create a subclass of it. - -Creating a ProfiledPIDSubsystem -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. note:: If ``periodic`` is overridden when inheriting from ``ProfiledPIDSubsystem``, make sure to call ``super.periodic()``! Otherwise, control functionality will not work properly. - -When subclassing ``ProfiledPIDSubsystem``, users must override two abstract methods to provide functionality that the class will use in its ordinary operation: - -getMeasurement() -~~~~~~~~~~~~~~~~ - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDSubsystem.java - :language: java - :lines: 85-85 - - .. tab-item:: C++ - :sync: C++ - - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDSubsystem.h - :language: cpp - :lines: 103-103 - -The ``getMeasurement`` method returns the current measurement of the process variable. The ``PIDSubsystem`` will automatically call this method from its ``periodic()`` block, and pass its value to the control loop. - -Users should override this method to return whatever sensor reading they wish to use as their process variable measurement. - -useOutput() -~~~~~~~~~~~ - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDSubsystem.java - :language: java - :lines: 78-78 - - .. tab-item:: C++ - :sync: C++ - - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDSubsystem.h - :language: cpp - :lines: 112-112 - - -The ``useOutput()`` method consumes the output of the Profiled PID controller, and the current setpoint state (which is often useful for computing a feedforward). The ``PIDSubsystem`` will automatically call this method from its ``periodic()`` block, and pass it the computed output of the control loop. - -Users should override this method to pass the final computed control output to their subsystem's motors. - -Passing In the Controller -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Users must also pass in a ``ProfiledPIDController`` to the ``ProfiledPIDSubsystem`` base class through the superclass constructor call of their subclass. This serves to specify the PID gains, the motion profile constraints, and the period (if the user is using a non-standard main robot loop period). - -Additional modifications (e.g. enabling continuous input) can be made to the controller in the constructor body by calling ``getController()``. - -Using a ProfiledPIDSubsystem -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Once an instance of a ``PIDSubsystem`` subclass has been created, it can be used by commands through the following methods: - -setGoal() -~~~~~~~~~ - -.. note:: If you wish to set the goal to a simple distance with an implicit target velocity of zero, an overload of ``setGoal()`` exists that takes a single distance value, rather than a full motion profile state. - -The ``setGoal()`` method can be used to set the setpoint of the ``PIDSubsystem``. The subsystem will automatically track to the setpoint using the defined output: - -.. tab-set-code:: - - .. code-block:: java - - // The subsystem will track to a goal of 5 meters and velocity of 3 meters per second. - examplePIDSubsystem.setGoal(5, 3); - - .. code-block:: c++ - - // The subsystem will track to a goal of 5 meters and velocity of 3 meters per second. - examplePIDSubsystem.SetGoal({5_m, 3_mps}); - -enable() and disable() -~~~~~~~~~~~~~~~~~~~~~~ - -The ``enable()`` and ``disable()`` methods enable and disable the automatic control of the ``ProfiledPIDSubsystem``. When the subsystem is enabled, it will automatically run the motion profile and the control loop and track to the goal. When it is disabled, no control is performed. - -Additionally, the ``enable()`` method resets the internal ``ProfiledPIDController``, and the ``disable()`` method calls the user-defined `useOutput()`_ method with both output and setpoint set to ``0``. - -Full ProfiledPIDSubsystem Example -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -What does a PIDSubsystem look like when used in practice? The following examples are taken from the ArmBot example project (`Java `__, `C++ `__): - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/ArmSubsystem.java - :language: java - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Header) - :sync: C++ (Header) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/ArmSubsystem.h - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Source) - :sync: C++ (Source) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmBot/cpp/subsystems/ArmSubsystem.cpp - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - -Using a ``ProfiledPIDSubsystem`` with commands can be very simple: - -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/RobotContainer.java - :language: java - :lines: 55-64 - :linenos: - :lineno-start: 55 - - .. tab-item:: C++ - :sync: C++ - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/ArmBot/cpp/RobotContainer.cpp - :language: c++ - :lines: 33-39 - :linenos: - :lineno-start: 32 - -ProfiledPIDCommand ------------------- +## ProfiledPIDCommand .. note:: In C++, the ``ProfiledPIDCommand`` class is templated on the unit type used for distance measurements, which may be angular or linear. The passed-in values *must* have units consistent with the distance units, or a compile-time error will be thrown. For more information on C++ units, see :ref:`docs/software/basic-programming/cpp-units:The C++ Units Library`. -The ``ProfiledPIDCommand`` class (`Java `__, `C++ `__) allows users to easily create commands with a built-in ProfiledPIDController. +The ``ProfiledPIDCommand`` class ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_profiled_p_i_d_command.html)) allows users to easily create commands with a built-in ProfiledPIDController. -Creating a PIDCommand -^^^^^^^^^^^^^^^^^^^^^ +### Creating a PIDCommand A ``ProfiledPIDCommand`` can be created two ways - by subclassing the ``ProfiledPIDCommand`` class, or by defining the command :ref:`inline `. Both methods ultimately extremely similar, and ultimately the choice of which to use comes down to where the user desires that the relevant code be located. @@ -180,89 +22,83 @@ In either case, a ``ProfiledPIDCommand`` is created by passing the necessary par .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java - :language: java - :lines: 29-44 - :linenos: - :lineno-start: 29 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-4/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java + :language: java + :lines: 29-44 + :linenos: + :lineno-start: 29 - .. tab-item:: C++ - :sync: C++ + .. tab-item:: C++ + :sync: C++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDCommand.h - :language: c++ - :lines: 38-52 - :linenos: - :lineno-start: 38 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibNewCommands/src/main/native/include/frc2/command/ProfiledPIDCommand.h + :language: c++ + :lines: 38-52 + :linenos: + :lineno-start: 38 -controller -~~~~~~~~~~ +#### controller The ``controller`` parameter is the ``ProfiledPIDController`` object that will be used by the command. By passing this in, users can specify the PID gains, the motion profile constraints, and the period for the controller (if the user is using a nonstandard main robot loop period). When subclassing ``ProfiledPIDCommand``, additional modifications (e.g. enabling continuous input) can be made to the controller in the constructor body by calling ``getController()``. -measurementSource -~~~~~~~~~~~~~~~~~ +#### measurementSource -The ``measurementSource`` parameter is a function (usually passed as a :ref:`lambda `) that returns the measurement of the process variable. Passing in the ``measurementSource`` function in ``ProfiledPIDCommand`` is functionally analogous to overriding the `getMeasurement()`_ function in ``ProfiledPIDSubsystem``. +The ``measurementSource`` parameter is a function (usually passed as a :ref:`lambda `) that returns the measurement of the process variable. When subclassing ``ProfiledPIDCommand``, advanced users may further modify the measurement supplier by modifying the class's ``m_measurement`` field. -goalSource -~~~~~~~~~~ +#### goalSource The ``goalSource`` parameter is a function (usually passed as a :ref:`lambda `) that returns the current goal state for the mechanism. If only a constant goal is needed, an overload exists that takes a constant goal rather than a supplier. Additionally, if goal velocities are desired to be zero, overloads exist that take a constant distance rather than a full profile state. When subclassing ``ProfiledPIDCommand``, advanced users may further modify the setpoint supplier by modifying the class's ``m_goal`` field. -useOutput -~~~~~~~~~ +#### useOutput -The ``useOutput`` parameter is a function (usually passed as a :ref:`lambda `) that consumes the output and setpoint state of the control loop. Passing in the ``useOutput`` function in ``ProfiledPIDCommand`` is functionally analogous to overriding the `useOutput()`_ function in ``ProfiledPIDSubsystem``. +The ``useOutput`` parameter is a function (usually passed as a :ref:`lambda `) that consumes the output and setpoint state of the control loop. When subclassing ``ProfiledPIDCommand``, advanced users may further modify the output consumer by modifying the class's ``m_useOutput`` field. -requirements -~~~~~~~~~~~~ +#### requirements Like all inlineable commands, ``ProfiledPIDCommand`` allows the user to specify its subsystem requirements as a constructor parameter. -Full ProfiledPIDCommand Example -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Full ProfiledPIDCommand Example -What does a ``ProfiledPIDCommand`` look like when used in practice? The following examples are from the GyroDriveCommands example project (`Java `__, `C++ `__): +What does a ``ProfiledPIDCommand`` look like when used in practice? The following examples are from the GyroDriveCommands example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands)): .. tab-set:: - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/commands/TurnToAngleProfiled.java - :language: java - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Header) - :sync: C++ (Header) - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/commands/TurnToAngleProfiled.h - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 - - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngleProfiled.cpp - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/commands/TurnToAngleProfiled.java + :language: java + :lines: 5- + :linenos: + :lineno-start: 5 + + .. tab-item:: C++ (Header) + :sync: C++ (Header) + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/commands/TurnToAngleProfiled.h + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 + + .. tab-item:: C++ (Source) + :sync: C++ (Source) + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/cpp/commands/TurnToAngleProfiled.cpp + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 .. todo:: inlined example? diff --git a/source/docs/software/commandbased/structuring-command-based-project.rst b/source/docs/software/commandbased/structuring-command-based-project.rst index 6136f396fc..f7edaa1c02 100644 --- a/source/docs/software/commandbased/structuring-command-based-project.rst +++ b/source/docs/software/commandbased/structuring-command-based-project.rst @@ -1,9 +1,8 @@ -Structuring a Command-Based Robot Project -========================================= +# Structuring a Command-Based Robot Project While users are free to use the command-based libraries however they like (and advanced users are encouraged to do so), new users may want some guidance on how to structure a basic command-based robot project. -A standard template for a command-based robot project is included in the WPILib examples repository (`Java `__, `C++ `__). This section will walk users through the structure of this template. +A standard template for a command-based robot project is included in the WPILib examples repository ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/templates/commandbased)). This section will walk users through the structure of this template. The root package/directory generally will contain four classes: @@ -11,21 +10,20 @@ The root package/directory generally will contain four classes: The root directory will also contain two sub-packages/sub-directories: ``Subsystems`` contains all user-defined subsystem classes. ``Commands`` contains all user-defined command classes. -Robot ------ +## Robot -As ``Robot`` (`Java `__, `C++ (Header) `__, `C++ (Source) `__) is responsible for the program’s control flow, and command-based is an declarative paradigm designed to minimize the amount of attention the user has to pay to explicit program control flow, the ``Robot`` class of a command-based project should be mostly empty. However, there are a few important things that must be included +As ``Robot`` ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Robot.java), [C++ (Header)](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/include/Robot.h), [C++ (Source)](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/Robot.cpp)) is responsible for the program’s control flow, and command-based is an declarative paradigm designed to minimize the amount of attention the user has to pay to explicit program control flow, the ``Robot`` class of a command-based project should be mostly empty. However, there are a few important things that must be included .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Robot.java - :language: java - :lines: 22-31 - :linenos: - :lineno-start: 22 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Robot.java + :language: java + :lines: 22-31 + :linenos: + :lineno-start: 22 In Java, an instance of ``RobotContainer`` should be constructed during the ``robotInit()`` method - this is important, as most of the declarative robot setup will be called from the ``RobotContainer`` constructor. @@ -33,197 +31,193 @@ In C++, this is not needed as RobotContainer is a value member and will be const .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Robot.java - :language: java - :lines: 33-47 - :linenos: - :lineno-start: 33 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Robot.java + :language: java + :lines: 33-47 + :linenos: + :lineno-start: 33 - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/Robot.cpp - :language: c++ - :lines: 11-22 - :linenos: - :lineno-start: 11 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/Robot.cpp + :language: c++ + :lines: 11-22 + :linenos: + :lineno-start: 11 The inclusion of the ``CommandScheduler.getInstance().run()`` call in the ``robotPeriodic()`` method is essential; without this call, the scheduler will not execute any scheduled commands. Since ``TimedRobot`` runs with a default main loop frequency of 50Hz, this is the frequency with which periodic command and subsystem methods will be called. It is not recommended for new users to call this method from anywhere else in their code. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Robot.java - :language: java - :lines: 56-65 - :linenos: - :lineno-start: 56 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Robot.java + :language: java + :lines: 56-65 + :linenos: + :lineno-start: 56 - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/Robot.cpp - :language: c++ - :lines: 32-42 - :linenos: - :lineno-start: 33 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/Robot.cpp + :language: c++ + :lines: 32-42 + :linenos: + :lineno-start: 33 The ``autonomousInit()`` method schedules an autonomous command returned by the ``RobotContainer`` instance. The logic for selecting which autonomous command to run can be handled inside of ``RobotContainer``. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Robot.java - :language: java - :lines: 71-80 - :linenos: - :lineno-start: 71 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Robot.java + :language: java + :lines: 71-80 + :linenos: + :lineno-start: 71 - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/Robot.cpp - :language: c++ - :lines: 46-55 - :linenos: - :lineno-start: 46 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/Robot.cpp + :language: c++ + :lines: 46-55 + :linenos: + :lineno-start: 46 The ``teleopInit()`` method cancels any still-running autonomous commands. This is generally good practice. Advanced users are free to add additional code to the various init and periodic methods as they see fit; however, it should be noted that including large amounts of imperative robot code in ``Robot.java`` is contrary to the declarative design philosophy of the command-based paradigm, and can result in confusingly-structured/disorganized code. -RobotContainer --------------- +## RobotContainer -This class (`Java `__, `C++ (Header) `__, `C++ (Source) `__) is where most of the setup for your command-based robot will take place. In this class, you will define your robot’s subsystems and commands, bind those commands to triggering events (such as buttons), and specify which command you will run in your autonomous routine. There are a few aspects of this class new users may want explanations for: +This class ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/RobotContainer.java), [C++ (Header)](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/include/RobotContainer.h), [C++ (Source)](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/RobotContainer.cpp)) is where most of the setup for your command-based robot will take place. In this class, you will define your robot’s subsystems and commands, bind those commands to triggering events (such as buttons), and specify which command you will run in your autonomous routine. There are a few aspects of this class new users may want explanations for: .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/RobotContainer.java - :language: java - :lines: 23 - :linenos: - :lineno-start: 23 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/RobotContainer.java + :language: java + :lines: 23 + :linenos: + :lineno-start: 23 - .. tab-item:: C++ (Header) - :sync: C++ (Header) + .. tab-item:: C++ (Header) + :sync: C++ (Header) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/commandbased/include/RobotContainer.h - :language: c++ - :lines: 32 - :linenos: - :lineno-start: 32 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/commandbased/include/RobotContainer.h + :language: c++ + :lines: 32 + :linenos: + :lineno-start: 32 Notice that subsystems are declared as private fields in ``RobotContainer``. This is in stark contrast to the previous incarnation of the command-based framework, but is much more-aligned with agreed-upon object-oriented best-practices. If subsystems are declared as global variables, it allows the user to access them from anywhere in the code. While this can make certain things easier (for example, there would be no need to pass subsystems to commands in order for those commands to access them), it makes the control flow of the program much harder to keep track of as it is not immediately obvious which parts of the code can change or be changed by which other parts of the code. This also circumvents the ability of the resource-management system to do its job, as ease-of-access makes it easy for users to accidentally make conflicting calls to subsystem methods outside of the resource-managed commands. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/RobotContainer.java - :language: java - :lines: 61 - :linenos: - :lineno-start: 61 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/RobotContainer.java + :language: java + :lines: 61 + :linenos: + :lineno-start: 61 - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/RobotContainer.cpp - :language: c++ - :lines: 34 - :linenos: - :lineno-start: 34 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/RobotContainer.cpp + :language: c++ + :lines: 34 + :linenos: + :lineno-start: 34 Since subsystems are declared as private members, they must be explicitly passed to commands (a pattern called "dependency injection") in order for those commands to call methods on them. This is done here with ``ExampleCommand``, which is passed a pointer to an ``ExampleSubsystem``. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/RobotContainer.java - :language: java - :lines: 35-52 - :linenos: - :lineno-start: 35 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/RobotContainer.java + :language: java + :lines: 35-52 + :linenos: + :lineno-start: 35 - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/RobotContainer.cpp - :language: c++ - :lines: 19-30 - :linenos: - :lineno-start: 19 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/RobotContainer.cpp + :language: c++ + :lines: 19-30 + :linenos: + :lineno-start: 19 As mentioned before, the ``RobotContainer()`` constructor is where most of the declarative setup for the robot should take place, including button bindings, configuring autonomous selectors, etc. If the constructor gets too "busy," users are encouraged to migrate code into separate subroutines (such as the ``configureBindings()`` method included by default) which are called from the constructor. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/RobotContainer.java - :language: java - :lines: 54-63 - :linenos: - :lineno-start: 54 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/RobotContainer.java + :language: java + :lines: 54-63 + :linenos: + :lineno-start: 54 - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/RobotContainer.cpp - :language: c++ - :lines: 32-35 - :linenos: - :lineno-start: 32 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/commandbased/cpp/RobotContainer.cpp + :language: c++ + :lines: 32-35 + :linenos: + :lineno-start: 32 Finally, the ``getAutonomousCommand()`` method provides a convenient way for users to send their selected autonomous command to the main ``Robot`` class (which needs access to it to schedule it when autonomous starts). -Constants ---------- +## Constants -The ``Constants`` class (`Java `__, `C++ (Header) `__) (in C++ this is not a class, but simply a header file in which several namespaces are defined) is where globally-accessible robot constants (such as speeds, unit conversion factors, PID gains, and sensor/motor ports) can be stored. It is recommended that users separate these constants into individual inner classes corresponding to subsystems or robot modes, to keep variable names shorter. +The ``Constants`` class ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/Constants.java), [C++ (Header)](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/templates/commandbased/include/Constants.h)) (in C++ this is not a class, but simply a header file in which several namespaces are defined) is where globally-accessible robot constants (such as speeds, unit conversion factors, PID gains, and sensor/motor ports) can be stored. It is recommended that users separate these constants into individual inner classes corresponding to subsystems or robot modes, to keep variable names shorter. In Java, all constants should be declared ``public static final`` so that they are globally accessible and cannot be changed. In C++, all constants should be ``constexpr``. For more illustrative examples of what a ``constants`` class should look like in practice, see those of the various command-based example projects: -* FrisbeeBot (`Java `__, `C++ `__) -* GyroDriveCommands (`Java `__, `C++ `__) -* Hatchbot (`Java `__, `C++ `__) -* RapidReactCommandBot (`Java `__, `C++ `__) +* FrisbeeBot ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/Constants.java), [C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/Constants.h)) +* GyroDriveCommands ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/Constants.java), [C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/Constants.h)) +* Hatchbot ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Constants.java), [C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/Constants.h)) +* RapidReactCommandBot ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/rapidreactcommandbot/Constants.java), [C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/RapidReactCommandBot/include/Constants.h)) In Java, it is recommended that the constants be used from other classes by statically importing the necessary inner class. An ``import static`` statement imports the static namespace of a class into the class in which you are working, so that any ``static`` constants can be referenced directly as if they had been defined in that class. In C++, the same effect can be attained with ``using namespace``: .. tab-set-code:: - .. code-block:: java + ```java + import static edu.wpi.first.wpilibj.templates.commandbased.Constants.OIConstants.*; + ``` - import static edu.wpi.first.wpilibj.templates.commandbased.Constants.OIConstants.*; + ```c++ + using namespace OIConstants; + ``` - .. code-block:: c++ - - using namespace OIConstants; - -Subsystems ----------- +## Subsystems User-defined subsystems should go in this package/directory. -Commands --------- +## Commands User-defined commands should go in this package/directory. diff --git a/source/docs/software/commandbased/subsystems.rst b/source/docs/software/commandbased/subsystems.rst index 286ff950a7..2e602e7bee 100644 --- a/source/docs/software/commandbased/subsystems.rst +++ b/source/docs/software/commandbased/subsystems.rst @@ -1,150 +1,210 @@ -Subsystems -========== +# Subsystems Subsystems are the basic unit of robot organization in the command-based paradigm. A subsystem is an abstraction for a collection of robot hardware that *operates together as a unit*. Subsystems form an :term:`encapsulation` for this hardware, "hiding" it from the rest of the robot code and restricting access to it except through the subsystem’s public methods. Restricting the access in this way provides a single convenient place for code that might otherwise be duplicated in multiple places (such as scaling motor outputs or checking limit switches) if the subsystem internals were exposed. It also allows changes to the specific details of how the subsystem works (the "implementation") to be isolated from the rest of robot code, making it far easier to make substantial changes if/when the design constraints change. Subsystems also serve as the backbone of the ``CommandScheduler``\ ’s resource management system. Commands may declare resource requirements by specifying which subsystems they interact with; the scheduler will never concurrently schedule more than one command that requires a given subsystem. An attempt to schedule a command that requires a subsystem that is already-in-use will either interrupt the currently-running command or be ignored, based on the running command's :ref:`Interruption Behavior `. -Subsystems can be associated with "default commands" that will be automatically scheduled when no other command is currently using the subsystem. This is useful for "background" actions such as controlling the robot drive, keeping an arm held at a setpoint, or stopping motors when the subsystem isn't used. Similar functionality can be achieved in the subsystem’s ``periodic()`` method, which is run once per run of the scheduler; teams should try to be consistent within their codebase about which functionality is achieved through either of these methods. Subsystems are represented in the command-based library by the ``Subsystem`` interface (`Java `__, `C++ `__). +Subsystems can be associated with "default commands" that will be automatically scheduled when no other command is currently using the subsystem. This is useful for "background" actions such as controlling the robot drive, keeping an arm held at a setpoint, or stopping motors when the subsystem isn't used. Similar functionality can be achieved in the subsystem’s ``periodic()`` method, which is run once per run of the scheduler; teams should try to be consistent within their codebase about which functionality is achieved through either of these methods. Subsystems are represented in the command-based library by the ``Subsystem`` interface ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Subsystem.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_subsystem.html), :external:py:class:`Python `). -Creating a Subsystem --------------------- +## Creating a Subsystem -The recommended method to create a subsystem for most users is to subclass the abstract ``SubsystemBase`` class (`Java `__, `C++ `__), as seen in the command-based template (`Java `__, `C++ `__): +The recommended method to create a subsystem for most users is to subclass the abstract ``SubsystemBase`` class in ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/SubsystemBase.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_subsystem_base.html)), as seen in the command-based template ([Java](https://github.com/wpilibsuite/allwpilib/blob/3eb372c25ad6079d6edfbdb4bb099a7bc00e4350/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/subsystems/ExampleSubsystem.java), [C++](https://github.com/wpilibsuite/allwpilib/blob/3eb372c25ad6079d6edfbdb4bb099a7bc00e4350/wpilibcExamples/src/main/cpp/templates/commandbased/include/subsystems/ExampleSubsystem.h)). In Python, because Python does not have interfaces, the ``Subsystem`` class is a concrete class that can be subclassed directly (:external:py:class:`Python `). The following example demonstrates how to create a simple subsystem in each of the supported languages: .. tab-set:: - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/subsystems/ExampleSubsystem.java - :language: java - :lines: 7- - :linenos: - :lineno-start: 7 - - .. tab-item:: C++ - :sync: C++ - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/commandbased/include/subsystems/ExampleSubsystem.h - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 + .. tab-item:: Java + :sync: Java + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/commandbased/subsystems/ExampleSubsystem.java + :language: java + :lines: 7- + :linenos: + :lineno-start: 7 + + .. tab-item:: C++ + :sync: C++ + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibcExamples/src/main/cpp/templates/commandbased/include/subsystems/ExampleSubsystem.h + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 + + .. tab-item:: Python + :sync: Python + + ```python + from commands2 import Command + from commands2 import Subsystem + class ExampleSubsystem(Subsystem): + def __init__(self): + """Creates a new ExampleSubsystem.""" + super().__init__() + def exampleMethodCommand()->Command: + """ + Example command factory method. + :return a command + """ + return self.runOnce( + lambda: # one-time action goes here # + ) + def exampleCondition(self)->bool: + """ + An example method querying a boolean state of the subsystem (for example, a digital sensor). + :return value of some boolean subsystem state, such as a digital sensor. + """ + #Query some boolean state, such as a digital sensor. + return False + def periodic(self): + # This method will be called once per scheduler run + pass + def simulationPeriodic(self): + # This method will be called once per scheduler run during simulation + pass + ``` This class contains a few convenience features on top of the basic ``Subsystem`` interface: it automatically calls the ``register()`` method in its constructor to register the subsystem with the scheduler (this is necessary for the ``periodic()`` method to be called when the scheduler runs), and also implements the ``Sendable`` interface so that it can be sent to the dashboard to display/log relevant status information. Advanced users seeking more flexibility may simply create a class that implements the ``Subsystem`` interface. -Simple Subsystem Example ------------------------- +## Simple Subsystem Example -What might a functional subsystem look like in practice? Below is a simple pneumatically-actuated hatch mechanism from the HatchBotTraditional example project (`Java `__, `C++ `__): +What might a functional subsystem look like in practice? Below is a simple pneumatically-actuated hatch mechanism from the HatchBotTraditional example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional), [Python](https://github.com/robotpy/examples/tree/main/HatchbotTraditional)): .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/HatchSubsystem.java + :language: java + :lines: 5- + :linenos: + :lineno-start: 5 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/HatchSubsystem.java - :language: java - :lines: 5- - :linenos: - :lineno-start: 5 + .. tab-item:: C++ (Header) + :sync: C++ (Header) - .. tab-item:: C++ (Header) - :sync: C++ (Header) + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/HatchSubsystem.h + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/HatchSubsystem.h - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/HatchSubsystem.cpp + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/subsystems/HatchSubsystem.cpp - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 + .. tab-item:: Python + :sync: Python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/main/HatchbotTraditional/subsystems/hatchsubsystem.py + :language: python + :lines: 7- + :linenos: + :lineno-start: 7 Notice that the subsystem hides the presence of the DoubleSolenoid from outside code (it is declared ``private``), and instead publicly exposes two higher-level, descriptive robot actions: ``grabHatch()`` and ``releaseHatch()``. It is extremely important that "implementation details" such as the double solenoid be "hidden" in this manner; this ensures that code outside the subsystem will never cause the solenoid to be in an unexpected state. It also allows the user to change the implementation (for instance, a motor could be used instead of a pneumatic) without any of the code outside of the subsystem having to change with it. -Alternatively, instead of writing ``void`` public methods that are called from commands, we can define the public methods as factories that return a command. Consider the following from the HatchBotInlined example project (`Java `__, `C++ `__): +Alternatively, instead of writing ``void`` public methods that are called from commands, we can define the public methods as factories that return a command. Consider the following from the HatchBotInlined example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/HatchbotInlined), [Python](https://github.com/robotpy/examples/tree/main/HatchbotInlined)): .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/HatchSubsystem.java - :language: java - :lines: 5- - :linenos: - :lineno-start: 5 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/HatchSubsystem.java + :language: java + :lines: 5- + :linenos: + :lineno-start: 5 - .. tab-item:: C++ (Header) - :sync: C++ (Header) + .. tab-item:: C++ (Header) + :sync: C++ (Header) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/HatchSubsystem.h - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/HatchSubsystem.h + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/HatchSubsystem.cpp - :language: c++ - :lines: 5- - :linenos: - :lineno-start: 5 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/subsystems/HatchSubsystem.cpp + :language: c++ + :lines: 5- + :linenos: + :lineno-start: 5 -Note the qualification of the ``RunOnce`` factory used here: this isn't the static factory in ``Commands``! Subsystems have similar instance factories that return commands requiring ``this`` subsystem. Here, the ``Subsystem.runOnce(Runnable)`` factory (`Java `__, `C++ `__) is used. + .. tab-item:: Python + :sync: Python -For a comparison between these options, see :ref:`docs/software/commandbased/organizing-command-based:Instance Command Factory Methods`. + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/main/HatchbotInlined/subsystems/hatchsubsystem.py + :language: python + :lines: 7- + :linenos: + :lineno-start: 7 -Periodic --------- -Subsystems have a ``periodic`` method that is called once every scheduler iteration (usually, once every 20 ms). This method is typically used for telemetry and other periodic actions that do not interfere with whatever command is requiring the subsystem. +Note the qualification of the ``RunOnce`` factory used here: this isn't the static factory in ``Commands``! Subsystems have similar instance factories that return commands requiring ``this`` (Java/C++) or ``self`` (Python) subsystem. Here, the ``Subsystem.runOnce(Runnable)`` factory ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/Subsystem.html#runOnce(java.lang.Runnable)), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_subsystem.html#a6b8b3b7dab6f54fb8635e335dad448fe), :external:py:meth:`Python `) is used. -.. tab-set:: - - .. tab-item:: Java - :sync: Java - - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java - :language: java - :lines: 117-125 - :linenos: - :lineno-start: 117 +For a comparison between these options, see :ref:`docs/software/commandbased/organizing-command-based:Instance Command Factory Methods`. - .. tab-item:: C++ (Header) - :sync: C++ (Header) +## Periodic - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/include/subsystems/DriveSubsystem.h - :language: c++ - :lines: 30-30 - :linenos: - :lineno-start: 30 +Subsystems have a ``periodic`` method that is called once every scheduler iteration (usually, once every 20 ms). This method is typically used for telemetry and other periodic actions that do not interfere with whatever command is requiring the subsystem. - .. tab-item:: C++ (Source) - :sync: C++ (Source) +.. tab-set:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/cpp/subsystems/DriveSubsystem.cpp - :language: c++ - :lines: 30-36 - :linenos: - :lineno-start: 30 + .. tab-item:: Java + :sync: Java + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-4/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java + :language: java + :lines: 117-125 + :linenos: + :lineno-start: 117 + + .. tab-item:: C++ (Header) + :sync: C++ (Header) + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-4/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/include/subsystems/DriveSubsystem.h + :language: c++ + :lines: 30-30 + :linenos: + :lineno-start: 30 + + .. tab-item:: C++ (Source) + :sync: C++ (Source) + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-4/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/cpp/subsystems/DriveSubsystem.cpp + :language: c++ + :lines: 30-36 + :linenos: + :lineno-start: 30 + + .. tab-item:: Python + :sync: Python + + ```python + def periodic(self): + #Update the odometry in the periodic block + self.odometry.update( + Rotation2d.fromDegrees(getHeading()), + self.leftEncoder.getDistance(), + self.rightEncoder.getDistance()) + self.fieldSim.setRobotPose(getPose()) + ``` There is also a ``simulationPeriodic()`` method that is similar to ``periodic()`` except that it is only run during :doc:`Simulation ` and can be used to update the state of the robot. -Default Commands ----------------- +## Default Commands .. note:: In the C++ command-based library, the CommandScheduler `owns` the default command object. @@ -154,22 +214,30 @@ Setting a default command for a subsystem is very easy; one simply calls ``Comma .. tab-set-code:: - .. code-block:: java - - CommandScheduler.getInstance().setDefaultCommand(exampleSubsystem, exampleCommand); + ```java + CommandScheduler.getInstance().setDefaultCommand(exampleSubsystem, exampleCommand); + ``` - .. code-block:: c++ + ```c++ + CommandScheduler.GetInstance().SetDefaultCommand(exampleSubsystem, std::move(exampleCommand)); + ``` - CommandScheduler.GetInstance().SetDefaultCommand(exampleSubsystem, std::move(exampleCommand)); + ```python + CommandScheduler.getInstance().setDefaultCommand(exampleSubsystem, exampleCommand) + ``` .. tab-set-code:: - .. code-block:: java - - exampleSubsystem.setDefaultCommand(exampleCommand); + ```java + exampleSubsystem.setDefaultCommand(exampleCommand); + ``` - .. code-block:: c++ + ```c++ + exampleSubsystem.SetDefaultCommand(std::move(exampleCommand)); + ``` - exampleSubsystem.SetDefaultCommand(std::move(exampleCommand)); + ```python + exampleSubsystem.setDefaultCommand(exampleCommand) + ``` .. note:: A command that is assigned as the default command for a subsystem must require that subsystem. diff --git a/source/docs/software/commandbased/what-is-command-based.rst b/source/docs/software/commandbased/what-is-command-based.rst index f8ac61a8c7..76438aacac 100644 --- a/source/docs/software/commandbased/what-is-command-based.rst +++ b/source/docs/software/commandbased/what-is-command-based.rst @@ -1,50 +1,61 @@ -What Is "Command-Based" Programming? -==================================== +# What Is "Command-Based" Programming? WPILib supports a robot programming methodology called "command-based" programming. In general, "command-based" can refer both the general programming paradigm, and to the set of WPILib library resources included to facilitate it. -"Command-based" programming is one possible :term:`design pattern` for robot software. It is not the only way to write a robot program, but it is a very effective one. Command-based robot code tends to be clean, extensible, and (with some tricks) easy to re-use from year to year. +"Command-based" programming is one possible :term:`design pattern` for robot software. It is not the only way to write a robot program, but it is a very effective one. Command-based robot code tends to be clean, extensible, and (with some tricks) easy to reuse from year to year. The command-based paradigm is also an example of :term:`declarative programming`. The command-based library allow users to define desired robot behaviors while minimizing the amount of iteration-by-iteration robot logic that they must write. For example, in the command-based program, a user can specify that "the robot should perform an action when a condition is true" (note the use of a :ref:`lambda `): .. tab-set-code:: - .. code-block:: java + ```java + new Trigger(condition::get).onTrue(Commands.runOnce(() -> piston.set(DoubleSolenoid.Value.kForward))); + ``` - new Trigger(condition::get).onTrue(Commands.runOnce(() -> piston.set(DoubleSolenoid.Value.kForward))); + ```c++ + Trigger([&condition] { return condition.Get()).OnTrue(frc2::cmd::RunOnce([&piston] { piston.Set(frc::DoubleSolenoid::kForward))); + ``` - .. code-block:: c++ - - Trigger([&condition] { return condition.Get()).OnTrue(frc2::cmd::RunOnce([&piston] { piston.Set(frc::DoubleSolenoid::kForward))); + ```python + Trigger(condition.get).onTrue(Commands.runOnce(lambda: piston.set(DoubleSolenoid.Value.kForward))) + ``` In contrast, without using command-based, the user would need to check the button state every iteration, and perform the appropriate action based on the state of the button. .. tab-set-code:: - .. code-block:: java - - if(condition.get()) { - if(!pressed) { - piston.set(DoubleSolenoid.Value.kForward); - pressed = true; - } - } else { - pressed = false; + ```java + if(condition.get()) { + if(!pressed) { + piston.set(DoubleSolenoid.Value.kForward); + pressed = true; } - - .. code-block:: c++ - - if(condition.Get()) { - if(!pressed) { - piston.Set(frc::DoubleSolenoid::kForward); - pressed = true; - } - } else { - pressed = false; + } else { + pressed = false; + } + ``` + + ```c++ + if(condition.Get()) { + if(!pressed) { + piston.Set(frc::DoubleSolenoid::kForward); + pressed = true; } - -Subsystems and Commands ------------------------ + } else { + pressed = false; + } + ``` + + ```python + if condition.get(): + if not pressed: + piston.set(DoubleSolenoid.Value.kForward) + pressed = True + else: + pressed = False + ``` + +## Subsystems and Commands .. image:: diagrams/subsystems-and-commands.drawio.svg :alt: image of subsystems and commands @@ -55,16 +66,14 @@ The command-based pattern is based around two core abstractions: **commands**, a **Subsystems** represent independently-controlled collections of robot hardware (such as motor controllers, sensors, pneumatic actuators, etc.) that operate together. Subsystems back the resource-management system of command-based: only one command can use a given subsystem at the same time. Subsystems allow users to "hide" the internal complexity of their actual hardware from the rest of their code - this both simplifies the rest of the robot code, and allows changes to the internal details of a subsystem's hardware without also changing the rest of the robot code. -How Commands Are Run --------------------- +## How Commands Are Run .. note:: For a more detailed explanation, see :doc:`command-scheduler`. -Commands are run by the ``CommandScheduler`` (`Java `__, `C++ `__) singleton, which polls triggers (such as buttons) for commands to schedule, preventing resource conflicts, and executing scheduled commands. The scheduler's ``run()`` method must be called; it is generally recommended to call it from the ``robotPeriodic()`` method of the ``Robot`` class, which is run at a default frequency of 50Hz (once every 20ms). +Commands are run by the ``CommandScheduler`` ([Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/wpilibj2/command/CommandScheduler.html), [C++](https://github.wpilib.org/allwpilib/docs/development/cpp/classfrc2_1_1_command_scheduler.html), :external:py:class:`Python `) singleton, which polls triggers (such as buttons) for commands to schedule, preventing resource conflicts, and executing scheduled commands. The scheduler's ``run()`` method must be called; it is generally recommended to call it from the ``robotPeriodic()`` method of the ``Robot`` class, which is run at a default frequency of 50Hz (once every 20ms). Multiple commands can run concurrently, as long as they do not require the same resources on the robot. Resource management is handled on a per-subsystem basis: commands specify which subsystems they interact with, and the scheduler will ensure that no more more than one command requiring a given subsystem is scheduled at a time. This ensures that, for example, users will not end up with two different pieces of code attempting to set the same motor controller to different output values. -Command Compositions --------------------- +## Command Compositions It is often desirable to build complex commands from simple pieces. This is achievable by creating a :term:`composition` of commands. The command-based library provides several types of :doc:`command compositions ` for teams to use, and users may write their own. As command compositions are commands themselves, they may be used in a :term:`recursive composition`. That is to say - one can create a command compositions from multiple command compositions. This provides an extremely powerful way of building complex robot actions from simple components. diff --git a/source/docs/software/convenience-features/event-based.rst b/source/docs/software/convenience-features/event-based.rst index d6506ddf88..c093b0adfd 100644 --- a/source/docs/software/convenience-features/event-based.rst +++ b/source/docs/software/convenience-features/event-based.rst @@ -1,114 +1,105 @@ -Event-Based Programming With EventLoop -====================================== +# Event-Based Programming With EventLoop Many operations in robot code are driven by certain conditions; buttons are one common example. Conditions can be polled with an :term:`imperative programming` style by using an ``if`` statement in a periodic method. As an alternative, WPILib offers an :term:`event-driven programming` style of API in the shape of the ``EventLoop`` and ``BooleanEvent`` classes. -.. note:: The example code here is taken from the EventLoop example project (`Java `__/`C++ `__). +.. note:: The example code here is taken from the EventLoop example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/v2023.2.1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java)/[C++](https://github.com/wpilibsuite/allwpilib/blob/v2023.2.1/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp)). -EventLoop ---------- +## EventLoop The ``EventLoop`` class is a "container" for pairs of conditions and actions, which can be polled using the ``poll()``/``Poll()`` method. When polled, every condition will be queried and if it returns ``true`` the action associated with the condition will be executed. .. tab-set-code:: - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java :language: java - :lines: 33-33, 87-91 + :lines: 32-33, 86-90 - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp - :language: cpp + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp + :language: c++ :lines: 94-94, 81-81 .. warning:: The ``EventLoop``'s ``poll()`` method should be called consistently in a ``*Periodic()`` method. Failure to do this will result in unintended loop behavior. -BooleanEvent ------------- +## BooleanEvent -The ``BooleanEvent`` class represents a boolean condition: a ``BooleanSupplier`` (`Java `__) / ``std::function`` (C++). +The ``BooleanEvent`` class represents a boolean condition: a ``BooleanSupplier`` ([Java](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/function/BooleanSupplier.html)) / ``std::function`` (C++). To bind a callback action to the condition, use ``ifHigh()``/``IfHigh()``: .. tab-set-code:: - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java :language: java - :lines: 72-78 + :lines: 71-77 - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp - :language: cpp + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp + :language: c++ :lines: 64-72 Remember that button binding is *declarative*: bindings only need to be declared once, ideally some time during robot initialization. The library handles everything else. -Composing Conditions --------------------- +## Composing Conditions ``BooleanEvent`` objects can be composed to create composite conditions. In C++ this is done using operators when applicable, other cases and all compositions in Java are done using methods. -and() / && -^^^^^^^^^^ +### and() / && The ``and()``/``&&`` composes two ``BooleanEvent`` conditions into a third condition that returns ``true`` only when **both** of the conditions return ``true``. .. tab-set-code:: - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java :language: java - :lines: 44-49 + :lines: 43-48 - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp - :language: cpp + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp + :language: c++ :lines: 35-40 -or() / || -^^^^^^^^^ +### or() / || The ``or()``/``||`` composes two ``BooleanEvent`` conditions into a third condition that returns ``true`` only when **either** of the conditions return ``true``. .. tab-set-code:: - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java :language: java - :lines: 51-57 + :lines: 50-56 - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp - :language: cpp + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp + :language: c++ :lines: 42-47 -negate() / ! -^^^^^^^^^^^^ +### negate() / ! The ``negate()``/``!`` composes one ``BooleanEvent`` condition into another condition that returns the opposite of what the original conditional did. .. tab-set-code:: - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java :language: java - :lines: 46-47 + :lines: 45-46 - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp - :language: cpp + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp + :language: c++ :lines: 37-38 -debounce() / Debounce() -^^^^^^^^^^^^^^^^^^^^^^^ +### debounce() / Debounce() To avoid rapid repeated activation, conditions (especially those originating from digital inputs) can be debounced with the :ref:`WPILib Debouncer class ` using the `debounce` method: .. tab-set-code:: - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java :language: java - :lines: 71-75 + :lines: 70-74 - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp - :language: cpp + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp + :language: c++ :lines: 64-69 -rising(), falling() -^^^^^^^^^^^^^^^^^^^ +### rising(), falling() Often times it is desired to bind an action not to the *current* state of a condition, but instead to when that state *changes*. For example, binding an action to when a button is newly pressed as opposed to when it is held. This is what the ``rising()`` and ``falling()`` decorators do: ``rising()`` will return a condition that is ``true`` only when the original condition returned ``true`` in the *current* polling and ``false`` in the *previous* polling; ``falling()`` returns a condition that returns ``true`` only on a transition from ``true`` to ``false``. @@ -116,27 +107,26 @@ Often times it is desired to bind an action not to the *current* state of a cond .. tab-set-code:: - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/eventloop/Robot.java :language: java - :lines: 79-84 + :lines: 78-83 - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp - :language: cpp + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibcExamples/src/main/cpp/examples/EventLoop/cpp/Robot.cpp + :language: c++ :lines: 74-78 -Downcasting ``BooleanEvent`` Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Downcasting ``BooleanEvent`` Objects To convert ``BooleanEvent`` objects to other types, most commonly the ``Trigger`` subclass used for :ref:`binding commands to conditions `, the generic ``castTo()``/``CastTo()`` decorator exists: .. tab-set-code:: - .. code-block:: java + ```java + Trigger trigger = booleanEvent.castTo(Trigger::new); + ``` - Trigger trigger = booleanEvent.castTo(Trigger::new); - - .. code-block:: c++ - - frc2::Trigger trigger = booleanEvent.CastTo(); + ```c++ + frc2::Trigger trigger = booleanEvent.CastTo(); + ``` .. note:: In Java, the parameter expects a method reference to a constructor accepting an ``EventLoop`` instance and a ``BooleanSupplier``. Due to the lack of method references, this parameter is defaulted in C++ as long as a constructor of the form ``Type(frc::EventLoop*, std::function)`` exists. diff --git a/source/docs/software/convenience-features/index.rst b/source/docs/software/convenience-features/index.rst index 70a11e23da..eb2b68194d 100644 --- a/source/docs/software/convenience-features/index.rst +++ b/source/docs/software/convenience-features/index.rst @@ -1,5 +1,4 @@ -Convenience Features -==================== +# Convenience Features This section covers some general convenience features that be used with other advanced programming features. .. toctree:: diff --git a/source/docs/software/convenience-features/scheduling-functions.rst b/source/docs/software/convenience-features/scheduling-functions.rst index 301e02b89a..3fbfd53644 100644 --- a/source/docs/software/convenience-features/scheduling-functions.rst +++ b/source/docs/software/convenience-features/scheduling-functions.rst @@ -1,5 +1,4 @@ -Scheduling Functions at Custom Frequencies -========================================== +# Scheduling Functions at Custom Frequencies ``TimedRobot``'s ``addPeriodic()`` method allows one to run custom methods at a rate faster than the default ``TimedRobot`` periodic update rate (20 ms). Previously, teams had to make a ``Notifier`` to run feedback controllers more often than the ``TimedRobot`` loop period of 20 ms (running ``TimedRobot`` more often than this is not advised). Now, users can run feedback controllers more often than the main robot loop, but synchronously with the ``TimedRobot`` periodic functions, eliminating potential thread safety issues. The ``addPeriodic()`` (Java) / ``AddPeriodic()`` (C++) method takes in a lambda (the function to run), along with the requested period and an optional offset from the common starting time. The optional third argument is useful for scheduling a function in a different timeslot relative to the other ``TimedRobot`` periodic methods. @@ -11,68 +10,63 @@ The ``addPeriodic()`` (Java) / ``AddPeriodic()`` (C++) method takes in a lambda .. tab-item:: Java :sync: Java - .. code-block:: java - - public class Robot extends TimedRobot { - private Joystick m_joystick = new Joystick(0); - private Encoder m_encoder = new Encoder(1, 2); - private Spark m_motor = new Spark(1); - private PIDController m_controller = new PIDController(1.0, 0.0, 0.5, 0.01); - - public Robot() { - addPeriodic(() -> { - m_motor.set(m_controller.calculate(m_encoder.getRate())); - }, 0.01, 0.005); - } - - @Override - public teleopPeriodic() { - if (m_joystick.getRawButtonPressed(1)) { - if (m_controller.getSetpoint() == 0.0) { - m_controller.setSetpoint(30.0); - } else { - m_controller.setSetpoint(0.0); - } + ```java + public class Robot extends TimedRobot { + private Joystick m_joystick = new Joystick(0); + private Encoder m_encoder = new Encoder(1, 2); + private Spark m_motor = new Spark(1); + private PIDController m_controller = new PIDController(1.0, 0.0, 0.5, 0.01); + public Robot() { + addPeriodic(() -> { + m_motor.set(m_controller.calculate(m_encoder.getRate())); + }, 0.01, 0.005); + } + @Override + public teleopPeriodic() { + if (m_joystick.getRawButtonPressed(1)) { + if (m_controller.getSetpoint() == 0.0) { + m_controller.setSetpoint(30.0); + } else { + m_controller.setSetpoint(0.0); } } - } + } + } + ``` .. tab-item:: C++ (Header) :sync: C++ (Header) - .. code-block:: cpp - - class Robot : public frc::TimedRobot { - private: - frc::Joystick m_joystick{0}; - frc::Encoder m_encoder{1, 2}; - frc::Spark m_motor{1}; - frc::PIDController m_controller{1.0, 0.0, 0.5, 10_ms}; - - Robot(); - - void TeleopPeriodic() override; - }; + ```c++ + class Robot : public frc::TimedRobot { + private: + frc::Joystick m_joystick{0}; + frc::Encoder m_encoder{1, 2}; + frc::Spark m_motor{1}; + frc::PIDController m_controller{1.0, 0.0, 0.5, 10_ms}; + Robot(); + void TeleopPeriodic() override; + }; + ``` .. tab-item:: C++ (Source) :sync: C++ (Source) - .. code-block:: cpp - - void Robot::Robot() { - AddPeriodic([&] { - m_motor.Set(m_controller.Calculate(m_encoder.GetRate())); - }, 10_ms, 5_ms); - } - - void Robot::TeleopPeriodic() { - if (m_joystick.GetRawButtonPressed(1)) { - if (m_controller.GetSetpoint() == 0.0) { - m_controller.SetSetpoint(30.0); - } else { - m_controller.SetSetpoint(0.0); - } - } + ```c++ + void Robot::Robot() { + AddPeriodic([&] { + m_motor.Set(m_controller.Calculate(m_encoder.GetRate())); + }, 10_ms, 5_ms); + } + void Robot::TeleopPeriodic() { + if (m_joystick.GetRawButtonPressed(1)) { + if (m_controller.GetSetpoint() == 0.0) { + m_controller.SetSetpoint(30.0); + } else { + m_controller.SetSetpoint(0.0); } + } + } + ``` The ``teleopPeriodic()`` method in this example runs every 20 ms, and the controller update is run every 10 ms with an offset of 5 ms from when ``teleopPeriodic()`` runs so that their timeslots don't conflict (e.g., ``teleopPeriodic()`` runs at 0 ms, 20 ms, 40 ms, etc.; the controller runs at 5 ms, 15 ms, 25 ms, etc.). diff --git a/source/docs/software/dashboards/advantagescope.rst b/source/docs/software/dashboards/advantagescope.rst new file mode 100644 index 0000000000..23b0cdd265 --- /dev/null +++ b/source/docs/software/dashboards/advantagescope.rst @@ -0,0 +1,19 @@ +# AdvantageScope + +AdvantageScope is a data visualization tool for :ref:`NetworkTables `, :ref:`WPILib data logs `, and :ref:`Driver Station logs `. It is a programmer's tool (rather than a competition dashboard) and can be used to debug real or simulated robot code from a log file or live over the network. + +In Visual Studio Code, press :kbd:`Ctrl+Shift+P` and type ``WPILib`` or click the WPILib logo in the top right to launch the WPILib Command Palette. Select :guilabel:`Start Tool`, then select :guilabel:`AdvantageScope`. You can also open any supported log file in AdvantageScope using a standard file browser. + +.. note:: Detailed documentation for AdvantageScope can be found [here](https://docs.advantagescope.org). It is also available offline by clicking the book icon in the tab bar. + +The capabilities of AdvantageScope include: + +- Display of numeric, textual, and boolean data in graphs and tables +- Visualization of pose and mechanism data in 2D and 3D, including custom 3D robot models +- Automatic synchronization of data sources, including log files, match videos, and [Zebra MotionWorks](https://www.firstinspires.org/robotics/frc/blog/2023-zebra-motionworks-for-first-robotics-competition-at-the-first-championship) tracking +- Specialized displays for joysticks, swerve module states, and console text +- Analysis of numeric fields using histograms and statistical measures +- Multiple export options, including CSV and WPILib data logs + +.. image:: images/advantagescope.png + :alt: Screenshot of an AdvantageScope window displaying a line graph, a list of fields, and a series of miscellaneous tabs. diff --git a/source/docs/software/dashboards/dashboard-intro.rst b/source/docs/software/dashboards/dashboard-intro.rst new file mode 100644 index 0000000000..a5df1e3738 --- /dev/null +++ b/source/docs/software/dashboards/dashboard-intro.rst @@ -0,0 +1,35 @@ +# Choosing a Dashboard + +A dashboard is a program used to retrieve and display information about the operation of your robot. There are two main types of dashboards that teams may need: driver and programmer dashboards. Some dashboards will try to accommodate both purposes. + +## Driver Dashboard + +During competition the drive team will use this dashboard to get information from the robot. It should focus on conveying key information instantly. This is often best accomplished by using large, colorful, and easy to understand visual elements. Most teams will also use this dashboard to select their autonomous routine. + +Take caution to carefully consider what *needs* to be on this dashboard and if there is another better way of communicating that information. Any members of the drive team (especially the driver) looking at the dashboard takes their focus away from the match. Using :ref:`LEDs ` to indicate the state of your robot is a good example of a way to communicate useful information to the driver without having to take their eyes off the robot. + +## Programming Dashboard + +This dashboard is designed for debugging code and analyzing data from the robot. It supports the monitoring of a wide variety of information simultaneously, prioritizing function and utility over simplicity or ease of use. This functionality often includes complex data visualization and graphing across extended periods. In scenarios where there is an overwhelming amount of data to review, real-time analysis becomes challenging. The capability to examine past data and replay it proves to be extremely beneficial. While some dashboards may log data transmitted to them, :ref:`on-robot telemetry ` using the ``DataLog`` class simplifies the process. + +## Specific Dashboards (oldest to newest) + +.. note:: SmartDashboard and Shuffleboard have a long history of aiding FRC teams. However, they do not have a person to maintain them so are not receiving bug fixes or improvements. Notably, Shuffleboard may experience performance issues on some machines under certain scenarios. PRs from external contributors will be reviewed. + +:ref:`LabVIEW Dashboard ` (Driver / Programming) - easy to use and provides a lot of features straight out of the box like: camera streams, autonomous selection, and joystick feedback. It can be customized using LabVIEW by creating a new Dashboard project. While it :ref:`can be used ` by Java or C++ teams, they generally prefer SmartDashboard or Shuffleboard which can be customized in their respective language. + +:ref:`SmartDashboard ` (Driver) - simple and efficient dashboard that uses relatively few computer resources. It does not have the fancy look or some of the features Shuffleboard has, but it displays network tables data with a variety of widgets without bogging down the driver station computer. + +:ref:`Shuffleboard ` (Driver) - straightforward and easily customizable dashboard. It displays network tables data using a variety of widgets that can be positioned and controlled with robot code. It includes many extra features like: tabs, recording / playback, and advanced custom widgets. + +:ref:`Glass ` (Programming) - robot data visualization tool. Its GUI is extremely similar to that of the :ref:`Simulation GUI `. In its current state, it is meant to be used as a programmer's tool rather than a proper dashboard in a competition environment, with a focus on high performance real time plotting. + +:ref:`AdvantageScope ` (Programming) - robot diagnostics, log review/analysis, and data visualization application. It reads the WPILib Data Log (``.wpilog``) and Driver Station Log (``.dslog`` / ``.dsevents``) file formats, plus live robot data viewing. + +## Third Party Dashboards + +[FRC Web Components](https://github.com/frc-web-components/frc-web-components) (Driver) - A web-based dashboard that can be installed as a standalone application, or as a JavaScript package for custom dashboard solutions. + +[Elastic](https://github.com/Gold872/elastic-dashboard) (Driver) - simple and modern Shuffleboard alternative made by Team 353. It is meant to serve as a dashboard for competition but can also be used for testing. It features draggable and resizable card widgets. + +[QFRCDashboard](https://github.com/Q-FRC/Dashboard) (Driver) - described as reliable, high-performance, low-footprint dashboard. QFRCDashboard has been specifically designed to use as few resources as possible. diff --git a/source/docs/software/dashboards/glass/command-based-widgets.rst b/source/docs/software/dashboards/glass/command-based-widgets.rst index fca51f5113..c1ecfcd5cb 100644 --- a/source/docs/software/dashboards/glass/command-based-widgets.rst +++ b/source/docs/software/dashboards/glass/command-based-widgets.rst @@ -1,27 +1,29 @@ -Widgets for the Command-Based Framework -======================================= +# Widgets for the Command-Based Framework Glass also has several widgets that are specific to the :ref:`command-based framework `. These include widgets to schedule commands, view actively running commands on a specific subsystem, or view the state of the :ref:`command scheduler `. -Command Selector Widget ------------------------ +## Command Selector Widget The :guilabel:`Command Selector` widget allows you to start and cancel a specific instance of a command (sent over NetworkTables) from Glass. For example, you can create an instance of ``MyCommand`` and send it to SmartDashboard: .. tab-set-code:: - .. code-block:: java - - MyCommand command = new MyCommand(...); - SmartDashboard.putData("My Command", command); - - .. code-block:: c++ - - #include - - ... - - MyCommand command{...}; - frc::SmartDashboard::PutData("My Command", &command); + ```java + MyCommand command = new MyCommand(...); + SmartDashboard.putData("My Command", command); + ``` + + ```c++ + #include + ... + MyCommand command{...}; + frc::SmartDashboard::PutData("My Command", &command); + ``` + + ```python + from wpilib import SmartDashboard + command = MyCommand(...) + SmartDashboard.putData("My Command", command) + ``` .. note:: The ``MyCommand`` instance can also be sent using a lower-level NetworkTables API or using the :ref:`Shuffleboard API `. In this case, the ``SmartDashboard`` API was used, meaning that the :guilabel:`Command Selector` widget will appear under the ``SmartDashboard`` table name. @@ -30,16 +32,14 @@ The :guilabel:`Command Selector` widget allows you to start and cancel a specif The widget has two states. When the command is not running, a :guilabel:`Run` button will appear -- clicking it will schedule the command. When the command is running, a :guilabel:`Cancel` button, accompanied by :guilabel:`Running...` text, will appear (as shown above). This will cancel the command. -Subsystem Widget ----------------- +## Subsystem Widget The :guilabel:`Subsystem` widget can be used to see the default command and the currently scheduled command on a specific subsystem. If you are using the ``SubsystemBase`` base class, your subsystem will be automatically sent to NetworkTables over LiveWindow. To view this widget, look under the :guilabel:`LiveWindow` main table name in the :guilabel:`NetworkTables` menu. .. image:: images/subsystem.png :alt: Subsystem widget showing the state of "DriveSubsystem". Default Command: "DefaultDrive". Current Command: "DriveDistance" -Command Scheduler Widget ------------------------- +## Command Scheduler Widget The :guilabel:`Command Scheduler` widget allows you to see all currently scheduled commands. In addition, any of these commands can be canceled from the GUI. diff --git a/source/docs/software/dashboards/glass/field2d-widget.rst b/source/docs/software/dashboards/glass/field2d-widget.rst index f678193e72..de60c1e074 100644 --- a/source/docs/software/dashboards/glass/field2d-widget.rst +++ b/source/docs/software/dashboards/glass/field2d-widget.rst @@ -1,82 +1,73 @@ -The Field2d Widget -================== +# The Field2d Widget Glass supports displaying your robot's position on the field using the :guilabel:`Field2d` widget. An instance of the ``Field2d`` class should be created, sent over NetworkTables, and updated periodically with the latest robot pose in your robot code. -Sending Robot Pose from User Code ---------------------------------- +## Sending Robot Pose from User Code To send your robot's position (usually obtained by :ref:`odometry ` or a pose estimator), a ``Field2d`` instance must be created in robot code and sent over NetworkTables. The instance must then be updated periodically with the latest robot pose. .. tab-set-code:: - .. code-block:: java - - private final Field2d m_field = new Field2d(); - - public Drivetrain() { - ... - SmartDashboard.putData("Field", m_field); - } - - ... - - public void periodic() { - ... - m_field.setRobotPose(m_odometry.getPoseMeters()); - } - - .. code-block:: c++ - - #include - #include - - frc::Field2d m_field; - - Drivetrain() { - ... - frc::SmartDashboard::PutData("Field", &m_field); - } - - ... - - void Periodic() { - ... - m_field.SetRobotPose(m_odometry.GetPose()); - } + ```java + private final Field2d m_field = new Field2d(); + // Do this in either robot or subsystem init + SmartDashboard.putData("Field", m_field); + // Do this in either robot periodic or subsystem periodic + m_field.setRobotPose(m_odometry.getPoseMeters()); + ``` + + ```c++ + #include + #include + frc::Field2d m_field; + // Do this in either robot or subsystem init + frc::SmartDashboard::PutData("Field", &m_field); + // Do this in either robot periodic or subsystem periodic + m_field.SetRobotPose(m_odometry.GetPose()); + ``` + + ```python + from wpilib import SmartDashboard, Field2d + self.field = Field2d() + # Do this in either robot or subsystem init + SmartDashboard.putData("Field", self.field) + # Do this in either robot periodic or subsystem periodic + self.field.setRobotPose(self.odometry.getPose()) + ``` .. note:: The ``Field2d`` instance can also be sent using a lower-level NetworkTables API or using the :ref:`Shuffleboard API `. In this case, the ``SmartDashboard`` API was used, meaning that the :guilabel:`Field2d` widget will appear under the ``SmartDashboard`` table name. -Sending Trajectories to Field2d -------------------------------- +## Sending Trajectories to Field2d Visualizing your trajectory is a great debugging step for verifying that your trajectories are created as intended. Trajectories can be easily visualized in :ref:`Field2d ` using the ``setTrajectory()``/``SetTrajectory()`` functions. .. tab-set-code:: - .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java + .. rli:: https://github.com/wpilibsuite/allwpilib/raw/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java :language: java :lines: 44-61 :linenos: :lineno-start: 44 - .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/RamseteController/cpp/Robot.cpp - :language: cpp + .. rli:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/RamseteController/cpp/Robot.cpp + :language: c++ :lines: 18-30 :linenos: :lineno-start: 18 -Viewing Trajectories with Glass -------------------------------- + .. rli:: https://raw.githubusercontent.com/robotpy/examples/2024.0.0b4/RamseteController/robot.py + :language: python + :lines: 19,26-39,46-53 + +## Viewing Trajectories with Glass The sent trajectory can be viewed with :ref:`Glass ` through the dropdown :guilabel:`NetworkTables` -> :guilabel:`SmartDashboard` -> :guilabel:`Field2d`. .. image:: images/sent-trajectory.png :alt: Picture containing Field2d and the generated trajectory -.. note:: The above example which uses the `RamseteController (Java) `__/`RamseteController (C++) `__ will not show the sent trajectory until autonomous is enabled at least once. +.. note:: The above example which uses the RamseteController ([Java](https://github.com/wpilibsuite/allwpilib/blob/a610379965680a8f9214d5f0db3a8e1bc20d4712/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java) / [C++](https://github.com/wpilibsuite/allwpilib/blob/a610379965680a8f9214d5f0db3a8e1bc20d4712/wpilibcExamples/src/main/cpp/examples/RamseteController/cpp/Robot.cpp) / [Python](https://github.com/robotpy/examples/tree/2024.0.0b4/RamseteController)) will not show the sent trajectory until autonomous is enabled at least once. -Viewing the Robot Pose in Glass -------------------------------- +## Viewing the Robot Pose in Glass After sending the ``Field2d`` instance over NetworkTables, the :guilabel:`Field2d` widget can be added to Glass by selecting :guilabel:`NetworkTables` in the menu bar, choosing the table name that the instance was sent over, and then clicking on the :guilabel:`Field` button. @@ -86,12 +77,11 @@ Once the widget appears, you can resize and place it on the Glass workspace as y You can choose from an existing field layout using the :guilabel:`Image` drop-down. Or you can select a custom file by setting the :guilabel:`Image` to ``Custom`` and selecting :guilabel:`Choose image...`. You can choose to either select an image file or a PathWeaver JSON file as long as the image file is in the same directory. Choosing the JSON file will automatically import the correct location of the field in the image and the correct size of the field. -.. note:: You can retrieve the latest field image and JSON files from `here `__. This is the same image and JSON that are used when generating paths using :ref:`PathWeaver `. +.. note:: You can retrieve the latest field image and JSON files from [here](https://github.com/wpilibsuite/allwpilib/tree/main/fieldImages/src/main/native/resources/edu/wpi/first/fields). This is the same image and JSON that are used when generating paths using :ref:`PathWeaver `. .. image:: images/field2d-options.png -Modifying Pose Style --------------------- +## Modifying Pose Style Poses can be customized in a plethora of ways by right clicking on the Field2d menu bar. Examples of customization are: line width, line weight, style, arrow width, arrow weight, color, etc. @@ -107,3 +97,10 @@ Now, uncheck the :guilabel:`Arrows` checkbox. This will cause our trajectory to .. image:: images/unchecked-arrow-trajectory.png :alt: Unchecked arrows checkbox to showcase fluid line. + +## Viewing Pose Data with AdvantageScope + +:ref:`AdvantageScope ` is an alternative option for viewing pose data from a ``Field2d`` object, including data recorded to a log file using :ref:`WPILib data logs `. Both 2D and 3D visualizations are supported. See the documentation for the [odometry](https://docs.advantagescope.org/tab-reference/odometry) and [3D field](https://docs.advantagescope.org/tab-reference/3d-field) tabs for more details. + +.. image:: images/advantagescope-field2d.png + :alt: Screenshot of an AdvantageScope window displaying a robot and trajectory on a 3D field. diff --git a/source/docs/software/dashboards/glass/images/advantagescope-field2d.png b/source/docs/software/dashboards/glass/images/advantagescope-field2d.png new file mode 100644 index 0000000000..ee11458243 Binary files /dev/null and b/source/docs/software/dashboards/glass/images/advantagescope-field2d.png differ diff --git a/source/docs/software/dashboards/glass/images/advantagescope-mechanism.png b/source/docs/software/dashboards/glass/images/advantagescope-mechanism.png new file mode 100644 index 0000000000..a9deb6f0c0 Binary files /dev/null and b/source/docs/software/dashboards/glass/images/advantagescope-mechanism.png differ diff --git a/source/docs/software/dashboards/glass/images/advantagescope-plot.png b/source/docs/software/dashboards/glass/images/advantagescope-plot.png new file mode 100644 index 0000000000..3c743969e8 Binary files /dev/null and b/source/docs/software/dashboards/glass/images/advantagescope-plot.png differ diff --git a/source/docs/software/dashboards/glass/images/changing-style-line.png b/source/docs/software/dashboards/glass/images/changing-style-line.png index 628da3942b..7671255c5d 100644 Binary files a/source/docs/software/dashboards/glass/images/changing-style-line.png and b/source/docs/software/dashboards/glass/images/changing-style-line.png differ diff --git a/source/docs/software/dashboards/glass/images/command-selector.png b/source/docs/software/dashboards/glass/images/command-selector.png index 3a6fe03aec..741e3e43de 100644 Binary files a/source/docs/software/dashboards/glass/images/command-selector.png and b/source/docs/software/dashboards/glass/images/command-selector.png differ diff --git a/source/docs/software/dashboards/glass/images/drag-plot.png b/source/docs/software/dashboards/glass/images/drag-plot.png index 56732cb649..71caf92435 100644 Binary files a/source/docs/software/dashboards/glass/images/drag-plot.png and b/source/docs/software/dashboards/glass/images/drag-plot.png differ diff --git a/source/docs/software/dashboards/glass/images/field2d-options.png b/source/docs/software/dashboards/glass/images/field2d-options.png index a204d98afb..39de5e9cbd 100644 Binary files a/source/docs/software/dashboards/glass/images/field2d-options.png and b/source/docs/software/dashboards/glass/images/field2d-options.png differ diff --git a/source/docs/software/dashboards/glass/images/fms-info.png b/source/docs/software/dashboards/glass/images/fms-info.png index b1375888c4..5a17a87aad 100644 Binary files a/source/docs/software/dashboards/glass/images/fms-info.png and b/source/docs/software/dashboards/glass/images/fms-info.png differ diff --git a/source/docs/software/dashboards/glass/images/glass-connected.png b/source/docs/software/dashboards/glass/images/glass-connected.png index d86b2d725b..485478c1d9 100644 Binary files a/source/docs/software/dashboards/glass/images/glass-connected.png and b/source/docs/software/dashboards/glass/images/glass-connected.png differ diff --git a/source/docs/software/dashboards/glass/images/glass-dark-mode.png b/source/docs/software/dashboards/glass/images/glass-dark-mode.png index 7360c0104e..afc236973c 100644 Binary files a/source/docs/software/dashboards/glass/images/glass-dark-mode.png and b/source/docs/software/dashboards/glass/images/glass-dark-mode.png differ diff --git a/source/docs/software/dashboards/glass/images/glass-disconnected.png b/source/docs/software/dashboards/glass/images/glass-disconnected.png index 668a7ec0c8..a0e654a1d7 100644 Binary files a/source/docs/software/dashboards/glass/images/glass-disconnected.png and b/source/docs/software/dashboards/glass/images/glass-disconnected.png differ diff --git a/source/docs/software/dashboards/glass/images/gyro.png b/source/docs/software/dashboards/glass/images/gyro.png index 5d9cdadc87..5e6019888e 100644 Binary files a/source/docs/software/dashboards/glass/images/gyro.png and b/source/docs/software/dashboards/glass/images/gyro.png differ diff --git a/source/docs/software/dashboards/glass/images/line-options.png b/source/docs/software/dashboards/glass/images/line-options.png index 2fa4df4ba8..c73b6c9161 100644 Binary files a/source/docs/software/dashboards/glass/images/line-options.png and b/source/docs/software/dashboards/glass/images/line-options.png differ diff --git a/source/docs/software/dashboards/glass/images/livewindow.png b/source/docs/software/dashboards/glass/images/livewindow.png index 35eb153d83..25f21e13e2 100644 Binary files a/source/docs/software/dashboards/glass/images/livewindow.png and b/source/docs/software/dashboards/glass/images/livewindow.png differ diff --git a/source/docs/software/dashboards/glass/images/locked-axis.png b/source/docs/software/dashboards/glass/images/locked-axis.png index f798a51bbb..c31149789d 100644 Binary files a/source/docs/software/dashboards/glass/images/locked-axis.png and b/source/docs/software/dashboards/glass/images/locked-axis.png differ diff --git a/source/docs/software/dashboards/glass/images/mechanism2d-widget.png b/source/docs/software/dashboards/glass/images/mechanism2d-widget.png index 25ee2234b3..b9bc168585 100644 Binary files a/source/docs/software/dashboards/glass/images/mechanism2d-widget.png and b/source/docs/software/dashboards/glass/images/mechanism2d-widget.png differ diff --git a/source/docs/software/dashboards/glass/images/nt-widget.png b/source/docs/software/dashboards/glass/images/nt-widget.png index 3fb7b39a3a..8ae869435f 100644 Binary files a/source/docs/software/dashboards/glass/images/nt-widget.png and b/source/docs/software/dashboards/glass/images/nt-widget.png differ diff --git a/source/docs/software/dashboards/glass/images/pid.png b/source/docs/software/dashboards/glass/images/pid.png index 78028dcf9b..2be3c0ce9f 100644 Binary files a/source/docs/software/dashboards/glass/images/pid.png and b/source/docs/software/dashboards/glass/images/pid.png differ diff --git a/source/docs/software/dashboards/glass/images/plot-options.png b/source/docs/software/dashboards/glass/images/plot-options.png index 4c40f99ba5..c65fc9c1b8 100644 Binary files a/source/docs/software/dashboards/glass/images/plot-options.png and b/source/docs/software/dashboards/glass/images/plot-options.png differ diff --git a/source/docs/software/dashboards/glass/images/scheduler.png b/source/docs/software/dashboards/glass/images/scheduler.png index 48bfcc0161..fa29434b3d 100644 Binary files a/source/docs/software/dashboards/glass/images/scheduler.png and b/source/docs/software/dashboards/glass/images/scheduler.png differ diff --git a/source/docs/software/dashboards/glass/images/second-axis.png b/source/docs/software/dashboards/glass/images/second-axis.png index 6d91f73473..846121d9d6 100644 Binary files a/source/docs/software/dashboards/glass/images/second-axis.png and b/source/docs/software/dashboards/glass/images/second-axis.png differ diff --git a/source/docs/software/dashboards/glass/images/select-field2d.png b/source/docs/software/dashboards/glass/images/select-field2d.png index 8462af9504..4f42fffd19 100644 Binary files a/source/docs/software/dashboards/glass/images/select-field2d.png and b/source/docs/software/dashboards/glass/images/select-field2d.png differ diff --git a/source/docs/software/dashboards/glass/images/select-mechanism2d.png b/source/docs/software/dashboards/glass/images/select-mechanism2d.png index 81f16e8327..4ad71adeb9 100644 Binary files a/source/docs/software/dashboards/glass/images/select-mechanism2d.png and b/source/docs/software/dashboards/glass/images/select-mechanism2d.png differ diff --git a/source/docs/software/dashboards/glass/images/sendable-chooser.png b/source/docs/software/dashboards/glass/images/sendable-chooser.png index 65d8261c43..5150167e77 100644 Binary files a/source/docs/software/dashboards/glass/images/sendable-chooser.png and b/source/docs/software/dashboards/glass/images/sendable-chooser.png differ diff --git a/source/docs/software/dashboards/glass/images/sent-trajectory.png b/source/docs/software/dashboards/glass/images/sent-trajectory.png index 4277f45608..221c283d18 100644 Binary files a/source/docs/software/dashboards/glass/images/sent-trajectory.png and b/source/docs/software/dashboards/glass/images/sent-trajectory.png differ diff --git a/source/docs/software/dashboards/glass/images/subsystem.png b/source/docs/software/dashboards/glass/images/subsystem.png index 50a2703043..4ff31e6743 100644 Binary files a/source/docs/software/dashboards/glass/images/subsystem.png and b/source/docs/software/dashboards/glass/images/subsystem.png differ diff --git a/source/docs/software/dashboards/glass/images/unchecked-arrow-trajectory.png b/source/docs/software/dashboards/glass/images/unchecked-arrow-trajectory.png index c3ef1dd077..4cb153edfa 100644 Binary files a/source/docs/software/dashboards/glass/images/unchecked-arrow-trajectory.png and b/source/docs/software/dashboards/glass/images/unchecked-arrow-trajectory.png differ diff --git a/source/docs/software/dashboards/glass/images/vs-code-glass-launch.png b/source/docs/software/dashboards/glass/images/vs-code-glass-launch.png index 3875f0483a..2ff70b8731 100644 Binary files a/source/docs/software/dashboards/glass/images/vs-code-glass-launch.png and b/source/docs/software/dashboards/glass/images/vs-code-glass-launch.png differ diff --git a/source/docs/software/dashboards/glass/index.rst b/source/docs/software/dashboards/glass/index.rst index 6bb981d77c..6d99b89d80 100644 --- a/source/docs/software/dashboards/glass/index.rst +++ b/source/docs/software/dashboards/glass/index.rst @@ -1,5 +1,4 @@ -Glass -===== +# Glass Glass is a new dashboard and robot data visualization tool. Its GUI is extremely similar to that of the :ref:`Simulation GUI `. In its current state, it is meant to be used as a programmer's tool rather than a proper dashboard in a competition environment. diff --git a/source/docs/software/dashboards/glass/introduction.rst b/source/docs/software/dashboards/glass/introduction.rst index d05916fbee..4968ee82fb 100644 --- a/source/docs/software/dashboards/glass/introduction.rst +++ b/source/docs/software/dashboards/glass/introduction.rst @@ -1,10 +1,8 @@ -Introduction to Glass -===================== +# Introduction to Glass Glass is a new dashboard and robot data visualization tool. It supports many of the same :ref:`widgets ` that the Simulation GUI supports, including robot pose visualization and advanced plotting. In its current state, it is meant to be used as a programmer's tool for debugging and not as a dashboard for competition use. -Opening Glass -------------- +## Opening Glass Glass can be launched by selecting the ellipsis menu (:guilabel:`...`) in VS Code, clicking on :guilabel:`Start Tool` and then choosing :guilabel:`Glass`. @@ -12,8 +10,7 @@ Glass can be launched by selecting the ellipsis menu (:guilabel:`...`) in VS Cod .. note:: You can also launch Glass directly by navigating to ``~/wpilib/YYYY/tools`` and running ``Glass.py`` (Linux and macOS) or by using the shortcut inside the WPILib Tools desktop folder (Windows). -Changing View Settings ----------------------- +## Changing View Settings The :guilabel:`View` menu item contains :guilabel:`Zoom` and :guilabel:`Style` settings that can be customized. The :guilabel:`Zoom` option dictates the size of the text in the application whereas the :guilabel:`Style` option allows you to select between the ``Classic``, ``Light``, and ``Dark`` modes. @@ -21,8 +18,7 @@ An example of the ``Dark`` style setting is below: .. image:: images/glass-dark-mode.png -Clearing Application Data -------------------------- +## Clearing Application Data Application data for Glass, including widget sizes and positions as well as other custom information for widgets is stored in a ``glass.ini`` file. The location of this file varies based on your operating system: diff --git a/source/docs/software/dashboards/glass/mech2d-widget.rst b/source/docs/software/dashboards/glass/mech2d-widget.rst index 3a93c7706f..2454cab1de 100644 --- a/source/docs/software/dashboards/glass/mech2d-widget.rst +++ b/source/docs/software/dashboards/glass/mech2d-widget.rst @@ -1,83 +1,104 @@ -The Mechanism2d Widget -====================== +# The Mechanism2d Widget Glass supports displaying stick-figure representations of your robot's mechanisms using the :guilabel:`Mechanism2d` widget. It supports combinations of ligaments that can rotate and / or extend or retract, such as arms and elevators and they can be combined for more complicated mechanisms. An instance of the ``Mechanism2d`` class should be created and populated, sent over NetworkTables, and updated periodically with the latest mechanism states in your robot code. It can also be used with the :doc:`Physics Simulation ` to visualize and program your robot's mechanisms before the robot is built. -Creating and Configuring the Mechanism2d Instance -------------------------------------------------- +## Creating and Configuring the Mechanism2d Instance The ``Mechanism2d`` object is the "canvas" where the mechanism is drawn. The root node is where the mechanism is anchored to ``Mechanism2d``. For a single jointed arm this would the pivot point. For an elevator, this would be where it's attached to the robot's base. To get a root node (represented by a ``MechanismRoot2d`` object), call ``getRoot(name, x, y)`` on the container ``Mechanism2d`` object. The name is used to name the root within NetworkTables, and should be unique, but otherwise isn't important. The ``x / y`` coordinate system follows the same orientation as ``Field2d`` - ``(0,0)`` is bottom left. -In the examples below, an elevator is drawn, with a rotational wrist on top of the elevator. The full Mechanism2d example is available in `Java `__ / `C++ `__ +In the examples below, an elevator is drawn, with a rotational wrist on top of the elevator. The full Mechanism2d example is available in [Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mechanism2d/Robot.java) / [C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/Mechanism2d/cpp/Robot.cpp) .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mechanism2d/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mechanism2d/Robot.java :language: java :lines: 43-46 :linenos: :lineno-start: 43 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/Mechanism2d/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/Mechanism2d/cpp/Robot.cpp + :language: c++ :lines: 59-62 :linenos: :lineno-start: 59 + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/2024.0.0b4/Mechanism2d/robot.py + :language: python + :lines: 32-35 + :linenos: + :lineno-start: 32 + Each ``MechanismLigament2d`` object represents a stage of the mechanism. It has a three required parameters, a name, an initial length to draw (relative to the size of the ``Mechanism2d`` object), and an initial angle to draw the ligament in degrees. Ligament angles are relative to the parent ligament, and follow math notation - the same as :ref:`Rotation2d ` (counterclockwise-positive). A ligament based on the root with an angle of zero will point right. Two optional parameters let you change the width (also relative to the size of the Mechanism2d object) and the color. Call ``append()``/``Append()`` on a root node or ligament node to add another node to the figure. In Java, pass a constructed ``MechanismLigament2d`` object to add it. In C++, pass the construction parameters in order to construct and add a ligament. .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mechanism2d/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mechanism2d/Robot.java :language: java :lines: 48-53 :linenos: :lineno-start: 48 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/Mechanism2d/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/Mechanism2d/cpp/Robot.cpp + :language: c++ :lines: 63-69 :linenos: :lineno-start: 63 + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/2024.0.0b4/Mechanism2d/robot.py + :language: python + :lines: 37-44 + :linenos: + :lineno-start: 37 + Then, publish the ``Mechanism2d`` object to NetworkTables: .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mechanism2d/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mechanism2d/Robot.java :language: java :lines: 55-56 :linenos: :lineno-start: 55 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/Mechanism2d/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/Mechanism2d/cpp/Robot.cpp + :language: c++ :lines: 36-37 :linenos: :lineno-start: 36 + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/2024.0.0b4/Mechanism2d/robot.py + :language: python + :lines: 46-47 + :linenos: + :lineno-start: 46 + .. note:: The ``Mechanism2d`` instance can also be sent using a lower-level NetworkTables API or using the :ref:`Shuffleboard API `. In this case, the ``SmartDashboard`` API was used, meaning that the :guilabel:`Mechanism2d` widget will appear under the ``SmartDashboard`` table name. To manipulate a ligament angle or length, call ``setLength()`` or ``setAngle()`` on the ``MechanismLigament2d`` object. When manipulating ligament length based off of sensor measurements, make sure to add the minimum length to prevent 0-length (and therefore invisible) ligaments. .. tab-set-code:: - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mechanism2d/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mechanism2d/Robot.java :language: java :lines: 59-64 :linenos: :lineno-start: 59 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/Mechanism2d/cpp/Robot.cpp - :language: cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/Mechanism2d/cpp/Robot.cpp + :language: c++ :lines: 40-45 :linenos: :lineno-start: 40 -Viewing the Mechanism2d in Glass --------------------------------- + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/2024.0.0b4/Mechanism2d/robot.py + :language: python + :lines: 49-54 + :linenos: + :lineno-start: 49 + +## Viewing the Mechanism2d in Glass After sending the ``Mechanism2d`` instance over NetworkTables, the :guilabel:`Mechanism2d` widget can be added to Glass by selecting :guilabel:`NetworkTables` in the menu bar, choosing the table name that the instance was sent over, and then clicking on the :guilabel:`Field` button. @@ -87,7 +108,14 @@ Once the widget appears as shown below, you can resize and place it on the Glass .. image:: images/mechanism2d-widget.png -Next Steps ----------- +## Viewing the Mechanism2d in AdvantageScope + +:ref:`AdvantageScope ` is an alternative option for viewing a ``Mechanism2d`` object, including data recorded to a log file using :ref:`WPILib data logs `. Both 2D and 3D visualizations are supported. See the documentation for the [mechanism](https://docs.advantagescope.org/tab-reference/mechanism) and [3D field](https://docs.advantagescope.org/tab-reference/3d-field) tabs for more details. + +.. image:: images/advantagescope-mechanism.png + :alt: Screenshot of an AdvantageScope window displaying a robot and mechanism in 3D. + :height: 500 + +## Next Steps -As mentioned above, the Mechanism2d visualization can be combined with :doc:`Physics Simulation ` to help you program mechanisms before your robot is built. The ArmSimulation (`Java `__ / `C++ `__) and ElevatorSimulation (`Java `__ / `C++ `__) examples combine physics simulation and Mechanism2d visualization so that you can practice programming a single jointed arm and elevator without a robot. +As mentioned above, the Mechanism2d visualization can be combined with :doc:`Physics Simulation ` to help you program mechanisms before your robot is built. The ArmSimulation ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java) / [C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp) / [Python](https://github.com/robotpy/examples/blob/2024.0.0b4/ArmSimulation/robot.py)) and ElevatorSimulation ([Java](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java) / [C++](https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/Robot.cpp) / [Python](https://github.com/robotpy/examples/blob/2024.0.0b4/ElevatorSimulation/robot.py)) examples combine physics simulation and Mechanism2d visualization so that you can practice programming a single jointed arm and elevator without a robot. diff --git a/source/docs/software/dashboards/glass/networktables-connection.rst b/source/docs/software/dashboards/glass/networktables-connection.rst index f4d4985089..c82626b8be 100644 --- a/source/docs/software/dashboards/glass/networktables-connection.rst +++ b/source/docs/software/dashboards/glass/networktables-connection.rst @@ -1,10 +1,8 @@ -Establishing NetworkTables Connections -====================================== +# Establishing NetworkTables Connections Glass uses the :ref:`NetworkTables ` protocol to establish a connection with your robot program. It is also used to transmit and receive data to and from the robot. -Connecting to a Robot ---------------------- +## Connecting to a Robot When Glass is first launched, you will see two widgets -- :guilabel:`NetworkTables Settings` and :guilabel:`NetworkTables`. To connect to a robot, select :guilabel:`Client` under :guilabel:`Mode` in the :guilabel:`NetworkTables Settings` widget, enter your team number and click on :guilabel:`Apply`. @@ -16,8 +14,7 @@ You can also connect to a robot that is running in simulation on your computer ( .. important:: The NetworkTables connection status is always visible on the title bar of the Glass application. -Viewing NetworkTables Entries ------------------------------ +## Viewing NetworkTables Entries The :guilabel:`NetworkTables` widget can be used to view all entries that are being sent over NetworkTables. These entries are hierarchically arranged by main table, sub-table, and so on. diff --git a/source/docs/software/dashboards/glass/plots.rst b/source/docs/software/dashboards/glass/plots.rst index aa773018f7..a2b9d60f74 100644 --- a/source/docs/software/dashboards/glass/plots.rst +++ b/source/docs/software/dashboards/glass/plots.rst @@ -1,17 +1,14 @@ -Plots -===== +# Plots Glass excels at high-performance, comprehensive plotting of data from NetworkTables. Some features include resizable plots, plots with multiple y axes and the ability to pause, examine, and resume plots. -Creating a Plot ---------------- +## Creating a Plot A new plot widget can be created by selecting the :guilabel:`Plot` button on the main menu bar and then clicking on :guilabel:`New Plot Window`. Several individual plots can be added to each plot window. To add a plot within a plot window, click the :guilabel:`Add plot` button inside the widget. Then you can drag various sources from the :guilabel:`NetworkTables` widget into the plot: .. image:: images/drag-plot.png -Manipulating Plots ------------------- +## Manipulating Plots You can click and drag on the plot to move around and scroll on top of the plot to zoom the y axes in and out. Double clicking on the graph will autoscale it so that the zoom and axis limits fit all of the data it is plotting. Furthermore, right-clicking on the plot will present you with a plethora of options, including whether you want to display secondary and tertiary y axes, if you wish to lock certain axes, etc. @@ -24,3 +21,10 @@ If you choose to make secondary and tertiary y axes available, you can drag data Then, you can lock certain axes so that their range always remains constant, regardless of panning. In this example, the secondary axis range (with the ``/SmartDashboard/Kp`` entry) was locked between 9 and 12. .. image:: images/locked-axis.png + +## Plotting with AdvantageScope + +:ref:`AdvantageScope ` is an alternative option for creating plots, including from data recorded to a log file using :ref:`WPILib data logs `. See the documentation for the [line graph](https://docs.advantagescope.org/tab-reference/line-graph) tab for more details. + +.. image:: images/advantagescope-plot.png + :alt: Screenshot of an AdvantageScope window displaying a line graph with several numeric and discrete fields. diff --git a/source/docs/software/dashboards/glass/widgets.rst b/source/docs/software/dashboards/glass/widgets.rst index 1cb3a5fe10..fd1dc84433 100644 --- a/source/docs/software/dashboards/glass/widgets.rst +++ b/source/docs/software/dashboards/glass/widgets.rst @@ -1,5 +1,4 @@ -Glass Widgets -============= +# Glass Widgets Specialized widgets are available for certain types that exist in robot code. These include objects that are manually sent over NetworkTables such as ``SendableChooser`` instances, or hardware that is automatically sent over :ref:`LiveWindow `. @@ -7,8 +6,7 @@ Specialized widgets are available for certain types that exist in robot code. Th .. note:: A widget can be renamed by right-clicking on its header and specifying a new name. -Hardware Widgets ----------------- +## Hardware Widgets Widgets for specific hardware (such as motor controllers) are usually available via LiveWindow. These can be accessed by selecting the :guilabel:`NetworkTables` menu option, clicking on :guilabel:`LiveWindow` and choosing the desired widget. @@ -26,19 +24,23 @@ Here is an example of the widget for gyroscopes: .. image:: images/gyro.png :alt: Gyro widget with both text and dial visualizations of the current gyro angle. Current Gyro Angle in degrees is -60.9704. -Sendable Chooser Widget ------------------------ +## Sendable Chooser Widget The :guilabel:`Sendable Chooser` widget represents a ``SendableChooser`` instance from robot code. It is often used to select autonomous modes. Like other dashboards, your ``SendableChooser`` instance simply needs to be sent using a NetworkTables API. The simplest is to use something like ``SmartDashboard``: .. tab-set-code:: - .. code-block:: java + ```java + SmartDashboard.putData("Auto Selector", m_selector); + ``` - SmartDashboard.putData("Auto Selector", m_selector); + ```c++ + frc::SmartDashboard::PutData("Auto Selector", &m_selector); + ``` - .. code-block:: c++ - - frc::SmartDashboard::PutData("Auto Selector", &m_selector); + ```python + from wpilib import SmartDashboard + SmartDashboard.putData("Auto Selector", selector) + ``` .. note:: For more information on creating a ``SendableChooser``, please see :ref:`this document `. @@ -46,28 +48,31 @@ The :guilabel:`Sendable Chooser` widget will appear in the :guilabel:`NetworkTab .. image:: images/sendable-chooser.png -PID Controller Widget ---------------------- +## PID Controller Widget The :guilabel:`PID Controller` widget allows you to quickly tune PID values for a certain controller. A ``PIDController`` instance must be sent using a NetworkTables API. The simplest is to use something like ``SmartDashboard``: .. tab-set-code:: - .. code-block:: java - - SmartDashboard.putData("Elevator PID Controller", m_elevatorPIDController); + ```java + SmartDashboard.putData("Elevator PID Controller", m_elevatorPIDController); + ``` - .. code-block:: c++ + ```c++ + frc::SmartDashboard::PutData("Elevator PID Controller", &m_elevatorPIDController); + ``` - frc::SmartDashboard::PutData("Elevator PID Controller", &m_elevatorPIDController); + ```python + from wpilib import SmartDashboard + SmartDashboard.putData("Elevator PID Controller", elevatorPIDController) + ``` This allows you to quickly tune P, I, and D values for various setpoints. .. image:: images/pid.png :alt: PID widget for the Elevator PID Controller. P = 3.0, I = 0.001, D = 0.050, Setpoint = 0.0. -FMSInfo Widget --------------- +## FMSInfo Widget -The :guilabel:`FMSInfo` widget is created by default when Glass connects to a robot. This widget displays basic information about the robot's enabled state, whether a Driver Station is connected, whether an FMS is connected, the game-specific data, etc. It can be viewed by selecting the :guilabel:`NetworkTables` menu item and clicking on :guilabel:`FMSInfo`. +The :guilabel:`FMSInfo` widget is created by default when Glass connects to a robot. This widget displays basic information about the robot's enabled state, whether a Driver Station is connected, whether an :term:`FMS` is connected, the game-specific data, etc. It can be viewed by selecting the :guilabel:`NetworkTables` menu item and clicking on :guilabel:`FMSInfo`. .. image:: images/fms-info.png diff --git a/source/docs/software/dashboards/images/advantagescope.png b/source/docs/software/dashboards/images/advantagescope.png new file mode 100644 index 0000000000..3d60569a43 Binary files /dev/null and b/source/docs/software/dashboards/images/advantagescope.png differ diff --git a/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/advantagescope-connection.png b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/advantagescope-connection.png new file mode 100644 index 0000000000..3aa05058da Binary files /dev/null and b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/advantagescope-connection.png differ diff --git a/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/black-diamonds.png b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/black-diamonds.png new file mode 100644 index 0000000000..62f24fca8e Binary files /dev/null and b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/black-diamonds.png differ diff --git a/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/connection-indicator.png b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/connection-indicator.png new file mode 100644 index 0000000000..32e89afbac Binary files /dev/null and b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/connection-indicator.png differ diff --git a/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/glass-connection.png b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/glass-connection.png new file mode 100644 index 0000000000..fcf868a2b8 Binary files /dev/null and b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/glass-connection.png differ diff --git a/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/labview-dashboard-connection.png b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/labview-dashboard-connection.png new file mode 100644 index 0000000000..ea3db3d87c Binary files /dev/null and b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/labview-dashboard-connection.png differ diff --git a/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/shuffleboard-connection.png b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/shuffleboard-connection.png new file mode 100644 index 0000000000..463c75a016 Binary files /dev/null and b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/shuffleboard-connection.png differ diff --git a/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/smartdashboard-connection.png b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/smartdashboard-connection.png new file mode 100644 index 0000000000..7f959207cb Binary files /dev/null and b/source/docs/software/dashboards/images/troubleshooting-dashboard-connectivity/smartdashboard-connection.png differ diff --git a/source/docs/software/dashboards/index.rst b/source/docs/software/dashboards/index.rst index e0e630dc31..a6d22fa5a5 100644 --- a/source/docs/software/dashboards/index.rst +++ b/source/docs/software/dashboards/index.rst @@ -1,12 +1,12 @@ -Dashboards -========== - -Click on each dashboard below to get a description of its advantages and disadvantages. +# Dashboards .. toctree:: :maxdepth: 1 + dashboard-intro shuffleboard/index smartdashboard/index glass/index + advantagescope.rst labview-dashboard/index + troubleshooting-dashboard-connectivity.rst diff --git a/source/docs/software/dashboards/labview-dashboard/driver-station-labview-dashboard.rst b/source/docs/software/dashboards/labview-dashboard/driver-station-labview-dashboard.rst index 13e287ba66..e85db145a4 100644 --- a/source/docs/software/dashboards/labview-dashboard/driver-station-labview-dashboard.rst +++ b/source/docs/software/dashboards/labview-dashboard/driver-station-labview-dashboard.rst @@ -1,12 +1,10 @@ .. include:: -FRC LabVIEW Dashboard -===================== +# FRC LabVIEW Dashboard The Dashboard application installed and launched by the FRC\ |reg| Driver Station is a LabVIEW program designed to provide teams with basic feedback from their robot, with the ability to expand and customize the information to suit their needs. This Dashboard application uses :term:`NetworkTables` and contains a variety of tools that teams may find useful. -LabVIEW Dashboard ------------------ +## LabVIEW Dashboard .. image:: images/driver-station-labview-dashboard/labview-dashboard.png :alt: Default screen of the LabVIEW Dashboard. @@ -24,8 +22,7 @@ The Dashboard is broken into two main sections. The left pane is for displaying The LabVIEW Dashboard also includes Record/Playback functionality, located in the bottom right. More detail about this feature is included below under `Record/Playback`_. -Camera Image and Controls -------------------------- +## Camera Image and Controls .. image:: images/driver-station-labview-dashboard/camera-image-and-controls.png :alt: Shows the main camera image on the left pane of the dashboard. @@ -33,15 +30,14 @@ Camera Image and Controls The left pane is used to display a video feed from acamera located on the robot. There are also some controls and indicators related to the camera below the tab area: 1. Camera Image Display -2. Mode Selector - This drop-down allows you to select the type of camera display to use. The choices are Camera Off, USB Camera SW (software compression), USB Camera HW (hardware compression) and IP Camera (Axis camera). Note that the IP Camera setting will not work when your PC is connected to the roboRIO over USB. +2. Mode Selector - This drop-down allows you to select the type of camera display to use. The choices are Camera Off, USB Camera SW (software compression), USB Camera HW (hardware compression) and IP Camera. Note that the IP Camera setting will not work when your PC is connected to the roboRIO over USB. 3. Camera Settings - This control allows you to change the resolution, framerate and compression of the image stream to the dashboard, click the control to pop-up the configuration. 4. Bandwidth Indicator - Indicates approximate bandwidth usage of the image stream. The indicator will display green for "safe" bandwidth usage, yellow when teams should use caution and red if the stream bandwidth is beyond levels that will work on the competition field. 5. Framerate - Indicates the approximate received framerate of the image stream. .. tip:: The bandwidth indicator indicates the combined bandwidth for all camera streams open. -Drive ------ +## Drive .. image:: images/driver-station-labview-dashboard/drive.png :alt: The "Drive" tab is the first on the right side. @@ -56,8 +52,7 @@ The center pane contains a section that provides feedback on the joysticks and d These indicators (other than the Gyro) are hooked up to appropriate values by default when using the LabVIEW framework. For information on using them with C++/Java code see :doc:`using-the-labview-dashboard-with-c++-java-code`. -Camera ------- +## Camera .. tip:: The left pane can only display a single camera output, so use the camera tab on the right pane to display a second camera output if needed. @@ -67,79 +62,70 @@ Camera The camera tab is used to display a video feed from a camera located on the robot. There are also some controls and indicators related to the camera below the tab area: 1. Camera Image Display -2. Mode Selector - This drop-down allows you to select the type of camera display to use. The choices are Camera Off, USB Camera SW (software compression), USB Camera HW (hardware compression) and IP Camera (Axis camera). Note that the IP Camera setting will not work when your PC is connected to the roboRIO over USB. +2. Mode Selector - This drop-down allows you to select the type of camera display to use. The choices are Camera Off, USB Camera SW (software compression), USB Camera HW (hardware compression) and IP Camera. Note that the IP Camera setting will not work when your PC is connected to the roboRIO over USB. 3. Camera Settings - This control allows you to change the resolution, framerate and compression of the image stream to the dashboard, click the control to pop-up the configuration. 4. Bandwidth Indicator - Indicates approximate bandwidth usage of the image stream. The indicator will display green for "safe" bandwidth usage, yellow when teams should use caution and red if the stream bandwidth is beyond levels that will work on the competition field. 5. Framerate - Indicates the approximate received framerate of the image stream. .. tip:: The bandwidth indicator indicates the combined bandwidth for all camera streams open. -Basic ------ +## Basic .. image:: images/driver-station-labview-dashboard/basic.png :alt: The "Basic" tab is the third on the right side. The Basic tab contains a variety of pre-populated bi-directional controls/indicators which can be used to control the robot or display information from the robot. The SmartDashboard key names associated with each item are labeled next to the indicator with the exception of the Strings which follow the same naming pattern and increment from DB/String 0 to DB/String 4 on the left and DB/String 5 to DB/String 9 on the right. The LabVIEW framework contains an example of reading from the Buttons and Sliders in Teleop. It also contains an example of customizing the labels in Begin. For more detail on using this tab with C++\Java code, see :doc:`using-the-labview-dashboard-with-c++-java-code`. -Custom ------- +## Custom .. image:: images/driver-station-labview-dashboard/custom.png :alt: The "Custom" tab is the fourth on the right side. The Custom tab allows you to add additional controls/indicators to the dashboard using LabVIEW without removing any existing functionality. To customize this tab you will need to create a Dashboard project in LabVIEW. -Test ----- +## Test .. image:: images/driver-station-labview-dashboard/test.png :alt: The "Test" tab is the fifth on the right side. The Test tab is for use with Test mode for teams using LabVIEW (Java and C++ teams should use SmartDashboard or Shuffleboard when using Test Mode). For many items in the libraries, Input/Output info will be populated here automatically. All items which have ** next to them are outputs that can be controlled by the dashboard. To control an output, click on it to select it, drag the slider to set the value then press and hold the green button to enable the output. As soon as the green button is released, the output will be disabled. This tab can also be used to run and monitor tests on the robot. An example test is provided in the LabVIEW framework. Selecting this test from the dropdown box will show the status of the test in place of the slider and enable controls. -Commands --------- +## Commands .. image:: images/driver-station-labview-dashboard/commands.png :alt: The "Commands" tab is the sixth on the right side. The Commands tab can be used with the Robot in Test mode to see which commands are running and to manually run commands for test purposes. -Checklist ---------- +## Checklist .. image:: images/driver-station-labview-dashboard/checklist.png :alt: The "Checklist" tab is the seventh on the right side. The Checklist tab can be used by teams to create a list of tasks to perform before or between matches. Instructions for using the Checklist tab are pre-populated in the default checklist file. -Variables ---------- +## Variables .. image:: images/driver-station-labview-dashboard/variables.png :alt: The "Variables" tab is the eighth on the right side. The Variables tab of the left pane shows all NetworkTables variables in a tree display. The Variable Name (Key), Value and data type are shown for each variable. Information about the NetworkTables bandwidth usage is also displayed in this tab. Entries will be shown with black diamonds if they are not currently synced with the robot. -Record/Playback ---------------- +## Record/Playback .. image:: images/driver-station-labview-dashboard/record-playback.png :alt: Highlights the green triangle, red circle, and red square that control dashboard playback. The LabVIEW Dashboard includes a Record/Playback feature that allows you to record video and NetworkTables data (such as the state of your Dashboard indicators) and play it back later. -Recording -^^^^^^^^^ +### Recording .. image:: images/driver-station-labview-dashboard/recording.png :alt: After hitting the red circle record button the bottom bar becomes red to indicate it is recording. To begin recording, click the red circular Record button. The background of the right pane will turn red to indicate you are recording. To stop recording, press the red square Stop button. -Playback -^^^^^^^^ +### Playback .. image:: images/driver-station-labview-dashboard/playback.png :alt: The green triangle will start playback which turns the bottom bar green. diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/autonomous-selection-box-and-connection-indicator.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/autonomous-selection-box-and-connection-indicator.png index 568a8e2422..30a72d3942 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/autonomous-selection-box-and-connection-indicator.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/autonomous-selection-box-and-connection-indicator.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/basic.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/basic.png index e25861cf5f..5d22dd0611 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/basic.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/basic.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/camera-image-and-controls.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/camera-image-and-controls.png index 1c2751fa16..3e6fb4dfc6 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/camera-image-and-controls.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/camera-image-and-controls.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/camera.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/camera.png index b3efb5bd63..cfba85daea 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/camera.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/camera.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/checklist.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/checklist.png index 2c4d153cef..fbf29db745 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/checklist.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/checklist.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/commands.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/commands.png index 6fb2c33c08..1d6a82fa26 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/commands.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/commands.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/custom.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/custom.png index 647d6c0ddb..6bf77f30c2 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/custom.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/custom.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/drive.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/drive.png index fff36bfc6d..badfe726ce 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/drive.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/drive.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/labview-dashboard.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/labview-dashboard.png index 838ecc3ed2..c42c2a2203 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/labview-dashboard.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/labview-dashboard.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/playback.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/playback.png index 5f0ea6a945..2564a6f370 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/playback.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/playback.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/record-playback.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/record-playback.png index 536eba5b7f..9f3674e187 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/record-playback.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/record-playback.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/recording.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/recording.png index 7915fac5d4..6462263311 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/recording.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/recording.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/test.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/test.png index e358740386..92292b6ab5 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/test.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/test.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/variables.png b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/variables.png index c70c8a74ce..d2b67f0fc6 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/variables.png and b/source/docs/software/dashboards/labview-dashboard/images/driver-station-labview-dashboard/variables.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/black-diamonds.png b/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/black-diamonds.png deleted file mode 100644 index 2d084c42ff..0000000000 Binary files a/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/black-diamonds.png and /dev/null differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/connection-indicator.png b/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/connection-indicator.png deleted file mode 100644 index 9dd746b870..0000000000 Binary files a/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/connection-indicator.png and /dev/null differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/glass-connection.png b/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/glass-connection.png deleted file mode 100644 index d5757a879a..0000000000 Binary files a/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/glass-connection.png and /dev/null differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/shuffleboard-connection.png b/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/shuffleboard-connection.png deleted file mode 100644 index 1befd631e5..0000000000 Binary files a/source/docs/software/dashboards/labview-dashboard/images/troubleshooting-dashboard-connectivity/shuffleboard-connection.png and /dev/null differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/basic-tab.png b/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/basic-tab.png index a92f9875d9..fea58f302c 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/basic-tab.png and b/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/basic-tab.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/buttons-and-leds.png b/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/buttons-and-leds.png index ce5075a20d..dc199a3eed 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/buttons-and-leds.png and b/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/buttons-and-leds.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/drive-tab.png b/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/drive-tab.png index 9cca2a9c91..150eb99a01 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/drive-tab.png and b/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/drive-tab.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/sliders.png b/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/sliders.png index c823ea7eb3..5916b36cbc 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/sliders.png and b/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/sliders.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/strings.png b/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/strings.png index 7d2a837ba5..0fc02bde3b 100644 Binary files a/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/strings.png and b/source/docs/software/dashboards/labview-dashboard/images/using-the-labview-dashboard-with-c++-java-code/strings.png differ diff --git a/source/docs/software/dashboards/labview-dashboard/index.rst b/source/docs/software/dashboards/labview-dashboard/index.rst index 75cc6d466f..9a73507f7c 100644 --- a/source/docs/software/dashboards/labview-dashboard/index.rst +++ b/source/docs/software/dashboards/labview-dashboard/index.rst @@ -1,11 +1,10 @@ -LabVIEW Dashboard -================= +# LabVIEW Dashboard -The LabVIEW Dashboard is easy to use and provides a lot of features straight out of the box like: camera streams, autonomous selection, and joystick feedback. It can be customized using LabVIEW by creating a new Dashboard project. While it :ref:`can be used ` by Java or C++ teams, they generally prefer SmartDashboard or Shuffleboard which can be customized in their respective language. +The LabVIEW Dashboard is easy to use and provides a lot of features straight out of the box like: camera streams, autonomous selection, and joystick feedback. It can be customized using LabVIEW by creating a new Dashboard project. While it :ref:`can be used ` by Java, C++, or Python teams, they generally prefer SmartDashboard or Shuffleboard which can be customized in their respective language. .. toctree:: :maxdepth: 1 driver-station-labview-dashboard using-the-labview-dashboard-with-c++-java-code - troubleshooting-dashboard-connectivity + ../troubleshooting-dashboard-connectivity diff --git a/source/docs/software/dashboards/labview-dashboard/troubleshooting-dashboard-connectivity.rst b/source/docs/software/dashboards/labview-dashboard/troubleshooting-dashboard-connectivity.rst deleted file mode 100644 index 7910922e42..0000000000 --- a/source/docs/software/dashboards/labview-dashboard/troubleshooting-dashboard-connectivity.rst +++ /dev/null @@ -1,64 +0,0 @@ -Troubleshooting Dashboard Connectivity -====================================== - -We have received a number of reports of Dashboard connectivity issues from events. This document will help explain how to recognize if the Dashboard is not connected to your robot, steps to troubleshoot this condition and a code modification you can make. - -LabVIEW Dashboard ------------------ - -This section discusses connectivity between the robot and LabVIEW dashboard - -Recognizing LabVIEW Dashboard Connectivity -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: images/troubleshooting-dashboard-connectivity/black-diamonds.png - :alt: Highlights black diamonds next to the variables indicating they are not synced to the robot. - -If you have an indicator on your dashboard that you expect to be changing it may be fairly trivial to recognize if the Dashboard is connected. If not, there is a way to check without making any changes to your robot code. On the Variables tab of the Dashboard, the variables are shown with a black diamond when they are not synced with the robot. Once the Dashboard connects to the robot and these variables are synced, the diamond will disappear. - -Troubleshooting LabVIEW Dashboard Connectivity -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If the Dashboard does not connect to the Robot (after the Driver Station has connected to the robot) the recommended troubleshooting steps are: - -1. Close the Driver Station and Dashboard, then re-open the Driver Station (which should launch the Dashboard). - -2. If that doesn't work, restart the Robot Code using the Restart Robot Code button on the Diagnostics tab of the Driver Station - -Recognizing Connectivity ------------------------- - -This section discusses connectivity between the robot and SmartDashboard - -Recognizing SmartDashboard Connectivity -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: images/troubleshooting-dashboard-connectivity/connection-indicator.png - :alt: Click "View" then "Add..." then Connection indicator to place one on the SmartDashboard. - -The typical way to recognize connectivity with the SmartDashboard is to add a Connection Indicator widget and to make sure your code is writing at least one key during initialization or disabled to trigger the connection indicator. The connection indicator can be moved or re-sized if the Editable checkbox is checked. - -Recognizing Shuffleboard Connectivity -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: images/troubleshooting-dashboard-connectivity/shuffleboard-connection.png - -Shuffleboard indicates if it is connected or not in the bottom right corner of the application as shown in the image above. - -Recognizing Glass Connectivity -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: images/troubleshooting-dashboard-connectivity/glass-connection.png - -Glass displays if it is connected or not in the bar across the top. See this :ref:`page ` for more on configuring the connection. - -Troubleshooting Connectivity -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If the Dashboard does not connect to the Robot (after the Driver Station has connected to the robot) the recommended troubleshooting steps are: - -1. Restart the Dashboard (there is no need to restart the Driver Station software) - -2. If that doesn't work, restart the Robot Code using the Restart Robot Code button on the Diagnostics tab of the Driver Station - -3. If it still doesn't connect, verify that the Team Number / Server is set properly in the Dashboard and that your Robot Code writes a value during initialization or disabled diff --git a/source/docs/software/dashboards/labview-dashboard/using-the-labview-dashboard-with-c++-java-code.rst b/source/docs/software/dashboards/labview-dashboard/using-the-labview-dashboard-with-c++-java-code.rst index fbb371cf00..36eaf71ef2 100644 --- a/source/docs/software/dashboards/labview-dashboard/using-the-labview-dashboard-with-c++-java-code.rst +++ b/source/docs/software/dashboards/labview-dashboard/using-the-labview-dashboard-with-c++-java-code.rst @@ -1,10 +1,8 @@ -Using the LabVIEW Dashboard with C++/Java Code -============================================== +# Using the LabVIEW Dashboard with C++, Java, or Python Code -The default LabVIEW Dashboard utilizes :term:`NetworkTables` to pass values and is therefore compatible with C++ and Java robot programs. This article covers the keys and value ranges to use to work with the Dashboard. +The default LabVIEW Dashboard utilizes :term:`NetworkTables` to pass values and is therefore compatible with C++, Java, and Python robot programs. This article covers the keys and value ranges to use to work with the Dashboard. -Drive Tab ---------- +## Drive Tab .. image:: images/using-the-labview-dashboard-with-c++-java-code/drive-tab.png @@ -12,69 +10,89 @@ The :guilabel:`Select Autonomous...` dropdown can be used so show the available .. tab-set-code:: - .. code-block:: java - - SmartDashboard.putStringArray("Auto List", {"Drive Forwards", "Drive Backwards", "Shoot"}); - - // At the beginning of auto - String autoName = SmartDashboard.getString("Auto Selector", "Drive Forwards") // This would make "Drive Forwards the default auto - switch(autoName) { - case "Drive Forwards": - // auto here - case "Drive Backwards": - // auto here - case "Shoot": - // auto here - } - - .. code-block:: c++ - - frc::SmartDashboard::PutStringArray("Auto List", {"Drive Forwards", "Drive Backwards", "Shoot"}); - - // At the beginning of auto - String autoName = SmartDashboard.GetString("Auto Selector", "Drive Forwards") // This would make "Drive Forwards the default auto - switch(autoName) { - case "Drive Forwards": - // auto here - case "Drive Backwards": - // auto here - case "Shoot": - // auto here - } + ```java + SmartDashboard.putStringArray("Auto List", {"Drive Forwards", "Drive Backwards", "Shoot"}); + // At the beginning of auto + String autoName = SmartDashboard.getString("Auto Selector", "Drive Forwards") // This would make "Drive Forwards the default auto + switch(autoName) { + case "Drive Forwards": + // auto here + case "Drive Backwards": + // auto here + case "Shoot": + // auto here + } + ``` + + ```c++ + frc::SmartDashboard::PutStringArray("Auto List", {"Drive Forwards", "Drive Backwards", "Shoot"}); + // At the beginning of auto + String autoName = SmartDashboard.GetString("Auto Selector", "Drive Forwards") // This would make "Drive Forwards the default auto + switch(autoName) { + case "Drive Forwards": + // auto here + case "Drive Backwards": + // auto here + case "Shoot": + // auto here + } + ``` + + ```python + from wpilib import SmartDashboard + SmartDashboard.putStringArray("Auto List", ["Drive Forwards", "Drive Backwards", "Shoot"]) + # At the beginning of auto + autoName = SmartDashboard.getString("Auto Selector", "Drive Forwards") # This would make "Drive Forwards the default auto + match autoName: + case "Drive Forwards": + # auto here + case "Drive Backwards": + # auto here + case "Shoot": + # auto here + ``` Sending to the "Gyro" NetworkTables entry will populate the gyro here. .. tab-set-code:: - .. code-block:: java - - SmartDashboard.putNumber("Gyro", drivetrain.getHeading()); + ```java + SmartDashboard.putNumber("Gyro", drivetrain.getHeading()); + ``` - .. code-block:: c++ + ```c++ + frc::SmartDashboard::PutNumber("Gyro", Drivetrain.GetHeading()); + ``` - frc::SmartDashboard::PutNumber("Gyro", Drivetrain.GetHeading()); + ```python + from wpilib import SmartDashboard + SmartDashboard.putNumber("Gyro", self.drivetrain.getHeading()) + ``` There are four outputs that show the motor power to the drivetrain. This is configured for 2 motors per side and a tank style drivetrain. This is done by setting "RobotDrive Motors" like the example below. .. tab-set-code:: - .. code-block:: java + ```java + SmartDashboard.putNumberArray("RobotDrive Motors", {drivetrain.getLeftFront(), drivetrain.getRightFront(), drivetrain.getLeftBack(), drivetrain.getRightBack()}); + ``` - SmartDashboard.putNumberArray("RobotDrive Motors", {drivetrain.getLeftFront(), drivetrain.getRightFront(), drivetrain.getLeftBack(), drivetrain.getRightBack()}); + ```c++ + frc::SmartDashboard::PutNumberArray("Gyro", {drivetrain.GetLeftFront(), drivetrain.GetRightFront(), drivetrain.GetLeftBack(), drivetrain.GetRightBack()}); + ``` - .. code-block:: c++ + ```python + from wpilib import SmartDashboard + SmartDashboard.putNumberArray("RobotDrive Motors", [self.drivetrain.getLeftFront(), self.drivetrain.getRightFront(), self.drivetrain.getLeftBack(), self.drivetrain.getRightBack()]) + ``` - frc::SmartDashboard::PutNumberArray("Gyro", {drivetrain.GetLeftFront(), drivetrain.GetRightFront(), drivetrain.GetLeftBack(), drivetrain.GetRightBack()}); - -Basic Tab ---------- +## Basic Tab .. image:: images/using-the-labview-dashboard-with-c++-java-code/basic-tab.png The Basic tab uses a number of keys in the a "DB" sub-table to send/receive Dashboard data. The LED's are output only, the other fields are all bi-directional (send or receive). -Strings -^^^^^^^ +### Strings .. image:: images/using-the-labview-dashboard-with-c++-java-code/strings.png @@ -82,28 +100,37 @@ The strings are labeled top-to-bottom, left-to-right from "DB/String 0" to "DB/S .. tab-set-code:: - .. code-block:: java - - SmartDashboard.putString("DB/String 0", "My 21 Char TestString"); + ```java + SmartDashboard.putString("DB/String 0", "My 21 Char TestString"); + ``` - .. code-block:: c++ + ```c++ + frc::SmartDashboard::PutString("DB/String 0", "My 21 Char TestString"); + ``` - frc::SmartDashboard::PutString("DB/String 0", "My 21 Char TestString"); + ```python + from wpilib import SmartDashboard + SmartDashboard.putString("DB/String 0", "My 21 Char TestString") + ``` To read string data entered on the Dashboard: .. tab-set-code:: - .. code-block:: java - - String dashData = SmartDashboard.getString("DB/String 0", "myDefaultData"); + ```java + String dashData = SmartDashboard.getString("DB/String 0", "myDefaultData"); + ``` - .. code-block:: c++ + ```c++ + std::string dashData = frc::SmartDashboard::GetString("DB/String 0", "myDefaultData"); + ``` - std::string dashData = frc::SmartDashboard::GetString("DB/String 0", "myDefaultData"); + ```python + from wpilib import SmartDashboard + dashData = SmartDashboard.getString("DB/String 0", "myDefaultData") + ``` -Buttons and LEDs -^^^^^^^^^^^^^^^^ +### Buttons and LEDs .. image:: images/using-the-labview-dashboard-with-c++-java-code/buttons-and-leds.png @@ -111,28 +138,37 @@ The Buttons and LEDs are boolean values and are labeled top-to-bottom from "DB/B .. tab-set-code:: - .. code-block:: java + ```java + SmartDashboard.putBoolean("DB/Button 0", true); + ``` - SmartDashboard.putBoolean("DB/Button 0", true); + ```c++ + frc::SmartDashboard::PutBoolean("DB/Button 0", true); + ``` - .. code-block:: c++ - - frc::SmartDashboard::PutBoolean("DB/Button 0", true); + ```python + from wpilib import SmartDashboard + SmartDashboard.putBoolean("DB/Button 0", true) + ``` To read from the Buttons: (default value is false) .. tab-set-code:: - .. code-block:: java - - boolean buttonValue = SmartDashboard.getBoolean("DB/Button 0", false); + ```java + boolean buttonValue = SmartDashboard.getBoolean("DB/Button 0", false); + ``` - .. code-block:: c++ + ```c++ + bool buttonValue = frc::SmartDashboard::GetBoolean("DB/Button 0", false); + ``` - bool buttonValue = frc::SmartDashboard::GetBoolean("DB/Button 0", false); + ```python + from wpilib import SmartDashboard + buttonValue = SmartDashboard.getBoolean("DB/Button 0", false) + ``` -Sliders -^^^^^^^ +### Sliders .. image:: images/using-the-labview-dashboard-with-c++-java-code/sliders.png @@ -140,22 +176,33 @@ The Sliders are bi-directional analog (double) controls/indicators with a range .. tab-set-code:: - .. code-block:: java - - SmartDashboard.putNumber("DB/Slider 0", 2.58); + ```java + SmartDashboard.putNumber("DB/Slider 0", 2.58); + ``` - .. code-block:: c++ + ```c++ + frc::SmartDashboard::PutNumber("DB/Slider 0", 2.58); + ``` - frc::SmartDashboard::PutNumber("DB/Slider 0", 2.58); + ```python + from wpilib import SmartDashboard + SmartDashboard.putNumber("DB/Slider 0", 2.58) + ``` To read values from the Dashboard into the robot program: (default value of 0.0) .. tab-set-code:: - .. code-block:: java + ```java + double dashData = SmartDashboard.getNumber("DB/Slider 0", 0.0); + ``` - double dashData = SmartDashboard.getNumber("DB/Slider 0", 0.0); + ```c++ + double dashData = frc::SmartDashboard::GetNumber("DB/Slider 0", 0.0); + ``` - .. code-block:: c++ + ```python + from wpilib import SmartDashboard + dashData = SmartDashboard.getNumber("DB/Slider 0", 0.0) + ``` - double dashData = frc::SmartDashboard::GetNumber("DB/Slider 0", 0.0); diff --git a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-1.png b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-1.png index c30b4f412f..96aa2e1b1a 100644 Binary files a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-1.png and b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-1.png differ diff --git a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-2.png b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-2.png index 495840e906..c56fec07ad 100644 Binary files a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-2.png and b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-2.png differ diff --git a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-3.png b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-3.png index 46f44d1fc8..5bcf8f2eed 100644 Binary files a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-3.png and b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/commands-subsystems-3.png differ diff --git a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-hierarchies/networktable.png b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-hierarchies/networktable.png index f482871334..24b83f904c 100644 Binary files a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-hierarchies/networktable.png and b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-hierarchies/networktable.png differ diff --git a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-hierarchies/smartdashboard-widget.png b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-hierarchies/smartdashboard-widget.png index ee80ffe8e0..2c3da5c5f9 100644 Binary files a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-hierarchies/smartdashboard-widget.png and b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-hierarchies/smartdashboard-widget.png differ diff --git a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-tuning-pid/pid-subsystem.png b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-tuning-pid/pid-subsystem.png index d0b015ffac..9c29ca04e5 100644 Binary files a/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-tuning-pid/pid-subsystem.png and b/source/docs/software/dashboards/shuffleboard/advanced-usage/images/shuffleboard-tuning-pid/pid-subsystem.png differ diff --git a/source/docs/software/dashboards/shuffleboard/advanced-usage/index.rst b/source/docs/software/dashboards/shuffleboard/advanced-usage/index.rst index efff674d02..c9216bee6b 100644 --- a/source/docs/software/dashboards/shuffleboard/advanced-usage/index.rst +++ b/source/docs/software/dashboards/shuffleboard/advanced-usage/index.rst @@ -1,5 +1,4 @@ -Shuffleboard - Advanced Usage -============================= +# Shuffleboard - Advanced Usage .. toctree:: :maxdepth: 1 diff --git a/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-commands-subsystems.rst b/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-commands-subsystems.rst index eb72869e76..2786243911 100644 --- a/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-commands-subsystems.rst +++ b/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-commands-subsystems.rst @@ -1,29 +1,31 @@ -Commands and Subsystems -======================= +# Commands and Subsystems When using the command-based framework Shuffleboard makes it easier to understand what the robot is doing by displaying the state of various commands and subsystems in real-time. -Displaying Subsystems ---------------------- +## Displaying Subsystems To see the status of a subsystem while the robot is operating in either autonomous or teleoperated modes, that is what its default command is and what command is currently using that subsystem, send a subsystem instance to Shuffleboard: .. tab-set-code:: - .. code-block:: java + ```java + SmartDashboard.putData(subsystem-reference); + ``` - SmartDashboard.putData(subsystem-reference); + ```c++ + SmartDashboard::PutData(subsystem-pointer); + ``` - .. code-block:: cpp - - SmartDashboard::PutData(subsystem-pointer); + ```python + from wpilib import SmartDashboard + SmartDashboard.putData(subsystem-reference) + ``` Shuffleboard will display the subsystem name, the default command associated with this subsystem, and the currently running command. In this example the default command for the Elevator subsystem is called ``AutonomousCommand`` and it is also the current command that is using the Elevator subsystem. .. image:: images/commands-subsystems-1.png -Subsystems in Test Mode ------------------------ +## Subsystems in Test Mode In Test mode (Test/Enabled in the driver station) subsystems may be displayed in the LiveWindow tab with the sensors and actuators of the subsystem. This is ideal for verifying of sensors are working by seeing the values that they are returning. In addition, actuators can be operated. For example, motors can be operated using sliders to set their commanded speed and direction. For PIDSubsystems the P, I, D, and F constants are displayed along with the setpoint and an enable control. This is useful for tuning PIDSubsystems by adjusting the constants, putting in a setpoint, and enabling the embedded PIDController. Then the mechanism's response can be observed. This cycle (change parameters, enable, and observe) can be repeated until a reasonable set of parameters is found. @@ -33,24 +35,28 @@ In Test mode (Test/Enabled in the driver station) subsystems may be displayed in More information on tuning PIDSubsystems can be found :doc:`here `. Using RobotBuilder will automatically generate the code to get the subsystem displayed in Test mode. The code that is necessary to have subsystems displayed is shown below where subsystem-name is a string containing the name of the subsystem: -.. code-block:: java - - setName(subsystem-name); +```java +setName(subsystem-name); +``` -Displaying Commands -------------------- +## Displaying Commands Using commands and subsystems makes very modular robot programs that can easily be tested and modified. Part of this is because commands can be written completely independently of other commands and can therefore be easily run from Shuffleboard. To write a command to Shuffleboard use the ``SmartDashboard.putData`` method as shown here: .. tab-set-code:: - .. code-block:: java - - SmartDashboard.putData("ElevatorMove: up", new ElevatorMove(2.7)); + ```java + SmartDashboard.putData("ElevatorMove: up", new ElevatorMove(2.7)); + ``` - .. code-block:: cpp + ```c++ + SmartDashboard::PutData("ElevatorMove: up", new ElevatorMove(2.7)); + ``` - SmartDashboard::PutData("ElevatorMove: up", new ElevatorMove(2.7)); + ```python + from wpilib import SmartDashboard + SmartDashboard.putData("ElevatorMove: up", ElevatorMove(2.7)) + ``` Shuffleboard will display the command name and a button to execute the command. In this way individual commands and command groups can easily be tested without needing special test code in a robot program. In the image below there are a number of commands contained in a Shuffleboard list. Pressing the button once runs the command and pressing it again stops the command. To use this feature the robot must be enabled in teleop mode. diff --git a/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-hierarchies.rst b/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-hierarchies.rst index 235b38b28d..f473035c35 100644 --- a/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-hierarchies.rst +++ b/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-hierarchies.rst @@ -1,5 +1,4 @@ -Viewing Hierarchies of Data -=========================== +# Viewing Hierarchies of Data Dragging a key with other keys below it (deeper in the hierarchy) displays the hierarchy in a tree, similar to the NetworkTables sources on the left. diff --git a/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-tuning-pid.rst b/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-tuning-pid.rst index 56f583ed2b..a4d0e44922 100644 --- a/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-tuning-pid.rst +++ b/source/docs/software/dashboards/shuffleboard/advanced-usage/shuffleboard-tuning-pid.rst @@ -1,7 +1,6 @@ -Testing and Tuning PID Loops -============================ +# Testing and Tuning PID Loops -One challenge in using sensors to control mechanisms is to have a good algorithm to drive the motors to the proper position or speed. The most commonly used control algorithm is called PID control. There is a `good set of videos `__ (look for the robot controls playlist) that explain the control algorithms described here. The PID algorithm converts sensor values into motor speeds by: +One challenge in using sensors to control mechanisms is to have a good algorithm to drive the motors to the proper position or speed. The most commonly used control algorithm is called PID control. There is a [good set of videos](https://wp.wpi.edu/wpilib/robotics-videos/) (look for the robot controls playlist) that explain the control algorithms described here. The PID algorithm converts sensor values into motor speeds by: 1. Reading sensor values to determine how far the robot or mechanism from the desired setpoint. The setpoint is the sensor value that corresponds to the expected goal. For example, a robot arm with a wrist joint should be able to move to a specified angle very quickly and stop at that angle as indicated by a sensor. A potentiometer is a sensor that can measure. rotational angle. By connecting it to an analog input, the program can get a voltage measurement that is directly proportional to the angle. 2. Compute an error (the difference between the sensor value and the desired value). The sign of the error value indicates which side of the setpoint the wrist is on. For example negative values might indicate that the measured wrist angle is larger than the desired wrist angle. The magnitude of the error is how far the measured wrist angle is from the actual wrist angle. If the error is zero, then the measured angle exactly matches the desired angle. The error can be used as an input to the PID algorithm to compute a motor speed. @@ -13,8 +12,7 @@ WPILib has a PIDController class that implements the PID algorithm and accepts c 2. I (integral) - this term is the sum of successive errors. The longer the error exists the larger the integral contribution will be. It is simply a sum of all the errors over time. If the wrist isn't quite getting to the setpoint because of a large load it is trying to move, the integral term will continue to increase (sum of the errors) until it contributes enough to the motor speed to get it to move to the setpoint. The sum of the errors is multiplied by a constant (Ki) to scale the integral term for the system. 3. D (differential) - this value is the rate of change of the errors. It is used to slow down the motor speed if it's moving too fast. It's computed by taking the difference between the current error value and the previous error value. It is also multiplied by a constant (kd) to scale it to match the rest of the system. -Tuning the PID Controller -------------------------- +## Tuning the PID Controller Tuning the PID controller consists of adjusting constants for accurate results. Shuffleboard helps this process by displaying the details of a PID subsystem with a user interface for setting constant values and testing how well it operates. This is displayed while the robot is operating in test mode (done by setting "Test" in the driver station). @@ -31,41 +29,50 @@ This is the test mode picture of a wrist subsystem that has a potentiometer as t Try various PID gains to get the desired motor performance. You can look at the video linked to at the beginning of this article or other sources on the internet to get the desired performance. -.. important:: The enable option does not affect the `PIDController `__ introduced in 2020, as the controller is updated every robot loop. See the example below on how to retain this functionality. +.. important:: The enable option does not affect the [PIDController](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/PIDController.html) introduced in 2020, as the controller is updated every robot loop. See the example below on how to retain this functionality. -Enable Functionality in the New PIDController ---------------------------------------------- +## Enable Functionality in the New PIDController The following example demonstrates how to create a button on your dashboard that will enable/disable the PIDController. .. tab-set-code:: - .. code-block:: java + ```java + ShuffleboardTab tab = Shuffleboard.getTab("Shooter"); + GenericEntry shooterEnable = tab.add("Shooter Enable", false).getEntry(); + // Command Example assumed to be in a PIDSubsystem + new NetworkButton(shooterEnable).onTrue(new InstantCommand(m_shooter::enable)); + // Timed Robot Example + if (shooterEnable.getBoolean()) { + // Calculates the output of the PID algorithm based on the sensor reading + // and sends it to a motor + motor.set(pid.calculate(encoder.getDistance(), setpoint)); + } + ``` + + ```c++ + frc::ShuffleboardTab& tab = frc::Shuffleboard::GetTab("Shooter"); + nt::GenericEntry& shooterEnable = *tab.Add("Shooter Enable", false).GetEntry(); + // Command-based assumed to be in a PIDSubsystem + frc2::NetworkButton(shooterEnable).OnTrue(frc2::InstantCommand([&] { m_shooter.Enable(); })); + // Timed Robot Example + if (shooterEnable.GetBoolean()) { + // Calculates the output of the PID algorithm based on the sensor reading + // and sends it to a motor + motor.Set(pid.Calculate(encoder.GetDistance(), setpoint)); + } + ``` + + ```python + from wpilib.shuffleboard import Shuffleboard + tab = Shuffleboard.getTab("Shooter") + shooterEnable = tab.add("Shooter Enable", false).getEntry() + # Command Example assumed to be in a PIDSubsystem + NetworkButton(shooterEnable).onTrue(InstantCommand(m_shooter.enable())) + # Timed Robot Example + if (shooterEnable.getBoolean()): + # Calculates the output of the PID algorithm based on the sensor reading + # and sends it to a motor + motor.set(pid.calculate(encoder.getDistance(), setpoint)) + ``` - ShuffleboardTab tab = Shuffleboard.getTab("Shooter"); - NetworkTableEntry shooterEnable = tab.add("Shooter Enable", false).getEntry(); - - // Command Example assumed to be in a PIDSubsystem - new NetworkButton(shooterEnable).onTrue(new InstantCommand(m_shooter::enable)); - - // Timed Robot Example - if (shooterEnable.getBoolean()) { - // Calculates the output of the PID algorithm based on the sensor reading - // and sends it to a motor - motor.set(pid.calculate(encoder.getDistance(), setpoint)); - } - - .. code-block:: c++ - - frc::ShuffleboardTab& tab = frc::Shuffleboard::GetTab("Shooter"); - nt::NetworkTableEntry shooterEnable = tab.Add("Shooter Enable", false).GetEntry(); - - // Command-based assumed to be in a PIDSubsystem - frc2::NetworkButton(shooterEnable).OnTrue(frc2::InstantCommand([&] { m_shooter.Enable(); })); - - // Timed Robot Example - if (shooterEnable.GetBoolean()) { - // Calculates the output of the PID algorithm based on the sensor reading - // and sends it to a motor - motor.Set(pid.Calculate(encoder.GetDistance(), setpoint)); - } diff --git a/source/docs/software/dashboards/shuffleboard/custom-widgets/builtin-plugins.rst b/source/docs/software/dashboards/shuffleboard/custom-widgets/builtin-plugins.rst index 12818d4d18..7106ce7554 100644 --- a/source/docs/software/dashboards/shuffleboard/custom-widgets/builtin-plugins.rst +++ b/source/docs/software/dashboards/shuffleboard/custom-widgets/builtin-plugins.rst @@ -1,25 +1,21 @@ .. include:: -Built-in Plugins -================ +# Built-in Plugins Shuffleboard provides a number of built-in plugins that handle common tasks for FRC\ |reg| use, such as camera streams, all widgets, and :term:`NetworkTables` connections. -Base Plugin ------------ +## Base Plugin The base plugin defines all the data types, widgets, and layouts necessary for FRC use. It does *not* define any of the source types, or any special data types or widgets for those source types. Those are handled by the `NetworkTables Plugin`_ and the `CameraServer Plugin`_. This separation of concerns makes it easier for teams to create plugins for custom source types or protocols (eg HTTP, ZeroMQ) for the FRC data types without needing a NetworkTables client. -CameraServer Plugin -------------------- +## CameraServer Plugin The camera server plugin provides sources and widgets for viewing camerastreams from the ``CameraServer`` WPILib class. This plugin depends on the `NetworkTables Plugin`_ in order to discover the available camera streams. -Stream discovery -^^^^^^^^^^^^^^^^ +### Stream discovery CameraServer sources are automatically discovered by looking at the ``/CameraPublisher`` NetworkTable. @@ -41,8 +37,7 @@ For example, a camera named "Camera" with a server at This setup will automatically discover all camera streams hosted on a roboRIO by the CameraServer class in WPILib. Any non-WPILib projects that want to have camera streams appear in shuffleboard will have to set the streams entry for the camera server. -NetworkTables Plugin --------------------- +## NetworkTables Plugin The NetworkTables plugin provides data sources backed by ntcore. Since the ``LiveWindow``, ``SmartDashboard``, and ``Shuffleboard`` classes in WPILib use NetworkTables to send the data to the driver station, this plugin will need to be loaded in order to use those classes. diff --git a/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-a-new-widget.rst b/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-a-new-widget.rst index e38a06af4c..307c0dc9e8 100644 --- a/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-a-new-widget.rst +++ b/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-a-new-widget.rst @@ -1,15 +1,13 @@ -Creating A Widget -================= +# Creating A Widget Widgets allow us to view, change, and interact with data published through different data sources. The CameraServer, NetworkTables, and Base plugins provide the widgets to control basic data types (including FRC-specific data types). However, custom widgets allow us to control our custom data types we made in the previous sections or Java Objects. The basic ``Widget`` interface inherits from the ``Component`` and ``Sourced`` interfaces. ``Component`` is the most basic building block of components that be displayed in Shuffleboard. ``Sourced`` is an interface for things that can handle and interface with data sources to display or modify data. Widgets that don't support data bindings but simply have child nodes would not use the ``Sourced`` interface but simply the ``Component`` interface. Both are basic building blocks towards making widgets and allows us to modify and display data. A good widget allows the end-user to customize the widget to suit their needs. An example could be to allow the user to control the range of the number slider, that is, its maximum and minimum or the orientation of the slider itself. The view of the widget or how it looks is defined using FXML. ``FXML`` is an XML based language that is useful for defining the static layout of the widget (Panes, Labels and Controls). -More about FXML can be found `here `_. +More about FXML can be found [here](https://openjfx.io/javadoc/11/javafx.fxml/javafx/fxml/doc-files/introduction_to_fxml.html). -Defining a Widget's FXML ------------------------- +## Defining a Widget's FXML In this example, we will create two sliders to help us control the X and Y coordinates of our Point2D data type we created in previous sections. It is helpful to place the FXML file in the same package as the Java class. In order to create an empty, blank window for our widget, we need to create a ``Pane``. A Pane is a parent node that contains other child nodes, in this case, 2 sliders. @@ -31,182 +29,157 @@ There are many different types of Pane, they are as noted: - Anchor Panes allow child elements to be placed in the top, bottom, left side, right side, or center of the pane. -Layout panes are also extremely useful for placing child nodes in one horizontal row using a `HBox `_ or one vertical column using a `VBox `_. +Layout panes are also extremely useful for placing child nodes in one horizontal row using a [HBox](https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/layout/HBox.html) or one vertical column using a [VBox](https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/layout/VBox.html). The basic syntax for defining a Pane using FXML would be as the following: -.. code-block:: xml - - - - ... - +```xml + + + ... + +``` The ``fx:controller`` attribute contains the name of the widget class. An instance of this class is created when the FXML file is loaded. For this to work, the controller class must have a no-argument constructor. -Creating A Widget Class ------------------------ +## Creating A Widget Class Now that we have a Pane, we can now add child elements to that pane. In this example, we can add two slider objects. Remember to add an ``fx:id`` to each element so they can be referenced in our Java class we will make later on. We will use a ``VBox`` to position our slider on top of each other. -.. code-block:: xml - - - - - - - - - - +```xml + + + + + + + +``` Now that we have finished creating our FXML file, we can now create a widget class. The widget class should include a ``@Description`` annotation that states the supported data types of the widget and the name of the widget. If a ``@Description`` annotation is not present, the plugin class must implement the ``get()`` method to return its widgets. It also must include a ``@ParametrizedController`` annotation that points to the FXML file containing the layout of the widget. If the class that only supports one data source it must extend the ``SimpleAnnotatedWidget`` class. If the class supports multiple data sources, it must extend the ``ComplexAnnotatedWidget`` class. For more information, see :doc:`widget-types`. -.. code-block:: java - - import edu.wpi.first.shuffleboard.api.widget.Description; - import edu.wpi.first.shuffleboard.api.widget.ParametrizedController; - import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget; - - /* - * If the FXML file and Java file are in the same package, that is the Java file is in src/main/java and the - * FXML file is under src/main/resources or your code equivalent package, the relative path will work - * However, if they are in different packages, an absolute path will be required. - */ - - @Description(name = "MyPoint2D", dataTypes = MyPoint2D.class) - @ParametrizedController("Point2DWidget.fxml") - public final class Point2DWidget extends SimpleAnnotatedWidget { - - } +```java +import edu.wpi.first.shuffleboard.api.widget.Description; +import edu.wpi.first.shuffleboard.api.widget.ParametrizedController; +import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget; +/* + * If the FXML file and Java file are in the same package, that is the Java file is in src/main/java and the + * FXML file is under src/main/resources or your code equivalent package, the relative path will work + * However, if they are in different packages, an absolute path will be required. +*/ +@Description(name = "MyPoint2D", dataTypes = MyPoint2D.class) +@ParametrizedController("Point2DWidget.fxml") +public final class Point2DWidget extends SimpleAnnotatedWidget { +} +``` If you are not using a custom data type, you can reference any Java data type (ie. ``Double.class``), or if the widget does not need data binding you can pass ``NoneType.class``. Now that we have created our class we can create fields for the widgets we declared in our FXML file using the ``@FXML`` annotation. For our two sliders, an example would be: -.. code-block:: java - - import edu.wpi.first.shuffleboard.api.widget.Description; - import edu.wpi.first.shuffleboard.api.widget.ParametrizedController; - import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget; - import javafx.fxml.FXML; - - @Description(name = "MyPoint2D", dataTypes = MyPoint2D.class) - @ParametrizedController("Point2DWidget.fxml") - public final class Point2DWidget extends SimpleAnnotatedWidget { - - @FXML - private Pane root; - - @FXML - private Slider xSlider; - - @FXML - private Slider ySlider; - } +```java +import edu.wpi.first.shuffleboard.api.widget.Description; +import edu.wpi.first.shuffleboard.api.widget.ParametrizedController; +import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget; +import javafx.fxml.FXML; +@Description(name = "MyPoint2D", dataTypes = MyPoint2D.class) +@ParametrizedController("Point2DWidget.fxml") +public final class Point2DWidget extends SimpleAnnotatedWidget { + @FXML + private Pane root; + @FXML + private Slider xSlider; + @FXML + private Slider ySlider; +} +``` In order to display our pane on our custom widget we need to override the ``getView()`` method and return our ``StackPane``. -.. code-block:: java - - import edu.wpi.first.shuffleboard.api.widget.Description; - import edu.wpi.first.shuffleboard.api.widget.ParametrizedController; - import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget; - import javafx.fxml.FXML; - - @Description(name = "MyPoint2D", dataTypes = MyPoint2D.class) - @ParametrizedController("Point2DWidget.fxml") - public final class Point2DWidget extends SimpleAnnotatedWidget { - - @FXML - private StackPane root; - - @FXML - private Slider xSlider; - - @FXML - private Slider ySlider; - - @Override - public Pane getView() { - return root; - } - +```java +import edu.wpi.first.shuffleboard.api.widget.Description; +import edu.wpi.first.shuffleboard.api.widget.ParametrizedController; +import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget; +import javafx.fxml.FXML; +@Description(name = "MyPoint2D", dataTypes = MyPoint2D.class) +@ParametrizedController("Point2DWidget.fxml") +public final class Point2DWidget extends SimpleAnnotatedWidget { + @FXML + private StackPane root; + @FXML + private Slider xSlider; + @FXML + private Slider ySlider; + @Override + public Pane getView() { + return root; } +} +``` -Binding Elements and Adding Listeners -------------------------------------- +## Binding Elements and Adding Listeners Binding is a mechanism that allows JavaFX widgets to express direct relationships with the data source. For example, changing a widget will change its related NetworkTableEntry and vice versa. An example, in this case, would be changing the X and Y coordinate of our 2D point by changing the values of xSlider and ySlider respectively. A good practice is to set bindings in the ``initialize()`` method tagged with the ``@FXML`` annotation which is required to call the method from FXML if the method is not ``public``. -.. code-block:: java - - import edu.wpi.first.shuffleboard.api.widget.Description; - import edu.wpi.first.shuffleboard.api.widget.ParametrizedController; - import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget; - import javafx.fxml.FXML; - - @Description(name = "MyPoint2D", dataTypes = MyPoint2D.class) - @ParametrizedController("Point2DWidget.fxml") - public final class Point2DWidget extends SimpleAnnotatedWidget { - - @FXML - private StackPane root; - - @FXML - private Slider xSlider; - - @FXML - private Slider ySlider; - - @FXML - private void initialize() { - xSlider.valueProperty().bind(dataOrDefault.map(MyPoint2D::getX)); - ySlider.valueProperty().bind(dataOrDefault.map(MyPoint2D::getY)); - } - - @Override - public Pane getView() { - return root; - } - - } +```java +import edu.wpi.first.shuffleboard.api.widget.Description; +import edu.wpi.first.shuffleboard.api.widget.ParametrizedController; +import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget; +import javafx.fxml.FXML; +@Description(name = "MyPoint2D", dataTypes = MyPoint2D.class) +@ParametrizedController("Point2DWidget.fxml") +public final class Point2DWidget extends SimpleAnnotatedWidget { + @FXML + private StackPane root; + @FXML + private Slider xSlider; + @FXML + private Slider ySlider; + @FXML + private void initialize() { + xSlider.valueProperty().bind(dataOrDefault.map(MyPoint2D::getX)); + ySlider.valueProperty().bind(dataOrDefault.map(MyPoint2D::getY)); + } + @Override + public Pane getView() { + return root; + } + } +``` The above ``initialize`` method binds the slider's value property to the ``MyPoint2D`` data class' corresponding X and Y value. Meaning, changing the slider will change the coordinate and vice versa. The ``dataOrDefault.map()`` method will get the data source's value, or, if no source is present, will return the default value. Using a listener is another way to change values when the slider or data source has changed. For example a listener for our slider would be: -.. code-block:: java - - xSlider.valueProperty().addListener((observable, oldValue, newValue) -> setData(getData().withX(newValue)); +```java +xSlider.valueProperty().addListener((observable, oldValue, newValue) -> setData(getData().withX(newValue)); +``` In this case, the ``setData()`` method sets the value in the data source of the widget to the ``newValue``. -Exploring Custom Components ---------------------------- +## Exploring Custom Components Widgets are not automatically discovered when loading plugins; the defining plugin must explicitly export it for it to be usable. This approach is taken to allow multiple plugins to be defined in the same JAR. -.. code-block:: java +```java +@Override +public List getComponents() { + return List.of(WidgetType.forAnnotatedWidget(Point2DWidget.class)); +} +``` - @Override - public List getComponents() { - return List.of(WidgetType.forAnnotatedWidget(Point2DWidget.class)); - } - -Set Default Widget For Data type --------------------------------- +## Set Default Widget For Data type In order to set your widget as default for your custom data type, you can override the ``getDefaultComponents()`` in your plugin class that stores a Map for all default widgets as noted below: -.. code-block:: java +```java +@Override +public Map getDefaultComponents() { + return Map.of(Point2DType.Instance, WidgetType.forAnnotatedWidget(Point2DWidget.class)); +} +``` - @Override - public Map getDefaultComponents() { - return Map.of(Point2DType.Instance, WidgetType.forAnnotatedWidget(Point2DWidget.class)); - } diff --git a/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-custom-data-types.rst b/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-custom-data-types.rst index 82ebcdbda9..d8ee6c6cef 100644 --- a/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-custom-data-types.rst +++ b/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-custom-data-types.rst @@ -1,142 +1,118 @@ -Creating Custom Data Types -========================== +# Creating Custom Data Types Widgets allow us to control and visualize different types of data. This data could be integers and doubles or even Java Objects. In order to display these types of data using widgets, it is helpful to create a container class for them. It is not necessary to create your own Data Class if the widget will handle single fielded data types such as doubles, arrays, or strings. -Creating The Data Class ------------------------ -In this example, we will create a custom data type for a 2D Point and its x and y coordinates. In order to create a custom data type class, it must extend the abstract class `ComplexData `_. +## Creating The Data Class +In this example, we will create a custom data type for a 2D Point and its x and y coordinates. In order to create a custom data type class, it must extend the abstract class [ComplexData](https://github.com/wpilibsuite/shuffleboard/blob/main/api/src/main/java/edu/wpi/first/shuffleboard/api/data/ComplexData.java). Your custom data class must also implement the ``asMap()`` method that returns the represented data as a simple map as noted below with the ``@Override`` annotation: -.. code-block:: java - - import edu.wpi.first.shuffleboard.api.data.ComplexData; - - import java.util.Map; - - public class MyPoint2D extends ComplexData { - - private final double x; - private final double y; - - //Constructor should take all the different fields needed and assign them their corresponding instance variables. - public MyPoint2D(double x, double y) { - this.x = x; - this.y = y; - } - - @Override - public Map asMap() { - return Map.of("x", x, "y", y); - } +```java +import edu.wpi.first.shuffleboard.api.data.ComplexData; +import java.util.Map; +public class MyPoint2D extends ComplexData { + private final double x; + private final double y; + //Constructor should take all the different fields needed and assign them their corresponding instance variables. + public MyPoint2D(double x, double y) { + this.x = x; + this.y = y; + } + @Override + public Map asMap() { + return Map.of("x", x, "y", y); } +} +``` It is also good practice to override the default ``equals`` and ``hashcode`` methods to ensure that different objects are considered equivalent when their fields are the same. The ``asMap()`` method should return the data represented in a simple Map object as it will be mapped to the NetworkTables entry it corresponds to. In this case, we can represent the point as its X and Y coordinates and return a ``Map`` containing them. -.. code-block:: java - - import edu.wpi.first.shuffleboard.api.data.ComplexData; - - import java.util.Map; - - public final class MyPoint2D extends ComplexData { - - private final double x; - private final double y; - - // Constructor should take all the different fields needed and assign them to their corresponding instance variables. - public Point(double x, double y) { - this.x = x; - this.y = y; - } - - @Override - public Map asMap() { - return Map.of("x", this.x, "y", this.y); - } - } +```java +import edu.wpi.first.shuffleboard.api.data.ComplexData; +import java.util.Map; +public final class MyPoint2D extends ComplexData { + private final double x; + private final double y; + // Constructor should take all the different fields needed and assign them to their corresponding instance variables. + public Point(double x, double y) { + this.x = x; + this.y = y; + } + @Override + public Map asMap() { + return Map.of("x", this.x, "y", this.y); + } + } +``` Other methods can be added to retrieve or edit fields and instance variables, however, it is good practice to make these classes immutable to prevent changing the source data objects. Instead, you can make a new copy object instead of manipulating the existing object. For example, if we wanted to change the y coordinate of our point, we can define the following method: -.. code-block:: java - - public MyPoint2D withY(double newY) { - return new MyPoint2D(this.x, newY); - } +```java +public MyPoint2D withY(double newY) { + return new MyPoint2D(this.x, newY); +} +``` This creates a new ``MyPoint2D`` object and returns it with the new y-coordinate. Same can be done for changing the x coordinate. -Creating a Data Type ---------------------- +## Creating a Data Type There are two different data types that can be made: Simple data types that have only one field (ie. a single number or string), and Complex data types that have multiple data fields (ie. multiple strings, multiple numbers). In order to define a simple data type, the class must extend the ``SimpleDataType`` class with the data type needed and implement the ``getDefaultValue()`` method. In this example, we will use a double as our simple data type. -.. code-block:: java - - public final class MyDoubleDataType extends SimpleDataType { - - private static final String NAME = "Double"; - - private MyDataType() { - super(NAME, Double.class); - } - - @Override - public Double getDefaultValue() { - return 0.0; - } - +```java +public final class MyDoubleDataType extends SimpleDataType { + private static final String NAME = "Double"; + private MyDataType() { + super(NAME, Double.class); + } + @Override + public Double getDefaultValue() { + return 0.0; } +} +``` The class constructor is set to private to ensure that only a single instance of the data type will exist. In order to define a complex data type, the class must extend the ``ComplexDataType`` class and override the ``fromMap()`` and ``getDefaultValue()`` methods. We will use our MyPoint2D class as an example to see what a complex data type class would look like. -.. code-block:: java - - public final class PointDataType extends ComplexDataType { - - private static final String NAME = "MyPoint2D"; - public static final PointDataType Instance = new PointDataType(); - - private PointDataType() { - super(NAME, MyPoint2D.class); - } - - @Override - public Function, MyPoint2D> fromMap() { - return map -> { - return new MyPoint2D((double) map.getOrDefault("x", 0.0), (double) map.getOrDefault("y", 0.0)); - }; - } - - @Override - public MyPoint2D getDefaultValue() { - // use default values of 0 for X and Y coordinates - return new MyPoint2D(0, 0); - } - +```java +public final class PointDataType extends ComplexDataType { + private static final String NAME = "MyPoint2D"; + public static final PointDataType Instance = new PointDataType(); + private PointDataType() { + super(NAME, MyPoint2D.class); + } + @Override + public Function, MyPoint2D> fromMap() { + return map -> { + return new MyPoint2D((double) map.getOrDefault("x", 0.0), (double) map.getOrDefault("y", 0.0)); + }; + } + @Override + public MyPoint2D getDefaultValue() { + // use default values of 0 for X and Y coordinates + return new MyPoint2D(0, 0); } +} +``` The following code above works as noted: The ``fromMap()`` method creates a new MyPoint2D using the values in the NetworkTables entry it is bound to. The ``getOrDefault`` method will return 0.0 if it cannot get the entry values. The ``getDefaultValue`` will return a new ``MyPoint2D`` object if no source is present. -Exporting Data Type To Plugin ------------------------------ +## Exporting Data Type To Plugin In order to have the data type be recognized by Shuffleboard, the plugin must export them by overriding the ``getDataTypes`` method. For example, -.. code-block:: java - - public class MyPlugin extends Plugin { - - @Override - public List getDataTypes() { - return List.of(PointDataType.Instance); - } - +```java +public class MyPlugin extends Plugin { + @Override + public List getDataTypes() { + return List.of(PointDataType.Instance); } +} +``` + diff --git a/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-plugins.rst b/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-plugins.rst index 70ac97edb8..f204cfd5a6 100644 --- a/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-plugins.rst +++ b/source/docs/software/dashboards/shuffleboard/custom-widgets/creating-plugins.rst @@ -1,43 +1,37 @@ .. include:: -Creating a Plugin -================= +# Creating a Plugin -Overview --------- +## Overview Plugins provide the ability to create custom widgets, layouts, data sources/types, and custom themes. Shuffleboard provides the following :ref:`built-in plugins `. - NetworkTables Plugin: To connect to data published over NetworkTables - Base Plugin: To display custom FRC\ |reg| data types in custom widgets - CameraServer Plugin: To view streams from the CameraServer -.. tip:: An example custom Shuffleboard plugin which creates a custom data type and a simple widget for displaying it can be found `here `__. +.. tip:: An example custom Shuffleboard plugin which creates a custom data type and a simple widget for displaying it can be found [here](https://github.com/wpilibsuite/shuffleboard/tree/main/example-plugins/custom-data-and-widget). -Create a Custom Plugin ----------------------- -In order to define a plugin, the plugin class must be a subclass of `edu.wpi.first.shuffleboard.api.Plugin `_ or one of its subclasses. An example of a plugin class would be as following. +## Create a Custom Plugin +In order to define a plugin, the plugin class must be a subclass of [edu.wpi.first.shuffleboard.api.Plugin](https://github.com/wpilibsuite/shuffleboard/blob/main/api/src/main/java/edu/wpi/first/shuffleboard/api/plugin/Plugin.java) or one of its subclasses. An example of a plugin class would be as following. .. tab-set-code:: - .. code-block:: java - - import edu.wpi.first.shuffleboard.api.plugin.Description; - import edu.wpi.first.shuffleboard.api.plugin.Plugin; - - @Description(group = "com.example", name = "MyPlugin", version = "1.2.3", summary = "An example plugin") - public class MyPlugin extends Plugin { - - } + ```java + import edu.wpi.first.shuffleboard.api.plugin.Description; + import edu.wpi.first.shuffleboard.api.plugin.Plugin; + @Description(group = "com.example", name = "MyPlugin", version = "1.2.3", summary = "An example plugin") + public class MyPlugin extends Plugin { + } + ``` -Additional explanations on how these attributes are used, including version numbers can be found `here `_. +Additional explanations on how these attributes are used, including version numbers can be found [here](https://semver.org/). Note the ``@Description`` annotation is needed to tell the plugin loader the properties of the custom plugin class. Plugin classes are permitted to have a default constructor but it cannot take any arguments. -Building plugin ---------------- +## Building plugin -The easiest way to build plugins is to utlize the `example-plugins` folder in the shufflebloard source tree. Clone Shuffleboard with ``git clone https://github.com/wpilibsuite/shuffleboard.git``, and checkout the version that corresponds to the WPILib version you have installed (e.g. 2023.2.1). ``git checkout v2023.2.1`` +The easiest way to build plugins is to utilize the `example-plugins` folder in the shufflebloard source tree. Clone Shuffleboard with ``git clone https://github.com/wpilibsuite/shuffleboard.git``, and checkout the version that corresponds to the WPILib version you have installed (e.g. 2023.2.1). ``git checkout v2023.2.1`` Put your plugin in the ``example-plugins\PLUGIN-NAME`` directory. Copy the ``custom-data-and-widget.gradle`` from ``example-plugins\custom-data-and-widget`` and rename to match your plugin name. @@ -47,26 +41,23 @@ Edit ``settings.gradle`` in the shuffleboard root directory to add ``include "ex Plugins are allowed to have dependencies on other plugins and libraries, however, they must be included correctly in the maven or gradle build file. When a plugin depends on other plugins, it is good practice to define those dependencies so the plugin does not load when the dependencies do not load as well. This can be done using the ``@Requires`` annotation as shown below: -.. code-block:: java - - @Requires(group = "com.example", name = "Good Plugin", minVersion = "1.2.3") - @Requires(group = "edu.wpi.first.shuffleboard", name = "Base", minVersion = "1.0.0") - @Description(group = "com.example", name = "MyPlugin", version = "1.2.3", summary = "An example plugin") - public class MyPlugin extends Plugin { - - } +```java +@Requires(group = "com.example", name = "Good Plugin", minVersion = "1.2.3") +@Requires(group = "edu.wpi.first.shuffleboard", name = "Base", minVersion = "1.0.0") +@Description(group = "com.example", name = "MyPlugin", version = "1.2.3", summary = "An example plugin") +public class MyPlugin extends Plugin { +} +``` The ``minVersion`` specifies the minimum allowable version of the plugin that can be loaded. For example, if the ``minVersion`` is 1.4.5, and the plugin with the version 1.4.7 is loaded, it will be allowed to do so. However, if the plugin with the version 1.2.4 is loaded, it will not be allowed to since it is less than the ``minVersion``. -Deploying Plugin To Shuffleboard --------------------------------- +## Deploying Plugin To Shuffleboard In order to load a plugin in Shuffleboard, you will need to generate a jar file of the plugin and put it in the ``~/Shuffleboard/plugins`` folder. This can be done automatically by running from the shuffleboard root ``gradlew :example-plugins:PLUGIN-NAME:installPlugin`` After deploying, Shuffleboard will cache the path of the plugin so it can be automatically loaded the next time Shuffleboard loads. It may be necessary to click on ``Clear Cache`` under the plugins menu to remove a plugin or reload a plugin into Shuffleboard. -Manually Adding Plugin ----------------------- +## Manually Adding Plugin The other way to add a plugin to Shuffleboard is to compile it to a jar file and add it from Shuffleboard. The jar file is located in ``example-plugins\PLUGIN-NAME\build\libs`` after running ``gradlew build`` in the shuffleboard root Open Shuffleboard, click on the file tab in the top left, and choose Plugins from the drop down menu. diff --git a/source/docs/software/dashboards/shuffleboard/custom-widgets/custom-themes.rst b/source/docs/software/dashboards/shuffleboard/custom-widgets/custom-themes.rst index 8140c24631..3b696599f4 100644 --- a/source/docs/software/dashboards/shuffleboard/custom-widgets/custom-themes.rst +++ b/source/docs/software/dashboards/shuffleboard/custom-widgets/custom-themes.rst @@ -1,7 +1,6 @@ -Custom Themes -============= +# Custom Themes -Since shuffleboard is a JavaFX application, it has support for custom themes via Cascading Stylesheets (**CSS** for short). These are commonly used on webpages for making HTML look nice, but JavaFX also has support, albeit for a different language subset (see `here `_ for documentation on how to use it). +Since shuffleboard is a JavaFX application, it has support for custom themes via Cascading Stylesheets (**CSS** for short). These are commonly used on webpages for making HTML look nice, but JavaFX also has support, albeit for a different language subset (see [here](https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/doc-files/cssref.html) for documentation on how to use it). Shuffleboard comes with three themes by default: Material Light, Material Dark, and Midnight. These are color variations on the same material design stylesheet. In addition, they inherit from a ``base.css`` stylesheet that defines styles for the custom components ,defined in shuffleboard or libraries that it uses; the base material design stylesheet only applies to the UI components built into JavaFX. @@ -14,8 +13,7 @@ There are two ways to define a custom theme: place the stylesheets in a director All the stylesheets in the directory will be treated as part of the theme. -Loading Themes via Plugins --------------------------- +## Loading Themes via Plugins Custom themes can also be defined by plugins. This makes them easier to share and bundle with custom widgets, but are slightly more difficult to define. The theme object will need a reference to a class defined in the plugin so that the plugin loader can determine where the stylesheets are located. If a class is passed that is *not* present in the JAR that the plugin is in, the theme will not be able to be used. @@ -33,8 +31,7 @@ Custom themes can also be defined by plugins. This makes them easier to share an } -Modifying or Extending Shuffleboard’s Default Themes ----------------------------------------------------- +## Modifying or Extending Shuffleboard’s Default Themes Shuffleboard’s Material Light and Material Dark themes provide a lot of the framework for light and dark themes, respectively, as well as many styles specific to shuffleboard, ControlsFX, and Medusa UI components to fit with the material-style design. @@ -50,8 +47,7 @@ Themes that want to modify these themes need to add ``import`` statements for th Note that ``base.css`` internally imports ``material.css``, and ``light.css``, ``dark.css``, and ``midnight.css`` all import ``base.css``, so importing ``light.css`` will implicitly import both ``base.css`` and ``material.css`` as well. -Source Code for the CSS Files -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Source Code for the CSS Files - _material.css: https://github.com/wpilibsuite/shuffleboard/blob/main/api/src/main/resources/edu/wpi/first/shuffleboard/api/material.css - _base.css: https://github.com/wpilibsuite/shuffleboard/blob/main/api/src/main/resources/edu/wpi/first/shuffleboard/api/base.css @@ -59,18 +55,15 @@ Source Code for the CSS Files - _dark.css: https://github.com/wpilibsuite/shuffleboard/blob/main/app/src/main/resources/edu/wpi/first/shuffleboard/app/dark.css - _midnight.css: https://github.com/wpilibsuite/shuffleboard/blob/main/app/src/main/resources/edu/wpi/first/shuffleboard/app/midnight.css -Material Design Color Swatches -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Material Design Color Swatches The material design CSS uses color swatch variables for almost everything. These variables can be set from custom CSS files, reducing the amount of custom code needed. The ``-swatch-<100|200|300|400|500>`` variables define progressively darker shades of the same primary color. The light theme uses the default shades of blue set in ``material.css``, but the dark theme overrides these with shades of red. ``-swatch-<|light|dark>-gray`` defines three levels of gray to use for various background or text colors. -Overriding the Swatch Colors -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Overriding the Swatch Colors -Replacing blue with red (light) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Replacing blue with red (light) :: @@ -84,8 +77,7 @@ Replacing blue with red (light) -swatch-500: hsb(0, 80%, 58%); } -Replacing red with blue (dark) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Replacing red with blue (dark) :: diff --git a/source/docs/software/dashboards/shuffleboard/custom-widgets/images/loading-plugin.png b/source/docs/software/dashboards/shuffleboard/custom-widgets/images/loading-plugin.png index 20e6a1d589..8a70366661 100644 Binary files a/source/docs/software/dashboards/shuffleboard/custom-widgets/images/loading-plugin.png and b/source/docs/software/dashboards/shuffleboard/custom-widgets/images/loading-plugin.png differ diff --git a/source/docs/software/dashboards/shuffleboard/custom-widgets/index.rst b/source/docs/software/dashboards/shuffleboard/custom-widgets/index.rst index ae5411e634..054711a69b 100644 --- a/source/docs/software/dashboards/shuffleboard/custom-widgets/index.rst +++ b/source/docs/software/dashboards/shuffleboard/custom-widgets/index.rst @@ -1,5 +1,4 @@ -Shuffleboard - Custom Widgets -============================= +# Shuffleboard - Custom Widgets .. toctree:: :maxdepth: 1 diff --git a/source/docs/software/dashboards/shuffleboard/custom-widgets/widget-types.rst b/source/docs/software/dashboards/shuffleboard/custom-widgets/widget-types.rst index 289d48ca5e..4376aad4d0 100644 --- a/source/docs/software/dashboards/shuffleboard/custom-widgets/widget-types.rst +++ b/source/docs/software/dashboards/shuffleboard/custom-widgets/widget-types.rst @@ -1,5 +1,4 @@ -Widget Types -============ +# Widget Types While ``Widget`` is pretty straightforward as far as the interface is concerned, there are several intermediate implementations to make it easier to define the widget. @@ -41,18 +40,15 @@ There are also two annotations to help define widgets: | | types be defined in a single line | +-----------------------------------+-----------------------------------+ -AbstractWidget --------------- +## AbstractWidget -This class implements ``getProperties()``, ``getSources()``, ``addSource()``, and ``titleProperty()``. It also defines a method ``exportProperties(Property...)`` method so subclasses can easy add custom widget properties, or properties for the JavaFX components in the widget. Most of the `widgets in the base plugin `_ use this. +This class implements ``getProperties()``, ``getSources()``, ``addSource()``, and ``titleProperty()``. It also defines a method ``exportProperties(Property...)`` method so subclasses can easy add custom widget properties, or properties for the JavaFX components in the widget. Most of the [widgets in the base plugin](https://github.com/wpilibsuite/shuffleboard/tree/main/plugins/base/src/main/java/edu/wpi/first/shuffleboard/plugin/base/widget) use this. -SingleTypeWidget ----------------- +## SingleTypeWidget A type of widget that only supports a single data type. This interface is parametrized and has methods for setting or getting the data, as well as a method for getting the (single) data type of the widget. -AnnotatedWidget ---------------- +## AnnotatedWidget This interface implements ``getDataTypes()`` and ``getName()`` by looking at the ``@Description`` annotation on the implementing class. This *requires* the annotation to be present, or the widget will not be able to be loaded and used. @@ -70,18 +66,15 @@ This interface implements ``getDataTypes()`` and ``getName()`` by looking at the // ... } -SingleSourceWidget ------------------- +## SingleSourceWidget A type of widget that only uses a single source. -SimpleAnnotatedWidget ---------------------- +## SimpleAnnotatedWidget A combination of ``SingleTypeWidget``, ``AnnotatedWidget``, and ``SingleSourceWidget``. Most widgets in the base plugin extend from this class. This also has a ``protected`` field called ``dataOrDefault`` that lets subclasses use a default data value if the widget doesn’t have a source, or if the source is providing ``null``. -@ParametrizedController ------------------------ +## @ParametrizedController This annotation can be placed on a widget class to let shuffleboard know that it’s an FXML controller for a JavaFX view defined via FXML. The annotation takes a single parameter that defines where the FXML file *in relation to the class on which it is placed*. For example, a widget in the directory ``src/main/java/com/acme`` that is an FXML controller for a FXML file in ``src/main/resources/com/acme`` can use the annotation as either @@ -95,7 +88,6 @@ or as @ParametrizedController("/com/acme/MyWidget.fxml") -@Description ------------- +## @Description This allows widgets to have their name and supported data types defined by a single annotation, when used alongside `AnnotatedWidget`_. diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/configuring-data.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/configuring-data.png index 30eaf6bee2..f42571e5d9 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/configuring-data.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/configuring-data.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/data-sources-2.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/data-sources-2.png index 8d6a93996c..ab7867d392 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/data-sources-2.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/data-sources-2.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/data-sources.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/data-sources.png index 02046f0663..8839b9d755 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/data-sources.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/data-sources.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/display-code-result.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/display-code-result.png index 791c381a13..57cbfbdb91 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/display-code-result.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/display-code-result.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/displaying-data-tabs.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/displaying-data-tabs.png index ae422be830..6f7f3194b8 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/displaying-data-tabs.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/displaying-data-tabs.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/driverstation-test-mode.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/driverstation-test-mode.png index e0d5fba7ae..25d1ce3153 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/driverstation-test-mode.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/driverstation-test-mode.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/joystick-value.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/joystick-value.png index 7aa0786084..d671a8bddc 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/joystick-value.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/joystick-value.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/list-1.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/list-1.png index 6ecb9ea27a..3e6a9b0ef6 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/list-1.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/list-1.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/list-2.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/list-2.png index 3133c3b8ac..f37bae19cb 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/list-2.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/list-2.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/list-3.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/list-3.png index 9256d5666f..522bf2f3a3 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/list-3.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/list-3.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/list-4.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/list-4.png index 1624e05a39..a0f6c67093 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/list-4.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/list-4.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/list-5.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/list-5.png index 1a8b71aa70..ebd0270041 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/list-5.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/list-5.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-1.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-1.png index a16c326dc7..af68f94486 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-1.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-1.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-2.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-2.png index 316425bdd0..fc0fb26735 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-2.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-2.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-3.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-3.png index 0170adb12b..816b8b3e83 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-3.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-3.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-4.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-4.png index 08715badf1..d3c5dd2108 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-4.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/preferences-4.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-displaying-camera/camera-stream.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-displaying-camera/camera-stream.png index 08a63e78cf..f7925ff406 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-displaying-camera/camera-stream.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-displaying-camera/camera-stream.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-displaying-camera/front-camera.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-displaying-camera/front-camera.png index 19c5f1450f..fc92736cee 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-displaying-camera/front-camera.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-displaying-camera/front-camera.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-driverstation.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-driverstation.png index 7ba0a9483a..05d211532a 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-driverstation.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-driverstation.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/graph-legend.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/graph-legend.png index 0c020641cb..d1e852f26a 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/graph-legend.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/graph-legend.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/graph-properties.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/graph-properties.png index 7e92ab621d..2046a6f2a8 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/graph-properties.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/graph-properties.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/show-as.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/show-as.png index 30c3bc58d0..9be3230a9c 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/show-as.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/show-as.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/time-interval.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/time-interval.png index ada8693db9..da765a57e3 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/time-interval.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/time-interval.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/two-sources.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/two-sources.png index d4818566df..525b465e03 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/two-sources.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-graphs/two-sources.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/convert-dialog.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/convert-dialog.png index 3a949190d2..f913807e98 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/convert-dialog.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/convert-dialog.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/convert.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/convert.png index dc5a57aae1..a823a4d498 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/convert.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/convert.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/playback-control.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/playback-control.png index e4b53b7e19..a02d74d06d 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/playback-control.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/playback-control.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/playback.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/playback.png index a78240877c..c6ef9eeb62 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/playback.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/playback.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/record.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/record.png index 02095dc8ee..e67b765de0 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/record.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-recording/record.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-tab-deletion.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-tab-deletion.png index a6958c702c..afec008d0e 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-tab-deletion.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-tab-deletion.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-tabs.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-tabs.png index cb6c629cca..ceda8b170d 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-tabs.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/shuffleboard-tabs.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/tabs-1.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/tabs-1.png index 8bcebf5115..efb8b4e6bc 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/tabs-1.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/tabs-1.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/tabs-2.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/tabs-2.png index 968f83d96b..49566c942c 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/tabs-2.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/tabs-2.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-1.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-1.png index 08b74fbf2b..4c7e9cc376 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-1.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-1.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-2.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-2.png index ca6cd71b06..5d9027b92b 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-2.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-2.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-3.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-3.png index 56bae18b10..ce1f8ae6e5 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-3.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-3.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-4.png b/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-4.png index 3f89b0cf27..f9af5440a3 100644 Binary files a/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-4.png and b/source/docs/software/dashboards/shuffleboard/getting-started/images/widgets-4.png differ diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/index.rst b/source/docs/software/dashboards/shuffleboard/getting-started/index.rst index 4fd2c2c4cb..550fef1a3c 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/index.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/index.rst @@ -1,5 +1,4 @@ -Shuffleboard - Getting Started -============================== +# Shuffleboard - Getting Started .. toctree:: :maxdepth: 1 diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-displaying-camera.rst b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-displaying-camera.rst index 872953ca5b..ad4241788c 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-displaying-camera.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-displaying-camera.rst @@ -1,10 +1,8 @@ -Displaying Camera Streams -========================= +# Displaying Camera Streams Camera streams from the robot can be viewed on a tab in Shuffleboard. This is useful for viewing what the robot is seeing to give a less obstructed view for operators or helping visualize the output from a vision algorithm running on the driver station computer or a coprocessor on the robot. Any stream that is running using the CameraServer API can be viewed in a camera stream widget. -Adding a Camera Stream ----------------------- +## Adding a Camera Stream To add a camera to your dashboard select "Sources" and view the "CameraServer" source in the left side panel in the Shuffleboard window as shown in the example below. A list of camera streams will be shown, in this case there is only one camera called "Robot Front Camera". Drag that to the tab where it should be displayed. Alternatively the stream can also be placed on the dashboard by right-clicking on the stream in the Sources list and selecting "Show as: Camera Stream". diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-displaying-data.rst b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-displaying-data.rst index b4078d481b..a555a1df5a 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-displaying-data.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-displaying-data.rst @@ -1,5 +1,4 @@ -Displaying data from your robot -=============================== +# Displaying data from your robot Your robot can display data in regular operating modes like Teleop and Autonomous modes but you can also display the status and operate all the robot subsystems when the robot is switched to Test mode. By default you'll see two tabs when you start Shuffleboard, one for Teleop/Autonomous and another for Test mode. The currently selected tab is underlined as can be seen in the picture below. @@ -8,23 +7,45 @@ Your robot can display data in regular operating modes like Teleop and Autonomou Often debugging or monitoring the status of a robot involves writing a number of values to the console and watching them stream by. With Shuffleboard you can put values to a GUI that is automatically constructed based on your program. As values are updated, the corresponding GUI element changes value - there is no need to try to catch numbers streaming by on the screen. -Displaying values in normal operating mode (autonomous or teleop) ------------------------------------------------------------------ +## Displaying values in normal operating mode (autonomous or teleop) .. tab-set-code:: - .. code-block:: java - - protected void execute() { - SmartDashboard.putBoolean("Bridge Limit", bridgeTipper.atBridge()); - SmartDashboard.putNumber("Bridge Angle", bridgeTipper.getPosition()); - SmartDashboard.putNumber("Swerve Angle", drivetrain.getSwerveAngle()); - SmartDashboard.putNumber("Left Drive Encoder", drivetrain.getLeftEncoder()); - SmartDashboard.putNumber("Right Drive Encoder", drivetrain.getRightEncoder()); - SmartDashboard.putNumber("Turret Pot", turret.getCurrentAngle()); - SmartDashboard.putNumber("Turret Pot Voltage", turret.getAverageVoltage()); - SmartDashboard.putNumber("RPM", shooter.getRPM()); - } + ```java + protected void execute() { + SmartDashboard.putBoolean("Bridge Limit", bridgeTipper.atBridge()); + SmartDashboard.putNumber("Bridge Angle", bridgeTipper.getPosition()); + SmartDashboard.putNumber("Swerve Angle", drivetrain.getSwerveAngle()); + SmartDashboard.putNumber("Left Drive Encoder", drivetrain.getLeftEncoder()); + SmartDashboard.putNumber("Right Drive Encoder", drivetrain.getRightEncoder()); + SmartDashboard.putNumber("Turret Pot", turret.getCurrentAngle()); + SmartDashboard.putNumber("Turret Pot Voltage", turret.getAverageVoltage()); + SmartDashboard.putNumber("RPM", shooter.getRPM()); + } + ``` + + ```c++ + frc::SmartDashboard::PutBoolean("Bridge Limit", bridgeTipper.AtBridge()); + frc::SmartDashboard::PutNumber("Bridge Angle", bridgeTipper.GetPosition()); + frc::SmartDashboard::PutNumber("Swerve Angle", drivetrain.GetSwerveAngle()); + frc::SmartDashboard::PutNumber("Left Drive Encoder", drivetrain.GetLeftEncoder()); + frc::SmartDashboard::PutNumber("Right Drive Encoder", drivetrain.GetRightEncoder()); + frc::SmartDashboard::PutNumber("Turret Pot", turret.GetCurrentAngle()); + frc::SmartDashboard::PutNumber("Turret Pot Voltage", turret.GetAverageVoltage()); + frc::SmartDashboard::PutNumber("RPM", shooter.GetRPM()); + ``` + + ```python + from wpilib import SmartDashboard + SmartDashboard.putBoolean("Bridge Limit", bridgeTipper.atBridge()) + SmartDashboard.putNumber("Bridge Angle", bridgeTipper.getPosition()) + SmartDashboard.putNumber("Swerve Angle", drivetrain.getSwerveAngle()) + SmartDashboard.putNumber("Left Drive Encoder", drivetrain.getLeftEncoder()) + SmartDashboard.putNumber("Right Drive Encoder", drivetrain.getRightEncoder()) + SmartDashboard.putNumber("Turret Pot", turret.getCurrentAngle()) + SmartDashboard.putNumber("Turret Pot Voltage", turret.getAverageVoltage()) + SmartDashboard.putNumber("RPM", shooter.getRPM()) + ``` .. figure:: images/display-code-result.png :alt: @@ -35,29 +56,25 @@ You can write Boolean, Numeric, or String values to Shuffleboard by simply calli - String types call SmartDashboard.putString("dashboard-name", value) - Boolean types call SmartDashboard.putBoolean("dashboard-name", value) -Changing the display type of data ---------------------------------- +## Changing the display type of data Depending on the data type of the values being sent to Shuffleboard you can often change the display format. In the previous example you can see that number values were displayed as either decimal numbers, a dial to better represent angles, and as a voltage view for the turret potentiometer. To set the display type right-click on the tile and select "Show as...". You can choose display types from the list in the popup menu. .. figure:: images/configuring-data.png :alt: -Displaying data in Test mode ----------------------------- +## Displaying data in Test mode You may add code to your program to display values for your sensors and actuators while the robot is in Test mode. This can be selected from the Driver Station whenever the robot is not on the field. The code to display these values is automatically generated by RobotBuilder or manually added to your program and is described in the next article. Test mode is designed to verify the correct operation of the sensors and actuators on a robot. In addition it can be used for obtaining setpoints from sensors such as potentiometers and for tuning PID loops in your code. -Setting test mode -~~~~~~~~~~~~~~~~~ +#### Setting test mode .. figure:: images/driverstation-test-mode.png :alt: Enable Test Mode in the Driver Station by clicking on the "Test" button and setting "Enable" on the robot. When doing this, Shuffleboard will display the status of any actuators and sensors used by your program organized by subsystem. -Getting data from the Sources view ----------------------------------- +## Getting data from the Sources view Normally :term:`NetworkTables` data automatically appears on one of the tabs and you just rearrange and use that data. Sometimes you might want to recover a value that was accidentally deleted from the tab or display a value that is not part of the SmartDashboard / NetworkTables key. For these cases the values can be dragged onto a pane from NetworkTables view under Sources on the left side of the window. Choose the value that you want to display and just drag it to the pane and it will be automatically created with the default type of widget for the data type. diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-faq.rst b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-faq.rst index f13e9211bf..e7efb1ca4a 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-faq.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-faq.rst @@ -1,23 +1,18 @@ -Shuffleboard FAQ, issues, and bugs -================================== +# Shuffleboard FAQ, issues, and bugs .. warning:: Shuffleboard as well as most of the other control system components were developed with Java 11 and will not work with Java 8. Be sure before reporting problems that your computer has Java 11 installed and is set as the default Java Environment. -Frequently Asked Questions --------------------------- +## Frequently Asked Questions -How do I report issues, bugs or feature requests with Shuffleboard? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### How do I report issues, bugs or feature requests with Shuffleboard? -Bugs, issues, and feature requests can be added on the Shuffleboard GitHub page by creating an issue. We will try to address them as they are entered into the system. Please try to look at existing issues before creating new ones to make sure you aren't duplicating something that has already been reported or work that is planned. You can find the issues on the `Shuffleboard GitHub page `__. +There is no active maintainer of Shuffleboard, but we are accepting pull requests. Bugs, issues, and feature requests can be added on the Shuffleboard GitHub page by creating an issue. Please try to look at existing issues before creating new ones to make sure you aren't duplicating something that has already been reported or work that is planned. You can find the issues on the [Shuffleboard GitHub page](https://github.com/wpilibsuite/shuffleboard). -How can I add my own widgets or other extensions to Shuffleboard? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### How can I add my own widgets or other extensions to Shuffleboard? -:doc:`Custom Widgets` has a large amount of documentation on extending the program with custom plugins. Sample plugin projects that can be used for additional custom widgets and themes can be found on the `Shuffleboard GitHub page `__. +:doc:`Custom Widgets ` has a large amount of documentation on extending the program with custom plugins. Sample plugin projects that can be used for additional custom widgets and themes can be found on the [Shuffleboard GitHub page](https://github.com/wpilibsuite/shuffleboard/tree/main/example-plugins). -How can I build Shuffleboard from the source code? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### How can I build Shuffleboard from the source code? You can get the source code by downloading, cloning, or forking the repository on the GitHub site. To build and run Shuffleboard from the source, make sure that the current directory is the top level source code and use one of these commands: diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-graphs.rst b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-graphs.rst index f3ce5d921d..191cf524c6 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-graphs.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-graphs.rst @@ -1,5 +1,4 @@ -Working with Graphs -=================== +# Working with Graphs With Shuffleboard you can graph numeric values over time. Graphs are very useful to see how sensor or motor values are changing as your robot is operating. For example the sensor value can be graphed in a PID loop to see how it is responding during tuning. @@ -13,8 +12,7 @@ The graph widget shows line plots of the value that you selected. It will automa .. image:: images/shuffleboard-graphs/time-interval.png :alt: The standard time interval is 30 seconds. -Adding Additional Data Values ------------------------------ +## Adding Additional Data Values For related values it is often desirable to show multiple values on the same graph. To do that, simply drag additional values from the NetworkTables source view (left side of the Shuffleboard window) and drop it onto the graph and that value will be added as shown below. You can continue to drag additional values onto the graph. @@ -26,8 +24,7 @@ You can resize the graph vertically to view the legend if it is not displayed as .. image:: images/shuffleboard-graphs/graph-legend.png :alt: When the graph is resized vertically it will show a legend indicating what color goes with what plot. -Setting Graph Properties ------------------------- +## Setting Graph Properties You can set the number of seconds that are shown in the graph by changing the "Visible time" in the graph widget properties. To access the properties, right-click on the graph and select "Edit properties". diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-lists.rst b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-lists.rst index 1b1ef9e57f..25ac751aad 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-lists.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-lists.rst @@ -1,5 +1,4 @@ -Working with Lists -================== +# Working with Lists Lists in Shuffleboard are sets of tiles grouped together in a vertical layout, making it visually obvious that those tiles are related. In addition, tiles in lists take up less screen space than individual tiles: @@ -9,8 +8,7 @@ Lists in Shuffleboard are sets of tiles grouped together in a vertical layout, m .. figure:: images/list-1.png :alt: -Creating a list ---------------- +## Creating a list .. figure:: images/list-2.png :alt: @@ -23,8 +21,7 @@ A list can be created as follows: Note that tiles in lists do not have header labels; their label is at the bottom of their list entry. -Adding tiles to/removing tiles from a list ------------------------------------------- +## Adding tiles to/removing tiles from a list A tile can be **added** to an existing list as follows: @@ -41,8 +38,7 @@ A tile can be **removed** from a list by following the process in reverse: .. figure:: images/list-3.png :alt: -Rearranging tiles in a list ---------------------------- +## Rearranging tiles in a list .. figure:: images/list-4.png :alt: @@ -59,8 +55,7 @@ Tiles in a list can be rearranged by right-clicking on the tile and selecting: - The **bottom** :guilabel:`Remove` button (below the pinline; section of dropdown with grayed-out *list* label) **deletes** the *list and all tiles inside it* from the Shuffleboard layout. - If you want to take an entry out of a list without deleting it, see `Adding tiles to/removing tiles from a list`_. -Renaming a list ---------------- +## Renaming a list You can rename a list by double-clicking on the list label and changing the name. Click outside the label to save changes. diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-preferences.rst b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-preferences.rst index c8993e7b28..4d97a96a15 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-preferences.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-preferences.rst @@ -1,34 +1,29 @@ -Setting global preferences for Shuffleboard -=========================================== +# Setting global preferences for Shuffleboard There are a number of settings that set the way Shuffleboard looks and behaves. Those are on the Shuffleboard Preferences pane that can be accessed from the File menu. -Setting the theme ------------------ +## Setting the theme Shuffleboard supports two themes, Material Dark and Material Light and the setting depends on your preferences. This uses css styles that apply to the entire application and can be changed any time. .. figure:: images/preferences-1.png :alt: -Setting the default tile size ------------------------------ +## Setting the default tile size Shuffleboard positions tiles on a grid when you are adding or moving them yourself or when they are auto-populated. You can set the default tile size when for each tab or it can be set globally for all the tabs created after the default setting is changed. Finer resolution in the grid results in finer control over placement of tiles. This can be set in the Shuffleboard Preferences window as shown below. .. figure:: images/preferences-2.png :alt: -Working with the layout save files ----------------------------------- +## Working with the layout save files You can save your layout using the File / Save and File / Save as... menu options. The preferences window has options to cause the previous layout to be automatically applied when Shuffleboard starts. In addition, Shuffleboard will display a "Save layout" window to remind you to save the layout on exit, if the layout has changed. You can choose to turn off the automatic prompt on exit, but be sure to save the layout manually in this case so you don't loose your changes. .. figure:: images/preferences-3.png :alt: -Setting the team number ------------------------ +## Setting the team number .. figure:: images/preferences-4.png :alt: diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-recording.rst b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-recording.rst index f944a5ec47..04bb3d4d69 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-recording.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-recording.rst @@ -1,18 +1,15 @@ -Recording and Playback -====================== +# Recording and Playback Shuffleboard can log all widget updates during a session. Later the log file can be "played back" to see what happened during a match or a practice run. This is especially useful if something doesn't operate as intended during a match and you want to see what happened. Each recording is captured in a recording file. -Creating a Recording --------------------- +## Creating a Recording When shuffleboard starts it begins recording to all the NetworkTables values are recorded and continues until stopped by hitting the record/stop button in the recorder controls as shown below. If a new recording is desired, such as when a new piece of code or mechanical system is being tested, stop the current recording if it is running, and click the record button. Click the button again to stop recording and close the recording file. If the button is round (as shown) then click it to start a recording. If the button is a square, then a recoding is currently running so click it to stop the recording. .. image:: images/shuffleboard-recording/record.png :alt: Highlights the record button with a filled in circle on it. -Playing a Recording -------------------- +## Playing a Recording Previous recordings can be played back by: @@ -22,8 +19,7 @@ Previous recordings can be played back by: .. image:: images/shuffleboard-recording/playback.png :alt: Choose "Recording" then "Load Playback" to select the file and start playback of the recording. -Controlling the Playback ------------------------- +## Controlling the Playback Selecting the recoding file will begin playback of that file. While the recording is playing the recording controls will show the current time within the recording as well as the option to loop the recording while watching it. When the recording is being played back the "transport" controls will allow the playback to be controlled. @@ -40,8 +36,7 @@ The controls work as follows: 6. The loop switch turns on playback looping, that is, the playback will run over and over until stopped 7. The time shows the current point within the recording and the total time of the recording -Converting to Different File Formats ------------------------------------- +## Converting to Different File Formats .. image:: images/shuffleboard-recording/convert.png :alt: To convert recordings to another format choose "Recording" then "Convert recodings" @@ -57,7 +52,6 @@ Converted recordings will be generated in the ``~/Shuffleboard/recordings`` dire Different converters can be selected with the dropdown in the top right. By default, only the CSV converter is available. Custom converters from plugins will appear as options in the dropdown. -Additional Notes ----------------- +## Additional Notes Graphs won't display properly while scrubbing the timeline but if it is playing through where the graph history can be captured by the graph then they will display as in the original run. diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-tabs.rst b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-tabs.rst index 9d7689a0fe..abba087100 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-tabs.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-tabs.rst @@ -1,10 +1,8 @@ -Creating and manipulating tabs -============================== +# Creating and manipulating tabs The tabbed layout the Shuffleboard uses help separate different "views" of your robot data and make the displays more useful. You might have a tab the has the display for helping debug the robot program and a different tab for use in competitions. There are a number of options that make tabs very powerful. You can control which data from NetworkTables or other sources appears in each of your tabs using the auto-populate options described later in this article. -Default tabs ------------- +## Default tabs When you open Shuffleboard for the first time there are two tabs, labeled SmartDashboard and LiveWindow. These correspond to the two views that SmartDashboard had depending on whether your robot is running in Autonomous/Teleop or Test mode. In shuffleboard both of these views are available any time. @@ -13,13 +11,11 @@ When you open Shuffleboard for the first time there are two tabs, labeled SmartD On the SmartDashboard tab all the values that are written using the SmartDashboard.putType() set of methods. On the LiveWindow tab all the autogenerated debugging values are shown. -Switching between tabs ----------------------- +## Switching between tabs You can switch between tabs clicking on the tab label at the top of the window. In the case above, simply click on SmartDashboard or LiveWindow to see the values that are associated with each tab. -Adding and Hiding Tabs ----------------------- +## Adding and Hiding Tabs You can add additional tabs by clicking on the plus(+) symbol just to the right of the last tab. Once you create a new tab you can set the label by double-clicking on the label in the tab and editing it. You can also right-click on the tab or use the Tab menu to bring up the tab preferences and from that window you can change the name by editing the Title field. @@ -31,17 +27,14 @@ You can hide tabs by clicking the minus(-) symbol to the left of the selected ta .. image:: images/shuffleboard-tab-deletion.png :alt: Deleting a Shuffleboard Tab -Setting the tab to auto-populate --------------------------------- +## Setting the tab to auto-populate One of the most powerful features with tabs is to have them automatically populate new values based on a source prefix that is supplied in the tab Preferences pane. In the above example the Preferences pane has a Source prefix of "SmartDashboard/Shooter" and Auto populate is turned on. Any values that are written using the SmartDashboard class that specifies a sub-key of Shooter will automatically appear on that tab. Note: keys that match more than one Source prefix will appear in both tabs. Because those keys also start with SmartDashboard/ and that's the Source prefix for the default SmartDashboard tab, those widgets will appear in both panes. To only have values appear in one pane, you can use NetworkTables to write labels and values and use a different path that is not under SmartDashboard. Alternatively you could let everything appear in the SmartDashboard tab making it very cluttered, but have specific tabs for your needs that will be better filtered. -Using the tab grid and spacing ------------------------------- +## Using the tab grid and spacing Each tab can have it's own Tile size (number of pixels per large square). So some tabs might have coarser resolution for easier layout and others might have a fine grid. The Tile size in the Tab preferences overrides any global settings in the Shuffleboard preferences. In addition, you can specify the padding between the drawing in the widget and the edge of the of the widget. If you program user interfaces these parameters are usually referred to as horizontal and vertical gap (hgap, vgap). -Moving widgets between tabs ---------------------------- +## Moving widgets between tabs Currently there is no way to easily move widgets between tabs without deleting it from one tab and dragging the field from the sources hierarchy on the left into the new pane. We hope to have that capability in a subsequent update soon. diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-tour.rst b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-tour.rst index e43e031928..894301312d 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-tour.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-tour.rst @@ -1,9 +1,8 @@ .. include:: -Tour of Shuffleboard -==================== +# Tour of Shuffleboard -Shuffleboard is a dashboard for FRC\ |reg| based on newer technologies such as JavaFX that are available to Java programs. It is designed to be used for creating dashboards for C++ and Java programs. If you've used SmartDashboard in the past then you are already familiar with many of the features of Shuffleboard since they fundamentally work the same way. But Shuffleboard has many features that aren't in SmartDashboard. Here are some of the highlights: +Shuffleboard is a dashboard for FRC\ |reg| based on newer technologies such as JavaFX that are available to Java programs. It is designed to be used for creating dashboards for C++, Java, and Python programs. If you've used SmartDashboard in the past then you are already familiar with many of the features of Shuffleboard since they fundamentally work the same way. But Shuffleboard has many features that aren't in SmartDashboard. Here are some of the highlights: - Graphics is based on **JavaFX**, the Java graphics standard. Each of the components has an associated style sheet so it becomes possible to have different "skins" or "themes" for Shuffleboard. We supply default light and dark themes. - Shuffleboard supports **multiple sheets for the display of your data**. In fact you can create a new sheet (shown as a tab in the Shuffleboard window) and indicate if and which data should be autopopulated on it. By default there is a Test tab and a SmartDashboard tab that are autopopulated as data arrives. Other tabs might be for robot debugging vs. driving. @@ -20,8 +19,7 @@ Shuffleboard is a dashboard for FRC\ |reg| based on newer technologies such as J 2. **Tab panes:** This is where you data is displayed from the robot or other sources. In this example it is Test-mode subsystems that are shown here in the LiveWindow tab. This area can show any number of tabbed windows, and each window has it's own set of properties like grid size and auto-populate. 3. **Record/playback controls:** set of media-like controls where you can playback the current session to see historical data -Starting Shuffleboard ---------------------- +## Starting Shuffleboard .. figure:: images/shuffleboard-driverstation.png :alt: @@ -36,16 +34,24 @@ You can start Shuffleboard in one of four ways: .. note:: The ``.vbs`` (Windows) and ``.py`` (macOS/Linux) scripts help launch the tools using the correct JDK. -Getting robot data onto the dashboard -------------------------------------- +## Getting robot data onto the dashboard -The easiest way to get data displayed on the dashboard is simply to use methods in the SmartDashboard class. For example to write a number to Shuffleboard write: +The easiest way to get data displayed on the dashboard is simply to use methods in the ``SmartDashboard`` class. For example to write a number to Shuffleboard write: .. tab-set-code:: - .. code-block:: java + ```java + SmartDashboard.putNumber("Joystick X value", joystick1.getX()); + ``` - SmartDashboard.putNumber("Joystick X value", joystick1.getX()); + ```c++ + frc::SmartDashboard::PutNumber("Joystick X value", joystick1.getX()); + ``` + + ```python + from wpilib import SmartDashboard + SmartDashboard.putNumber("Joystick X value", joystick1.getX()) + ``` to see a field displayed with the label "Joystick X value" and a value of the X value of the joystick. Each time this line of code is executed, a new joystick value will be sent to Shuffleboard. Remember: you must write the joystick value whenever you want to see an updated value. Executing this line once at the start of the program will only display the value once at the time the line of code was executed. diff --git a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-widgets.rst b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-widgets.rst index 5753c80b78..075804118c 100644 --- a/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-widgets.rst +++ b/source/docs/software/dashboards/shuffleboard/getting-started/shuffleboard-widgets.rst @@ -1,39 +1,33 @@ -Working with widgets -==================== +# Working with widgets The visual displays that you manipulate on the screen in Shuffleboard are called widgets. Widgets are generally automatically displayed from values that the robot program publishes with NetworkTables. -Moving widgets --------------- +## Moving widgets Widgets can be moved simply with drag and drop. Just move the cursor over the widget, left-click and drag it to the new position. When dragging you can only place widgets on grid squares and the size of the grid will effect the resolution of your display. When dragging a red or green outline will be displayed. Green generally means that there is enough room at the current location to drop the widget and red generally means that it will overlap or be too big to drop. In the example below a widget is being moved to a location where it doesn't fit. .. figure:: images/widgets-1.png :alt: -Resizing widgets ----------------- +## Resizing widgets Widgets can be resized by clicking and dragging the edge or corner of the widget image. The cursor will change to a resize-cursor when it is in the right position to resize the widget. As with moving widgets, a green or red outline will be drawn indicating that the widget can be resized or not. The example below shows a widget being resized to a larger area with the green outline indicating that there is no overlap with surrounding widgets. .. figure:: images/widgets-2.png :alt: -Changing the display type of widgets ------------------------------------- +## Changing the display type of widgets Shuffleboard is very rich in display types depending on the data published from the robot. It will automatically choose a default display type, but you might want to change it depending on the application. To see what the possible displays are for any widget, right-click on the widget and select the "Show as..." and from the popup menu, choose the desired type. In the example below are two data values, one a number and the other a boolean. You can see the different types of display options that are available to each. The boolean value has only two possible values (true/false) it can be shown as a boolean box (the red/green color), or text, or a toggle button or toggle switch. The. number value can be displayed as a graph, number bar, number slider, dial, text, or a voltage view depending on the context of the value. .. figure:: images/widgets-3.png :alt: -Changing the title of widgets ------------------------------ +## Changing the title of widgets You can change the title of widgets by double-clicking in their title bar and editing the title to the new value. If a widget is contained in a layout, then right-click on the widget and select the properties. From there you can change the widget title that is displayed. -Changing widget properties --------------------------- +## Changing widget properties You can change the appearance of a widget such as the range of values represented, colors or some other visual element. In cases where this is possible right-click on the widget and select "Edit properties" from the popup menu. In this boolean value widget shown below, the widget title, true color and false color can all be edited. diff --git a/source/docs/software/dashboards/shuffleboard/index.rst b/source/docs/software/dashboards/shuffleboard/index.rst index ee06acdce6..d85ee60c4d 100644 --- a/source/docs/software/dashboards/shuffleboard/index.rst +++ b/source/docs/software/dashboards/shuffleboard/index.rst @@ -1,7 +1,6 @@ -Shuffleboard -============ +# Shuffleboard -Shuffleboard is a modern looking driveteam focused dashboard. It displays network tables data using a variety of widgets that can be positioned and controlled with robot code. It includes many extra features like: tabs, recording / playback, and advanced custom widgets. +Shuffleboard is a straightforward and easily customizable driveteam focused dashboard. It displays network tables data using a variety of widgets that can be positioned and controlled with robot code. It includes many extra features like: tabs, recording / playback, and advanced custom widgets. .. toctree:: :maxdepth: 2 diff --git a/source/docs/software/dashboards/shuffleboard/layouts-with-code/configuring-widgets.rst b/source/docs/software/dashboards/shuffleboard/layouts-with-code/configuring-widgets.rst index b1a89bbbc3..9cb86e277a 100644 --- a/source/docs/software/dashboards/shuffleboard/layouts-with-code/configuring-widgets.rst +++ b/source/docs/software/dashboards/shuffleboard/layouts-with-code/configuring-widgets.rst @@ -1,64 +1,79 @@ -Configuring widgets -=================== +# Configuring widgets Robot programs can specify exactly which widget to use to display a data point, as well as how that widget should be configured. As there are too many widgets to be listed here, consult the docs for details. -Specifying a widget -------------------- +## Specifying a widget Call ``withWidget`` after ``add`` in the call chain: .. tab-set-code:: - .. code-block:: java - - Shuffleboard.getTab("Drive") - .add("Max Speed", 1) - .withWidget(BuiltInWidgets.kNumberSlider) // specify the widget here - .getEntry(); - - .. code-block:: cpp - - frc::Shuffleboard::GetTab("Drive") - .Add("Max Speed", 1) - .WithWidget(frc::BuiltInWidgets::kNumberSlider) // specify the widget here - .GetEntry(); + ```java + Shuffleboard.getTab("Drive") + .add("Max Speed", 1) + .withWidget(BuiltInWidgets.kNumberSlider) // specify the widget here + .getEntry(); + ``` + + ```c++ + frc::Shuffleboard::GetTab("Drive") + .Add("Max Speed", 1) + .WithWidget(frc::BuiltInWidgets::kNumberSlider) // specify the widget here + .GetEntry(); + ``` + + ```python + from wpilib.shuffleboard import Shuffleboard + from wpilib.shuffleboard import BuiltInWidgets + (Shuffleboard.getTab("Drive") + .add("Max Speed", 1) + .withWidget(BuiltInWidgets.kNumberSlider) # specify the widget here + .getEntry()) + ``` In this example, we configure the "Max Speed" widget to use a slider to modify the values instead of a basic text field. .. image:: images/configuring-widgets/maxspeed-negative.png :alt: The max speed widget is placed but it correctly goes from -1 to 1. -Setting widget properties -------------------------- +## Setting widget properties Since the maximum speed only makes sense to be a value from 0 to 1 (full stop to full speed), a slider from -1 to 1 can cause problems if the value drops below zero. Fortunately, we can modify that using the ``withProperties`` method .. tab-set-code:: - .. code-block:: java - - Shuffleboard.getTab("Drive") - .add("Max Speed", 1) - .withWidget(BuiltInWidgets.kNumberSlider) - .withProperties(Map.of("min", 0, "max", 1)) // specify widget properties here - .getEntry(); - - .. code-block:: cpp - - frc::Shuffleboard::GetTab("Drive") - .Add("Max Speed", 1) - .WithWidget(frc::BuiltInWidgets::kNumberSlider) - .WithProperties({ // specify widget properties here - {"min", nt::Value::MakeDouble(0)}, - {"max", nt::Value::MakeDouble(1)} - }) - .GetEntry(); + ```java + Shuffleboard.getTab("Drive") + .add("Max Speed", 1) + .withWidget(BuiltInWidgets.kNumberSlider) + .withProperties(Map.of("min", 0, "max", 1)) // specify widget properties here + .getEntry(); + ``` + + ```c++ + frc::Shuffleboard::GetTab("Drive") + .Add("Max Speed", 1) + .WithWidget(frc::BuiltInWidgets::kNumberSlider) + .WithProperties({ // specify widget properties here + {"min", nt::Value::MakeDouble(0)}, + {"max", nt::Value::MakeDouble(1)} + }) + .GetEntry(); + ``` + + ```python + from wpilib.shuffleboard import Shuffleboard + from wpilib.shuffleboard import BuiltInWidgets + (Shuffleboard.getTab("Drive") + .add("Max Speed", 1) + .withWidget(BuiltInWidgets.kNumberSlider) + .withProperties(map("min", 0, "max", 1)) # specify widget properties here + .getEntry()) + ``` .. image:: images/configuring-widgets/maxspeed.png :alt: The max speed widget limited from 0 to 1. -Notes ------ +## Notes Widgets can be specified by name; however, names are case- and whitespace-sensitive ("Number Slider" is different from "Number slider" and "NumberSlider"). For this reason, it is recommended to use the built in widgets class to specify the widget instead of by raw name. However, a custom widget can only be specified by name or by creating a custom ``WidgetType`` for that widget. Widget property names are neither case-sensitive nor whitespace-sensitive ("Max" and "max" are the same). Consult the documentation on the widget in the BuiltInWidgets class for details on the properties of that widget. diff --git a/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/configuring-widgets/maxspeed-negative.png b/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/configuring-widgets/maxspeed-negative.png index b7c0c06bb9..9e79694974 100644 Binary files a/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/configuring-widgets/maxspeed-negative.png and b/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/configuring-widgets/maxspeed-negative.png differ diff --git a/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/configuring-widgets/maxspeed.png b/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/configuring-widgets/maxspeed.png index 6a3c311d64..086f567df4 100644 Binary files a/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/configuring-widgets/maxspeed.png and b/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/configuring-widgets/maxspeed.png differ diff --git a/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/organizing-widgets/organized.png b/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/organizing-widgets/organized.png index d9ba1e2e8f..8aa8b9d239 100644 Binary files a/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/organizing-widgets/organized.png and b/source/docs/software/dashboards/shuffleboard/layouts-with-code/images/organizing-widgets/organized.png differ diff --git a/source/docs/software/dashboards/shuffleboard/layouts-with-code/index.rst b/source/docs/software/dashboards/shuffleboard/layouts-with-code/index.rst index 518fbbcb78..91f8134778 100644 --- a/source/docs/software/dashboards/shuffleboard/layouts-with-code/index.rst +++ b/source/docs/software/dashboards/shuffleboard/layouts-with-code/index.rst @@ -1,5 +1,4 @@ -Shuffleboard - Layouts with Code -================================ +# Shuffleboard - Layouts with Code .. toctree:: :maxdepth: 1 diff --git a/source/docs/software/dashboards/shuffleboard/layouts-with-code/organizing-widgets.rst b/source/docs/software/dashboards/shuffleboard/layouts-with-code/organizing-widgets.rst index 5aafeb263f..d647c96963 100644 --- a/source/docs/software/dashboards/shuffleboard/layouts-with-code/organizing-widgets.rst +++ b/source/docs/software/dashboards/shuffleboard/layouts-with-code/organizing-widgets.rst @@ -1,8 +1,6 @@ -Organizing Widgets -================== +# Organizing Widgets -Setting Widget Size and Position --------------------------------- +## Setting Widget Size and Position Call ``withSize`` and ``withPosition`` to set the size and position of the widget in the tab. @@ -12,54 +10,67 @@ Call ``withSize`` and ``withPosition`` to set the size and position of the widge .. tab-set-code:: - .. code-block:: java - - Shuffleboard.getTab("Pre-round") - .add("Auto Mode", autoModeChooser) - .withSize(2, 1) // make the widget 2x1 - .withPosition(0, 0); // place it in the top-left corner - - .. code-block:: cpp - - frc::Shuffleboard::GetTab("Pre-round") - .Add("Auto Mode", autoModeChooser) - .WithSize(2, 1) - .WithPosition(0,0); - -Adding Widgets to Layouts -------------------------- + ```java + Shuffleboard.getTab("Pre-round") + .add("Auto Mode", autoModeChooser) + .withSize(2, 1) // make the widget 2x1 + .withPosition(0, 0); // place it in the top-left corner + ``` + + ```c++ + frc::Shuffleboard::GetTab("Pre-round") + .Add("Auto Mode", autoModeChooser) + .WithSize(2, 1) + .WithPosition(0,0); + ``` + + ```python + from wpilib.shuffleboard import Shuffleboard + (Shuffleboard.getTab("Pre-round") + .add("Auto Mode", autoModeChooser) + .withSize(2, 1) # make the widget 2x1 + .withPosition(0, 0)) # place it in the top-left corner + ``` + +## Adding Widgets to Layouts If there are many widgets in a tab with related data, it can be useful to place them into smaller subgroups instead of loose in the tab. Much like how the handle to a tab is retrieved with ``Shuffleboard.getTab``, a layout inside a tab (or even in another layout) can be retrieved with ``ShuffleboardTab.getLayout``. .. tab-set-code:: - .. code-block:: java - - ShuffleboardLayout elevatorCommands = Shuffleboard.getTab("Commands") - .getLayout("Elevator", BuiltInLayouts.kList) - .withSize(2, 2) - .withProperties(Map.of("Label position", "HIDDEN")); // hide labels for commands - + ```java + ShuffleboardLayout elevatorCommands = Shuffleboard.getTab("Commands") + .getLayout("Elevator", BuiltInLayouts.kList) + .withSize(2, 2) + .withProperties(Map.of("Label position", "HIDDEN")); // hide labels for commands elevatorCommands.add(new ElevatorDownCommand()); - elevatorCommands.add(new ElevatorUpCommand()); - // Similarly for the claw commands - - .. code-block:: cpp - - wpi::StringMap> properties{ - std::make_pair("Label position", nt::Value::MakeString("HIDDEN")) - }; - - frc::ShuffleboardLayout& elevatorCommands = frc::Shuffleboard::GetTab("Commands") - .GetLayout("Elevator", frc::BuiltInLayouts::kList) - .WithSize(2, 2) - .WithProperties(properties); - - ElevatorDownCommand* elevatorDown = new ElevatorDownCommand(); - ElevatorUpCommand* elevatorUp = new ElevatorUpCommand(); - - elevatorCommands.Add("Elevator Down", elevatorDown); - elevatorCommands.Add("Elevator Up", elevatorUp); + elevatorCommands.add(new ElevatorUpCommand()); + ``` + + ```c++ + wpi::StringMap> properties{ + std::make_pair("Label position", nt::Value::MakeString("HIDDEN")) + }; + frc::ShuffleboardLayout& elevatorCommands = frc::Shuffleboard::GetTab("Commands") + .GetLayout("Elevator", frc::BuiltInLayouts::kList) + .WithSize(2, 2) + .WithProperties(properties); + ElevatorDownCommand* elevatorDown = new ElevatorDownCommand(); + ElevatorUpCommand* elevatorUp = new ElevatorUpCommand(); + elevatorCommands.Add("Elevator Down", elevatorDown); + elevatorCommands.Add("Elevator Up", elevatorUp); + ``` + + ```python + from wpilib.shuffleboard import Shuffleboard + from wpilib.shuffleboard import BuiltInLayouts + (elevatorCommands = Shuffleboard.getTab("Commands") + .getLayout("Elevator", BuiltInLayouts.kList) + .withSize(2, 2) + .withProperties(map("Label position", "HIDDEN"))) # hide labels for commands + elevatorCommands.add(ElevatorDownCommand()) + elevatorCommands.add(ElevatorUpCommand()) + ``` .. image:: images/organizing-widgets/organized.png :alt: Commands buttons organized by the order they are added for the Elevator and Claw subsystems. diff --git a/source/docs/software/dashboards/shuffleboard/layouts-with-code/retrieving-data.rst b/source/docs/software/dashboards/shuffleboard/layouts-with-code/retrieving-data.rst index fc55981af0..47a817694c 100644 --- a/source/docs/software/dashboards/shuffleboard/layouts-with-code/retrieving-data.rst +++ b/source/docs/software/dashboards/shuffleboard/layouts-with-code/retrieving-data.rst @@ -1,24 +1,37 @@ -Retrieving data -=============== +# Retrieving data Unlike ``SmartDashboard.getNumber`` and friends, retrieving data from Shuffleboard is also done through the NetworkTableEntries, which we covered in the previous article. .. tab-set-code:: - .. code-block:: java + ```java + class DriveBase extends Subsystem { + private ShuffleboardTab tab = Shuffleboard.getTab("Drive"); + private GenericEntry maxSpeed = + tab.add("Max Speed", 1) + .getEntry(); + private DifferentialDrive robotDrive = ...; + public void drive(double left, double right) { + // Retrieve the maximum speed from the dashboard + double max = maxSpeed.getDouble(1.0); + robotDrive.tankDrive(left * max, right * max); + } + } + ``` - class DriveBase extends Subsystem { - private ShuffleboardTab tab = Shuffleboard.getTab("Drive"); - private NetworkTableEntry maxSpeed = - tab.add("Max Speed", 1) - .getEntry(); - - private DifferentialDrive robotDrive = ...; - - public void drive(double left, double right) { - // Retrieve the maximum speed from the dashboard - double max = maxSpeed.getDouble(1.0); - robotDrive.tankDrive(left * max, right * max); - } - } + ```python + import commands2 + import wpilib.drive + from wpilib.shuffleboard import Shuffleboard + class DriveSubsystem(commands2.SubsystemBase): + def __init__(self) -> None: + super().__init__() + tab = Shuffleboard.getTab("Drive") + self.maxSpeed = tab.add("Max Speed", 1).getEntry() + this.robotDrive = ... + def drive(self, left: float, right: float): + # Retrieve the maximum speed from the dashboard + max = self.maxSpeed.getDouble(1.0) + self.robotDrive.tankDrive(left * max, right * max) + ``` This basic example has a glaring flaw: the maximum speed can be set on the dashboard to a value outside [0, 1] - which could cause the inputs to be saturated (always at maximum speed), or even reversed! Fortunately, there is a way to avoid this problem - covered in the next article. diff --git a/source/docs/software/dashboards/shuffleboard/layouts-with-code/sending-data.rst b/source/docs/software/dashboards/shuffleboard/layouts-with-code/sending-data.rst index 8da65c6800..bc8092034a 100644 --- a/source/docs/software/dashboards/shuffleboard/layouts-with-code/sending-data.rst +++ b/source/docs/software/dashboards/shuffleboard/layouts-with-code/sending-data.rst @@ -1,43 +1,54 @@ -Sending data -============ +# Sending data Unlike SmartDashboard, data cannot be sent directly to Shuffleboard without first specifying what tab the data should be placed in. -Sending simple data -------------------- +## Sending simple data Sending simple data (numbers, strings, booleans, and arrays of these) is done by calling ``add`` on a tab. This method will set the value if not already present, but will not overwrite an existing value. .. tab-set-code:: - .. code-block:: java + ```java + Shuffleboard.getTab("Numbers") + .add("Pi", 3.14); + ``` - Shuffleboard.getTab("Numbers") - .add("Pi", 3.14); + ```c++ + frc::Shuffleboard::GetTab("Numbers") + .Add("Pi", 3.14); + ``` - .. code-block:: c++ - - frc::Shuffleboard::GetTab("Numbers") - .Add("Pi", 3.14); + ```python + from wpilib.shuffleboard import Shuffleboard + Shuffleboard.getTab("Tab Title").add("Pi", 3.14) + ``` If data needs to be updated (for example, the output of some calculation done on the robot), call ``getEntry()`` after defining the value, then update it when needed or in a ``periodic`` function .. tab-set-code:: - .. code-block:: java - - class VisionCalculator { - private ShuffleboardTab tab = Shuffleboard.getTab("Vision"); - private NetworkTableEntry distanceEntry = - tab.add("Distance to target", 0) - .getEntry(); - - public void calculate() { - double distance = ...; - distanceEntry.setDouble(distance); - } - } - -Making choices persist between reboots --------------------------------------- + ```java + class VisionCalculator { + private ShuffleboardTab tab = Shuffleboard.getTab("Vision"); + private GenericEntry distanceEntry = + tab.add("Distance to target", 0) + .getEntry(); + public void calculate() { + double distance = ...; + distanceEntry.setDouble(distance); + } + } + ``` + + ```python + from wpilib.shuffleboard import Shuffleboard + def robotInit(self): + tab = Shuffleboard.getTab("Vision") + self.distanceEntry = tab.add("Distance to target", 0).getEntry() + def teleopPeriodic(self): + distance = self.encoder.getDistance() + self.distanceEntry.setDouble(distance) + ``` + +## Making choices persist between reboots When configuring a robot from the dashboard, some settings may want to persist between robot or driverstation reboots instead of having drivers remember (or forget) to configure the settings before each match. @@ -47,29 +58,41 @@ Simply using `addPersistent` instead of `add` will make the value saved on the r .. tab-set-code:: - .. code-block:: java - - Shuffleboard.getTab("Drive") - .addPersistent("Max Speed", 1.0); + ```java + Shuffleboard.getTab("Drive") + .addPersistent("Max Speed", 1.0); + ``` - .. code-block:: c++ + ```c++ + frc::Shuffleboard::GetTab("Drive") + .AddPersistent("Max Speed", 1.0); + ``` - frc::Shuffleboard::GetTab("Drive") - .AddPersistent("Max Speed", 1.0); + ```python + from wpilib.shuffleboard import Shuffleboard + (Shuffleboard.getTab("Drive") + .addPersistent("Max Speed", 1.0)) + ``` -Sending sensors, motors, etc ----------------------------- +## Sending sensors, motors, etc Analogous to ``SmartDashboard.putData``, any ``Sendable`` object (most sensors, motor controllers, and SendableChoosers) can be added to any tab .. tab-set-code:: - .. code-block:: java + ```java + Shuffleboard.getTab("Tab Title") + .add("Sendable Title", mySendable); + ``` - Shuffleboard.getTab("Tab Title") - .add("Sendable Title", mySendable); + ```c++ + frc::Shuffleboard::GetTab("Tab Title") + .Add("Sendable Title", mySendable); + ``` - .. code-block:: c++ + ```python + from wpilib.shuffleboard import Shuffleboard + (Shuffleboard.getTab("Tab Title") + .add("Sendable Title", mySendable)) + ``` - frc::Shuffleboard::GetTab("Tab Title") - .Add("Sendable Title", mySendable); diff --git a/source/docs/software/dashboards/shuffleboard/layouts-with-code/using-tabs.rst b/source/docs/software/dashboards/shuffleboard/layouts-with-code/using-tabs.rst index b110659596..5935307b6c 100644 --- a/source/docs/software/dashboards/shuffleboard/layouts-with-code/using-tabs.rst +++ b/source/docs/software/dashboards/shuffleboard/layouts-with-code/using-tabs.rst @@ -1,5 +1,4 @@ -Using tabs -========== +# Using tabs Shuffleboard is a tabbed interface. Each tab organizes widgets in a logical grouping. By default, Shuffleboard has tabs for the legacy SmartDashboard and LiveWindow - but new tabs can now be created in Shuffleboard directly from a robot program for better organization. @@ -7,37 +6,43 @@ Creating a new tab .. tab-set-code:: - .. code-block:: java + ```java + ShuffleboardTab tab = Shuffleboard.getTab("Tab Title"); + ``` - ShuffleboardTab tab = Shuffleboard.getTab("Tab Title"); - - .. code-block:: c++ - - ShuffleboardTab& tab = Shuffleboard::GetTab("Tab Title"); + ```c++ + ShuffleboardTab& tab = Shuffleboard::GetTab("Tab Title"); + ``` + ```python + from wpilib.shuffleboard import Shuffleboard + tab = Shuffleboard.getTab("Tab Title") + ``` Creating a new tab is as simple as calling a single method on the Shuffleboard class, which will create a new tab on Shuffleboard and return a handle for adding your data to the tab. Calling getTab multiple times with the same tab title will return the same handle each time. -Selecting a tab ---------------- +## Selecting a tab .. tab-set-code:: - .. code-block:: java - - Shuffleboard.selectTab("Tab Title"); - - .. code-block:: c++ + ```java + Shuffleboard.selectTab("Tab Title"); + ``` - Shuffleboard::SelectTab("Tab Title"); + ```c++ + Shuffleboard::SelectTab("Tab Title"); + ``` + ```python + from wpilib.shuffleboard import Shuffleboard + Shuffleboard.selectTab("Tab Title") + ``` This method lets a tab be selected by title. This is case-sensitive (so "Tab Title" and "Tab title" are two individual tabs), and only works if a tab with that title exists at the time the method is called, so calling ``selectTab("Example")``\ will only have an effect if a tab named "Example" has previously been defined. This method can be used to select any tab in Shuffleboard, not just ones created by the robot program. -Caveats -------- +## Caveats Tabs created from a robot program differ in a few important ways from normal tabs created from the dashboard: diff --git a/source/docs/software/dashboards/smartdashboard/changing-display-properties.rst b/source/docs/software/dashboards/smartdashboard/changing-display-properties.rst index d676187413..b81afb24e8 100644 --- a/source/docs/software/dashboards/smartdashboard/changing-display-properties.rst +++ b/source/docs/software/dashboards/smartdashboard/changing-display-properties.rst @@ -1,42 +1,36 @@ -Changing the display properties of a value -========================================== +# Changing the display properties of a value Each value displayed with SmartDashboard has a set of properties that effect the way it's displayed. -Setting the SmartDashboard display into editing mode ----------------------------------------------------- +## Setting the SmartDashboard display into editing mode .. image:: images/changing-display-properties/editable.png :alt: Select Editable from the View menu. The SmartDashboard has two modes it can operate in, display mode and edit mode. In edit mode you can move around widgets on the screen and edit their properties. To put the SmartDashboard into edit mode, click the "View" menu, then select "Editable" to turn on edit mode. -Getting the properties editor of a widget ------------------------------------------ +## Getting the properties editor of a widget .. image:: images/changing-display-properties/widget-properties.png :alt: Right click on any widget and choose properties. Once in edit mode, you can display and edit the properties for a widget. Right-click on the widget and select "Properties...". -Editing the properties on a field ---------------------------------- +## Editing the properties on a field .. image:: images/changing-display-properties/edit.png :alt: Edit properties from the dialog window that pops up. A dialog box will be shown in response to the "Properties..." menu item on the widgets right-click context menu. -Editing the widgets background color ------------------------------------- +## Editing the widgets background color .. image:: images/changing-display-properties/background.png :alt: Change the background color of the widget using a color palette. To edit a property value, say, Background color, click the background color shown (in this case grey), and choose a color from the color editor that pops up. This will be used as the widgets background color. -Edit properties of other widgets --------------------------------- +## Edit properties of other widgets .. image:: images/changing-display-properties/other-properties.png :alt: Each widget has unique properties. diff --git a/source/docs/software/dashboards/smartdashboard/changing-display-widget-type.rst b/source/docs/software/dashboards/smartdashboard/changing-display-widget-type.rst index fa4a943f4c..2b8f4caff1 100644 --- a/source/docs/software/dashboards/smartdashboard/changing-display-widget-type.rst +++ b/source/docs/software/dashboards/smartdashboard/changing-display-widget-type.rst @@ -1,26 +1,22 @@ -Changing the Display Widget Type for a Value -============================================ +# Changing the Display Widget Type for a Value One can change the type of widget that displays values with the SmartDashboard. The allowable widgets depend on the type of the value being displayed. -Setting Edit Mode ------------------ +## Setting Edit Mode .. image:: images/changing-display-widget-type/set-edit-mode.png :alt: Select Editable from the View menu. Make sure that the SmartDashboard is in edit mode. This is done by selecting ``Editable`` from the ``View menu``. -Choosing Widget Type --------------------- +## Choosing Widget Type .. image:: images/changing-display-widget-type/choose-new-widget-type.png :alt: When editable right click on any widget and choose "Change to...". Right-click on the widget and select ``Change to...``. Then, pick the type of widget to use for the particular value. In this case we choose ``LinePlot``. -Showing New Widget Type ------------------------ +## Showing New Widget Type .. image:: images/changing-display-widget-type/new-widget-type-shown.png :alt: A line plot widget. diff --git a/source/docs/software/dashboards/smartdashboard/choosing-an-autonomous-program-from-smartdashboard.rst b/source/docs/software/dashboards/smartdashboard/choosing-an-autonomous-program-from-smartdashboard.rst index 54305455e4..a2623e2f3a 100644 --- a/source/docs/software/dashboards/smartdashboard/choosing-an-autonomous-program-from-smartdashboard.rst +++ b/source/docs/software/dashboards/smartdashboard/choosing-an-autonomous-program-from-smartdashboard.rst @@ -1,114 +1,147 @@ -Choosing an Autonomous Program -============================== +# Choosing an Autonomous Program Often teams have more than one autonomous program, either for competitive reasons or for testing new software. Programs often vary by adding things like time delays, different strategies, etc. The methods to choose the strategy to run usually involves switches, joystick buttons, knobs or other hardware based inputs. With the SmartDashboard you can simply display a widget on the screen to choose the autonomous program that you would like to run. And with command based programs, that program is encapsulated in one of several commands. This article shows how to select an autonomous program with only a few lines of code and a nice looking user interface, with examples for both TimedRobot and Command-Based Robots. -TimedRobot ----------- +## TimedRobot -.. note:: The code snippets shown below are part of the TimedRobot template (`Java `__, `C++ `__): +.. note:: The code snippets shown below are part of the TimedRobot template ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timed), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/templates/timed)): -Creating SendableChooser Object -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Creating SendableChooser Object In ``Robot.java`` / ``Robot.h``, create a variable to hold a reference to a ``SendableChooser`` object. Two or more auto modes can be added by creating strings to send to the chooser. Using the ``SendableChooser``, one can choose between them. In this example, ``Default`` and ``My Auto`` are shown as options. You will also need a variable to store which auto has been chosen, ``m_autoSelected``. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timed/Robot.java - :language: java - :lines: 18-21 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timed/Robot.java + :language: Java + :lines: 18-21 - .. tab-item:: C++ - :sync: C++ + .. tab-item:: C++ + :sync: C++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/timed/include/Robot.h - :language: c++ - :lines: 28-31 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/timed/include/Robot.h + :language: C++ + :lines: 28-31 -Setting Up Options -^^^^^^^^^^^^^^^^^^ + .. tab-item:: Python + :sync: Python + + ```python + import wpilib + self.defaultAuto = "Default" + self.customAuto = "My Auto"; + self.chooser = wpilib.SendableChooser() + ``` + +### Setting Up Options The chooser allows you to pick from a list of defined elements, in this case the strings we defined above. In ``robotInit``, add your options created as strings above using ``setDefaultOption`` or ``addOption``. ``setDefaultOption`` will be the one selected by default when the dashboard starts. The ``putData`` function will push it to the dashboard on your driver station computer. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timed/Robot.java + :language: java + :lines: 28-32 + + .. tab-item:: C++ + :sync: C++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timed/Robot.java - :language: java - :lines: 28-32 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/timed/cpp/Robot.cpp + :language: c++ + :lines: 10-14 - .. tab-item:: C++ - :sync: C++ + .. tab-item:: Python + :sync: Python - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/timed/cpp/Robot.cpp - :language: c++ - :lines: 10-14 + ```python + from wpilib import SmartDashboard + self.chooser.setDefaultOption("Default Auto", self.defaultAuto) + self.chooser.addOption("My Auto", self.customAuto) + SmartDashboard.putData("Auto choices", self.chooser) + ``` -Running Autonomous Code -^^^^^^^^^^^^^^^^^^^^^^^ +### Running Autonomous Code Now, in ``autonomousInit`` and ``autonomousPeriodic``, you can use the ``m_autoSelected`` variable to read which option was chosen, and change what happens during the autonomous period. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timed/Robot.java - :language: java - :lines: 54-56, 58-73 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/timed/Robot.java + :language: Java + :lines: 54-56, 58-73 - .. tab-item:: C++ - :sync: C++ + .. tab-item:: C++ + :sync: C++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/templates/timed/cpp/Robot.cpp - :language: c++ - :lines: 37-38, 41-57 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/templates/timed/cpp/Robot.cpp + :language: C++ + :lines: 37-38, 41-57 + .. tab-item:: Python + :sync: Python -Command-Based -------------- + ```python + def autonomousInit(self): + self.autoSelected = self.chooser.getSelected() + print("Auto selected: " + self.autoSelected) + def autonomousPeriodic(self): + match self.autoSelected: + case self.customAuto: + # Put custom auto code here + case _: + # Put default auto code here + ``` -.. note:: The code snippets shown below are part of the HatchbotTraditional example project (`Java `__, `C++ `__): +## Command-Based -Creating the SendableChooser Object -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. note:: The code snippets shown below are part of the HatchbotTraditional example project ([Java](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional), [C++](https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional), [Python](https://github.com/robotpy/examples/tree/main/HatchbotTraditional)): + +### Creating the SendableChooser Object In ``RobotContainer``, create a variable to hold a reference to a ``SendableChooser`` object. Two or more commands can be created and stored in new variables. Using the ``SendableChooser``, one can choose between them. In this example, ``SimpleAuto`` and ``ComplexAuto`` are shown as options. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java + :language: Java + :lines: 40-49 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java - :language: java - :lines: 40-49 + .. tab-item:: C++ (using raw pointers) + :sync: C++ (using raw pointers) - .. tab-item:: C++ (using raw pointers) - :sync: C++ (using raw pointers) + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/RobotContainer.h + :language: C++ + :lines: 38-44 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/RobotContainer.h - :language: c++ - :lines: 38-44 + .. tab-item:: C++ (using ``CommandPtr``) + :sync: C++ (using ``CommandPtr``) - .. tab-item:: C++ (using ``CommandPtr``) - :sync: C++ (using ``CommandPtr``) + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/RobotContainer.h + :language: C++ + :lines: 45-50 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/RobotContainer.h - :language: c++ - :lines: 45-50 + .. tab-item:: Python + :sync: Python -Setting up SendableChooser -^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/2024.0.0b4/HatchbotTraditional/robotcontainer.py + :language: Python + :lines: 45-54 + +### Setting up SendableChooser Imagine that you have two autonomous programs to choose between and they are encapsulated in commands ``SimpleAuto`` and ``ComplexAuto``. To choose between them: @@ -116,125 +149,162 @@ In ``RobotContainer``, create a ``SendableChooser`` object and add instances of .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java + :language: java + :lines: 69-71 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java - :language: java - :lines: 69-71 + .. tab-item:: C++ (using raw pointers) + :sync: C++ (using raw pointers) - .. tab-item:: C++ (using raw pointers) - :sync: C++ (using raw pointers) + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp + :language: c++ + :lines: 18-20 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp - :language: c++ - :lines: 18-20 + .. tab-item:: C++ (using ``CommandPtr``) + :sync: C++ (using ``CommandPtr``) - .. tab-item:: C++ (using ``CommandPtr``) - :sync: C++ (using ``CommandPtr``) + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/RobotContainer.cpp + :language: c++ + :lines: 12-15 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/cpp/RobotContainer.cpp - :language: c++ - :lines: 12-15 + .. tab-item:: Python + :sync: Python + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/2024.0.0b4/HatchbotTraditional/robotcontainer.py + :language: Python + :lines: 56-58 Then, publish the chooser to the dashboard: -.. tab-set-code:: +.. tab-set:: - .. code-block:: java + .. tab-item:: Java + :sync: Java + ```java // Put the chooser on the dashboard SmartDashboard.putData(m_chooser); + ``` - .. code-block:: c++ + .. tab-item:: C++ + :sync: C++ + ```c++ // Put the chooser on the dashboard frc::SmartDashboard::PutData(&m_chooser); + ``` + + .. tab-item:: Python + :sync: Python + + ```python + from wpilib import SmartDashboard + # Put the chooser on the dashboard + SmartDashboard.putData(chooser) + ``` -Starting an Autonomous Command -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Starting an Autonomous Command In ``Robot.java``, when the autonomous period starts, the ``SendableChooser`` object is polled to get the selected command and that command must be scheduled. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java - :language: java - :lines: 124-126 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/RobotContainer.java + :language: java + :lines: 124-126 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Robot.java - :language: java - :lines: 67-68,76-81 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Robot.java + :language: java + :lines: 67-68,76-81 - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp - :language: c++ - :lines: 81-84 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/RobotContainer.cpp + :language: c++ + :lines: 81-84 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/Robot.cpp - :language: c++ - :lines: 46-52 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/Robot.cpp + :language: c++ + :lines: 46-52 -Running the Scheduler during Autonomous -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. tab-item:: Python + :sync: Python -In ``Robot.java``, this will run the scheduler every driver station update period (about every 20ms) and cause the selected autonomous command to run. + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/2024.0.0b4/HatchbotTraditional/robotcontainer.py + :language: Python + :lines: 93-94 + + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/2024.0.0b4/HatchbotTraditional/robot.py + :language: Python + :lines: 41-46 + +### Running the Scheduler during Autonomous + +In ``Robot.java``, this will run the scheduler every driver station update period (about every 20ms) and cause the selected autonomous command to run. In Python the scheduler runs automatically when ``TimedCommandRobot`` is used. .. note:: Running the scheduler can occur in the ``autonomousPeriodic()`` function or ``robotPeriodic()``, both will function similarly in autonomous mode. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Robot.java - :language: java - :lines: 49-50,55-56 - :linenos: - :lineno-start: 40 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Robot.java + :language: java + :lines: 49-50,55-56 + :linenos: + :lineno-start: 40 - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/Robot.cpp - :language: c++ - :lines: 29-31 - :linenos: - :lineno-start: 29 + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/Robot.cpp + :language: c++ + :lines: 29-31 + :linenos: + :lineno-start: 29 -Canceling the Autonomous Command -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Canceling the Autonomous Command In ``Robot.java``, when the teleop period begins, the autonomous command will be canceled. .. tab-set:: - .. tab-item:: Java - :sync: Java + .. tab-item:: Java + :sync: Java + + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Robot.java + :language: java + :lines: 87-96 + :linenos: + :lineno-start: 78 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/Robot.java - :language: java - :lines: 87-96 - :linenos: - :lineno-start: 78 + .. tab-item:: C++ (Source) + :sync: C++ (Source) - .. tab-item:: C++ (Source) - :sync: C++ (Source) + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/Robot.cpp + :language: c++ + :lines: 56-65 + :linenos: + :lineno-start: 56 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.1.1-beta-3/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/cpp/Robot.cpp - :language: c++ - :lines: 56-65 - :linenos: - :lineno-start: 56 + .. tab-item:: Python + :sync: Python + .. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/2024.0.0b4/HatchbotTraditional/robot.py + :language: Python + :lines: 51-57 + :linenos: + :lineno-start: 51 -SmartDashboard Display -^^^^^^^^^^^^^^^^^^^^^^ +### SmartDashboard Display .. image:: images/choosing-an-autonomous-program-from-smartdashboard/smartdashboard-display.png :alt: SendableChooser shows two selectable autos: Simple Auto and Complex Auto. diff --git a/source/docs/software/dashboards/smartdashboard/displaying-expressions.rst b/source/docs/software/dashboards/smartdashboard/displaying-expressions.rst index 7c002494c3..5c741e7a1f 100644 --- a/source/docs/software/dashboards/smartdashboard/displaying-expressions.rst +++ b/source/docs/software/dashboards/smartdashboard/displaying-expressions.rst @@ -1,48 +1,56 @@ -Displaying Expressions from a Robot Program -=========================================== +# Displaying Expressions from a Robot Program .. note:: Often debugging or monitoring the status of a robot involves writing a number of values to the console and watching them stream by. With SmartDashboard you can put values to a GUI that is automatically constructed based on your program. As values are updated, the corresponding GUI element changes value - there is no need to try to catch numbers streaming by on the screen. -Writing Values to SmartDashboard ------------------------------------- +## Writing Values to SmartDashboard .. tab-set-code:: - .. code-block:: java - - protected void execute() { - SmartDashboard.putBoolean("Bridge Limit", bridgeTipper.atBridge()); - SmartDashboard.putNumber("Bridge Angle", bridgeTipper.getPosition()); - SmartDashboard.putNumber("Swerve Angle", drivetrain.getSwerveAngle()); - SmartDashboard.putNumber("Left Drive Encoder", drivetrain.getLeftEncoder()); - SmartDashboard.putNumber("Right Drive Encoder", drivetrain.getRightEncoder()); - SmartDashboard.putNumber("Turret Pot", turret.getCurrentAngle()); - SmartDashboard.putNumber("Turret Pot Voltage", turret.getAverageVoltage()); - SmartDashboard.putNumber("RPM", shooter.getRPM()); - } - - .. code-block:: cpp - - void Command::Execute() { - frc::SmartDashboard::PutBoolean("Bridge Limit", BridgeTipper.AtBridge()); - frc::SmartDashboard::PutNumber("Bridge Angle", BridgeTipper.GetPosition()); - frc::SmartDashboard::PutNumber("Swerve Angle", Drivetrain.GetSwerveAngle()); - frc::SmartDashboard::PutNumber("Left Drive Encoder", Drivetrain.GetLeftEncoder()); - frc::SmartDashboard::PutNumber("Right Drive Encoder", Drivetrain.GetRightEncoder()); - frc::SmartDashboard::PutNumber("Turret Pot", Turret.GetCurrentAngle()); - frc::SmartDashboard::PutNumber("Turret Pot Voltage", Turret.GetAverageVoltage()); - frc::SmartDashboard::PutNumber("RPM", Shooter.GetRPM()); - } + ```java + protected void execute() { + SmartDashboard.putBoolean("Bridge Limit", bridgeTipper.atBridge()); + SmartDashboard.putNumber("Bridge Angle", bridgeTipper.getPosition()); + SmartDashboard.putNumber("Swerve Angle", drivetrain.getSwerveAngle()); + SmartDashboard.putNumber("Left Drive Encoder", drivetrain.getLeftEncoder()); + SmartDashboard.putNumber("Right Drive Encoder", drivetrain.getRightEncoder()); + SmartDashboard.putNumber("Turret Pot", turret.getCurrentAngle()); + SmartDashboard.putNumber("Turret Pot Voltage", turret.getAverageVoltage()); + SmartDashboard.putNumber("RPM", shooter.getRPM()); + } + ``` + + ```c++ + void Command::Execute() { + frc::SmartDashboard::PutBoolean("Bridge Limit", BridgeTipper.AtBridge()); + frc::SmartDashboard::PutNumber("Bridge Angle", BridgeTipper.GetPosition()); + frc::SmartDashboard::PutNumber("Swerve Angle", Drivetrain.GetSwerveAngle()); + frc::SmartDashboard::PutNumber("Left Drive Encoder", Drivetrain.GetLeftEncoder()); + frc::SmartDashboard::PutNumber("Right Drive Encoder", Drivetrain.GetRightEncoder()); + frc::SmartDashboard::PutNumber("Turret Pot", Turret.GetCurrentAngle()); + frc::SmartDashboard::PutNumber("Turret Pot Voltage", Turret.GetAverageVoltage()); + frc::SmartDashboard::PutNumber("RPM", Shooter.GetRPM()); + } + ``` + + ```python + from wpilib import SmartDashboard + SmartDashboard.putBoolean("Bridge Limit", bridgeTipper.atBridge()) + SmartDashboard.putNumber("Bridge Angle", bridgeTipper.getPosition()) + SmartDashboard.putNumber("Swerve Angle", drivetrain.getSwerveAngle()) + SmartDashboard.putNumber("Left Drive Encoder", drivetrain.getLeftEncoder()) + SmartDashboard.putNumber("Right Drive Encoder", drivetrain.getRightEncoder()) + SmartDashboard.putNumber("Turret Pot", turret.getCurrentAngle()) + SmartDashboard.putNumber("Turret Pot Voltage", turret.getAverageVoltage()) + SmartDashboard.putNumber("RPM", shooter.getRPM()) + ``` You can write Boolean, Numeric, or String values to the SmartDashboard by simply calling the correct method for the type and including the name and the value of the data, no additional code is required. Any time in your program that you write another value with the same name, it appears in the same UI element on the screen on the driver station or development computer. As you can imagine this is a great way of debugging and getting status of your robot as it is operating. -Creating Widgets on SmartDashboard ----------------------------------- +## Creating Widgets on SmartDashboard Widgets are populated on the SmartDashboard automatically, no user intervention is required. Note that the widgets are only populated when the value is first written, you may need to enable your robot in a particular mode or trigger a particular code routine for an item to appear. To alter the appearance of the widget, see the next two sections :doc:`Changing the Display Properties of a Value ` and :doc:`Changing the Display Widget Type for a Value `. -Stale Data ----------- +## Stale Data SmartDashboard uses :term:`NetworkTables` for communicating values between the robot and the driver station laptop. NetworkTables acts as a distributed table of name and value pairs. If a name/value pair is added to either the client (laptop) or server (robot) it is replicated to the other. If a name/value pair is deleted from, say, the robot but the SmartDashboard or OutlineViewer are still running, then when the robot is restarted, the old values will still appear in the SmartDashboard and OutlineViewer because they never stopped running and continue to have those values in their tables. When the robot restarts, those old values will be replicated to the robot. To ensure that the SmartDashboard and OutlineViewer are showing current values, it is necessary to restart the NetworkTables clients and robot at the same time. That way, old values that one is holding won't get replicated to the others. diff --git a/source/docs/software/dashboards/smartdashboard/displaying-status-of-commands-and-subsystems.rst b/source/docs/software/dashboards/smartdashboard/displaying-status-of-commands-and-subsystems.rst index 4a13158cff..2c628244e7 100644 --- a/source/docs/software/dashboards/smartdashboard/displaying-status-of-commands-and-subsystems.rst +++ b/source/docs/software/dashboards/smartdashboard/displaying-status-of-commands-and-subsystems.rst @@ -1,10 +1,8 @@ -Displaying the Status of Commands and Subsystems -================================================ +# Displaying the Status of Commands and Subsystems If you are using the command-based programming features of WPILib, you will find that they are very well integrated with SmartDashboard. It can help diagnose what the robot is doing at any time and it gives you control and a view of what's currently running. -Overview of Command and Subsystem Displays ------------------------------------------- +## Overview of Command and Subsystem Displays .. image:: images/displaying-status-of-commands-and-subsystems/command-system-displays.png :alt: An exmample SmartDashboard screen showing the Scheduler and what commands are running. @@ -18,18 +16,23 @@ With SmartDashboard you can display the status of the commands and subsystems in In the following examples, you'll see what the screen would look like when there are commands running, and the code that produces this display. -Displaying the Scheduler Status -------------------------------- +## Displaying the Scheduler Status .. tab-set-code:: - .. code-block:: java + ```java + SmartDashboard.putData(CommandScheduler.getInstance()); + ``` - SmartDashboard.putData(CommandScheduler.getInstance()); + ```c++ + frc::SmartDashboard::PutData(frc2::CommandScheduler::GetInstance()); + ``` - .. code-block:: c++ - - frc::SmartDashboard::PutData(frc2::CommandScheduler::GetInstance()); + ```python + from wpilib import SmartDashboard + from commands2 import CommandScheduler + SmartDashboard.putData(CommandScheduler.getInstance()) + ``` You can display the status of the Scheduler (the code that schedules your commands to run). This is easily done by adding a single line to the ``RobotInit`` method in your RobotProgram as shown here. In this example the Scheduler instance is written using the ``putData`` method to SmartDashboard. This line of code produces the display in the previous image. @@ -38,18 +41,22 @@ You can display the status of the Scheduler (the code that schedules your comman This is the scheduler status when there are two commands running, ``ExampleCommand`` and ``newCommand``. This replaces the ``No commands running.`` message from the previous screen image. You can see commands displayed on the dashboard as the program runs and various commands are triggered. -Displaying Subsystem Status ---------------------------- +## Displaying Subsystem Status .. tab-set-code:: - .. code-block:: java - - SmartDashboard.putData(exampleSubsystem); + ```java + SmartDashboard.putData(exampleSubsystem); + ``` - .. code-block:: c++ + ```c++ + frc::SmartDashboard::PutData(&exampleSubsystem); + ``` - frc::SmartDashboard::PutData(&exampleSubsystem); + ```python + from wpilib import SmartDashboard + SmartDashboard.putData(exampleSubsystem) + ``` In this example we are writing the command instance, ``exampleSubsystem`` and instance of the ``ExampleSubsystem`` class to the SmartDashboard. This causes the display shown in the previous image. The text field will either contain a few dashes, ``---`` indicating that no command is current using this subsystem, or the name of the command currently using this subsystem. @@ -58,18 +65,22 @@ In this example we are writing the command instance, ``exampleSubsystem`` and in Running commands will "require" subsystems. That is the command is reserving the subsystem for its exclusive use. If you display a subsystem on SmartDashboard, it will display which command is currently using it. In this example, ``ExampleSubsystem`` is in use by ``ExampleCommand``. -Activating Commands with a Button ---------------------------------- +## Activating Commands with a Button .. tab-set-code:: - .. code-block:: java - - SmartDashboard.putData("Autonomous Command", exampleCommand); + ```java + SmartDashboard.putData("Autonomous Command", exampleCommand); + ``` - .. code-block:: c++ + ```c++ + frc::SmartDashboard::PutData("Autonomous Command", &exampleCommand); + ``` - frc::SmartDashboard::PutData("Autonomous Command", &exampleCommand); + ```python + from wpilib import SmartDashboard + SmartDashboard.putData("Autonomous Command", exampleCommand) + ``` This is the code required to create a button for the command on SmartDashboard. Pressing the button will schedule the command. While the command is running, the button label changes from ``start`` to ``cancel`` and pressing the button will cancel the command. diff --git a/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/background.png b/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/background.png index c60ab21bff..0b4ab268cb 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/background.png and b/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/background.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/edit.png b/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/edit.png index 9b0d471afd..912d6d1fa9 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/edit.png and b/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/edit.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/editable.png b/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/editable.png index 3521968cf2..db2d7da989 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/editable.png and b/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/editable.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/other-properties.png b/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/other-properties.png index 439b3f5104..9ef21b684b 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/other-properties.png and b/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/other-properties.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/widget-properties.png b/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/widget-properties.png index e7be217615..c773cb9023 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/widget-properties.png and b/source/docs/software/dashboards/smartdashboard/images/changing-display-properties/widget-properties.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/choose-new-widget-type.png b/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/choose-new-widget-type.png index 283fb8990c..0029449431 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/choose-new-widget-type.png and b/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/choose-new-widget-type.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/new-widget-type-shown.png b/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/new-widget-type-shown.png index f49a53d739..68ee7b659b 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/new-widget-type-shown.png and b/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/new-widget-type-shown.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/set-edit-mode.png b/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/set-edit-mode.png index 88b7e639bb..ee1809c667 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/set-edit-mode.png and b/source/docs/software/dashboards/smartdashboard/images/changing-display-widget-type/set-edit-mode.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/choosing-an-autonomous-program-from-smartdashboard/smartdashboard-display.png b/source/docs/software/dashboards/smartdashboard/images/choosing-an-autonomous-program-from-smartdashboard/smartdashboard-display.png index ddffcbd9d0..5b4d5a8fa3 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/choosing-an-autonomous-program-from-smartdashboard/smartdashboard-display.png and b/source/docs/software/dashboards/smartdashboard/images/choosing-an-autonomous-program-from-smartdashboard/smartdashboard-display.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/add-command-button.png b/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/add-command-button.png index fe47a2c25f..ecf9c858e7 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/add-command-button.png and b/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/add-command-button.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/command-system-displays.png b/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/command-system-displays.png index c352410a08..854e7ca9cd 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/command-system-displays.png and b/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/command-system-displays.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/commands-running.png b/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/commands-running.png index 256109ea4f..46ba1034fa 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/commands-running.png and b/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/commands-running.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/display-subsystem.png b/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/display-subsystem.png index 2819f02013..3287da06eb 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/display-subsystem.png and b/source/docs/software/dashboards/smartdashboard/images/displaying-status-of-commands-and-subsystems/display-subsystem.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-connection.png b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-connection.png index 7854378347..49f87c0698 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-connection.png and b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-connection.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-driverstation.png b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-driverstation.png index b29bf956c2..3a866f0b17 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-driverstation.png and b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-driverstation.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-example.png b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-example.png index 33670384de..6d852f68f2 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-example.png and b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-example.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-networktables.png b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-networktables.png index a39503eede..a6e4b14b46 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-networktables.png and b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-networktables.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-save.png b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-save.png index a568fe982c..5ccc5b718c 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-save.png and b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-save.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-teamnumber.png b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-teamnumber.png index ca29cf5f08..1f386afb10 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-teamnumber.png and b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-intro/smartdashboard-teamnumber.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/data-values.png b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/data-values.png index abc5f08289..5734c6aca7 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/data-values.png and b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/data-values.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/livewindow-data-values.png b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/livewindow-data-values.png index 58efcc4519..67d3da3dfb 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/livewindow-data-values.png and b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/livewindow-data-values.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/view-smartdashboard.png b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/view-smartdashboard.png index 852b78cad7..d4c9c2beae 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/view-smartdashboard.png and b/source/docs/software/dashboards/smartdashboard/images/smartdashboard-namespace/view-smartdashboard.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/connection-indicator-connected.png b/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/connection-indicator-connected.png index 3ae1e08034..9cd0c27605 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/connection-indicator-connected.png and b/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/connection-indicator-connected.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/connection-indicator-disconnected.png b/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/connection-indicator-disconnected.png index b62878ae84..87c067cebf 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/connection-indicator-disconnected.png and b/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/connection-indicator-disconnected.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/outlineviewer.png b/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/outlineviewer.png index eb33e24ec9..4f5a998073 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/outlineviewer.png and b/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/outlineviewer.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/smartdashboard-output-sample-program.png b/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/smartdashboard-output-sample-program.png index 8ff3e8a4b9..bdbd299aef 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/smartdashboard-output-sample-program.png and b/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/smartdashboard-output-sample-program.png differ diff --git a/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/verifying-ip-address.png b/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/verifying-ip-address.png index e360aeca9d..4c164688d3 100644 Binary files a/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/verifying-ip-address.png and b/source/docs/software/dashboards/smartdashboard/images/verifying-smartdashboard-is-working/verifying-ip-address.png differ diff --git a/source/docs/software/dashboards/smartdashboard/index.rst b/source/docs/software/dashboards/smartdashboard/index.rst index 4160a4e285..97bb4586cc 100644 --- a/source/docs/software/dashboards/smartdashboard/index.rst +++ b/source/docs/software/dashboards/smartdashboard/index.rst @@ -1,5 +1,4 @@ -SmartDashboard -============== +# SmartDashboard SmartDashboard is a simple and efficient dashboard that uses relatively few computer resources. It does not have the fancy look or some of the features Shuffleboard has, but it displays network tables data with a variety of widgets without bogging down the driver station computer. diff --git a/source/docs/software/dashboards/smartdashboard/smartdashboard-intro.rst b/source/docs/software/dashboards/smartdashboard/smartdashboard-intro.rst index e26b80cfda..ad8533e932 100644 --- a/source/docs/software/dashboards/smartdashboard/smartdashboard-intro.rst +++ b/source/docs/software/dashboards/smartdashboard/smartdashboard-intro.rst @@ -1,7 +1,6 @@ .. include:: -SmartDashboard Introduction -=========================== +# SmartDashboard Introduction .. image:: images/smartdashboard-intro/smartdashboard-example.png :alt: An example of what the SmartDashboard UI can look like. @@ -15,16 +14,14 @@ The SmartDashboard is a Java program that will display robot data in real time. The displayed data is automatically formatted in real-time as the data is sent from the robot, but you can change the format or the display widget types and then save the new screen layouts to be used again later. And with all these options, it is still extremely simple to use. To display some data on the dashboard, simply call one of the SmartDashboard methods with the data and its name and the value will automatically appear on the dashboard screen. -Installing the SmartDashboard ------------------------------ +## Installing the SmartDashboard .. image:: images/smartdashboard-intro/smartdashboard-driverstation.png :alt: Using the 3rd tab of the Driver Station to select SmartDashboard from the Dashboard Type dropdown so that it will be launched. The SmartDashboard is packaged with the WPILib Installer and can be launched directly from the Driver Station by selecting the **SmartDashboard** button on the Setup tab. -Configuring the Team Number ---------------------------- +## Configuring the Team Number .. image:: images/smartdashboard-intro/smartdashboard-teamnumber.png :alt: Checking the "Team Number" property in the Preferences dialog box. @@ -33,8 +30,7 @@ The first time you launch the SmartDashboard you should be prompted for your tea .. note:: SmartDashboard will take a moment to configure itself for the team number, do not be alarmed. -Setting a Custom NetworkTables Server Location -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Setting a Custom NetworkTables Server Location By default, SmartDashboard will look for NetworkTables instances running on a connected RoboRIO, but it's sometimes useful to look for NetworkTables at a different IP address. To connect to SmartDashboard from a host other than the roboRIO, open SmartDashboard preferences under the ``File`` menu and in the ``Team Number`` field, enter the IP address or hostname of the NetworkTables host. @@ -43,23 +39,20 @@ This option is incredibly useful for using SmartDashboard with :doc:`WPILib simu .. image:: images/smartdashboard-intro/smartdashboard-networktables.png :alt: Using localhost to connect to a robot simulation. -Locating the Save File ----------------------- +## Locating the Save File .. image:: images/smartdashboard-intro/smartdashboard-save.png :alt: In Preferences the "Save File" key shows the location of the save file. Users may wish to customize the save location of the SmartDashboard. To do this click the box next to **Save File** then browse to the folder where you would like to save the configuration. Files saved in the installation directories for the WPILib components will likely be overwritten on updates to the tools. -Adding a Connection Indicator ------------------------------ +## Adding a Connection Indicator .. image:: images/smartdashboard-intro/smartdashboard-connection.png :alt: Select View->Add...->Connection Indicator to show when SmartDashboard is connected. It is often helpful to see if the SmartDashboard is connected to the robot. To add a connection indicator, select **View > Add > Connection Indicator**. This indicator will be red when disconnected and green when connected. To move or resize this indicator, select **View > Editable** to toggle the SmartDashboard into editable mode, then drag the center of the indicator to move it or the edges to resize. Select the **Editable** item again to lock it in place. -Adding Widgets to the SmartDashboard ------------------------------------- +## Adding Widgets to the SmartDashboard Widgets are automatically added to the SmartDashboard for each "key" sent by the robot code. For instructions on adding robot code to write to the SmartDashboard see :doc:`Displaying Expressions from Within the Robot Program `. diff --git a/source/docs/software/dashboards/smartdashboard/smartdashboard-namespace.rst b/source/docs/software/dashboards/smartdashboard/smartdashboard-namespace.rst index 16a231d46e..be06aee79f 100644 --- a/source/docs/software/dashboards/smartdashboard/smartdashboard-namespace.rst +++ b/source/docs/software/dashboards/smartdashboard/smartdashboard-namespace.rst @@ -1,5 +1,4 @@ -SmartDashboard Namespace -======================== +# SmartDashboard Namespace SmartDashboard uses NetworkTables to send data between the robot and the Dashboard (Driver Station) computer. NetworkTables sends data as name, value pairs, like a distributed hashtable between the robot and the computer. When a value is changed in one place, its value is automatically updated in the other place. This mechanism and a standard set of name (keys) is how data is displayed on the SmartDashboard. @@ -7,70 +6,59 @@ There is a hierarchical structure in the name space creating a set of tables and For informational purposes, the names and values can be displayed using the OutlineViewer application that is installed in the same location as the SmartDashboard. It will display all the NetworkTables keys and values as they are updated. -SmartDashboard Data Values --------------------------- +## SmartDashboard Data Values .. image:: images/smartdashboard-namespace/data-values.png :alt: The SmartDashboard keys in NetworkTables always begin with "/SmartDashboard/*" SmartDashboard values are created with key names that begin with ``SmartDashboard/``. The above values viewed with OutlineViewer correspond to data put to the SmartDashboard with the following statements: -.. code-block:: java - - chooser = new SendableChooser(); - chooser.setDefaultOption("defaultAuto", new AutonomousCommand()); - chooser.addOption("secondAuto", new AutonomousCommand()); - chooser.addOption("thirdAuto", new AutonomousCommand()); - SmartDashboard.putData("Chooser", chooser); - SmartDashboard.putNumber("Arm position in degrees", 52.0); - SmartDashboard.putString("Program Version", "V1.2"); +```java +chooser = new SendableChooser(); +chooser.setDefaultOption("defaultAuto", new AutonomousCommand()); +chooser.addOption("secondAuto", new AutonomousCommand()); +chooser.addOption("thirdAuto", new AutonomousCommand()); +SmartDashboard.putData("Chooser", chooser); +SmartDashboard.putNumber("Arm position in degrees", 52.0); +SmartDashboard.putString("Program Version", "V1.2"); +``` The ``Arm position`` is created with the ``putNumber()`` call. The ``AutonomousCommand`` is written with a ``putData("Autonomous Command", command)`` that is not shown in the above code fragment. The chooser is created as a ``SendableChooser`` object and the string value, ``Program Version`` is created with the ``putString()`` call. -View of SmartDashboard ----------------------- +## View of SmartDashboard .. image:: images/smartdashboard-namespace/view-smartdashboard.png :alt: SmartDashboard display of the values generated in the code above. The code from the previous step generates the table values as shown and the SmartDashboard display as shown here. The numbers correspond to the NetworkTables variables shown in the previous step. -LiveWindow Data Values ----------------------- +## LiveWindow Data Values .. image:: images/smartdashboard-namespace/livewindow-data-values.png :alt: Viewing all of the LiveWindow data in SmartDashboard when in Test mode. LiveWindow data is automatically grouped by subsystem. The data is viewable in the SmartDashboard when the robot is in Test mode (set on the Driver Station). If you are not writing a command based program, you can still cause sensors and actuators to be grouped for easy viewing by specifying the subsystem name. In the above display you can see the key names and the resultant output in Test mode on the SmartDashboard. All the strings start with ``/LiveWindow`` then the Subsystem name, then a group of values that are used to display each element. The code that generates this LiveWindow display is shown below: -.. code-block:: java - - drivetrainLeft = new PWMVictorSPX(1); - drivetrainLeft.setName("Drive train", "Left"); - - drivetrainRight = new PWMVictorSPX(1); - drivetrainRight.setName("Drive train", "Right"; - - drivetrainRobotDrive = new DifferentialDrive(drivetrainLeft, drivetrainRight); - drivetrainRobotDrive.setSafetyEnabled(false); - drivetrainRobotDrive.setExpiration(0.1); - - drivetrainUltrasonic = new AnalogInput(3); - drivetrainUltrasonic.setName("Drive train", "Ultrasonic"); - - elevatorMotor = new PWMVictorSPX(6); - elevatorMotor.setName("Elevator", "Motor"); - - elevatorPot = new AnalogInput(4); - elevatorPot.setName("Elevator", "Pot"); - - wristPot = new AnalogInput(2); - wristPot.setName("Wrist", "Pot"); - - wristMotor = new PWMVictorSPX(3); - wristMotor.setName("Wrist", "Motor"); - - clawMotor = new PWMVictorSPX(5); - clawMotor.setName("Claw", "Motor"); +```java +drivetrainLeft = new PWMVictorSPX(1); +drivetrainLeft.setName("Drive train", "Left"); +drivetrainRight = new PWMVictorSPX(1); +drivetrainRight.setName("Drive train", "Right"; +drivetrainRobotDrive = new DifferentialDrive(drivetrainLeft, drivetrainRight); +drivetrainRobotDrive.setSafetyEnabled(false); +drivetrainRobotDrive.setExpiration(0.1); +drivetrainUltrasonic = new AnalogInput(3); +drivetrainUltrasonic.setName("Drive train", "Ultrasonic"); +elevatorMotor = new PWMVictorSPX(6); +elevatorMotor.setName("Elevator", "Motor"); +elevatorPot = new AnalogInput(4); +elevatorPot.setName("Elevator", "Pot"); +wristPot = new AnalogInput(2); +wristPot.setName("Wrist", "Pot"); +wristMotor = new PWMVictorSPX(3); +wristMotor.setName("Wrist", "Motor"); +clawMotor = new PWMVictorSPX(5); +clawMotor.setName("Claw", "Motor"); +``` Values that correspond to actuators are not only displayed, but can be set using sliders created in the SmartDashboard in Test mode. diff --git a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/displaying-LiveWindow-values.rst b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/displaying-LiveWindow-values.rst index 79fded01a1..4ece393c33 100644 --- a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/displaying-LiveWindow-values.rst +++ b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/displaying-LiveWindow-values.rst @@ -1,65 +1,76 @@ -Displaying LiveWindow Values -============================ +# Displaying LiveWindow Values LiveWindow will automatically add your sensors and actuators for you. There is no need to do it manually. LiveWindow values may also be displayed by writing the code yourself and adding it to your robot program. This allows you to customize the names and group them in subsystems. This is a convenient method of displaying whether they are actual command based program subsystems or just a grouping that you decide to use in your program. -Adding the Necessary Code to your Program ------------------------------------------ +## Adding the Necessary Code to your Program For each sensor or actuator that is created, set the subsystem name and display name by calling ``setName`` (``SetName`` in C++). When the SmartDashboard is put into LiveWindow mode, it will display the sensors and actuators. .. tab-set-code:: - .. code-block:: java - - Ultrasonic ultrasonic = new Ultrasonic(1, 2); - SendableRegistry.setName(ultrasonic, "Arm", "Ultrasonic"); - - Jaguar elbow = new Jaguar(1); - SendableRegistry.setName(elbow, "Arm", "Elbow"); - - Victor wrist = new Victor(2); - SendableRegistry.setName(wrist, "Arm", "Wrist"); - - .. code-block:: cpp - - frc::Ultrasonic ultrasonic{1, 2}; - SendableRegistry::SetName(ultrasonic, "Arm", "Ultrasonic"); - - frc::Jaguar elbow{1}; - SendableRegistry::SetName(elbow, "Arm", "Elbow"); - - frc::Victor wrist{2}; - SendableRegistry::SetName(wrist, "Arm", "Wrist"); + ```java + Ultrasonic ultrasonic = new Ultrasonic(1, 2); + SendableRegistry.setName(ultrasonic, "Arm", "Ultrasonic"); + Jaguar elbow = new Jaguar(1); + SendableRegistry.setName(elbow, "Arm", "Elbow"); + Victor wrist = new Victor(2); + SendableRegistry.setName(wrist, "Arm", "Wrist"); + ``` + + ```c++ + frc::Ultrasonic ultrasonic{1, 2}; + SendableRegistry::SetName(ultrasonic, "Arm", "Ultrasonic"); + frc::Jaguar elbow{1}; + SendableRegistry::SetName(elbow, "Arm", "Elbow"); + frc::Victor wrist{2}; + SendableRegistry::SetName(wrist, "Arm", "Wrist"); + ``` + + ```python + from wpilib import Jaguar, Ultrasonic, Victor + from wpiutil import SendableRegistry + ultrasonic = Ultrasonic(1, 2) + SendableRegistry.setName(ultrasonic, "Arm", "Ultrasonic") + elbow = Jaguar(1) + SendableRegistry.setName(elbow, "Arm", "Elbow") + wrist = Victor(2) + SendableRegistry.setName(wrist, "Arm", "Wrist") + ``` If your objects are in a ``Subsystem``, this can be simplified using the addChild method of ``SubsystemBase`` .. tab-set-code:: - .. code-block:: java - - Ultrasonic ultrasonic = new Ultrasonic(1, 2); - addChild("Ultrasonic", ultrasonic); - - Jaguar elbow = new Jaguar(1); - addChild("Elbow", elbow); - - Victor wrist = new Victor(2); - addChild("Wrist", wrist); - - .. code-block:: cpp - - frc::Ultrasonic ultrasonic{1, 2}; - AddChild("Ultrasonic", ultrasonic); - - frc::Jaguar elbow{1}; - AddChild("Elbow", elbow); - - frc::Victor wrist{2}; - AddChild("Wrist", wrist); - -Viewing the Display in SmartDashboard ------------------------------------------ + ```java + Ultrasonic ultrasonic = new Ultrasonic(1, 2); + addChild("Ultrasonic", ultrasonic); + Jaguar elbow = new Jaguar(1); + addChild("Elbow", elbow); + Victor wrist = new Victor(2); + addChild("Wrist", wrist); + ``` + + ```c++ + frc::Ultrasonic ultrasonic{1, 2}; + AddChild("Ultrasonic", ultrasonic); + frc::Jaguar elbow{1}; + AddChild("Elbow", elbow); + frc::Victor wrist{2}; + AddChild("Wrist", wrist); + ``` + + ```python + from wpilib import Jaguar, Ultrasonic, Victor + from commands2 import SubsystemBase + ultrasonic = Ultrasonic(1, 2) + SubsystemBase.addChild("Ultrasonic", ultrasonic) + elbow = Jaguar(1) + SubsystemBase.addChild("Elbow", elbow) + wrist = Victor(2) + SubsystemBase.addChild("Wrist", wrist) + ``` + +## Viewing the Display in SmartDashboard .. image:: images/displaying-LiveWindow-values/view-display.png :alt: Modifying the components of a subsystem in SmartDashboard. diff --git a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/enabling-test-mode.rst b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/enabling-test-mode.rst index e4187ceb0c..6477f9ae46 100644 --- a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/enabling-test-mode.rst +++ b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/enabling-test-mode.rst @@ -1,54 +1,80 @@ -Enabling Test mode (LiveWindow) -=============================== +# Enabling Test mode (LiveWindow) -You may add code to your program to display values for your sensors and actuators while the robot is in Test mode. This can be selected from the Driver Station whenever the robot is not on the field. The code to display these values is automatically generated by RobotBuilder and is described in the next article. Test mode is designed to verify the correct operation of the sensors and actuators on a robot. In addition it can be used for obtaining setpoints from sensors such as potentiometers and for tuning PID loops in your code. +You may add code to your program to display values for your sensors and actuators while the robot is in Test mode. This can be selected from the Driver Station whenever the robot is not on the field (see :ref:`docs/software/basic-programming/using-test-mode:Enabling Test Mode` for details on how to do this). Test mode is designed to verify the correct operation of the sensors and actuators on a robot. In addition it can be used for obtaining setpoints from sensors such as potentiometers and for tuning PID loops in your code. When the robot is enabled in Test mode, the SmartDashboard display will switch to test mode (LiveWindow) and will display the status of any actuators and sensors used by your program. -Setting Test mode with the Driver Station ------------------------------------------ -.. image:: images/enabling-test-mode/setting-test-mode-driver-station.png - :alt: Selecting the "Test" button on the Driver Station and then "Enable". +.. important:: Since 2024, LiveWindow is not enabled by default in Test mode! -Enable Test Mode in the Driver Station by clicking on the "Test" button and setting "Enable" on the robot. When doing this, the SmartDashboard display will switch to test mode (LiveWindow) and will display the status of any actuators and sensors used by your program. +## Enabling LiveWindow in Test Mode -Explicitly vs. implicit test mode display ------------------------------------------ +To run LiveWindow in Test Mode, the following code is needed in the ``Robot`` class: .. tab-set-code:: - .. code-block:: java + ```java + @Override + public void robotInit() { + enableLiveWindowInTest(true); + } + ``` - PWMSparkMax leftDrive; - PWMSparkMax rightDrive; - PWMVictorSPX arm; - BuiltInAccelerometer accel; + ```c++ + void Robot::RobotInit() { + EnableLiveWindowInTest(true); + } + ``` - @Override - public void robotInit() { - leftDrive = new PWMSparkMax(0); - rightDrive = new PWMSparkMax(1); - arm = new PWMVictorSPX(2); - accel = new BuiltInAccelerometer(); - SendableRegistry.setName(arm, "SomeSubsystem", "Arm"); - SendableRegistry.setName(accel, "SomeSubsystem", "Accelerometer"); - } + ```python + def robotInit(self) -> None: + enableLiveWindowInTest(true) + ``` - .. code-block:: cpp +## Explicitly vs. implicit test mode display - frc::PWMSparkMax leftDrive{0}; - frc::PWMSparkMax rigthDrive{1}; - frc::BuiltInAccelerometer accel{}; - frc::PWMVictorSPX arm{3}; +.. tab-set-code:: - void Robot::RobotInit() { - wpi::SendableRegistry::SetName(&arm, "SomeSubsystem", "Arm"); - wpi::SendableRegistry::SetName(&accel, "SomeSubsystem", "Accelerometer"); - } + ```java + PWMSparkMax leftDrive; + PWMSparkMax rightDrive; + PWMVictorSPX arm; + BuiltInAccelerometer accel; + @Override + public void robotInit() { + leftDrive = new PWMSparkMax(0); + rightDrive = new PWMSparkMax(1); + arm = new PWMVictorSPX(2); + accel = new BuiltInAccelerometer(); + SendableRegistry.setName(arm, "SomeSubsystem", "Arm"); + SendableRegistry.setName(accel, "SomeSubsystem", "Accelerometer"); + } + ``` + + ```c++ + frc::PWMSparkMax leftDrive{0}; + frc::PWMSparkMax rigthDrive{1}; + frc::BuiltInAccelerometer accel{}; + frc::PWMVictorSPX arm{3}; + void Robot::RobotInit() { + wpi::SendableRegistry::SetName(&arm, "SomeSubsystem", "Arm"); + wpi::SendableRegistry::SetName(&accel, "SomeSubsystem", "Accelerometer"); + } + ``` + + ```python + from wpilib import BuiltInAccelerometer, PWMSparkMax, PWMVictorSPX + from wpiutil import SendableRegistry + def robotInit(self) -> None: + leftDrive = PWMSparkMax(0) + rightDrive = PWMSparkMax(1) + arm = PWMVictorSPX(2) + accel = BuiltInAccelerometer() + SendableRegistry.setName(arm, "SomeSubsystem", "Arm") + SendableRegistry.setName(accel, "SomeSubsystem", "Accelerometer") + ``` All sensors and actuators will automatically be displayed on the SmartDashboard in test mode and will be named using the object type (such as PWMSparkMax, PWMVictorSPX, BuiltInAccelerometer, etc.) with channel number with which the object was created. In addition, the program can explicitly add sensors and actuators to the test mode display, in which case programmer-defined subsystem and object names can be specified making the program clearer. This example illustrates explicitly defining those sensors and actuators. -Understanding what is displayed in Test mode --------------------------------------------- +## Understanding what is displayed in Test mode .. image:: images/enabling-test-mode/test-mode-display.png :alt: Highlights both ungrouped and subsystem motors displayed in test mode. diff --git a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/finding-setpoint-values.png b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/finding-setpoint-values.png index f8fdf89387..55a94ccae3 100644 Binary files a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/finding-setpoint-values.png and b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/finding-setpoint-values.png differ diff --git a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/tuning-pid-controller.png b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/tuning-pid-controller.png index 6431ffbe4a..a8954936cf 100644 Binary files a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/tuning-pid-controller.png and b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/tuning-pid-controller.png differ diff --git a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/viewing-pid-controller.png b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/viewing-pid-controller.png index f15310dba5..bd0a594fda 100644 Binary files a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/viewing-pid-controller.png and b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/PID-tuning-with-SmartDashboard/viewing-pid-controller.png differ diff --git a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/displaying-LiveWindow-values/view-display.png b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/displaying-LiveWindow-values/view-display.png index a8a399c576..f1782a5fd1 100644 Binary files a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/displaying-LiveWindow-values/view-display.png and b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/displaying-LiveWindow-values/view-display.png differ diff --git a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/enabling-test-mode/setting-test-mode-driver-station.png b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/enabling-test-mode/setting-test-mode-driver-station.png index 02abb2c1cf..06d23f2716 100644 Binary files a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/enabling-test-mode/setting-test-mode-driver-station.png and b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/enabling-test-mode/setting-test-mode-driver-station.png differ diff --git a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/enabling-test-mode/test-mode-display.png b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/enabling-test-mode/test-mode-display.png index 6f9d1c36dd..afdfb2ec32 100644 Binary files a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/enabling-test-mode/test-mode-display.png and b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/images/enabling-test-mode/test-mode-display.png differ diff --git a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/index.rst b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/index.rst index f6140826ab..5c25f2e7ce 100644 --- a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/index.rst +++ b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/index.rst @@ -1,5 +1,4 @@ -SmartDashboard: Test Mode and Live Window -========================================= +# SmartDashboard: Test Mode and Live Window .. toctree:: :maxdepth: 1 diff --git a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/pid-tuning-with-smartdashboard.rst b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/pid-tuning-with-smartdashboard.rst index 9b6fa54421..eec6b238f9 100644 --- a/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/pid-tuning-with-smartdashboard.rst +++ b/source/docs/software/dashboards/smartdashboard/test-mode-and-live-window/pid-tuning-with-smartdashboard.rst @@ -1,28 +1,24 @@ -PID Tuning with SmartDashboard -============================== +# PID Tuning with SmartDashboard The PID (Proportional, Integral, Differential) is an algorithm for determining the motor speed based on sensor feedback to reach a setpoint as quickly as possible. For example, a robot with an elevator that moves to a predetermined position should move there as fast as possible then stop without excessive overshoot leading to oscillation. Getting the PID controller to behave this way is called "tuning". The idea is to compute an error value that is the difference between the current value of the mechanism feedback element and the desired (setpoint) value. In the case of the arm, there might be a potentiometer connected to an analog channel that provides a voltage that is proportional to the position of the arm. The desired value is the voltage that is predetermined for the position the arm should move to, and the current value is the voltage for the actual position of the arm. -Finding the setpoint values with LiveWindow -------------------------------------------- +## Finding the setpoint values with LiveWindow .. image:: images/PID-tuning-with-SmartDashboard/finding-setpoint-values.png :alt: Setting a motor's speed with LiveWindow. Create a PID Subsystem for each mechanism with feedback. The PID Subsystems contain the actuator (motor) and the feedback sensor (potentiometer in this case). You can use Test mode to display the subsystem sensors and actuators. Using the slider manually adjust the actuator to each desired position. Note the sensor values (2) for each of the desired positions. These will become the setpoints for the PID controller. -Viewing the PIDController in LiveWindow ---------------------------------------- +## Viewing the PIDController in LiveWindow .. image:: images/PID-tuning-with-SmartDashboard/viewing-pid-controller.png :alt: Controlling a PIDController in LiveWindow. In Test mode, the PID Subsystems display their P, I, and D parameters that are set in the code. The P, I, and D values are the weights applied to the computed error (P), sum of errors over time (I), and the rate of change of errors (D). Each of those terms is multiplied by the weights and added together to form the motor value. Choosing the optimal P, I, and D values can be difficult and requires some amount of experimentation. The Test mode on the robot allows the values to be modified, and the mechanism response observed. -.. important:: The enable option does not affect the `PIDController `__ introduced in 2020, as the controller is updated every robot loop. See the example :ref:`here ` on how to retain this functionality. +.. important:: The enable option does not affect the [PIDController](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/PIDController.html) introduced in 2020, as the controller is updated every robot loop. See the example :ref:`here ` on how to retain this functionality. -Tuning the PIDController ------------------------- +## Tuning the PIDController .. image:: images/PID-tuning-with-SmartDashboard/tuning-pid-controller.png :alt: Using the PIDController to tune the control. diff --git a/source/docs/software/dashboards/smartdashboard/verifying-smartdashboard-is-working.rst b/source/docs/software/dashboards/smartdashboard/verifying-smartdashboard-is-working.rst index 1271744dff..eee94956a5 100644 --- a/source/docs/software/dashboards/smartdashboard/verifying-smartdashboard-is-working.rst +++ b/source/docs/software/dashboards/smartdashboard/verifying-smartdashboard-is-working.rst @@ -1,8 +1,6 @@ -Verifying SmartDashboard is working -=================================== +# Verifying SmartDashboard is working -Connection Indicator --------------------- +## Connection Indicator SmartDashboard will automatically include the connection status and IP address of the NetworkTables source in the title of the window. @@ -14,63 +12,59 @@ SmartDashboard will automatically include the connection status and IP address o :alt: SmartDashboard connected and showing the IP address. :width: 350 -Connection Indicator Widget ---------------------------- +## Connection Indicator Widget SmartDashboard includes a connection indicator widget which will turn red or green depending on the connection to NetworkTables, usually provided by the roboRIO. For instructions to add this widget, look at :ref:`Adding a Connection Indicator ` in the SmartDashboard Intro. -Robot Program Example ---------------------- +## Robot Program Example .. tab-set-code:: - .. code-block:: java - - public class Robot extends TimedRobot { - double counter = 0.0; - - public void teleopPeriodic() { - SmartDashboard.putNumber("Counter", counter++); - } - } - - .. code-block:: c++ - - #include "Robot.h" - float counter = 0.0; - - void Robot::TeleopPeriodic() { - frc::SmartDashboard::PutNumber("Counter", counter++); + ```java + public class Robot extends TimedRobot { + double counter = 0.0; + public void teleopPeriodic() { + SmartDashboard.putNumber("Counter", counter++); } + } + ``` + + ```c++ + #include "Robot.h" + float counter = 0.0; + void Robot::TeleopPeriodic() { + frc::SmartDashboard::PutNumber("Counter", counter++); + } + ``` + + ```python + from wpilib import SmartDashboard + self.counter = 0.0 + def teleopPeriodic(self) -> None: + SmartDashboard.putNumber("Counter", self.counter += 1) + ``` This is a minimal robot program that writes a value to the SmartDashboard. It simply increments a counter 50 times per second to verify that the connection is working. However, to minimize bandwidth usage, NetworkTables by default will throttle the updates to 10 times per second. -SmartDashboard Output for the Sample Program --------------------------------------------- +## SmartDashboard Output for the Sample Program .. image:: images/verifying-smartdashboard-is-working/smartdashboard-output-sample-program.png :alt: SmartDashboard showing the output of "counter" set up in the code above. The SmartDashboard display should look like this after about 6 seconds of the robot being enabled in Teleop mode. If it doesn't, then you need to check that the connection is correctly set up. -Verifying the IP address in SmartDashboard ------------------------------------------- +## Verifying the IP address in SmartDashboard .. image:: images/verifying-smartdashboard-is-working/verifying-ip-address.png :alt: Checking the "Team Number" property in the Preferences dialog box. If the display of the value is not appearing, verify that the team number is correctly set as shown in this picture. The preferences dialog can be viewed by selecting ``File``, then ``Preferences``. -Verifying Program using OutlineViewer -------------------------------------- - -You can verify that the robot program is generating SmartDashboard values by using the OutlineViewer program. This is a Java program, ``OutlineViewer.jar``, that is located in ``~/wpilib/YYYY/tools`` (where YYYY is the year and ~ is ``C:\Users\Public`` on Windows). - -OutlineViewer is downloaded as part of the WPILib Offline Installer. For more information, see the :ref:`Windows/macOS/Linux installation guides `. In Visual Studio Code, press :kbd:`Ctrl+Shift+P` and type "WPILib" or click the WPILib logo in the top right to launch the WPILib Command Palette. Select :guilabel:`Start Tool`, and then select :guilabel:`OutlineViewer`. +## Verifying Program using OutlineViewer -In the "Server Location" box, enter your team number with no leading zeroes. Then, click ``Start``. +You can verify that the robot program is generating SmartDashboard values by using the :doc:`OutlineViewer program `. -Look at the second row in the table, the value ``SmartDashboard/Counter`` is the variable written to the SmartDashboard via NetworkTables. As the program runs you should see the value increasing (``41.0`` in this case). If you don't see this variable in the OutlineViewer, look for something wrong with the robot program or the network configuration. +Expand the SmartDashboard row. The value ``Counter`` is the variable written to the SmartDashboard via NetworkTables. As the program runs you should see the value increasing (``1398.0`` in this case). If you don't see this variable in the OutlineViewer, look for something wrong with the robot program or the network configuration. .. image:: /docs/software/wpilib-tools/outlineviewer/images/outlineviewer.png :alt: Using OutlineViewer to view the NetworkTables data used by the program. diff --git a/source/docs/software/dashboards/troubleshooting-dashboard-connectivity.rst b/source/docs/software/dashboards/troubleshooting-dashboard-connectivity.rst new file mode 100644 index 0000000000..11e4b3fcc0 --- /dev/null +++ b/source/docs/software/dashboards/troubleshooting-dashboard-connectivity.rst @@ -0,0 +1,57 @@ +# Troubleshooting Dashboard Connectivity + +This document will help explain how to recognize if the Dashboard is not connected to your robot, steps to troubleshoot this condition and a code modification you can make. + +## Recognizing LabVIEW Dashboard Connectivity + +.. image:: images/troubleshooting-dashboard-connectivity/labview-dashboard-connection.png + :alt: NT Connection LED on LabVIEW Dashboard front panel + +The LabVIEW Dashboard has a NetworkTables Connection indicator on the front panel. + +.. image:: images/troubleshooting-dashboard-connectivity/black-diamonds.png + :alt: Highlights black diamonds next to the variables indicating they are not synced to the robot. + +On the Variables tab of the Dashboard, the variables are shown with a black diamond when they are not synced with the robot. Once the Dashboard connects to the robot and these variables are synced, the diamond will disappear. + +## Recognizing SmartDashboard Connectivity +.. image:: images/troubleshooting-dashboard-connectivity/smartdashboard-connection.png + :alt: The titlebar of SmartDashboard showing a connection to IP 172.22.11.2 + +SmartDashboard indicates if it is connected or not in the title bar. It shows the IP address it is connected to. See :ref:`this page ` for more on configuring the connection. + +.. image:: images/troubleshooting-dashboard-connectivity/connection-indicator.png + :alt: Click "View" then "Add..." then Connection indicator to place one on the SmartDashboard. + +For more visibility, you can also add a Connection Indicator widget. The connection indicator can be moved or re-sized if the Editable checkbox is checked. + +## Recognizing Shuffleboard Connectivity + +.. image:: images/troubleshooting-dashboard-connectivity/shuffleboard-connection.png + :alt: The bottom bar of Shuffleboard showing no connection + +Shuffleboard indicates if it is connected or not in the bottom right corner of the application as shown in the image above. See :ref:`page ` for more on configuring the connection. + +## Recognizing Glass Connectivity + +.. image:: images/troubleshooting-dashboard-connectivity/glass-connection.png + :alt: The titlebar of Glass showing a connection to 127.0.0.1 + +Glass indicates if it is connected or not in the title bar. It shows the IP address it is connected to. See this :ref:`page ` for more on configuring the connection. + +## Recognizing AdvantageScope Connectivity + +.. image:: images/troubleshooting-dashboard-connectivity/advantagescope-connection.png + :alt: The titlebar of AdvantageScope showing a connection to 172.22.11.2 + +AdvantageScope indicates if it is connected or not in the title bar. It shows the IP address it is connected to, or else the IP address it is attempting to connect to. See the [AdvantageScope Documentation](https://docs.advantagescope.org/getting-started/open-live) for more on configuring the connection. + +## Troubleshooting Connectivity + +If the Dashboard does not connect to the Robot (after the Driver Station has connected to the robot) the recommended troubleshooting steps are: + +1. Restart the Dashboard (there is no need to restart the Driver Station software) + +2. If that doesn't work, restart the Robot Code using the Restart Robot Code button on the Diagnostics tab of the Driver Station + +3. If it still doesn't connect, verify that the Team Number / Server is set properly in the Dashboard and that your Robot Code writes a value during initialization or disabled diff --git a/source/docs/software/driverstation/driver-station-best-practices.rst b/source/docs/software/driverstation/driver-station-best-practices.rst index 236d5516a2..e52321e8b9 100644 --- a/source/docs/software/driverstation/driver-station-best-practices.rst +++ b/source/docs/software/driverstation/driver-station-best-practices.rst @@ -1,21 +1,22 @@ .. include:: -Driver Station Best Practices -============================= +# Driver Station Best Practices -This document was created by Steve Peterson, with contributions from Juan Chong, James Cole-Henry, Rick Kosbab, Greg McKaskle, Chris Picone, Chris Roadfeldt, Joe Ross, and Ryan Sjostrand. The original post and follow-up posts can be found `here `__. +This document was created by Steve Peterson, with contributions from Juan Chong, James Cole-Henry, Rick Kosbab, Greg McKaskle, Chris Picone, Chris Roadfeldt, Joe Ross, and Ryan Sjostrand. The original post and follow-up posts can be found [here](https://www.chiefdelphi.com/t/paper-driver-station-best-practices/164429). Want to ensure the driver station isn't a stopper for your team at the FIRST Robotics Competition (FRC) field? Building and configuring a solid driver station laptop is an easy project for the time between stop build day and your competition. Read on to find lessons learned by many teams over thousands of matches. -Prior To Departing For The Competition --------------------------------------- +## Prior To Departing For The Competition 1. Dedicate a laptop to be used solely as a driver station. Many teams do. A dedicated machine allows you manage the configuration for one goal – being ready to compete at the field. Dedicated means no other software except the FRC-provided Driver Station software and associated Dashboard installed or running. 2. Use a business-class laptop for your driver station. Why? They're much more durable than the $300 Black Friday special at Best Buy. They'll survive being banged around at the competition. Business-class laptops have higher quality device drivers, and the drivers are maintained for a longer period than consumer laptops. This makes your investment last longer. Lenovo ThinkPad T series and Dell Latitude are two popular business-class brands you'll commonly see at competitions. There are thousands for sale every day on eBay. The laptop provided in recent rookie kits is a good entry level machine. Teams often graduate from it to bigger displays as they do more with vision and dashboards. 3. Consider used laptops rather than new. The FRC\ |reg| Driver Station and dashboard software uses very few system resources, so you don't need to buy a new laptop -- instead, buy a cheap 4-5 year old used one. You might even get one donated by a used computer store in your area. + +.. note:: Before buying a used laptop ensure it is compatible with [Windows 11](https://support.microsoft.com/en-us/windows/windows-11-system-requirements-86c11283-ea52-4782-9efd-7674389a7ba3). For example, only Intel 8th generation core processors and later are compatible. + 4. Laptop recommended features - a. RAM -- 4GB of RAM + a. RAM -- 8GB of RAM or greater b. A display size of 13" or greater, with minimum resolution of 1440x1050. c. Ports @@ -25,8 +26,9 @@ Prior To Departing For The Competition iv. 2 USB ports minimum d. A keyboard. It’s hard to quickly do troubleshooting on touch-only computers at the field. - e. A solid-state disk (SSD). If the laptop has a rotating disk, spend $50 and replace it with a SSD. - f. Updated to the current release of Windows 10 or 11. Being the most common OS now seen at competitions, bugs are more likely to be found and fixed for Windows 10 and 11 than on older Windows versions. + e. A solid-state disk (SSD), 256 GB or larger. If the laptop has a rotating disk, spend $50 and replace it with a SSD. + f. Updated to the current release of Windows 10 or 11. + g. A laptop that supports Wi-Fi 6E (6 GHz) is recommended for use with the [Wi-Fi 6E radio](https://www.firstinspires.org/robotics/frc/blog/2023-technology-updates-past-present-future-and-beyond) for 2025 and later. 5. Install all Windows updates a week before the competition. This allows you time to ensure the updates will not interfere with driver station functions. To do so, open the Windows Update settings page and see that you're up-to-date. Install pending updates if not. Reboot and check again to make sure you’re up to date. 6. Change "Active Hours" for Windows Updates to prevent updates from installing during competition hours. Navigate to Start -> Settings -> Update & Security -> Windows Update, then select Change active hours. If you’re traveling to a competition, take time zone differences into account. This will help ensure your driver station does not reboot or fail due to update installing on the field. @@ -35,21 +37,21 @@ Prior To Departing For The Competition 9. Laptop battery / power a. Turn off Put the computer to sleep in your power plan for both battery and powered operation. - b. Turn off USB Selective Suspend: + b. Turn off Battery Saver and set Power Mode to Best Performance. + c. Turn off USB Selective Suspend: i. Right click on the battery/charging icon in the tray, then select Power Options. ii. Edit the plan settings of your power plan. iii. Click the Change advanced power settings link. iv. Scroll down in the advanced settings and disable the USB selective suspend setting for both Battery and Plugged in. - c. Ensure the laptop battery can hold a charge for at least an hour after making the changes above. This allows plenty of time for the robot and drive team to go through the queue and reach the alliance station without mains power. + d. Ensure the laptop battery can hold a charge for at least an hour after making the changes above. This allows plenty of time for the robot and drive team to go through the queue and reach the alliance station without mains power. 10. Bring a trusted USB and Ethernet cable for use connecting to the roboRIO. 11. Add retention/strain relief to prevent your joystick/gamepad controllers from falling on the floor and/or yanking on the USB ports. This helps prevent issues with intermittent controller connections. 12. The Windows user account you use to drive must be a member of the Administrator group. -At The Competition ------------------- +## At The Competition 1. Turn off Windows firewall using :ref:`these instructions `. 2. Turn off the Wi-Fi adapter, either using the dedicated hardware Wi-Fi switch or by disabling it in the Adapter Settings control panel. @@ -59,8 +61,7 @@ At The Competition 6. Limit web browsing to FRC related web sites. This minimizes the chance of getting malware during the competition. 7. Don't plan on using internet access to do software updates. There likely won't be any in the venue, and hotel Wi-Fi varies widely in quality. If you do need updates, contact a Control System Advisor in the pit. -Before Each Match ------------------ +## Before Each Match 1. Make sure the laptop is on and logged in prior to the end of the match before yours. 2. Close programs that aren’t needed during the match – e.g., Visual Studio Code or LabView – when you are competing. @@ -69,5 +70,8 @@ Before Each Match 5. Ensure joysticks and controllers are assigned to the correct USB ports. a. In the USB tab in the FRC Driver Station software, drag and drop to assign joysticks as needed. - b. Use the rescan button (F1) if joysticks / controllers do not appear green + b. Use the rescan button (F1) if joysticks / controllers do not appear green. c. Use the rescan button (F1) during competition if joystick or controllers become unplugged and then are plugged back in or otherwise turn gray during competition. + +6. Ensure your :doc:`Dashboard is connected to the robot ` after your driver station connects to the robot. +7. Ensure driver station is fully visible and in focus during the match to avoid Windows de-priortizing it, and to ease troubleshooting. diff --git a/source/docs/software/driverstation/driver-station-errors-warnings.rst b/source/docs/software/driverstation/driver-station-errors-warnings.rst index 9f2c0936d4..fe3112c444 100644 --- a/source/docs/software/driverstation/driver-station-errors-warnings.rst +++ b/source/docs/software/driverstation/driver-station-errors-warnings.rst @@ -1,76 +1,68 @@ -Driver Station Errors/Warnings -============================== +# Driver Station Errors/Warnings -In an effort to provide both Teams and Volunteers (FTAs/CSAs/etc.) more information to use when diagnosing robot problems, a number of Warning and Error messages have been added to the Driver Station. These messages are displayed in the DS diagnostics tab when they occur and are also included in the DS Log Files that can be viewed with the Log File Viewer. This document discusses the messages produced by the DS (messages produced by WPILib can also appear in this box and the DS Logs). +In an effort to provide both Teams and Volunteers (:term:`FTA` / :term:`CSA` / etc.) more information to use when diagnosing robot problems, a number of Warning and Error messages have been added to the Driver Station. These messages are displayed in the DS diagnostics tab when they occur and are also included in the DS Log Files that can be viewed with the Log File Viewer. This document discusses the messages produced by the DS (messages produced by WPILib can also appear in this box and the DS Logs). -Joystick Unplugged ------------------- +## Joystick Unplugged -.. code-block:: - - ERROR-44009 occurred at Driver Station -